Search Unity

Question Rollback only GhostField?

Discussion in 'NetCode for ECS' started by AlexandrKvitka, Oct 10, 2023.

  1. AlexandrKvitka


    Jun 11, 2022

    For example, I have two components
    Code (CSharp):
    1. struct Foo {[GhostField] X}
    2. struct Bar {[GhostField] Y}
    Both I used in the prediction loop. But Y depends on X. I can calculate it, but Y uses a lot of systems, and I want to cache it. Based on this, I don't need to sync the Y field. But if I remove [GhostField] from Y, Y won't do a rollback, which will lead to a resimulation error.
    I found the Send argument in GhostField, but it doesn't help me.

    Of course, I can make the correct order of system execution like this

    Code (CSharp):
    1. BeginPredictionGroup
    2. -SystemOfCaluculationgY
    3. -System1WhichUseY
    4. -System2WhichUseY
    5. EndPredictionGroup
    But in a real project, I will have a lot of problems. I need to control the snapshot size, control the execution order, and always think about whether this component is recalculated. Sounds like hell.

    Maybe this solution will be better
    Code (CSharp):
    1. struct Foo {[GhostField] X}
    2. struct Bar {[GhostField(RollbackOnly=true)] Y}
    Or is there any best practice to organize it in a better way?
    What do you think about it?
    How do you solve this problem?
  2. CMarastoni


    Unity Technologies

    Mar 18, 2020
    If the value of Y depends only on value of X (or any other replicated field) the simplest solution is just to recalculate Y evert tick or if X has actually changed. You can use change filtering for example, even though is suboptimal in that sense.
    But you said, you want to avoid because it is costly.

    The first question I have is: how this caching work? do you check that X has changed to update the Y value? For example using the latest value of X somewhere and compare?

    In any case, if X change you need to update the cached value (once arguably) so you need to perform this X check either every frame (pretty much) or/when new data has been received an rollback occurs.
    But when new data is received, you are going to re-simulate N ticks. Can the value of X change during this re-simulation ? If the answer is yes, then you still need to compute that multiple time.
    At this point the solution
    Code (csharp):
    2. [LIST=1]
    3. [*]BeginPredictionGroup
    4. [*]-SystemOfCaluculationg
    5. ...
    6. [/LIST]
    make sense.

    Rollback always occurs anyway because of partial ticks, so the value of X (even in case youen don't received data from the server) may change in next partial, if the value of X is predicted by the client. If that value is not predicted (and it is modified by server only), then using change filtering may be the a good choice: You can check easily if the component Bar has been changed because the server changed that, and recompute only in that case.

  3. AlexandrKvitka


    Jun 11, 2022
    >What the RollbackOnly property should do? Should store the value locally (only client side) and rollback the value if new data has been received?

    Yes, but I have just understood that it won't work =)

    frame 10:
    Y = 1;

    frame 11:
    SystemA read Y (1)
    SystemB recalculate Y
    Y = 2
    SystemC read Y(2)
    freme 20:
    Y = 100;

    If I do a rollback to 10 without the RollbackOnly field, in frame 11, systemA reads Y = 100; Only SystemC gets the correct value.
    If I do this with RollbackOnly, I restore Y from the history, and SystemA reads "correctly". But if X has a different value on the server, SystemA still gets an incorrect value.