Use DOTS NetCode for Collisions and Destroying Bullet Prefabs
Code and workflows to spawn ghosted bullets and update to server-side destruction
What you'll develop on this page

We will "turn" our bullet prefabs "into" ghosts and update the entity destruction flow so it is server-authoritative.
Github branch link: https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/tree/Updating-Bullets-and-Destruction
NetCode client-predicted model background
Entity spawningWhen the client side receives a new ghost, the ghost type is determined by a set of classification systems and then a spawn system spawns it. There is no specific spawn message, and when the client receives an unknown ghost ID, it counts as an implicit spawn.
Because the client interpolates snapshot data, Unity cannot spawn entities immediately, unless it was preemptively spawned, such as with spawn prediction. This is because the data is not ready for the client to interpolate it. Otherwise, the object would appear and then not get any more updates until the interpolation is ready.
Therefore normal spawns happen in a delayed manner. Spawning is split into three main types as follows:
Delayed or interpolated spawning. The entity is spawned when the interpolation system is ready to apply updates. This is how remote entities are handled, because they are interpolated in a straightforward manner.
Predicted spawning for the client predicted player object. The object is predicted so the input handling applies immediately. Therefore, it doesn't need to be delay spawned. While the snapshot data for this object arrives, the update system applies the data directly to the object and then plays back the local inputs which have happened since that time, and corrects mistakes in the prediction.
Predicted spawning for player spawned objects. These are objects that the player input spawns, like in-game bullets or rockets that the player fires.
Implement Predicted Spawning for player spawned objectsThe spawn code needs to run on the client, in the client prediction system. The spawn should use the predicted client version of the ghost prefab and add a PredictedGhostSpawnRequestComponent to it. Then, when the first snapshot update for the entity arrives it will apply to that predict spawned object (no new entity is created). After this, the snapshot updates are applied the same as in the predicted spawning for client predicted player object model. To create the prefab for predicted spawning, you should use the utility method GhostCollectionSystem.CreatePredictedSpawnPrefab.
You need to implement some specific code to handle the predicted spawning for player spawned objects. You need to create a system updating in the ClientSimulationSystemGroup after GhostSpawnClassificationSystem. The system needs to go through the GhostSpawnBuffer buffer stored on a singleton with a GhostSpawnQueueComponent. For each entry in that list it should compare to the entries in the PredictedGhostSpawn buffer on the singleton with a PredictedGhostSpawnList component. If the two entries are the same the classification system should set the PredictedSpawnEntity property in the GhostSpawnBuffer and remove the entry from GhostSpawnBuffer.
NetCode spawns entities on clients when there is a Prefab available for it. Pre spawned ghosts will work without any special consideration since they are referenced in a sub scene, but for manually spawned entities you must make sure that the prefabs exist on the client. You make sure that happens by having a component in a scene which references the prefab you want to spawn.
We are now on the third type of spawning listed above, "Predicted spawning for player spawned objects". Asteroids were "Delayed or interpolated spawning".
A fair amount of the workflow in this section is similar to spawning the Player prefab from the previous section because it is also predicted. We will have a BulletGhostSpawnClassificationSystem that is similar to PlayerGhostSpawnClassificationSystem. However, there will be a bit of a difference in workflow within BulletGhostSpawnClassificationSystem when it comes to identifying the client's bullets.
Updating our bullet spawn
Updating the Bullet Prefab
Navigate to the Bullet prefab
Add GhostAuthoringComponent
Name = Bullet
Importance = 200
Supported Ghost Mode = All
Default Ghost Mode = Owner Predicted
Optimization Mode = Dynamic
Check "Has Owner"

Updating to predicted bullet spawning
We are going to change our implementation of rate limiting from rate limiting on the client side, to rate limiting on the server side
This is more line with the authoritative model, the server should be in control of these limits
Paste the code snippet below into InputSystem.cs:

