private bool UnpackDelta(ReArrayIdPool <SnapHistory <TSnap, TStatic> > data, int innerid, byte[] blob, int blobstart, int blobcount, ushort timestamp, ushort basisTimestamp) { SnapHistory <TSnap, TStatic> sh = data.Values[data.IdsToIndices[innerid]]; int index = sh.FindIndex(timestamp); if (index < 0 || index >= sh.Shots.Length) { return(false); // out of bounds } int basisIndex = sh.FindIndex(basisTimestamp); if (basisIndex < 0 || basisIndex >= sh.Shots.Length) { return(false); // out of bounds } Packer.UnpackDelta(ref sh.Shots[index], sh.Shots[basisIndex], blob, blobstart, blobcount); sh.Timestamps[index] = timestamp; sh.Flags[index] = SnapHistory <TSnap, TStatic> .FlagGold; // handle flag rollover Rollover(sh, index, timestamp); return(true); }
private bool UnpackDelta(ReArrayIdPool <SnapHistory <TSnap, TStatic> > data, int innerid, byte[] blob, int blobstart, int blobcount, ushort timestamp, ushort basisTimestamp) { SnapHistory <TSnap, TStatic> sh = data.Values[data.IdsToIndices[innerid]]; int index = sh.FindIndex(timestamp); if (index < 0 || index >= sh.Shots.Length) { return(false); // out of bounds } int basisIndex = sh.FindIndex(basisTimestamp); if (basisIndex < 0 || basisIndex >= sh.Shots.Length) { return(false); // out of bounds } Packer.UnpackDelta(ref sh.Shots[index], sh.Shots[basisIndex], blob, blobstart, blobcount); sh.Timestamps[index] = timestamp; sh.Flags[index] = SnapHistory <TSnap, TStatic> .FlagGold; // ask the server to resimulate from this timestamp // to ensure our silver guesses get updated NetSnapper.RequestResimulate(timestamp); return(true); }
public byte PrepGhostSecond(ushort entityid, ushort timestamp) { // if ``, can't possibly belong to this snapper if (entityid >= SecondEntityIdToInnerId.Length) { return(0); } // if ``, this snapper doesn't have this entity int innerid = SecondEntityIdToInnerId[entityid]; if (innerid == -1) { return(0); } SnapHistory <TSnap, TStatic> h = SecondData.Values[SecondData.IdsToIndices[innerid]]; TempWriteHistory = h; TempWriteIndex = h.FindIndex(timestamp); if (TempWriteIndex < 0) { return(0); } return(Packer.PrepPackFull( h.Shots[TempWriteIndex], h.StaticData, out PackInfo)); }
private bool Deghost(ReArrayIdPool <SnapHistory <TSnap, TStatic> > data, int innerid, ushort timestamp) { SnapHistory <TSnap, TStatic> sh = data.Values[data.IdsToIndices[innerid]]; int index = sh.FindIndex(timestamp); if (index < 0 || index >= sh.Shots.Length) { return(false); // out of bounds } sh.Flags[index] = SnapHistory <TSnap, TStatic> .FlagDeghosted; // scroll forward and deghost subsequent timestamps! // what to do if deghost and ghost arrive out of order? // simple: by only overwriting SILVER flags, we ensure that deghosting // will not overwrite fresh / more recent data int nindex = index + 1; ushort nextTime = timestamp; while (true) { if (nindex == sh.Shots.Length) { nindex = 0; } if (nextTime == ushort.MaxValue) { nextTime = 0; } else { nextTime++; } // we can only rollover onto silver flags if (sh.Flags[nindex] != SnapHistory <TSnap, TStatic> .FlagSilver && sh.Flags[nindex] != SnapHistory <TSnap, TStatic> .FlagEmpty) { break; } // can only rollover onto subsequent timestamps if (sh.Timestamps[nindex] != nextTime) { break; } // now we're ready to roll sh.Flags[nindex] = SnapHistory <TSnap, TStatic> .FlagDeghosted; nindex++; } return(true); }
public bool PrepDeltaSecond(ushort entityid, ushort timestamp, ushort basisTimestamp, out byte len) { len = 0; // if ``, can't possibly belong to this snapper if (entityid >= SecondEntityIdToInnerId.Length) { return(false); } // if ``, this snapper doesn't have this entity int innerid = SecondEntityIdToInnerId[entityid]; if (innerid == -1) { return(false); } SnapHistory <TSnap, TStatic> h = SecondData.Values[SecondData.IdsToIndices[innerid]]; int index = h.FindIndex(timestamp); if (index == -1) { return(false); } int basisIndex = h.FindIndex(basisTimestamp); if (basisIndex == -1) { return(false); } TempWriteHistory = h; TempWriteIndex = index; len = Packer.PrepPackDelta( h.Shots[index], h.Shots[basisIndex], out PackInfo); return(true); }
private bool UnpackFull(ReArrayIdPool <SnapHistory <TSnap, TStatic> > data, int innerid, byte[] blob, int blobstart, int blobcount, ushort timestamp) { SnapHistory <TSnap, TStatic> sh = data.Values[data.IdsToIndices[innerid]]; int index = sh.FindIndex(timestamp); if (index < 0) { return(false); // out of bounds-- too far in future or past } Packer.UnpackFull(ref sh.Shots[index], ref sh.StaticData, blob, blobstart, blobcount); sh.Timestamps[index] = timestamp; sh.Flags[index] = SnapHistory <TSnap, TStatic> .FlagGold; // handle flag rollover Rollover(sh, index, timestamp); return(true); }
private bool UnpackFull(ReArrayIdPool <SnapHistory <TSnap, TStatic> > data, int innerid, byte[] blob, int blobstart, int blobcount, ushort timestamp) { SnapHistory <TSnap, TStatic> sh = data.Values[data.IdsToIndices[innerid]]; int index = sh.FindIndex(timestamp); if (index < 0) { return(false); // out of bounds-- too far in future or past } Packer.UnpackFull(ref sh.Shots[index], ref sh.StaticData, blob, blobstart, blobcount); sh.Timestamps[index] = timestamp; sh.Flags[index] = SnapHistory <TSnap, TStatic> .FlagGold; // ask the server to resimulate from this timestamp // to ensure our silver guesses get updated NetSnapper.RequestResimulate(timestamp); return(true); }
private int Ghost(ReArrayIdPool <SnapHistory <TSnap, TStatic> > data, ushort entityid, byte[] blob, int blobstart, int blobcount, ushort timestamp) { SnapHistory <TSnap, TStatic> sh = data.Request(); sh.EntityId = entityid; sh.LeadingIndex = 0; sh.Timestamps[0] = CurrentTime; // setup the rest of the future timestamps ushort itime = sh.Timestamps[0]; for (int i = 1; i <= sh.Shots.Length / 2; i++) { if (itime == ushort.MaxValue) { itime = 0; } else { itime++; } sh.Timestamps[i] = itime; } // and the past itime = sh.Timestamps[0]; for (int i = 0; i < sh.Shots.Length / 2; i++) { if (itime == 0) { itime = ushort.MaxValue; } else { itime--; } sh.Timestamps[sh.Shots.Length - 1 - i] = itime; } int expIndex = sh.FindIndex(timestamp); if (expIndex == -1) { // in this case, we need to just abandon the snapshot we // received. This can obviously create issues since we'll // need a full snapshot again. // however, we still unpack it in full, so the static data // can be constructed Packer.UnpackFull(ref sh.Shots[0], ref sh.StaticData, blob, blobstart, blobcount); sh.Flags[0] = SnapHistory <TSnap, TStatic> .FlagEmpty; } else { // if the snapshot we received has a space in the current window, // so just unpack into there Packer.UnpackFull(ref sh.Shots[expIndex], ref sh.StaticData, blob, blobstart, blobcount); sh.Timestamps[expIndex] = timestamp; sh.Flags[expIndex] = SnapHistory <TSnap, TStatic> .FlagGold; } // return the innerid return(sh.PoolId); }
// predicts forward a whole tick // this returns true if all impulse entities have conf snapshots // indicating that we can turn up the impulseTimestamp public bool ClientPredictTick(ushort confTimestamp) { ushort prevConfTimestamp = confTimestamp; if (prevConfTimestamp == 0) prevConfTimestamp = ushort.MaxValue; else prevConfTimestamp--; bool noMissingImpulse = true; for (int d = 0; d < NentDatas.Length; d++) { ReArrayIdPool<SnapHistory<TSnap, TStatic>> data = NentDatas[d]; for (int i = 0; i < data.Count; i++) { SnapHistory<TSnap, TStatic> h = data.Values[i]; if (h.PrevFlag == SnapHistory<TSnap, TStatic>.FlagEmpty || h.PrevFlag == SnapHistory<TSnap, TStatic>.FlagDeghosted) continue; // if the prev flag is empty, this entity does // not exist yet // if the entity has impulse, we may need to predict // if the current timestamp is after the impulse timestamp if (h.HasImpulse && ((NetSnapper.SimulateTimestamp >= h.ImpulseTimestamp && !(NetSnapper.SimulateTimestamp >= ushort.MaxValue - 500 && h.ImpulseTimestamp <= 500)) || (NetSnapper.SimulateTimestamp <= 500 && h.ImpulseTimestamp >= ushort.MaxValue - 500))) { // cases: // 0. if SimulateTimestamp is before h.ImpulseTimestamp, don't // predict, do normal interp // 1. if this entity has a gold snapshot in SimulateTimestamp + I, // then don't predict; use the server result // 2. if not, advance a predicted result // case 0 is accounted for by the if statement above // case 1 int confIndex = h.FindIndex(confTimestamp); if (confIndex != -1 && h.Flags[confIndex] == SnapHistory<TSnap, TStatic>.FlagGold && h.ImpulseTimestamp == NetSnapper.SimulateTimestamp) { // in this case, our job is easy, we have a server confirmed snap // so we just use that h.ImpulseShot = h.Shots[confIndex]; //NetSnapper.Server.NetLogger.Log(NetSnapper.SimulateTimestamp + " / " + confTimestamp + " F"); } else { // case 2 // in this case, we must predict based on the current ImpulseShot // because we got here, we are missing a conf for this impulse // so we can't move up the NetSnapper's ImpulseTimestamp noMissingImpulse = false; if (h.ImpulseTimestamp == NetSnapper.SimulateTimestamp) { // special consideration: if this is our first impulse, // we must populate ImpulseShot // if we have a confirmed shot for the previous timestamp, // we should use that // if we don't, this must be our first prediction, so we // can simply use the previous snapshot int prevConfIndex = h.FindIndex(prevConfTimestamp); if (prevConfIndex != -1 && h.Flags[prevConfIndex] == SnapHistory<TSnap, TStatic>.FlagGold) { h.ImpulseShot = h.Shots[prevConfIndex]; //NetSnapper.Server.NetLogger.Log(h.ImpulseTimestamp + " / " + prevConfTimestamp + " C"); } else { h.ImpulseShot = h.Shots[h.PrevIndex]; //NetSnapper.Server.NetLogger.Log(h.ImpulseTimestamp + " / " + prevConfTimestamp + " P"); } } // first, check if we have an input // we could only have an input if we are the owner of this object byte inputPid = Advancer.GetInputPlayer(h.ImpulseShot, h.StaticData); if (inputPid == Server.OurPlayerId) { // now check if we have any inputs // if so, process them if (!InputChecker(h, ref h.ImpulseShot, inputPid, NetSnapper.TickMSTarget)) { // if not, do a normal advance Advancer.AdvanceLogic(AdvancerConfig, h, ref h.ImpulseShot, NetSnapper.TickMSTarget); } } else { // if we couldn't have an input, just do a normal advance Advancer.AdvanceLogic(AdvancerConfig, h, ref h.ImpulseShot, NetSnapper.TickMSTarget); } } } // even if we did impulse above, we still want to interp // because we may need to create silver snapshots to use for // blending or impulse later // if we have a gold from the server, we don't need to do anything // because the current shot is already good if (h.CurrentFlag == SnapHistory<TSnap, TStatic>.FlagGold) continue; // (you might notice from the following, that we resimulate silver // flags [client guesses] each tick; this way they continue to update // their guesses if new server info arrives) // if not, we need to create a silver for the current shot // we know the previous is gold or silver already, since we checked // for empty or ghost way back above // so we know the prev is good for interp, but is the next? // if the next is also good for interp, we'll just interp h.Shots[h.CurrentIndex] = h.Shots[h.PrevIndex]; if (h.NextFlag == SnapHistory<TSnap, TStatic>.FlagGold || h.NextFlag == SnapHistory<TSnap, TStatic>.FlagSilver) { Advancer.InterpTickLogic(h); } else { // otherwise, do extrapolation Advancer.AdvanceLogic(AdvancerConfig, h, ref h.Shots[h.CurrentIndex], NetSnapper.TickMSTarget); } // save our guess snapshot as a new silver Nents.ClientSaveSimIntoCurrent(h); } } return noMissingImpulse; }
private int Ghost(ReArrayIdPool <SnapHistory <TSnap, TStatic> > data, ushort entityid, byte[] blob, int blobstart, int blobcount, ushort timestamp) { SnapHistory <TSnap, TStatic> sh = data.Request(); sh.EntityId = entityid; Packer.UnpackFull(ref sh.Shots[0], ref sh.StaticData, blob, blobstart, blobcount); sh.Timestamps[0] = timestamp; sh.Flags[0] = SnapHistory <TSnap, TStatic> .FlagGold; sh.LeadingIndex = 0; // now, figure how close we are to current time // under normal conditions, we should be receiving snapshots // very close to the CurrentTime int expIndex = sh.FindIndex(CurrentTime); // however, if the expIndex is -1, it means the snapshot // we just received is outside the current window // (because CurrentTime relative to it is outside the window) if (expIndex == -1) { // in this case, we need to just abandon the snapshot we // received. This can obviously create issues since we'll // need a full snapshot again. sh.Timestamps[0] = CurrentTime; sh.Flags[0] = SnapHistory <TSnap, TStatic> .FlagEmpty; } else { // in this case, we're within the window, so let's // just set the leading index properly. sh.LeadingIndex = expIndex; sh.Timestamps[sh.LeadingIndex] = CurrentTime; } // setup the rest of the future timestamps ushort itime = sh.Timestamps[0]; for (int i = 1; i <= sh.Shots.Length / 2; i++) { if (itime == ushort.MaxValue) { itime = 0; } else { itime++; } sh.Timestamps[i] = itime; } // and the past itime = sh.Timestamps[0]; for (int i = 0; i < sh.Shots.Length / 2; i++) { if (itime == 0) { itime = ushort.MaxValue; } else { itime--; } sh.Timestamps[sh.Shots.Length - 1 - i] = itime; } // return the innerid return(sh.PoolId); }