Ejemplo n.º 1
0
        private T CreateEntity <T>()
            where T : RailEntityServer
        {
            T entity = RailEntityServer.Create <T>(Resource);

            entity.AssignId(nextEntityId);
            nextEntityId = nextEntityId.GetNext();
            return(entity);
        }
Ejemplo n.º 2
0
        private void ProduceScoped(Tick serverTick, IEnumerable <RailEntityServer> activeEntities)
        {
            // TODO: should be doable without the copy using a LINQ expression.
            entryList.Clear();

            foreach (RailEntityServer entity in activeEntities)
            {
                if (entity.IsRemoving)
                {
                }
                // Controlled entities are always in scope to their controller
                else if (entity.Controller == owner)
                {
                    entryList.Add(new KeyValuePair <float, RailEntityBase>(float.MinValue, entity));
                }
                else if (GetPriority(entity, serverTick, out float priority))
                {
                    entryList.Add(new KeyValuePair <float, RailEntityBase>(priority, entity));
                }
                else if (RailEntityBase.CanFreeze)
                {
                    // We only want to send a freeze state if we aren't already frozen
                    RailViewEntry latest = ackedByClient.GetLatest(entity.Id);
                    if (latest.IsFrozen == false)
                    {
                        frozenList.Add(
                            RailStateDelta.CreateFrozen(stateCreator, serverTick, entity.Id));
                    }
                }
            }

            entryList.Sort(priorityComparer);
            foreach (KeyValuePair <float, RailEntityBase> entry in entryList)
            {
                RailViewEntry    latest = ackedByClient.GetLatest(entry.Value.Id);
                RailEntityServer entity = entry.Value as RailEntityServer;

                // Force a complete update if the entity is frozen so it unfreezes
                // TODO: Currently if we're unfreezing we force the server to send a
                //       delta with the FULL mutable dataset. There is probably a
                //       less wasteful option, like having clients send back
                //       what tick they last received a non-frozen packetToClient on.
                //       However, this would cause some tedious tick comparison.
                //       Should investigate a smarter way to handle this later.
                RailStateDelta delta = entity.ProduceDelta(
                    stateCreator,
                    latest.LastReceivedTick,
                    owner,
                    latest.IsFrozen);

                if (delta != null)
                {
                    activeList.Add(delta);
                }
            }
        }
Ejemplo n.º 3
0
        /// <summary>
        ///     Marks an entity for removal from the room and presumably destruction.
        ///     This is deferred until the next frame.
        /// </summary>
        public void MarkForRemoval(RailEntityBase entity)
        {
            if (entity.IsRemoving == false)
            {
                RailEntityServer serverEntity = entity as RailEntityServer;
                if (serverEntity == null)
                {
                    throw new ArgumentNullException(
                              nameof(entity),
                              $"unexpected type of entity to remove: {entity}");
                }

                serverEntity.MarkForRemoval();
                server.LogRemovedEntity(serverEntity);
            }
        }
Ejemplo n.º 4
0
        /// <summary>
        ///     Cleans out any removed entities from the removed list
        ///     if they have been acked by all clients.
        /// </summary>
        private void CleanRemovedEntities()
        {
            // TODO: Retire the Id in all of the views as well?

            foreach (KeyValuePair <EntityId, RailEntityServer> kvp in removedEntities)
            {
                bool             canRemove = true;
                EntityId         id        = kvp.Key;
                RailEntityServer entity    = kvp.Value;

                foreach (RailServerPeer peer in clients.Values)
                {
                    Tick lastSent = peer.Scope.GetLastSent(id);
                    if (lastSent.IsValid == false)
                    {
                        continue;                            // Was never sent in the first place
                    }
                    Tick lastAcked = peer.Scope.GetLastAckedByClient(id);
                    if (lastAcked.IsValid && lastAcked >= entity.RemovedTick)
                    {
                        continue; // Remove tick was acked by the client
                    }

                    // Otherwise, not safe to remove
                    canRemove = false;
                    break;
                }

                if (canRemove)
                {
                    toRemove.Add(entity);
                }
            }

            toRemove.ForEach(entityToRemove => removedEntities.Remove(entityToRemove.Id));
            toRemove.Clear();
        }
