Beispiel #1
0
        /// <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);
        }
Beispiel #2
0
        /// <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);
                    }
                }
            }
        }
Beispiel #3
0
        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++;
        }
Beispiel #4
0
        /// <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);
            }
        }