public SDS MergeWith(SDS other, MergeStrategy strategy, EntityChange.ExecutionContext ctx) { if (Generation != other.Generation) { throw new IntegrityViolation("Generation mismatch: " + Generation + " != " + other.Generation); } SDS exclusiveSource = null; int exclusiveChoice = 0; if (strategy == MergeStrategy.Exclusive || strategy == MergeStrategy.ExclusiveWithPositionCorrection) { exclusiveChoice = SelectExclusiveSource(this, other); exclusiveSource = (exclusiveChoice == -1 ? this : other); } InconsistencyCoverage merged = InconsistencyCoverage.GetMinimum(IC, other.IC); EntityPool pool = new EntityPool(ctx); foreach (var e in this.FinalEntities) { if (IC.IsInconsistentR(ctx.LocalSpace.Relativate(e.ID.Position))) { continue; //for now } pool.Insert(e); } foreach (var e in other.FinalEntities) { if (other.IC.IsInconsistentR(ctx.LocalSpace.Relativate(e.ID.Position))) { continue; //for now } if (pool.Contains(e.ID.Guid)) { continue; } pool.Insert(e); } //at this point we merged all fully consistent entities from either. If the inconsistent areas did not overlap then the result should contain all entities in their consistent state if (!merged.IsFullyConsistent) { if (strategy == MergeStrategy.EntitySelective) { MergeInconsistentEntitiesComp(pool, this, other, merged, ctx); } else { MergeInconsistentEntitiesEx(pool, exclusiveSource, strategy == MergeStrategy.ExclusiveWithPositionCorrection, merged, ctx); } } return(new SDS(Generation, pool.ToArray(), merged)); }
private static void MergeInconsistentEntitiesEx(EntityPool pool, SDS source, bool correctLocations, InconsistencyCoverage ic, EntityChange.ExecutionContext ctx) { const float searchScope = 0.5f; foreach (var e0 in source.FinalEntities) { if (pool.Contains(e0.ID.Guid)) { continue; //already good } var c0 = ctx.LocalSpace.Relativate(e0.ID.Position); if (ic.IsInconsistentR(c0)) { pool.Insert(e0); } else { if (correctLocations) { var c = c0; if (ic.FindInconsistentPlacementCandidateR(ref c, searchScope)) { Entity me = e0.Relocate(c); pool.Insert(me); } } //only increases overaccounted entities: //if (merged.ic.FindInconsistentPlacementCandidate(c0,searchScope)) //{ // Entity me = *e0; // me.coordinates = c0 + shardOffset; // merged.entities.InsertEntity(me); //} } } }
public int ResolveConflictFreeOperations(InconsistencyCoverage ic, EntityChange.ExecutionContext ctx) { int errors = 0; Parallel.ForEach(deferredInserts.Values, bag => { Entity e; InsertPriority best = new InsertPriority(); Interlocked.Add(ref errors, bag.Count - 1); while (bag.TryTake(out e)) { var candidate = new InsertPriority(e, ctx, ic); if (candidate > best) { best = candidate; } } if (best.Destination == null || !Insert(best.Destination)) { Interlocked.Increment(ref errors); } }); deferredInserts.Clear(); Parallel.ForEach(fullMap.Values, ctr => { if (ctr.deferredUpdates.IsEmpty) { return; } Tuple <EntityID, Entity> tuple; bool inc = ic.IsInconsistentR(ctx.LocalSpace.Relativate(ctr.entity.ID.Position)); MovementPriority best = new MovementPriority(); Interlocked.Add(ref errors, ctr.deferredUpdates.Count - 1); while (ctr.deferredUpdates.TryTake(out tuple)) { bool destInc = ic.IsInconsistentR(ctx.LocalSpace.Relativate(tuple.Item2.ID.Position)); var candidate = new MovementPriority(ctr.entity, tuple.Item1, tuple.Item2, ctx, inc, destInc); if (candidate > best) { best = candidate; } } if (best.Destination != null) { ctr.entity = best.Destination; tree = null; } else { Interlocked.Increment(ref errors); } }); EntityID id; while (deferredRemovals.TryTake(out id)) { if (CheckFindAndRemove(id)) { tree = null; } else { errors++; } } return(errors); }
private static void MergeInconsistentEntitiesComp(EntityPool pool, SDS s0, SDS s1, InconsistencyCoverage ic, EntityChange.ExecutionContext ctx) { var a = new EntityPool(s0.FinalEntities, ctx); var b = new EntityPool(s1.FinalEntities, ctx); const float searchScope = 0.5f; foreach (var e0 in a) { if (pool.Contains(e0.ID.Guid)) { continue; //already good } //entity is inconsistent and not in merged state yet var c0 = ctx.LocalSpace.Relativate(e0.ID.Position); var e1 = b.Find(e0.ID.Guid); var c1 = e1 != null?ctx.LocalSpace.Relativate(e1.ID.Position) : Vec3.Zero; if (!ic.IsInconsistentR(c0)) { //this is tricky. entity not merged, but would reside in consistent space (bad). if (e1 != null) { // ASSERT__(b.ic.IsInconsistent(c1)); //otherwise it would have been added prior, and we would not be here //so this entity exists in both SDS' { //we now have the same entity twice, both inconsistent, residing each in the consistent space of the other SDS' //let's assume the entity isn't supposed to exist here anyways Entity candidate = null; int sc = s0.IC.GetInconsistencyAtR(c0).CompareTo(s1.IC.GetInconsistencyAtR(c1)); if (sc < 0) { candidate = e0; } else if (sc > 0) { candidate = e1; } else if (e0.CompareTo(e1) < 0) { candidate = e0; } else { candidate = e1; } var c = ctx.LocalSpace.Relativate(candidate.ID.Position); if (ic.FindInconsistentPlacementCandidateR(ref c, searchScope)) { Entity me = candidate.Relocate(ctx.LocalSpace.DeRelativate(c)); pool.Insert(me); } } } else { //entity exists only in local SDS. //let's assume the entity isn't supposed to exist here anyways //TEntityCoords c = Frac(e0->coordinates); //if (merged.ic.FindInconsistentPlacementCandidate(c,searchScope)) //{ // Entity copy = *e0; // copy.coordinates = c + shardOffset; // //ASSERT__(merged.ic.IsInconsistent(Frac(copy.coordinates))); // merged.entities.InsertEntity(copy); //} //else // FATAL__("bad"); } } else { //entity location is inconsistent in both SDS'. This is expected to be the most common case if (e1 != null) { if (e1.Equals(e0)) { //probably actually consistent // ASSERT__(merged.ic.IsInconsistent(Frac(e0->coordinates))); pool.Insert(e0); } else { if (!ic.IsInconsistentR(c1)) { Debug.Assert(ic.IsInconsistentR(c0)); pool.Insert(e0); } else { Entity candidate = null; int sc = s0.IC.GetInconsistencyAtR(c0).CompareTo(s1.IC.GetInconsistencyAtR(c1)); if (sc < 0) { candidate = e0; } else if (sc > 0) { candidate = e1; } else if (e0.CompareTo(e1) < 0) { candidate = e0; } else { candidate = e1; } //common case. Choose one //ASSERT__(ic.IsInconsistentR(candidate->coordinates-shardOffset)); pool.Insert(candidate); } } } else { //only e0 exists int sc = s0.IC.GetInconsistencyAtR(c0).CompareTo(s1.IC.GetInconsistencyAtR(c0)); //ASSERT__(merged.ic.IsInconsistent(Frac(e0->coordinates))); if (sc <= 0) { pool.Insert(e0); } } } } foreach (var e0 in b) { if (pool.Contains(e0.ID.Guid)) { continue; //already good } //entity is inconsistent and not in merged state yet var c0 = ctx.LocalSpace.Relativate(e0.ID.Position); //const auto c1 = e1 ? (e1->coordinates - shardOffset) : TEntityCoords(); if (!ic.IsInconsistentR(c0)) { #if false //this is tricky. entity not merged, but would reside in consistent space (bad). if (e1) { //case handled //FATAL__("bad"); } else { //entity exists only in local SDS. //let's assume the entity isn't supposed to exist here anyways //TEntityCoords c = Frac(e0->coordinates); //if (merged.ic.FindInconsistentPlacementCandidate(c,searchScope)) //{ // Entity copy = *e0; // copy.coordinates = c + shardOffset; // //ASSERT__(merged.ic.IsInconsistent(Frac(copy.coordinates))); // merged.entities.InsertEntity(copy); //} /* else * FATAL__("bad");*/ } #endif } else { var e1 = a.Find(e0.ID.Guid); //entity location is inconsistent in both SDS'. This is expected to be the most common case if (e1 != null) { //case handled //FATAL__("bad"); } else { //only e0 exists int sc = s0.IC.GetInconsistencyAtR(c0).CompareTo(s1.IC.GetInconsistencyAtR(c0)); //ASSERT__(merged.ic.IsInconsistent(Frac(e0->coordinates))); if (sc >= 0) { pool.Insert(e0); } } } } }
public List <EntityError> Evolve(IReadOnlyList <Entity> entities, Dictionary <Guid, ClientMessage[]> clientMessages, InconsistencyCoverage ic, TimeSpan budget, EntityChange.ExecutionContext ctx) { if (ic.Size != InconsistencyCoverage.CommonResolution) { throw new IntegrityViolation("Trying to evolve with an IC of size " + ic.Size + ". Should be " + InconsistencyCoverage.CommonResolution); } int numErrors = 0; List <Task> tasks = new List <Task>(); List <Entity.TimeTrace> tables = new List <Entity.TimeTrace>(); ClientMessage[] clientBroadcasts = null; if (clientMessages != null) { clientMessages.TryGetValue(Guid.Empty, out clientBroadcasts); } Stopwatch watch0 = new Stopwatch(); object lazyLock = new object(); bool exceeded = false; var rs = new LazyList <EntityError>(); Parallel.For(0, entities.Count, i => { Entity e = entities[i]; ClientMessage[] messages = null; if (clientMessages != null) { clientMessages.TryGetValue(e.ID.Guid, out messages); } var t = new Entity.TimeTrace(watch0); //tables.Add(t); EntityLogic st = null; try { if (!exceeded) { st = e.Evolve(t, this, Helper.Concat(clientBroadcasts, messages), ctx, ic.IsInconsistentR(ctx.LocalSpace.Relativate(e.ID.Position))); if (e.transientDeserializedLogic != null) { throw new IntegrityViolation("Transient deserialized logic was not wiped"); } } if (exceeded || watch0.Elapsed > budget) { exceeded = true; throw new TimeBudgetException(budget, t); } } catch (AssertFailedException) { throw; } catch (Exception ex) { if (st == null) { st = (EntityLogic)Helper.Deserialize(e.SerialLogicState); } var error = new EntityError(e, st, ex); lock (lazyLock) rs.Add(error); ic.FlagInconsistentR(ctx.LocalSpace.Relativate(e.ID.Position)); Interlocked.Increment(ref numErrors); } }); /* * * watch0.Start(); * foreach (var e in entities) * { * EntityMessage[] messages = null; * if (clientMessages != null) * { * clientMessages.TryGetValue(e.ID.Guid, out messages); * } * var t = new Entity.TimeTrace(watch0); * tables.Add(t); * tasks.Add(e.EvolveAsync(t,this, roundNumber, maySendMessages,Helper.Concat(clientBroadcasts, messages))); * } * int at = 0; * * Stopwatch watch = new Stopwatch(); * watch.Start(); * * LazyList<EntityEvolutionException> rs = new LazyList<EntityEvolutionException>(); * * foreach (var e in entities) * { * try * { * var remaining = (budget - watch.Elapsed).NotNegative(); * if (!tasks[at].Wait( remaining)) * throw new ExecutionException(e.ID, "Failed to execute " + (EntityLogic)Helper.Deserialize(e.SerialLogicState)+" in "+remaining.TotalMilliseconds+" ms"); * } * catch (Exception ex) * { * rs.Add(new EntityEvolutionException(e, ex, tables[at])); * * ic.FlagInconsistentR(Simulation.MySpace.Relativate(e.ID.Position)); * * Interlocked.Increment(ref numErrors); * } * at++; * }*/ return(rs.InternalList); }