Ejemplo n.º 5
0
        public void ServerUpdate()
        {
            Tick = Tick.GetNext();
            OnPreRoomUpdate(Tick);

            // Collect the entities in the priority order and
            // separate them out for either update or removal
            foreach (RailEntityBase railEntityBase in Entities)
            {
                RailEntityServer entity = (RailEntityServer)railEntityBase;
                if (entity.ShouldRemove)
                {
                    ToRemove.Add(entity);
                }
                else
                {
                    ToUpdate.Add(entity);
                }
            }

            // Wave 0: Remove all sunsetted entities
            ToRemove.ForEach(RemoveEntity);

            // Wave 1: Start/initialize all entities
            ToUpdate.ForEach(e => e.PreUpdate());

            // Wave 2: Update all entities
            ToUpdate.ForEach(e => e.ServerUpdate());

            // Wave 3: Post-update all entities
            ToUpdate.ForEach(e => e.PostUpdate());

            ToRemove.Clear();
            ToUpdate.Clear();
            OnPostRoomUpdate(Tick);
        }
Ejemplo n.º 6
0
 public void LogRemovedEntity(RailEntityServer entity)
 {
     removedEntities.Add(entity.Id, entity);
 }
Ejemplo n.º 7
0
        private void ClientServerCommunication()
        {
            // Initialization
            CampaignTimeControlMode      expectedTimeControl = CampaignTimeControlMode.StoppablePlay;
            RailClientRoom               clientRoom          = m_Client.StartRoom();
            RailServerRoom               serverRoom          = m_Server.StartRoom();
            RailEntityServer <SomeState> entityServerSide    =
                serverRoom.AddNewEntity <RailEntityServer <SomeState> >();

            entityServerSide.State.Mode = expectedTimeControl;
            m_Server.AddClient(m_PeerServerSide.Object, "");
            m_Client.SetPeer(m_PeerClientSide.Object);
            Assert.Empty(clientRoom.Entities);
            Assert.Single(serverRoom.Entities);

            // Sync entity from server to client
            for (int i = 0; i < RailConfig.SERVER_SEND_RATE + RailConfig.CLIENT_SEND_RATE + 1; ++i)
            {
                m_ConClientSide.ExecuteSends();
                m_Server.Update();
                m_ConServerSide.ExecuteSends();
                m_Client.Update();
            }

            // The client has received the entity.
            Assert.Single(clientRoom.Entities);
            Assert.Single(serverRoom.Entities);

            // Clients representation of the entity is identical to the server
            RailEntityBase entityProxy = clientRoom.Entities.First();

            Assert.IsType <RailEntityClient <SomeState> >(entityProxy);
            RailEntityClient <SomeState> entityClientSide =
                entityProxy as RailEntityClient <SomeState>;

            Assert.NotNull(entityClientSide);
            Assert.Equal(entityServerSide.Id, entityProxy.Id);
            Assert.Equal(expectedTimeControl, entityServerSide.State.Mode);
            Assert.Equal(expectedTimeControl, entityClientSide.State.Mode);

            // Change the entity on server side and sync to the client
            expectedTimeControl         = CampaignTimeControlMode.Stop;
            entityServerSide.State.Mode = expectedTimeControl;

            // Let the server detect the change and send the packet
            bool bWasSendTick = false;

            while (!bWasSendTick)
            {
                m_Server.Update();
                bWasSendTick = serverRoom.Tick.IsSendTick(RailConfig.SERVER_SEND_RATE);
            }

            // Let the client receive & process the packet. We need to bring the client up to the same tick as the server to see the result.
            while (clientRoom.Tick < serverRoom.Tick)
            {
                m_ConServerSide.ExecuteSends();
                m_Client.Update();
            }

            Assert.Equal(expectedTimeControl, entityServerSide.State.Mode);
            Assert.Equal(expectedTimeControl, entityClientSide.State.Mode);
        }
Ejemplo n.º 8
0
 public void RevokeControl(RailEntityServer entity)
 {
     RevokeControlInternal(entity);
 }
Ejemplo n.º 9
0
 public void GrantControl(RailEntityServer entity)
 {
     GrantControlInternal(entity);
 }