private void HandleItemCreated(INetworkEntity item) { if (item is ILayerable layerable) { _layerables.TryAdd(layerable); } }
/// <summary> /// Called when an Update message has arrived, applies the new state to the entity. /// </summary> /// <param name="entityId">The unique identifier for the entity.</param> /// <param name="payload">The object that will be used to apply the entity's starting state.</param> /// <param name="isReckoning">True if this is a reckoning update.</param> /// <param name="time">The time the message was sent, used for projecting the state to current time.</param> private void UpdateEntity(long entityId, long ownerId, object payload, bool isReckoning, double time) { INetworkEntity targetEntity = mEntities.Where(e => e.EntityId == entityId && e.OwnerId == ownerId).SingleOrDefault(); // TODO: automatically create entity? // ignore if null, entity creation message may not have arrived mLog?.Info($"Receiving update from {ownerId} to update entity {payload?.GetType()}"); if (targetEntity != null) { if (targetEntity.OwnerId != this.NetworkId || isReckoning) { targetEntity.UpdateFromState(payload, time); } BroadcastIfServer(entityId, targetEntity.OwnerId, payload, isReckoning ? NetworkMessageType.Reckoning : NetworkMessageType.Update ); } else { mLog.Debug("Couldn't find entity to update: " + entityId); } }
public Session(INetworkEntity entity, int accountID) { if (entity.GetEntityType() != EntityType.PC) { throw new ArgumentException("Cannot start session with non player entity"); } AccountID = accountID; this.Entity = entity; }
/// <summary> /// Returns the owning player. /// </summary> /// <param name="entity">The entity to receive the owning player from</param> /// <returns>The player that owns the entity.</returns> public static IPlayer GetOwner([NotNull] this INetworkEntity entity) { int ownerId = entity.OwnerId; if (entity.OwnerId == -1) { return(entity.Manager.GetServerPlayer()); } return(entity.Manager?.GetPlayer(ownerId)); }
/// <summary> /// Broadcasts a message to all clients to update the provided entity. /// Note that this is broadcast only. No updating should happen until /// a message is received. /// </summary> /// <param name="entity">The entity to update.</param> public void RequestUpdateEntity(INetworkEntity entity) { if (Role == NetworkRole.Server) { UpdateEntity(entity.EntityId, entity.OwnerId, entity.GetState(), true, ServerTime); } else { SendDataMessage(entity, NetworkMessageType.Update); } }
public void AddToEntities(INetworkEntity entityToAdd) { #if DEBUG if (mEntities.Contains(entityToAdd)) { throw new InvalidOperationException("This entity is already part of the list, it can't be added again."); } #endif entityToAdd.EntityId = GetUniqueEntityId(); mEntities.Add(entityToAdd); }
/// <summary> /// Broadcasts a message to all clients to destroy the provided entity. /// Note that this is broadcast only. No destruction should happen until /// a message is received. /// </summary> /// <param name="entity">The entity to destroy.</param> public void RequestDestroyEntity(INetworkEntity entity) { if (Role == NetworkRole.Server) { DestroyEntity(entity.EntityId, entity.OwnerId); } else { SendDataMessage(entity, NetworkMessageType.Destroy); } }
/// <summary> /// Uses the provided entity to compose a data message. /// ReliableSequenced method is suggested to balance performance with deliverability. /// </summary> /// <param name="entity">The entity to build a message from.</param> /// <param name="action">The type of message to send.</param> /// <param name="method">Delivery method.</param> /// <param name="recipient">The recipient connection. Will send to all if null.</param> private void SendDataMessage(INetworkEntity entity, NetworkMessageType action, NetConnection recipient = null) { // clients can't force a message for an entity they don't own if (Role != NetworkRole.Server && entity.OwnerId != NetworkId) { throw new RedGrinException("Cannot send an update for an entity that is not owned by this client!"); } object payload = entity.GetState(); SendDataMessage(entity.EntityId, entity.OwnerId, payload, action, recipient); }
/// <summary> /// Called when a Destroy message has arrived, destroys an entity. /// </summary> /// <param name="entityId">The unique ID of the entity to be destroyed</param> private void DestroyEntity(long entityId) { INetworkEntity target = mEntities.Where(e => e.EntityId == entityId).SingleOrDefault(); if (target != null) { BroadcastIfServer(target.EntityId, target.OwnerId, null, NetworkMessageType.Destroy); mEntities.Remove(target); GameArena?.RequestDestroyEntity(target); } else { mLog.Debug("Couldn't find entity marked for destruction: " + entityId); } }
/// <summary> /// Called when an Update message has arrived, applies the new state to the entity. /// </summary> /// <param name="entityId">The unique identifier for the entity.</param> /// <param name="payload">The object that will be used to apply the entity's starting state.</param> /// <param name="isReckoning">True if this is a reckoning update.</param> /// <param name="time">The time the message was sent, used for projecting the state to current time.</param> private void UpdateEntity(ulong id, object payload, bool isReckoning, double time) { INetworkEntity targetEntity = entities.Where(e => e.Id == id).SingleOrDefault(); if (targetEntity != null) { targetEntity.UpdateFromState(payload, time, isReckoning); BroadcastIfServer(id, payload, isReckoning ? NetworkMessageType.Reckoning : NetworkMessageType.Update); } else { // ignore if null, entity creation message may not have arrived yet log.Debug("Couldn't find entity to update: " + id); } }
/// <summary> /// Called when a Destroy message has arrived, destroys an entity. /// </summary> /// <param name="entityId">The unique ID of the entity to be destroyed</param> private void DestroyEntity(ulong id) { INetworkEntity target = entities.Where(e => e.Id == id).SingleOrDefault(); if (target != null) { log.Debug($"Destroying entity {id}"); BroadcastIfServer(target.Id, null, NetworkMessageType.Destroy); entities.Remove(target); GameArena?.HandleDestroyEntity(target); } else { log.Debug($"Couldn't find entity marked for destruction: {id}"); } }
/// <summary> /// Sends a Destroy message for all entities owned by a specific NetworkId. /// Usually called in Server mode when a client disconnects to update other clients /// </summary> /// <param name="ownerId">The OwnerId of entities to destroy.</param> private void DestroyAllOwnedById(long ownerId) { for (int i = mEntities.Count - 1; i > -1; i--) { INetworkEntity entity = mEntities[i]; if (entity.OwnerId == ownerId) { SendDataMessage(entity, NetworkMessageType.Destroy); if (Role == NetworkRole.Server) { DestroyEntity(entity.EntityId, ownerId); } } } }
/// <summary> /// Called when a Create message has arrived, gets an entity from the game screen. /// </summary> /// <param name="ownerId">The NetworkId of the peer that controls the new entity.</param> /// <param name="entityId">The unique identifier for the entity.</param> /// <param name="payload">The object that will be used to apply the entity's starting state.</param> /// <param name="time">The time the message was sent, used for projecting the state to current time.</param> private void CreateEntity(ulong id, object payload, double time) { // check entity with ID already exists INetworkEntity targetEntity = entities.Where(e => e.Id == id).SingleOrDefault(); if (targetEntity == null) { targetEntity = GameArena?.HandleCreateEntity(id, payload); // force correct unique ID targetEntity.Id = id; } targetEntity.UpdateFromState(payload, time, false); entities.Add(targetEntity); BroadcastIfServer(id, payload, NetworkMessageType.Create); }
/// <summary> /// Called when an Update message has arrived, applies the new state to the entity. /// </summary> /// <param name="entityId">The unique identifier for the entity.</param> /// <param name="payload">The object that will be used to apply the entity's starting state.</param> /// <param name="isReckoning">True if this is a reckoning update.</param> /// <param name="time">The time the message was sent, used for projecting the state to current time.</param> private void UpdateEntity(long entityId, long ownerId, object payload, bool isReckoning, double time) { INetworkEntity targetEntity = mEntities.Where(e => e.EntityId == entityId && e.OwnerId == ownerId).SingleOrDefault(); // TODO: automatically create entity? // ignore if null, entity creation message may not have arrived if (targetEntity != null) { targetEntity.UpdateState(payload, time, isReckoning); BroadcastIfServer(entityId, targetEntity.OwnerId, payload, isReckoning ? NetworkMessageType.Update : NetworkMessageType.Reckoning); } else { mLog.Debug("Couldn't find entity to update: " + entityId); } }
/// <summary> /// Called when a Create message has arrived, gets an entity from the game screen. /// </summary> /// <param name="ownerId">The NetworkId of the peer that controls the new entity.</param> /// <param name="entityId">The unique identifier for the entity.</param> /// <param name="payload">The object that will be used to apply the entity's starting state.</param> /// <param name="time">The time the message was sent, used for projecting the state to current time.</param> private void CreateEntity(long ownerId, long entityId, object payload, double time) { // check if already exists. INetworkEntity targetEntity = mEntities.Where(e => e.EntityId == entityId && e.OwnerId == ownerId).SingleOrDefault(); if (targetEntity == null) { targetEntity = GameArena.RequestCreateEntity(ownerId, entityId, payload); } else { mLog.Warning("Attempted to create entity for ID that already exists: " + entityId); } targetEntity.OwnerId = ownerId; targetEntity.EntityId = entityId; targetEntity.UpdateState(payload, time); mEntities.Add(targetEntity); BroadcastIfServer(entityId, ownerId, payload, NetworkMessageType.Create); }
/// <summary> /// Sends a Destroy message for all entities that no longer have an owner /// </summary> private void DestroyUnownedEntities() { // ensure our connections list is accurate RefreshConnectionCollection(); for (int i = entities.Count - 1; i > -1; i--) { INetworkEntity entity = entities[i]; var id = entity.GetClientId(); // if we don't own the entity and its ClientId isn't in the map // the client must have disconnected and the entity should be destroyed if (id != ClientId && !clientIdConnectionMap.ContainsKey(entity.GetClientId())) { SendDataMessage(entity, NetworkMessageType.Destroy); if (Role == NetworkRole.Server) { DestroyEntity(entity.Id); } } } }
/// <summary> /// Returns true if the entity belongs to the local client or false if it belongs to a remote client. /// </summary> /// <param name="entity">The entity to test for</param> /// <returns>true if the entity belongs to the local client or false if it belongs to a remote client</returns> /// <exception cref="InvalidOperationException">If the Entity is not initialized</exception> public static bool IsLocal(this INetworkEntity entity) { if (entity.Manager == null) { return(false); } var connection = entity.Manager?.Connection; int localClientId = connection.LocalClient.ClientId; if (localClientId == -1) { return(false); } if (connection.IsServer() && entity.IsOwnedByScene()) { return(true); } return(localClientId != -1 && entity.OwnerId == localClientId); }
/// <summary> /// Called when a Create message has arrived, gets an entity from the game screen. /// </summary> /// <param name="ownerId">The NetworkId of the peer that controls the new entity.</param> /// <param name="entityId">The unique identifier for the entity.</param> /// <param name="payload">The object that will be used to apply the entity's starting state.</param> /// <param name="time">The time the message was sent, used for projecting the state to current time.</param> private void CreateEntity(long ownerId, long entityId, object payload, double time) { // this is a brand new entity, get a new ID if (entityId == -1) { if (Role == NetworkRole.Server) { entityId = GetUniqueEntityId(); } else { var msg = "Something went wrong, client received bad entity ID."; mLog.Error(msg); throw new RedGrinException(msg); } } // check entity with ID already exists INetworkEntity targetEntity = mEntities.Where(e => e.EntityId == entityId).SingleOrDefault(); if (targetEntity == null) { targetEntity = GameArena?.RequestCreateEntity(ownerId, payload); } else { var msg = "Attempted to create entity for ID that already exists: " + entityId; mLog?.Error(msg); throw new RedGrinException(msg); } targetEntity.UpdateFromState(payload, time); targetEntity.OwnerId = ownerId; targetEntity.EntityId = entityId; mEntities.Add(targetEntity); BroadcastIfServer(entityId, ownerId, payload, NetworkMessageType.Create); }
/// <summary> /// Destroys given entity on all clients. /// </summary> /// Unregisters the given entity and sends its destroy event to all other clients. /// <param name="entity">The entity to destroy</param> public static void Destroy(this INetworkEntity entity) { entity.Manager?.DestroyEntity(entity); }
/// <summary> /// Returns the owning player. /// </summary> /// <param name="entity">The entity to receive the owning player from</param> /// <returns>The player that owns the entity.</returns> public static TPlayerEntity GetOwner <TPlayerEntity>([NotNull] this INetworkEntity entity) where TPlayerEntity : class, IPlayer { return(entity.Manager?.GetPlayer(entity.OwnerId) as TPlayerEntity); }
/// <summary> /// Uses the provided entity to compose a data message. /// ReliableSequenced method is suggested to balance performance with deliverability. /// </summary> /// <param name="entity">The entity to build a message from.</param> /// <param name="action">The type of message to send.</param> /// <param name="method">Delivery method.</param> /// <param name="recipient">The recipient connection. Will send to all if null.</param> private void SendDataMessage(INetworkEntity entity, NetworkMessageType action, NetConnection recipient = null) { object payload = entity.GetState(); SendDataMessage(entity.Id, payload, action, recipient); }
public static T[] GetNetworkComponents <T>(this INetworkEntity entity) where T : INetworkComponent { return(entity.Components.OfType <T>().ToArray()); }
public static T GetNetworkComponent <T>(this INetworkEntity entity) where T : INetworkComponent { return(entity.Components.OfType <T>().FirstOrDefault()); }
/// <summary> /// Checks whether an entity is owned by this client on the /// network by unpacking the client ID from the entity and /// comparing it to the network's client ID /// </summary> /// <param name="entity">The entity to check</param> /// <returns>True if entity's owner ID matches this Client ID</returns> public static bool IsOwned(this INetworkEntity entity) { var id = UnpackClientId(entity.Id); return(id == NetworkManager.Self.ClientId); }
/// <summary> /// Returns if the entity is owned by the scene. /// </summary> /// Scene-owned entities are controlled by the server. /// /// <param name="entity">The entity to test for</param> /// <returns>Whether the entity is owned by the scene</returns> public static bool IsOwnedByScene([NotNull] this INetworkEntity entity) { return(entity.OwnerId == -1); }
/// <summary> /// Gets the owning client's identifier from an entity's UniqueId /// </summary> /// <param name="entity">The INetworkEntity to use</param> /// <returns>The client ID that created the entity</returns> public static byte GetClientId(this INetworkEntity entity) { return(UnpackClientId(entity.Id)); }
/// <summary> /// Sets the Unique Id on the provided entity by packing the /// client ID and entity Id values. /// </summary> /// <param name="entity">The INetworkEntity to affect</param> /// <param name="clientId">The client identifier creating the entity</param> /// <param name="entityId">The entity's local identifier on the client</param> public static void SetEntityId(this INetworkEntity entity, byte clientId, uint entityId) { entity.Id = PackUniqueId(clientId, entityId); }
private void HandleNetworkEntityRemovedFromPipe(IServiceList <INetworkEntity> sender, INetworkEntity networkEntity) { // Broadcast the delete message to all users who are not also in the new NE's pipe. networkEntity.Messages[Guppy.Network.Constants.Messages.NetworkEntity.Delete].Create( this.Entity.Users.Connections.Except(networkEntity.Pipe?.Users.Connections ?? Enumerable.Empty <NetConnection>())); }
private void HandleNetworkEntityAddedToPipe(IServiceList <INetworkEntity> sender, INetworkEntity networkEntity) { // Broadcast create message through the pipe... networkEntity.Messages[Guppy.Network.Constants.Messages.NetworkEntity.Create].Create(this.Entity); }
public static ISerializer GetSerializer([NotNull] this INetworkEntity entity) { return(entity.Manager?.GetSerializer()); }