public void ServerQueueSecondDestruct(ushort eid) { SecondDestructQueue.Add(eid); SecondDestructQueueTime.Add(CurrentTime); NetSnapper.ServerSendDeghostSecondAll(eid); }
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); }
// queue destruction of an entity. It will be deghosted first and then // destroyed later once enough time has passed. public void ServerQueueFirstDestruct(byte eid) { FirstDestructQueue.Add(eid); FirstDestructQueueTime.Add(CurrentTime); NetSnapper.ServerSendDeghostFirstAll(eid); }
public bool ServerAddEntitySecond(TSnap initalSnap, TStatic staticData, out ushort eid, bool ghostAll = true) { if (!NetSnapper.ServerRequestEntitySecond(EntityType, out eid)) { return(false); // NetSnapper says: no entity space available } if (SecondEntityIdToInnerId.Length <= eid) { ExpandSecondEntityIdMap(eid); } SnapHistory <TSnap, TStatic> h = SecondData.Request(); h.StaticData = staticData; h.EntityId = eid; h.Timestamps[0] = NetSnapper.CurrentTime; h.Flags[0] = SnapHistory <TSnap, TStatic> .FlagGold; h.LeadingIndex = 0; h.Shots[0] = initalSnap; SecondEntityIdToInnerId[eid] = h.PoolId; if (ghostAll) { // ghost the new entity to all players NetSnapper.ServerSendGhostAllSecond(eid, EntityType); } return(true); }
// Simulant Helpers public void ServerSaveSimIntoCurrent(SnapHistory <TSnap, TStatic> ent) { ent.Flags[ent.CurrentIndex] = SnapHistory <TSnap, TStatic> .FlagGold; if (ent.First) { NetSnapper.ServerSendDeltaAllFirst((byte)ent.EntityId, EntityType); } else { NetSnapper.ServerSendDeltaAllSecond(ent.EntityId, EntityType); } }
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); }
// Prime Logic // Advancement occurs every tick public void Advance(ushort currentTime) { CurrentTime = currentTime; // when we advance, push every leading edge forward for (int i = 0; i < FirstData.Count; i++) { SnapHistory <TSnap, TStatic> sh = FirstData.Values[i]; ushort last = sh.Timestamps[sh.LeadingIndex]; ushort next = last == ushort.MaxValue ? (ushort)0 : (ushort)(last + 1); int startIndex = sh.LeadingIndex; sh.LeadingIndex++; if (sh.LeadingIndex == sh.Shots.Length) { sh.LeadingIndex = 0; } if (sh.Timestamps[sh.LeadingIndex] != next) { // we haven't received the next snap yet, so destroy it and // ensure that it is empty for the simulator sh.Timestamps[sh.LeadingIndex] = next; sh.Flags[sh.LeadingIndex] = SnapHistory <TSnap, TStatic> .FlagEmpty; // note: we don't need to actually call Clear on the snapshot // since the fact that it's marked with an empty flag means // the simulator should avoid using it. // if the previous snapshot was deghosted, // deghost this one as well. if (sh.Flags[startIndex] == SnapHistory <TSnap, TStatic> .FlagDeghosted) { sh.Flags[sh.LeadingIndex] = SnapHistory <TSnap, TStatic> .FlagDeghosted; } } } // same logic as above but for second data // only reason we don't use a method for this is having that many method // calls seems like a waste. for (int i = 0; i < SecondData.Count; i++) { SnapHistory <TSnap, TStatic> sh = SecondData.Values[i]; ushort last = sh.Timestamps[sh.LeadingIndex]; ushort next = last == ushort.MaxValue ? (ushort)0 : (ushort)(last + 1); int startIndex = sh.LeadingIndex; sh.LeadingIndex++; if (sh.LeadingIndex == sh.Shots.Length) { sh.LeadingIndex = 0; } if (sh.Timestamps[sh.LeadingIndex] != next) { sh.Timestamps[sh.LeadingIndex] = next; sh.Flags[sh.LeadingIndex] = SnapHistory <TSnap, TStatic> .FlagEmpty; if (sh.Flags[startIndex] == SnapHistory <TSnap, TStatic> .FlagDeghosted) { sh.Flags[sh.LeadingIndex] = SnapHistory <TSnap, TStatic> .FlagDeghosted; } } } // now check our destruct queues if (FirstDestructQueue.Count > 0) { for (int i = FirstDestructQueue.Count - 1; i >= 0; i--) { ushort destructTime = FirstDestructQueueTime[i]; int dist = 0; if (currentTime < destructTime) { dist = (ushort.MaxValue - destructTime) + currentTime; } else { dist = currentTime - destructTime; } if (dist < FirstWindowLength) { break; } NetSnapper.ServerDestructFirst(FirstDestructQueue[i]); FirstDestructQueue.RemoveAt(i); FirstDestructQueueTime.RemoveAt(i); } } if (SecondDestructQueue.Count > 0) { for (int i = SecondDestructQueue.Count - 1; i >= 0; i--) { ushort destructTime = SecondDestructQueueTime[i]; int dist = 0; if (currentTime < destructTime) { dist = (ushort.MaxValue - destructTime) + currentTime; } else { dist = currentTime - destructTime; } if (dist < FirstWindowLength) { break; } NetSnapper.ServerDestructSecond(SecondDestructQueue[i]); SecondDestructQueue.RemoveAt(i); SecondDestructQueueTime.RemoveAt(i); } } }
private void Rollover(SnapHistory <TSnap, TStatic> sh, int index, ushort timestamp) { // this whole idea has to be reworked // I guess an issue is that one of our key assumptions // is that the indices will always be populated with timestamps // so we need to have some kind of rollover // but doing the extrap action here is not good // 1. it's not real simulated // 2. why not do interp when available? // one option would be to do simple extrap/interp here // or even just copy the same snapshot over and over // and then, when we get an older timestamp, flag the simulator // as "needs rollback calculation" (ONLY IF WE ACTUALLY HAVE ANY // SILVER SNAPSHOTS AHEAD OF THIS TIMESTAMP) // then, at the end of each processing, if the simulator has // this flag set, it will run again from whatever the oldest time // flagged was (need to track the timestamp of each flagging as well) // this will generate new silver snapshots, and they'll be accurate to boot. // RE: THIS DISCUSSION // we decided to do the "resimulate flagging" approach // so the way this works now is that if we receive a timestamp, // and there's unknowns in front of it, // the system gets asked to resimulate from that timestamp int nindex = index + 1; ushort nextTime = timestamp; 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) { return; } // can only rollover onto subsequent timestamps if (sh.Timestamps[nindex] != nextTime) { return; } // if we get here, then we hit a silver or an empty that must be filled out // tell the snap system to resimulate NetSnapper.RequestResimulate(timestamp); // VVVV OLD VVVV /* * // flags need to act a certain way to allow us to have * // snapshots roll over when no changes are needed * // e.g.: * // 1 = from server (GOLD STANDARD) * // 2 = roll over, this occurs when no snapshot is received * // we assume shot is unchanged and use it again for next * // timestamp * // now imagine we have some snaps: * // aaaaabb * // 1222212 * // ^ and here we receive a new snapshot c * // we SCROLL FORWARD and replace all 2s with the new snapshot: * // aacccbb * // 1212212 * // this way we can let client continue to scroll forward but * // also correct itself properly * int nindex = index + 1; * ushort nextTime = timestamp; * int rolls = 1; * 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<T>.FlagSilver && sh.Flags[nindex] != SnapHistory<T>.FlagEmpty) * break; * * // can only rollover onto subsequent timestamps * if (sh.Timestamps[nindex] != nextTime) * break; * * // now we're ready to roll * ExtrapolateAction(ref sh.Shots[nindex], sh.Shots[index], TickMS * rolls); * rolls++; * * nindex++; * }*/ }