Netcode Entities Client Prediction That Does Not Involve Player Input

Hello! I’m experimenting with predicted behaviors in netcode entities that are not driven by direct player input. For example, suppose there is one (or more) crop field that automatically generates 1 unit of food every second.

If I only run this logic on the server, the client sees updates (the state of the food inventory, UI, etc) with an RTT delay since it has to wait for the snapshot update from the server. To reduce this delay, I tried running the same system on both the client and server in a predicted system, so the client would immediately see food being generated and then rollback if the server snapshot disagreed.

Is this a valid use case for prediction in NetCode (even though it doesn’t involve client input)? Right now, when I run it in a predicted system on both client and server, the values keep rolling back (occasionally the increment is preserved, but most of the time the update is not updated properly). The code itself should be correct since when I run it on the server only, it works consistently.

What is the correct approach for this type of gameplay logic that does not involve player input. Is there a way to mitigate the delay?

Thank you!

The use case is definitely completely valid. Prediction does not necessarily require player input to run.

If you haven’t already read them, the docs will show the general idea of prediction in Netcode for Entities. While you don’t need to use input for prediction, everything that is predicted needs to be GhostFields, or at least derived directly from GhostFields. If some calculation is not based on a GhostField, then you introduce the possibility of mispredictions since the prediction system cannot ensure that all the data is the same, barring lots of packet loss or delay of course.

You also need to make sure you include the Simulate tag in your prediction queries, since our prediction system controls Simulate enabled state to update at the correct times.

It is normal that the values roll back within a frame. The prediction system will usually roll back and rerun ticks, but will always end up after the tick from the previous frame. So if that’s what you’re seeing as rollback, then that’s to be expected. But if you’re seeing mispredictions, then it is usually because some of your values are not calculated from GhostFields or derived from them.

Upon further debugging, I found that the job rarely executes on the server (I pass state.WorldUnmanaged.IsServer() into my job and prints out its value). I also logged state.WorldUnmanaged.IsServer() in the OnUpdate method (running in PredictedSimulationSystemGroup), and confirmed from the logs that both the client and server worlds are running, with the client running more frequently than the server, which is expected.

However, the logs inside the job suggest that more than 95% of the time it’s the client world running, and in those cases the ghost field value is not updated (rolled back). Only on the rare occasions when the server world runs the job does the ghost field value get updated. More concretely, every 1 second (the interval we set) the job runs on the client once, but not on the server. After a very long time the server would run once, update the resource value correctly once, but unlike the client it still does not execute consistently on the 1-second interval.

I’m really confused why this is happening. I’ve already added ghost fields to all relevant components, and I’m also using tags like Simulate and GhostOwnerIsLocal in the jobs.

