protected override void HandleMessage(EntityActorMessageContext messageContext, DefaultEntityActorStateContainer state, PendingSpellCastFinishedWaitingMessage message) { //This indicates that the spell cast has finished. state.EntityData.SetFieldValue(EntityObjectField.UNIT_FIELD_CASTING_SPELLID, 0); messageContext.Entity.TellSelf(new ImmediateCastSpellMessage(message.Pending)); }
protected override void HandleMessage(EntityActorMessageContext messageContext, DefaultEntityActorStateContainer state, InteractWithEntityActorMessage message) { if(Logger.IsDebugEnabled) Logger.Debug($"Entity: {message.EntityInteracting} Interacted with Creature: {state.EntityGuid}"); messageContext.Sender.Tell(new AddPlayerExperienceActorMessage(50)); //Let's create the path data. IMovementGenerator<GameObject> movementGenerator = MovementGeneratorMappable.RetrieveEntity(state.EntityGuid); var playerMovementGenerator = MovementGeneratorMappable.RetrieveEntity(message.EntityInteracting); //WARNING: NEVER DO THIS, NOT SAFE TO ACCESS PLAYER DATA. Vector3 creatureCurrentPosition = movementGenerator.CurrentPosition; Vector3 playerCurrentPosition = playerMovementGenerator.CurrentPosition; UnityAsyncHelper.UnityMainThreadContext.Post(o => { var path = new NavMeshPath(); NavMesh.CalculatePath(creatureCurrentPosition, playerCurrentPosition, NavMesh.AllAreas, path); if(path.status != NavMeshPathStatus.PathComplete) if(Logger.IsWarnEnabled) Logger.Warn($"Produced invalid PathResult: {path.status} for Entity: {state.EntityGuid} Start: {creatureCurrentPosition} End: {playerCurrentPosition}"); //Don't broadcast if not invalid if(path.status == NavMeshPathStatus.PathComplete) messageContext.Entity.Tell(new CreatureSetPathMovementMessage(creatureCurrentPosition, path.corners)); }, null); }
private bool TrySendMessage(object message) { EntityActorMessage castedMessage = (EntityActorMessage)message; EntityActorMessageContext context = new EntityActorMessageContext(Sender, Self, Context.System.Scheduler); return(MessageRouter.RouteMessage(context, ActorState, castedMessage)); }
public void HandleMessage(EntityActorMessageContext messageContext, TEntityStateType state, EntityActorMessage message) { //Assume caller has verified this will work //We downcast so the API consumed is simplier/easier. //We need the interface to not be specific so it can be discovered, registered and handled. HandleMessage(messageContext, state, (TEntityMessageType)message); }
protected override void HandleMessage(EntityActorMessageContext messageContext, DefaultEntityActorStateContainer state, WorldTeleportPlayerEntityActorMessage message) { //Something just told player we are teleporting to another world //TODO: Kind of a potential exploit here. Since the process to transfer sessions //is async they could potentially log off and log back in quickly and avoid a trasnfer //So if this is a FORCED transfer, like for death or something or a kick, they could potentially avoid it?? ProjectVersionStage.AssertBeta(); //TODO: Find a way to make it so actors state is always valid. //If this throws then the entity dies. So no reason to check it. EntitySaveableConfiguration entity = PersistenceConfigurationMappable.RetrieveEntity(state.EntityGuid); entity.isWorldTeleporting = true; ZoneToSeverClient.TryWorldTeleportCharacter(new ZoneServerWorldTeleportCharacterRequest(state.EntityGuid, message.WorldTeleportGameObjectId)) .ContinueWith((task, o) => { //Whether this succeeds or not this continuation will occur //either way we should just disconnect the player because we //either have uncoverable issues or they are going to be transfered after //disconnection. //ConnectionServiceMappable.RetrieveEntity(args.TeleportingPlayer).Disconnect(); //Since this is async the entity might actually be gone, so we used the actor messaging system //To indicate that it should disconnect messageContext.Entity.Tell(new DisconnectNetworkPlayerEntityActorMessage()); }, TaskContinuationOptions.ExecuteSynchronously); }
public bool RouteMessage(EntityActorMessageContext messageContext, TEntityActorStateType state, EntityActorMessage message) { if (messageContext == null) { throw new ArgumentNullException(nameof(messageContext)); } if (state == null) { throw new ArgumentNullException(nameof(state)); } if (message == null) { throw new ArgumentNullException(nameof(message)); } if (EntityHandlerMap.ContainsKey(message.GetType())) { foreach (var handler in EntityHandlerMap[message.GetType()]) { handler.HandleMessage(messageContext, state, message); } return(true); } else { return(false); } }
protected override void HandleMessage(EntityActorMessageContext messageContext, DefaultEntityActorStateContainer state, HealEntityActorCurrentHealthMessage message) { //Increases health clamped by current max health. if (state.EntityData.DataSetIndicationArray.Get((int)EntityObjectField.UNIT_FIELD_HEALTH)) { state.EntityData.SetFieldValue(EntityObjectField.UNIT_FIELD_HEALTH, Math.Min(state.EntityData.GetFieldValue <int>(EntityObjectField.UNIT_FIELD_MAXHEALTH), state.EntityData.GetFieldValue <int>(EntityObjectField.UNIT_FIELD_HEALTH) + message.HealAmount)); } }
protected override void HandleMessage(EntityActorMessageContext messageContext, DefaultEntityActorStateContainer state, ReinitializeEntityActorStatsMessage message) { EntityBaseStatsModel baseStats = GenerateEntityBaseStats(state.EntityGuid, state.EntityData); //TODO: Do base eventually. Right now we're only doing regular health fields since that's all we deal with clientside atm. state.EntityData.SetFieldValue(EntityObjectField.UNIT_FIELD_HEALTH, baseStats.BaseHealth); state.EntityData.SetFieldValue(EntityObjectField.UNIT_FIELD_MAXHEALTH, baseStats.BaseHealth); }
protected override void HandleMessage(EntityActorMessageContext messageContext, DefaultCreatureActorState state, EntityActorInitializationSuccessMessage message) { //We need to initialize our replicateable state here. int creatureLevel = CreatureInitializationRandomGenerator.Value.Next(state.TemplateModel.MinimumLevel, state.TemplateModel.MaximumLevel + 1); //uppbound is exclusive //We should send ourselves the level initialization message because it takes care of stats messageContext.Entity.Tell(new SetEntityActorLevelMessage(creatureLevel)); }
protected override void HandleMessage(EntityActorMessageContext messageContext, DefaultEntityActorStateContainer state, DamageEntityActorCurrentHealthMessage message) { //Simple, if the entity has health we just try to subtract from it. if (state.EntityData.DataSetIndicationArray.Get((int)EntityObjectField.UNIT_FIELD_HEALTH)) { state.EntityData.SetFieldValue(EntityObjectField.UNIT_FIELD_HEALTH, Math.Max(0, state.EntityData.GetFieldValue <int>(EntityObjectField.UNIT_FIELD_HEALTH) - message.Damage)); } }
protected override void HandleMessage(EntityActorMessageContext messageContext, DefaultEntityActorStateContainer state, CreatureSetPathMovementMessage message) { PathBasedMovementData data = new PathBasedMovementData(message.PathPoints, TimeService.CurrentLocalTime); IMovementGenerator <GameObject> generator = MovementGeneratorFactory.Create(new EntityAssociatedData <IMovementData>(state.EntityGuid, data)); MovementDataMappable.ReplaceObject(state.EntityGuid, data); MovementGeneratorMappable.ReplaceObject(state.EntityGuid, generator); }
protected override void HandleMessage(EntityActorMessageContext messageContext, DefaultEntityActorStateContainer state, DisconnectNetworkPlayerEntityActorMessage message) { if (Logger.IsInfoEnabled) { Logger.Info($"Entity: {state.EntityGuid} actor handling disconnection message."); } //TODO: This can throw, but the entity will die. We should make it so that entity data always exists if the actor is alive. ConnectionServiceMappable.RetrieveEntity(state.EntityGuid).Disconnect(); }
protected override void HandleMessage(EntityActorMessageContext messageContext, DefaultEntityActorStateContainer state, ChangeEntityActorDisplayModelMessage message) { //Basically we if (Logger.IsDebugEnabled) { Logger.Debug($"Entity: {state.EntityGuid} changing to ModelId: {message.NewModelId}"); } //We just set the model id, it's fine as simple as this. state.EntityData.SetFieldValue(BaseObjectField.UNIT_FIELD_DISPLAYID, message.NewModelId); }
protected override void HandleMessage(EntityActorMessageContext messageContext, DefaultEntityActorStateContainer state, EntityActorInitializationSuccessMessage message) { //We should send ourselves the level initialization message because it takes care of stats CharacterDataInstance entity = InitialCharacterDataMappable.RetrieveEntity(state.EntityGuid); IEntityDataFieldContainer entityDataFieldContainer = state.EntityData; entityDataFieldContainer.SetFieldValue((int)PlayerObjectField.PLAYER_TOTAL_EXPERIENCE, entity.Experience); //Set the level from the initial experience data. messageContext.Entity.Tell(new SetEntityActorLevelMessage(LevelStrategy.ComputeLevelFromExperience(entity.Experience))); }
protected override void HandleMessage(EntityActorMessageContext messageContext, DefaultEntityActorStateContainer state, CancelSpellCastMessage message) { //If we have a pending cast then cancel it. if (PendingSpellCastMappable.ContainsKey(state.EntityGuid)) { PendingSpellCastMappable.RetrieveEntity(state.EntityGuid).PendingCancel.Cancel(); } //Just set the casting state to 0. state.EntityData.SetFieldValue(EntityObjectField.UNIT_FIELD_CASTING_SPELLID, 0); }
protected override void HandleMessage(EntityActorMessageContext messageContext, BehaviourGameObjectState <AvatarPedestalInstanceModel> state, EntityActorInitializationSuccessMessage message) { //We need to initialize our replicateable state here. //TODO: Better handling of enum names for specialized gameobjects //Basically this is how clients see the avatar id. state.EntityData.SetFieldValue(GameObjectField.RESERVED_DATA_1, state.BehaviourData.AvatarModelId); //avatar pedestal can be interacted with. state.EntityData.AddBaseObjectFieldFlag(BaseObjectFieldFlags.UNIT_FLAG_INTERACTABLE); }
private static void DispatchPendingSpellCast(EntityActorMessageContext messageContext, PendingSpellCastData castData) { //Instant casts can just directly send. if (castData.isInstantCast) { messageContext.Entity.TellSelf(new PendingSpellCastFinishedWaitingMessage(castData)); } else { messageContext.ContinuationScheduler.ScheduleTellOnce(castData.CastTime, messageContext.Entity, new PendingSpellCastFinishedWaitingMessage(castData), messageContext.Entity, castData.PendingCancel); } }
protected override void HandleMessage(EntityActorMessageContext messageContext, DefaultEntityActorStateContainer state, EntityActorSelectedMessage message) { if (Logger.IsDebugEnabled) { Logger.Debug($"Entity: {message.SelectorGuid} Interacted with Player: {state.EntityGuid}"); } //Just tell whatever selected us that they should now target us. messageContext.Sender.Tell(new SetEntityActorTargetMessage(state.EntityGuid)); //TODO: This is just for testing messageContext.Entity.TellSelf(new DamageEntityActorCurrentHealthMessage(1)); }
protected override void HandleMessage(EntityActorMessageContext messageContext, BehaviourGameObjectState <WorldTeleporterInstanceModel> state, InteractWithEntityActorMessage message) { if (Logger.IsDebugEnabled) { Logger.Debug($"Entity: {message.EntityInteracting} Interacted with World Teleporter: {state.EntityGuid}"); } //Only players should be able to interact with this. if (message.EntityInteracting.EntityType != EntityType.Player) { return; } //Right now there is no validation, just teleport them. messageContext.Sender.Tell(new WorldTeleportPlayerEntityActorMessage(state.BehaviourData.RemoteSpawnPointId, state.BehaviourData.LinkedGameObjectId)); }
protected override void HandleMessage(EntityActorMessageContext messageContext, DefaultEntityActorStateContainer state, AddPlayerExperienceActorMessage message) { int newExperienceAmount = state.EntityData.GetFieldValue <int>(PlayerObjectField.PLAYER_TOTAL_EXPERIENCE) + message.ExperienceAmount; //Just set the experience. state.EntityData.SetFieldValue(PlayerObjectField.PLAYER_TOTAL_EXPERIENCE, newExperienceAmount); int currentLevel = state.EntityData.GetFieldValue <int>(BaseObjectField.UNIT_FIELD_LEVEL); //We dinged! But don't assume it was only 1 level. Could be multiple if (currentLevel < LevelStrategy.ComputeLevelFromExperience(newExperienceAmount)) { int newLevel = LevelStrategy.ComputeLevelFromExperience(newExperienceAmount); messageContext.Entity.Tell(new SetEntityActorLevelMessage(newLevel)); } }
protected override void HandleMessage(EntityActorMessageContext messageContext, DefaultEntityActorStateContainer state, PlayerMovementStateChangedMessage message) { //If we started moving then we should check the current spell cast. if (message.isMoving) { if (PendingSpellCastMappable.ContainsKey(state.EntityGuid)) { PendingSpellCastData castData = PendingSpellCastMappable.RetrieveEntity(state.EntityGuid); if (castData.isInstantCast || castData.isCastCanceled || castData.IsSpellcastFinished(TimeService.CurrentLocalTime)) { return; } //Ok, so we're casting and moving. Let's cancel the cast. messageContext.Entity.TellSelf(new CancelSpellCastMessage()); } } }
protected override void HandleMessage(EntityActorMessageContext messageContext, NetworkedObjectActorState state, BroadcastToInterestSetMessage message) { IPeerPayloadSendService <GameServerPacketPayload> sender = null; foreach (NetworkEntityGuid guid in state.Interest.ContainedEntities) { //If it's not a player or the message specified that we shouldn't send to self and it's ourselves if (guid.EntityType != EntityType.Player || !message.SendToSelf && guid == state.EntityGuid) { continue; } //Try to get the sender and then just forward the message. if (SendServiceMappable.TryGetValue(guid, out sender)) { sender.SendMessage(message.Message); } } }
protected override void HandleMessage(EntityActorMessageContext messageContext, WorldActorState state, CreateCreatureEntityActorMessage message) { if (message.EntityGuid.EntityType != EntityType.Creature) { throw new InvalidOperationException($"Tried to create Creature Actor for non-Creature Entity: {message.EntityGuid}"); } try { CreateActor(state, message); } catch (Exception e) { if (Logger.IsErrorEnabled) { Logger.Error($"Failed to create Actor: {e.Message}\n\nStack: {e.StackTrace}"); } throw; } }
protected override void HandleMessage(EntityActorMessageContext messageContext, DefaultEntityActorStateContainer state, EntityActorSelectedMessage message) { if (Logger.IsDebugEnabled) { Logger.Debug($"Entity: {message.SelectorGuid} selected Creature: {state.EntityGuid}"); } //Make sure the creature can be selected. if (state.EntityData.HasBaseObjectFieldFlag(BaseObjectFieldFlags.UNIT_FLAG_NOT_SELECTABLE)) { if (Logger.IsWarnEnabled) { Logger.Warn($"Entity: {message.SelectorGuid} tried to select unselectable Creature: {state.EntityGuid}"); } return; } //Just tell whatever interacted with us that they should now target us. messageContext.Sender.Tell(new SetEntityActorTargetMessage(state.EntityGuid)); }
protected override void HandleMessage(EntityActorMessageContext messageContext, WorldActorState state, CreateEntityActorMessage message) { //Below we just forward to the appropriate handler. switch (message.EntityGuid.EntityType) { case EntityType.Player: messageContext.Entity.Tell(new CreatePlayerEntityActorMessage(message.EntityGuid)); break; case EntityType.GameObject: messageContext.Entity.Tell(new CreateGameObjectEntityActorMessage(message.EntityGuid)); break; case EntityType.Creature: messageContext.Entity.Tell(new CreateCreatureEntityActorMessage(message.EntityGuid)); break; default: throw new ArgumentOutOfRangeException($"Cannot handle EntityType: {message.EntityGuid.EntityType}"); } }
protected override void HandleMessage(EntityActorMessageContext messageContext, BehaviourGameObjectState <AvatarPedestalInstanceModel> state, InteractWithEntityActorMessage message) { if (Logger.IsDebugEnabled) { Logger.Debug($"Entity: {message.EntityInteracting} Interacted with Avatar Pedestal: {state.EntityGuid}"); } //Only players should be able to interact with this. if (message.EntityInteracting.EntityType != EntityType.Player) { return; } //Initially, we should make the assumption that they CAN change their avatr with this pedestal //so we will send the change packet BEFORE checking. messageContext.Sender.Tell(new ChangeEntityActorDisplayModelMessage(state.BehaviourData.AvatarModelId)); //Now we REALLY need to be sure. ZoneServerDataClient.UpdatePlayerAvatar(new ZoneServerAvatarPedestalInteractionCharacterRequest(message.EntityInteracting, state.BehaviourData.LinkedGameObjectId)) .ContinueWith((task, o) => { if (task.IsFaulted) { if (Logger.IsErrorEnabled) { Logger.Error($"TODO: Log"); //lazy } return; } //Now, we should verify the response. If the model id differ then it failed //or some race condition externally, the desired result is the player gets changed back. //To prevent exploits if (state.BehaviourData.AvatarModelId != task.Result.PersistedModelId) { messageContext.Sender.Tell(new ChangeEntityActorDisplayModelMessage(task.Result.PersistedModelId)); } }, TaskContinuationOptions.ExecuteSynchronously); }
protected override void HandleMessage(EntityActorMessageContext messageContext, WorldActorState state, CreateGameObjectEntityActorMessage message) { if (message.EntityGuid.EntityType != EntityType.GameObject) { throw new InvalidOperationException($"Tried to create GameObject Actor for non-GameObject Entity: {message.EntityGuid}"); } try { CreateActor(state, message); } catch (Exception e) { if (Logger.IsErrorEnabled) { Logger.Error($"Failed to create Actor: {e.Message}\n\nStack: {e.StackTrace}"); } //Rather than throw and kill the world let's just despawn this failed object DestructionEventPublisher.PublishEvent(this, new EntityDeconstructionRequestedEventArgs(message.EntityGuid)); } }
protected override void HandleMessage(EntityActorMessageContext messageContext, WorldActorState state, CreatePlayerEntityActorMessage message) { if (message.EntityGuid.EntityType != EntityType.Player) { throw new InvalidOperationException($"Tried to create Player Actor for non-Player Entity: {message.EntityGuid}"); } try { IActorRef actor = CreateActor(state, message); //If it succeeded, add the death watch message state.DeathWatchService.WatchWith(actor, new KillPlayerActorMessage(message.EntityGuid)); } catch (Exception e) { if (Logger.IsErrorEnabled) { Logger.Error($"Failed to create Actor: {e.Message}\n\nStack: {e.StackTrace}"); } throw; } }
protected override void HandleMessage(EntityActorMessageContext messageContext, DefaultEntityActorStateContainer state, TryCastSpellMessage message) { //Validate the cast before we starer it at all. SpellCastResult spellCastAttemptValidationResult = SpellCastValidator.ValidateSpellCast(state, message.SpellId); if (spellCastAttemptValidationResult != SpellCastResult.SPELL_FAILED_SUCCESS) { messageContext.Entity.TellSelf(new SpellCastFailedMessage(spellCastAttemptValidationResult, message.SpellId)); return; } //We also need to check if we're moving. If the generator isn't finished then that means we're actually moving. if (state.EntityGuid.EntityType == EntityType.Player) //only players should get successful callbacks { messageContext.Entity.TellSelf(new SpellCastFailedMessage(SpellCastResult.SPELL_FAILED_SUCCESS, message.SpellId)); } PendingSpellCastData castData = CreatePendingSpellData(state, message); SetCastingEntityState(state, message); DispatchPendingSpellCast(messageContext, castData); }
protected abstract void HandleMessage(EntityActorMessageContext messageContext, TEntityStateType state, TEntityMessageType message);