We need a way to store firing data on the player entity so the server can know if the client's firing system has "cooled down"
So we are going to repurpose our BulletSpawnOffsetComponent and rename it to PlayerStateAndOffsetComponent
We could make a new component for rate limiting but this would mean we would need to put 9 different components into our .ForEach() for our InputResponseSpawnSystem 😱
This is doable, but a little overkill for this gitbook
Paste the code snippet below into PlayerStateAndOffsetComponent.cs:

We also included a "State" field in the component that can be updated when the user is thrusting or firing
Although we won't be doing anything with "State" in this project, you are totally free to add something like change a client's color when it is thrusting or firing, if you want! To do this update the State value and create a client-only system that updates Player prefab meshes based on the State value
Just a thought!
Since we updated BulletOffsetComponent we will also need to update our SetBulletSpawnOffset which references it
We will not rename the SetBulletSpawnOffset system and can keep the system as the same name, because even though we are adding the PlayerStateAndOffsetComponent to the entity the primary purpose of this component is to set the bullet offset
So the name still works (kind of)
Paste the code snippet below into SetBulletSpawnOffset.cs:

Now let's re-add our SetBulletSpawnOffset system onto our Player prefab and drag the Bullet Spawn GameObject into the bulletSpawn field
This really isn't necessary, but sometimes updating components that are on prefabs causes errors so better to be safe than sorry

Now we need to create the system that will be responding to Commands that have to do with spawning
Similar to InputResponseMovementSystem, which responds to inputs for movement, we need to create InputResponseSpawnSystem (which responds to inputs for spawning)
Create InputResponseSpawnSystem in the Mixed/Systems folder
Paste the code snippet below into InputResponseSpawnSystem.cs:
We identify the bullet as a Predicted spawning prefab by adding "PredictedGhostSpawnRequestComponent"
This allows us to identify it in the BulletGhostSpawnClassificationSystem
The bulletOffset.WeaponCooldown may seem confusing
It makes sure that the Player can only fire every 5 server ticks
If you want to increase or decrease the rate of fire update
k_CoolDownTicksCount
Implement Predicted Spawning for player spawned objectsThe spawn code needs to run on the client, in the client prediction system. The spawn should use the predicted client version of the ghost prefab and add a PredictedGhostSpawnRequestComponent to it. Then, when the first snapshot update for the entity arrives it will apply to that predict spawned object (no new entity is created). After this, the snapshot updates are applied the same as in the predicted spawning for client predicted player object model. To create the prefab for predicted spawning, you should use the utility method GhostCollectionSystem.CreatePredictedSpawnPrefab.
You need to implement some specific code to handle the predicted spawning for player spawned objects. You need to create a system updating in the ClientSimulationSystemGroup after GhostSpawnClassificationSystem. The system needs to go through the GhostSpawnBuffer buffer stored on a singleton with a GhostSpawnQueueComponent. For each entry in that list it should compare to the entries in the PredictedGhostSpawn buffer on the singleton with a PredictedGhostSpawnList component. If the two entries are the same the classification system should set the PredictedSpawnEntity property in the GhostSpawnBuffer and remove the entry from GhostSpawnBuffer.
NetCode spawns entities on clients when there is a Prefab available for it. Pre spawned ghosts will work without any special consideration since they are referenced in a sub scene, but for manually spawned entities you must make sure that the prefabs exist on the client. You make sure that happens by having a component in a scene which references the prefab you want to spawn.

Now we must classify these predicted bullets with BulletGhostSpawnClassificationSystem
Create BulletGhostSpawnClassificationSystem in the Client/Systems folder
Paste the code snippet below into BulletGhostSpawnClassificationSystem.cs:
This code implements the steps described in the official Unity NetCode documentation
It's a bit weird to follow, but no need to sweat it; this is just what needs to be done with predicted spawning
If you have other predicted-spawning objects you want to include in your project (i.e. not bullets) just make sure to follow the same steps as in this system

Navigate to Multiplayer > PlayMode Tools and make sure "Num Thin Clients" is at 0 and save
Hit play, then hit spacebar to spawn bullets

