private T CreateEntity <T>() where T : RailEntityServer { T entity = RailEntityServer.Create <T>(Resource); entity.AssignId(nextEntityId); nextEntityId = nextEntityId.GetNext(); return(entity); }
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); } } }
/// <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); } }
/// <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(); }
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); }
public void LogRemovedEntity(RailEntityServer entity) { removedEntities.Add(entity.Id, entity); }
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); }
public void RevokeControl(RailEntityServer entity) { RevokeControlInternal(entity); }
public void GrantControl(RailEntityServer entity) { GrantControlInternal(entity); }