feat: implement NetworkTickSystem based on NetworkUpdateLoop#502
feat: implement NetworkTickSystem based on NetworkUpdateLoop#502jeffreyrainy merged 6 commits intodevelopfrom
Conversation
| public static NetworkTickSystem Instance | ||
| { | ||
| get | ||
| { | ||
| if (m_Instance == null) | ||
| { | ||
| m_Instance = new NetworkTickSystem(); | ||
| } | ||
| return m_Instance; | ||
| } | ||
| } |
There was a problem hiding this comment.
singleton on its own vs an instance attached to a specific NetworkManager? I prefer the latter, what do you think? :)
There was a problem hiding this comment.
Attached to a specific NetworkManager is cleaner.
But the singleton PR has not landed yet. So, I'd rather push this as-is, and when we have converged on the correct way to deal with singletons, we replicate the pattern for this, too? Workable?
There was a problem hiding this comment.
you can still put instances under NetworkManager and access via NetworkManager.Singleton later on right?
There was a problem hiding this comment.
Why not instantiate it within the NetworkUpdateLoop itself? That was my original plan, have it accessible via NetworkUpdateLoop. Really, this is not the final version for the tick system, so NetworkManager or NetworkUpdateLoop... one of the two would be nice.
There was a problem hiding this comment.
NetworkUpdateLoop will always be a static class but NetworkManager is not a static class and will not be a singleton in the future so I'd suggest putting this instance under NetworkManager instead of NetworkUpdateLoop — also I don't want to repeat singleton pattern, we should be trying to avoid and remove them, not introduce them IMHO.
There was a problem hiding this comment.
See how the RPCQueueContainer is instantiated
There was a problem hiding this comment.
Addressed in latest update. Thanks!
| /// <summary> | ||
| /// Constructor | ||
| /// Defaults to k_DefaultTickDuration if no tick duration is specified | ||
| /// </summary> | ||
| /// <param name="tickDuration">Duration of a network tick</param> | ||
| private NetworkTickSystem(double tickDuration = k_DefaultTickDuration) | ||
| { | ||
| NetworkUpdateLoop.RegisterNetworkUpdate(this, NetworkUpdateStage.EarlyUpdate); | ||
|
|
||
| //Assure we don't specify a value less than or equal to zero for tick frequency | ||
| m_TickDuration = (tickDuration <= 0d) ? k_DefaultTickDuration : tickDuration; | ||
|
|
||
| // ticks might not start at 0, so let's update right away at construction | ||
| UpdateNetworkTick(); | ||
| } |
There was a problem hiding this comment.
super-minor: should we move this constructor block up above Dispose()? it took me a CTRL+F to find .RegisterNetworkUpdate() and constructor :P
There was a problem hiding this comment.
yeah, will move constructor and Dispose at the top. 👍
| /// </summary> | ||
| private void UpdateNetworkTick() | ||
| { | ||
| m_NetworkTick = (int)(Time.unscaledTime / m_TickDuration); |
There was a problem hiding this comment.
why (int)(float / double) instead of (int)(float / float)? m_TickDuration could be a float no?
There was a problem hiding this comment.
I had tried to keep the design for the original branch and thus kept the doubles. But yeah, you raise a good point if our source data is float, no point going over-board with double. I've got in my plans to eventually review the float usage for precision. But float would give us already ~2ms resolution after 5hours game time. Good enough for now. Will change.
There was a problem hiding this comment.
Let's add a comment saying we plan to move to FixedTime, etc.
| { | ||
| public class NetworkTickSystem : INetworkUpdateSystem, IDisposable | ||
| { | ||
| private const double k_DefaultTickDuration = 0.016; |
There was a problem hiding this comment.
hmm, 62.5 ticks per sec. do you mind inlining 1/0.016 math here and also explain what this variable is and why 62.5 tps instead of 20 or 30 or something else for now?
There was a problem hiding this comment.
The games I shipped in the past used integer number of millis to be able to do all computation on integers. So, that's why 16 milliseconds felt natural. But I don't really care since we're using floats now. Do you insist on making this 1/60f ?
There was a problem hiding this comment.
you can keep 0.016 if you want but I'm asking for a comment explaining its math + why 62.5fps specifically instead of any other number.
| private double m_TickDuration; | ||
| private int m_NetworkTick; //How many network ticks have passed? |
There was a problem hiding this comment.
| private double m_TickDuration; | |
| private int m_NetworkTick; //How many network ticks have passed? | |
| private float m_TickInternalMs; | |
| private int m_NetworkTickCount; |
I feel a little bit mixed here. if variable name is not good enough, we should probably change its name rather than comment next to it IMHO. I'd propose m_NetworkTickCount instead of m_NetworkTick and m_TickIntervalMs instead of m_TickDuration here. what do you think?
There was a problem hiding this comment.
Yeah, will change. I was trying to stay aligned with original branch, but it's worth improving upon.
There was a problem hiding this comment.
Noted. I think the long-term goal will be:
Read float unscaledtime, because that's the precision we have.
Maintain an integral number of millis for precision alignment and then surface out any time via APIs in double
Or better: accumulate FixedUpdate time as integral, quantize it during EarlyUpdate in ticks. Offer API for people who want to know the time in float/double.
c8f7c23 to
a983537
Compare
faa53b6 to
a983537
Compare
Implements the very minimal part of a tick system we need for synced networked variables.
I tried to keep it to the bare minimum and avoid any difficult design decision. This would be a first stepping stone.
Later on, we can consider what it means to pause/resume the tick system, to scale time, etc. Tests are pending, I'll get started on them.