public virtual void RemoveAt(int index) { if (index < 0) { throw new ArgumentOutOfRangeException(nameof(index), index, "The position must be greater than or equal to zero."); } if (index >= m_AtomList.Count) { lastOp = null; return; } BeforeRemoveItem(index); var op = new KSEQOperation <T> { replicaId = replicaId, realTime = GetWallTime(), op = KSEQOperationTypes.Remove, id = m_AtomList[index].id }; Apply(op); lastOp = op; }
public void Insert(int index, T item) { if (index < 0) { throw new ArgumentOutOfRangeException(nameof(index), index, "The position must be greater than or equal to zero"); } BeforeInsertItem(ref item); var beforeId = index - 1 < m_AtomList.Count && index - 1 >= 0 ? m_AtomList[index - 1].id : null; var afterId = index < m_AtomList.Count && index >= 0 ? m_AtomList[index].id : null; var id = m_IdentGenerator.GetIdent(replicaId, ++time, beforeId, afterId); var op = new KSEQOperation <T> { id = id, op = KSEQOperationTypes.Insert, realTime = GetWallTime(), value = item, replicaId = replicaId }; Apply(op); lastOp = op; }
public void SendReplicationOp(string gameId, KSEQOperation <Record> op) { var game = m_Host.games[gameId]; game.replicatedData.Apply(op); OthersInGame(gameId).SendAsync(nameof(NetworkedClientPeer.ReceiveReplicationOp), gameId, op).Start(); }
public void Apply(KSEQOperation <T>?op) { if (op == null) { return; } Apply(op.Value); }
public override T this[int index] { get => base[index]; set { SetId(ref value); var beforeId = index - 1 < m_AtomList.Count && index - 1 >= 0 ? m_AtomList[index - 1].id : null; var afterId = index < m_AtomList.Count && index >= 0 ? m_AtomList[index].id : null; var id = m_IdentGenerator.GetIdent(replicaId, ++time, beforeId, afterId); var op = new KSEQOperation <T> { id = id, op = KSEQOperationTypes.Set, realTime = GetWallTime(), replicaId = replicaId, value = value }; lastOp = op; Apply(op, false); } }
public override ApplicationResult Apply(KSEQOperation <T> op, bool quiet = false) { // Retrieve the newest queued set for the given ID if the item already exists, performing an insert with its // id iif the id is later, and using the appropriate value. if (op.op == KSEQOperationTypes.Insert && m_QueuedSet.ContainsKey(op.value.id)) { var replacementOp = m_QueuedSet[op.value.id]; if (op.id < replacementOp.id) { throw new ArgumentException( $"op {op} has an ident with precedence over a set but matching id {op.value.id}", nameof(op)); } m_QueuedSet.Remove(op.value.id); op.value = replacementOp.value; op.id = replacementOp.id; } var handled = base.Apply(op, quiet); if (handled.applied) { switch (op.op) { // Worst case: O(N) (op to the beginning of the list // Best case: O(1) (op to the end of the list) case KSEQOperationTypes.Insert: m_IdToIndex[op.value.id] = handled.index; for (var i = handled.index + 1; i < Count; i++) { m_IdToIndex[m_AtomList[i].value.id] = i; } break; case KSEQOperationTypes.Remove: m_IdToIndex.Remove(op.value.id); m_RemovedIds.Remove(op.value.id); for (var i = handled.index; i < Count; i++) { m_IdToIndex[m_AtomList[i].value.id] = i; } break; } return(handled); } switch (op.op) { case KSEQOperationTypes.Set: if (m_RemovedIds.Contains(op.value.id) || m_Removed.Contains(op.id)) { break; } // Find an existing record. Otherwise, queue up if (m_IdToIndex.ContainsKey(op.value.id)) { var i = m_IdToIndex[op.value.id]; var atom = m_AtomList[i]; var originalValue = atom.value; if (originalValue.id != op.value.id) { throw new ArgumentException($"invalid set, {op.value.id}!={originalValue.id}"); } // If this is later (last write first), go ahead and replace the atom with the new value. // Otherwise, do nothing. if (op.id < atom.id) { var oldAtom = atom; atom.id = op.id; atom.value = op.value; Remove(oldAtom.id); var j = Add(op.id, op.value); for (var k = Math.Min(i, j); k <= Math.Max(i, j); k++) { m_IdToIndex[m_AtomList[k].value.id] = k; } if (!quiet) { var collectionReplaceEvent = new CollectionReplaceEvent <T>(i, originalValue, op.value); collectionReplace?.OnNext( collectionReplaceEvent); if (i != j) { collectionMove?.OnNext(new CollectionMoveEvent <T>(i, j, op.value)); } if (op.replicaId == replicaId) { collectionThisReplicaReplaced?.OnNext(collectionReplaceEvent); } } } return(new ApplicationResult() { applied = true, index = i }); } else { if (!m_QueuedSet.ContainsKey(op.value.id) || (m_QueuedSet.ContainsKey(op.value.id) && op.id < m_QueuedSet[op.value.id].id)) { // Only replace the existing queued set if this op is later m_QueuedSet[op.value.id] = op; } return(new ApplicationResult() { applied = true }); } } return(new ApplicationResult()); }
public virtual ApplicationResult Apply(KSEQOperation <T> op, bool quiet = false) { switch (op.op) { case KSEQOperationTypes.Insert: if (m_Removed.Contains(op.id)) { break; } var indexAdded = Add(op.id, op.value); if (!quiet) { var collectionAddEvent = new CollectionAddEvent <T>(indexAdded, op.value); collectionAdd?.OnNext(collectionAddEvent); countChanged?.OnNext(m_AtomList.Count); if (op.replicaId == replicaId) { collectionThisReplicaAdded?.OnNext(collectionAddEvent); } } return(new ApplicationResult() { applied = true, index = indexAdded }); case KSEQOperationTypes.Remove: if (m_Removed.Contains(op.id)) { break; } m_Removed.Add(op.id); var indexRemoved = IndexOf(op.id); if (indexRemoved < 0) { break; } var oldValue = m_AtomList[indexRemoved]; m_AtomList.RemoveAt(indexRemoved); if (!quiet) { var collectionRemoveEvent = new CollectionRemoveEvent <T>(indexRemoved, oldValue.value); collectionRemove?.OnNext(collectionRemoveEvent); countChanged?.OnNext(m_AtomList.Count); if (op.replicaId == replicaId) { collectionThisReplicaRemoved?.OnNext(collectionRemoveEvent); } } return(new ApplicationResult() { applied = true, index = indexRemoved }); } return(new ApplicationResult() { }); }
public void ReceiveReplicationOp(string gameId, KSEQOperation <Record> op) { ((ReplicatedGameContext)games.First(g => g.gameId == gameId)).replicatedData.Apply(op); }