/// <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); }
/// <summary> /// Responsible for encoding and decoding packet information. /// </summary> //public class Interpreter /// <summary> /// Interpreter for converting byte input to a BitBuffer. /// </summary> //protected Interpreter Interpreter { get; } = new Interpreter(); protected void ProcessPayload(Controller connection, Packet reusableIncoming, Func <T, bool> getSafeToExecute) { /// <summary> /// Records acknowledging information for the packet. /// </summary> // Connection.ProcessPacket connection.RemoteClock.UpdateLatest(reusableIncoming.SenderTick); foreach (var evnt in FilterIncomingEvents(reusableIncoming.Events)) { /// <summary> /// Handles the execution of an incoming event. /// </summary> // private void ProcessEvent(Event evnt) { if (evnt.EntityId.IsValid) { World_Entities.TryGetValue(evnt.EntityId, out var entity); if (getSafeToExecute(entity)) { Event_InvokeEntity(evnt, connection, entity); } } else { Event_Invoke(evnt, connection); } connection.ProcessedEventHistory = connection.ProcessedEventHistory.Store(evnt.EventId); } } var ackedEventId = reusableIncoming.AckEventId; { /// <summary> /// Removes any acked or expired outgoing events. /// </summary> //void CleanOutgoingEvents(SequenceId ackedEventId) if (ackedEventId.IsValid != false) { while (connection.OutgoingEvents.Count > 0) { var top = connection.OutgoingEvents.Peek(); // Stop if we hit an un-acked reliable event if (top.IsReliable) { if (top.EventId > ackedEventId) { break; } } // Stop if we hit an unreliable event with remaining attempts else { if (top.Attempts > 0) { break; } } var val = connection.OutgoingEvents.Dequeue(); val.Pool.Deallocate(val); } } } /// <summary> /// Gets all events that we haven't processed yet, in order with no gaps. /// </summary> IEnumerable <Event> FilterIncomingEvents(IEnumerable <Event> events) { foreach (var evnt in events) { if (connection.ProcessedEventHistory.IsNewId(evnt.EventId)) { yield return(evnt); } } } }
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++; }
/// <summary> /// Wraps an incoming connection in a peer and stores it. /// </summary> public void AddConnection(IConnection connection) { if (!clients.ContainsKey(connection)) { var controller = new ServerController(connection); // ServerConnection.ctor controller.Connection.PayloadReceived += (data, length) => { //Console.WriteLine($"data {data.Length} length {length}"); // 253 245 var reusableIncoming = _protocol.Decode(data, length); if (reusableIncoming != default) { ProcessPayload(controller, reusableIncoming, entity => { // Entity events can only be executed on controlled entities return(entity != null && entity.Controller == controller); }); //onProcessPacket(); // Integrate foreach (var pair in reusableIncoming.View.LatestUpdates) { controller.Scope.AckedByClient.RecordUpdate(pair.Key, pair.Value); } // client.PacketReceived.Invoke(client, clientPacket); { // client.PacketReceived += (_peer, clientPacket) => foreach (var update in reusableIncoming.ReceivedCommandUpdates) { { // ServerManager.ProcessCommandUpdate if (World_Entities.TryGetValue(update.EntityId, out var entity)) { foreach (var command in update.Commands.GetValues()) { if (entity.Controller == controller && entity.IncomingCommands.Store(command)) { command.IsNewCommand = true; } else { command.Pool.Deallocate(command); } } } } } } } else { Console.WriteLine("Bad packet read, discarding..."); } reusableIncoming.Reset(); }; clients.Add(connection, controller); ControllerJoined.Invoke(controller); } }