/// <summary> /// Creates an entity of a given type and adds it to the world. /// </summary> public Entity AddNewEntity(Type type) { int typeCode = _pools.EntityTypeToKey[type]; //var entity = Entity.Create(factoryType); var entity = _pools.EntityFactories[typeCode].Allocate(); //entity.factoryType = factoryType; entity.StateBase = _pools.CreateState(typeCode); entity.Id = nextEntityId; nextEntityId = nextEntityId.GetNext(); //world.AddEntity(entity); World_Entities.Add(entity.Id, entity); return(entity); }
public void Update() { if (controller == null) { return; } DoStart(); controller.RemoteClock.Update(); /// <summary> /// Updates the room a number of ticks. If we have entities waiting to be /// added, this function will check them and add them if applicable. /// </summary> // ClientManager.UpdateRoom var estimatedServerTick = controller.EstimatedRemoteTick; { /// <summary> /// Checks to see if any pending entities can be added to the world and /// adds them if applicable. /// </summary> // ClientManager.UpdatePendingEntities //var serverTick = estimatedServerTick; foreach (var entity in pendingEntities.Values) { var hasReadyState = entity.IncomingStates.GetLatestAt(estimatedServerTick) != null; if (hasReadyState) { //world.AddEntity(entity); World_Entities.Add(entity.Id, entity); entitiesToRemove.Add(entity); } } foreach (var entity in entitiesToRemove) { pendingEntities.Remove(entity.Id); } entitiesToRemove.Clear(); } // ClientWorld.ClientUpdate // Perform regular update cadence and mark entities for removal World_Update(estimatedServerTick, entity => { { // Entity.UpdateAuthState // Apply all un-applied deltas to the auth state var toApply = entity.IncomingStates.GetRangeAndNext(entity.AuthTick, World_Tick, out var next); foreach (var delta in toApply) { if (entity.AuthTick == Tick.START) { Debug.Assert(delta.State.HasImmutableData); } if (!delta.IsFrozen) { StateUtils.ApplyDelta(entity.AuthStateBase, delta); } entity.ShouldBeFrozen = delta.IsFrozen; entity.AuthTick = delta.Tick; } // If there was a next state, update the next state var canGetNext = !entity.ShouldBeFrozen; if (canGetNext && next != null && !next.IsFrozen) { entity.NextStateBase.OverwriteFrom(entity.AuthStateBase); StateUtils.ApplyDelta(entity.NextStateBase, next); entity.NextTick = next.Tick; } else { entity.NextTick = Tick.INVALID; } } entity.StateBase.OverwriteFrom(entity.AuthStateBase); //entity.Initialize(); if (!entity.HasStarted) { Entity_OnStart(entity); } entity.HasStarted = true; //entity.NotifyControllerChanged(); if (entity.DeferNotifyControllerChanged) { Entity_OnControllerChanged(entity); } entity.DeferNotifyControllerChanged = false; { // Entity.SetFreeze var shouldBeFrozen = entity.ShouldBeFrozen; if (!entity.IsFrozen && shouldBeFrozen) { Entity_OnFrozen(entity); } else if (entity.IsFrozen && !shouldBeFrozen) { Entity_OnUnfrozen(entity); } entity.IsFrozen = shouldBeFrozen; } if (!entity.IsFrozen) { if (entity.Controller == null) { Entity_UpdateProxy(entity); } else { entity.NextTick = Tick.INVALID; // Entity.UpdateControlled Debug.Assert(entity.Controller != null); if (entity.OutgoingCommands.Count < Config.COMMAND_BUFFER_COUNT) { var command = _pools.CommandPool.Allocate(); command.ClientTick = localTick; command.IsNewCommand = true; Entity_UpdateControlGeneric(entity, command); entity.OutgoingCommands.Enqueue(command); } // Entity.UpdatePredicted // Bring the main state up to the latest (apply all deltas) var deltas = entity.IncomingStates.GetRange(entity.AuthTick); foreach (var delta in deltas) { StateUtils.ApplyDelta(entity.StateBase, delta); } Entity_Revert(entity); // Forward-simulate foreach (var command in entity.OutgoingCommands) { Entity_ApplyControlGeneric(entity, command); command.IsNewCommand = false; } } Entity_PostUpdate(entity); } }, entity => Entity_OnShutdown(entity)); if (localTick.IsSendTick()) { // TODO: Sort me by most recently sent var controlledEntities = controller.ControlledEntities; { // internal void SendPacket(Tick localTick, IEnumerable<Entity> controlledEntities) // Packet.PrepareSend //var packet_ = controller.ReusableOutgoing; //packet_.Reset(); var packet_ = new ClientOutgoingPacket(); packet_.Initialize(localTick, controller.RemoteClock.LatestRemote, controller.ProcessedEventHistory.Latest, FilterOutgoingEvents(controller)); var commandUpdates = ProduceCommandUpdates(controlledEntities); { // public void Populate(IEnumerable<CommandUpdate> commandUpdates, View view) packet_.PendingCommandUpdates.AddRange(commandUpdates); // Integrate foreach (var pair in controller.LocalView.LatestUpdates) { packet_.View.RecordUpdate(pair.Key, pair.Value); } } Order(packet_.View); /// <summary> /// Views sort in descending tick order. When sending a view to the server /// we send the most recent updated entities since they're the most likely /// to actually matter to the server/client scope. /// </summary> void Order(View view) { // TODO: If we have an entity frozen, we probably shouldn't constantly // send view acks for it unless we're getting requests to freeze. view.SortList.Clear(); view.SortList.AddRange(view.LatestUpdates); view.SortList.Sort(_viewComparer); view.SortList.Reverse(); } // Send the packet var t = _protocol.Encode(packet_); controller.Connection.SendPayload(t.Item1, t.Item2); foreach (var commandUpdate in packet_.SentCommandUpdates) { commandUpdate.Entity.LastSentCommandTick = localTick; } } } localTick++; }