Let’s take a step back. Is lag affecting your gameplay in anyway? Is there a skill check that requires good timing from your player to be able to react to crops yielding resources? You’ll want prediction in cases where you have your predicted player interacting with something else that’s also server authoritative. If you were to have your player on a predicted timeline, interacting with something on the interpolated timeline, you might see delay. So what delay are you trying to solve here? (and is that delay that bad you need to predict it.

Cause else crops could be on the interpolated timeline no? Prediction has a simulation cost you could try to avoid here.

Else, in general prediction is meant to be run repeatedly, for every tick. What do your system triggering those jobs look like?
Also do those components containing ghost fields live on a ghost entity? (An entity that was baked with the GhostAuthoringComponent). It should contain a GhostInstance component for example. Was your entity configured to be predicted?

You’re using GhostOwnerIsLocal for your fields? does your player’s connection own those ghosts? Is the ghost setup for Prediction or OwnerPredicted? OwnerPredicted will be interpolated on non-owners

This is precisely what I wanted to ask: Exactly which systems are supposed to run with client prediction? To me, client prediction means: the client stores an identical copy of server data locally → runs the same code as the server → periodically receives snapshots from the server → rolls back if local data mismatches the server, otherwise keeps going. Following this logic, it feels like every system could be client-predicted (since no extra code is needed and network delay is effectively zero, at the cost of higher CPU usage. This trade-off feels worthwhile, since CPU cycles are far cheaper than RTT delays).

In my current setup:

  • Resources (e.g., crop fields): default ghost mode = predicted, server-spawned and not owned by any particular client.
  • Player resource inventory: default ghost mode = owner predicted, since each player should only own their own inventory.
  • Resource extractor: default ghost mode = owner predicted, since each player can spawn and use their own extractors.

The issue I’m seeing in this case:
I did some more investigation and I believe I found out the true culprit. The server does also run jobs, but because the client is a few ticks ahead (normal for prediction), and I’m storing cooldown in an ICommandData buffer, the client keeps pushing ReadyToExtractAtTick further into the future with each prediction. Thus, by the time the server reaches the previous “ready tick”, the client already updated the “ready tick” further into the future, and the server never reaches “ready tick” as a result. This is why it seems “only the client is running the job”. It is indeed the case that client is the only one who is trying to update the resource value, but since server is never able to update, the value keeps getting rolled back.

cooldown.AddCommandData(new ResourceGatherCoolDown { Tick = CurrentTick,
                                                     ReadyToExtractAtTick = CurrentTick + cd })

Evidence:

As you can see in the screenshot, by the time server reaches tick 498, the previous ready tick, the client has already set the cd to 618, the next ready tick, so server would never reach a ready tick.

PS: The reason I’m using ICommandData and not IInputComponentData is because: Upon experimentation, it looks like IInputComponentData would override the previous tick’s value upon entering the next tick, but ICommandData persists data. Also this is not really player input, which is what IInputComponentData is optimized for.

Questions:

  1. Exactly which systems, if not all systems, are supposed to use client prediction?
  2. How can I resolve the problem described above?
  3. Do all prediction systems require a command stream? Are the fields in the command stream (either ICommandData or IInputComponentData) supposed to be ghostfields or no?
  4. Should “server-centric” ghosts be interpolated or predicted (eg. server-spawned resources or AI NPCs)? Intuitively I think they should be interpolated (since they’re authoritative on the server with no client input), but if they interact with “client-centric” ghosts (like extractors or client armies fighting AI NPCs), wouldn’t making them predicted make more sense?
  5. Any elaboration on misconceptions about client prediction would be greatly appreciated.

Thank you so much!

@SamuelBellomoUnity @jonathan-hertz
I’m still very interested in your expertise on each of the questions I raised earlier. If you have the time, I’d greatly appreciate your insights, as I believe the answers would be valuable for others in the community as well. If it’s outside your scope, could you kindly suggest the right person I might reach out to?

  • Exactly which systems, if not all systems, are supposed to use client prediction?

Definitely not all systems are supposed to be client prediction. Prediction is meant for things which need to be snappy. This is usually characters, their movement, shooting etc. If something changes often (as in each frame) and the client needs to use it to respond with what’s on screen, that is also a good candidate for prediction. So for example, do your crop fields change often? Is it important that those crop fields react instantly when the player removes it? If no to both question, then there is not much reason for it to stay in the prediction loop, and it would be fine to have it interpolated. But if yes, then you can put them in the prediction loop.

How can I resolve the problem described above?

Generally, you would not want to put a cooldown inside the command data. Command data is usually used for inputs. The cooldown is then a good candidate to put on a GhostField which is processed in the prediction loop, because that is directly controlled by the input. So instead of syncing the cooldown through the command, you would sync the input through the command instead, and then sync the cooldown through a GhostField so that it is rolled back and predicted correctly.

Do all prediction systems require a command stream? Are the fields in the command stream (either ICommandData or IInputComponentData) supposed to be ghostfields or no?

The prediction loop will only run if you have predicted ghosts, and they do not require commands. For example, you could predict other player character’s movements without their actual input by looking at the velocity from the previous tick. The command and input data do not need to be GhostFields. The only reason you would do this is if you wanted to predict the movement of other clients, which can be useful for physics simulations and really twitchy movement where their inputs are assumed to be the same as the last tick. But in most cases, this is not needed.

Should “server-centric” ghosts be interpolated or predicted (eg. server-spawned resources or AI NPCs)? Intuitively I think they should be interpolated (since they’re authoritative on the server with no client input), but if they interact with “client-centric” ghosts (like extractors or client armies fighting AI NPCs), wouldn’t making them predicted make more sense?

There is no one answer to that question, it is highly context-dependent on your game and it really is up to you to choose the solution as it’s more of a design question at this point. I would say that by default, go with interpolation. If that is too slow or not reactive enough, you can start predicting some of the systems. Prediction only adds more CPU cost on the client side than interpolation, it doesn’t change the bandwidth requirements.

Let me know if that answers your questions!

Thank you very much for your answer! A few quick follow-ups:

  1. I would argue that this applies not only to player input. In the example above, the crop field update could occur quite frequently (eg. once per second) and doesn’t involve any player input. Of course crop fields is just one example and could easily be extended to other systems/scenarios. Ideally, everything in the game should feel “snappy,” regardless of whether it’s directly triggered by player input.

And given that additional CPU cost is generally far cheaper than increased bandwidth usage, wouldn’t it still make sense to use prediction as much as possible?

  1. Should physics systems run only on the server, or can they also run in the predicted simulation group (eg. raycasts and collider checks)? I’m guessing they should be server-only, but doesn’t that mean any component data populated from physics queries would have to deal with snapshot delay?

While you do want as much to be snappy as possible, it is always a tradeoff. Once a second however is, in the scale of what games are dealing with, not that often. Compare that to the movement of a character which moves every single frame. Every second is every 60 frames in a 60 FPS game. The thing you have to ask yourself, what do you actually gain from predicting something?

Ah, I think I see the misunderstanding. Adding prediction to something will not decrease the bandwidth used. The server is still sending the same data. Netcode for Entities is a server-authoritative model, so it is always sending the state of things, whether they are predicted or interpolated. Prediction will always incur a greater CPU cost on the client, in favor of instant feedback to the player. That is the tradeoff you are making.

That is again up to you and depends entirely on your game and your needs. Does your character need collisions to move? Is it important that the physics simulation happens instantly for clients? Are you okay with potentially inaccurate collisions? Is your physics simulation important for the gameplay, or is it only secondary? These are some questions that can help you answer whether the physics simulation needs to run in client prediction.