Although we are able to spawn bullets at first, after a while errors appear, what gives?
This is because our client is destroying bullets in BulletAgeSystem
We know that only the server can make those kind of decisions, so we will fix that in the next section
Now for some housekeeping:
Move into Mixed/Components
BulletTag
BulletAgeComponent
BulletAuthoringComponent
PlayerStateAndOffsetComponent
Create a new folder in Scripts and Prefabs called "Authoring" (same level as Client/Mixed/Server/Multiplayer Setup)
Here we will start moving items that aren't necessary at runtime, just during authoring
Move SetBulletSpawnOffset into the "Authoring" folder
Move SetGameSettingsSystem into the "Authoring" folder
No gif here, we believe in you 💪
We can now predicted-spawn bullets
We updated our Bullet Prefab
We updated InputSystem and removed rate-limiting
We updated BulletOffsetComponent to PlayerStateAndOffsetComponent
Changed the added component in SetBulletSpawnOffset
Created InputResponseSpawnSystem
Created BulletGhostSpawnClassificationSystem
Updating destruction workflows
Bullet destruction
We will need to update our BulletAgeSystem to run only on the server
The server must be the authority on when entities are destroyed, not the client
Move BulletAgeSystem into the Server/Systems folder
Update BulletAgeSystem.cs by pasting in the code snippet below:

Player destruction
Currently we have PlayerDestructionSystem running on both the client and the server
Let's update this system so that it only runs on the server
We'll also set the NCE CommandTargetComponent's targetEntity back to being equal to null (how it was before we spawned a player). Think of this as a "clean up"
It needs to be set back to null because if not, the server will not respond to any more player spawn requests from that NCE
This is because the server checks if the NCE is null before it spawns a player in PlayerSpawnSystem (that is one of those 4 checks)
First move the PlayerDestructionSystem file to the Server/Systems folder
Paste the code snippet below into PlayerDestructionSystem.cs:

Hit play, shoot around, self-destruct, and re-spawn to check out the updates

You might notice that sometimes asteroids and players sometimes turn red, but do not get destroyed. What's up with that?? Doesn't changing the render mesh to red mean the bullet and the object collided?!
Remember that both the client and server are running ChangeMaterialAndDestroySystem
So at times a client might "predict" that a bullet collided with an object and change the render mesh of the collided object
But the server calculated that those two entities did not actually collide and so the server does not add the "DestroyTag" to the object, and therefore it does not get destroyed
The render mesh of the object is not ghosted
That means the value of the render mesh is not synchronized between the server and clients
So because the client predicted something would happen that did not actually happen, we are left with red objects that do not actually get destroyed
We could build a workflow that changes the render mesh back to the appropriate color if the server does not confirm the hit, but that's out of scope of this gitbook
If you want to do this yourself and you have a cool solution you're willing to share, please let us know in the Moetsi Discord
Now let's add 2 Thin Clients and make sure everything still functions with Thin Clients
Go to Multiplayer menu > PlayMode Tools > Type 2 in the Num Thin Clients field

Boy, do those guys zip around!
You will notice that based on the randomly-generated inputs in InputSystem the Thin Clients accelerate into their own bullets which causes them to get destroyed
This is actually useful for testing because they continue spawning as you continue to try getting shot and try shooting them. This helps ensure you that all workflows are functioning
Final housekeeping in this section:
Move ChangeMaterialAndDestroySystem to Mixed/Systems
Move StatefulCollisionEventBufferAuthoring to Authoring
Move StatefulTriggerEventBufferAuthoring to Authoring
Move StatefulTriggerEventBufferSystem to Mixed/Systems
Move StatefulCollisionEventBufferSystem to Mixed/Systems
Move to Mixed/Components
IStatefulSimulationEvent
StatefulCollisionEvent
StatefulSimulationEventBuffers
StatefulTriggerEvent
No gif here, we believe in you 💪
We now are able to destroy bullets and players in NetCode
We updated BulletAgeSystem to run on server
We updated PlayerDestructionSystem to run on the server
Github branch link:
git clone https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/
git checkout 'Updating-Bullets-and-Destruction'
Last updated