Exemplo n.º 1
0
        /// <summary>
        /// Called when a player dies, in conjunction with Die()
        /// </summary>
        /// <param name="lastDamager">The last damager that landed the death blow</param>
        /// <param name="damageType">The damage type for the death message</param>
        public override DeathMessage OnDeath(WorldObject lastDamager, DamageType damageType, bool criticalHit = false)
        {
            var deathMessage = base.OnDeath(lastDamager, damageType, criticalHit);

            var playerMsg    = string.Format(deathMessage.Victim, Name, lastDamager.Name);
            var msgYourDeath = new GameEventYourDeath(Session, playerMsg);

            Session.Network.EnqueueSend(msgYourDeath);

            // broadcast to nearby players
            var nearbyMsg     = string.Format(deathMessage.Broadcast, Name, lastDamager.Name);
            var broadcastMsg  = new GameMessageSystemChat(nearbyMsg, ChatMessageType.Broadcast);
            var nearbyPlayers = EnqueueBroadcast(false, broadcastMsg);

            var excludePlayers = nearbyPlayers.ToList();

            excludePlayers.Add(this);   // exclude self

            // if the player's lifestone is in a different landblock, also broadcast their demise to that landblock
            if (Sanctuary != null && Location.Landblock != Sanctuary.Landblock)
            {
                // ActionBroadcastKill might not work if other players around lifestone aren't aware of this player yet...
                // this existing broadcast method is also based on the current visible objects to the player,
                // and the player hasn't entered portal space or teleported back to the lifestone yet, so this doesn't work
                //ActionBroadcastKill(nearbyMsg, Guid, lastDamager.Guid);

                // instead, we get all of the players in the lifestone landblock + adjacent landblocks,
                // and possibly limit that to some radius around the landblock?
                var lifestoneBlock = LandblockManager.GetLandblock(new LandblockId(Sanctuary.Landblock << 16 | 0xFFFF), true);
                lifestoneBlock.EnqueueBroadcast(excludePlayers, true, broadcastMsg);
            }

            // reset damage history for this player
            DamageHistory.Reset();

            return(deathMessage);
        }
Exemplo n.º 2
0
        public void Tick(double lastTickDuration, long currentTimeTick)
        {
            // Here we'd move server objects in motion (subject to landscape) and do physics collision detection
            var allworldobj = worldObjects.Values;
            var allplayers  = allworldobj.OfType <Player>().ToList();

            UpdateStatus(allplayers.Count);

            if (IsActive)
            {
                var wos = worldObjects.Values.ToList();

                foreach (var wo in wos)
                {
                    wo.Tick(lastTickDuration, currentTimeTick);
                }
            }

            // Heartbeat
            if (lastHeartBeat + heartbeatInterval <= DateTime.UtcNow)
            {
                if (IsActive)
                {
                    // tick decayable objects ?? Is this still needed now that we've migrated to the new Tick architecture?

                    // tick items sold to vendors ?? Is this still needed now that we've migrated to the new Tick architecture?
                }

                // TODO: handle perma-loaded landblocks
                if (!Permaload && lastActiveTime + unloadInterval < DateTime.UtcNow)
                {
                    LandblockManager.AddToDestructionQueue(this);
                }

                lastHeartBeat = DateTime.UtcNow;
            }
        }
Exemplo n.º 3
0
        public static void CreateTrainingWand(Session session, params string[] parameters)
        {
            if (!(parameters?.Length > 0))
            {
                ChatPacket.SendServerMessage(session, "Usage: @ctw me or @ctw ground",
                                             ChatMessageType.Broadcast);
                return;
            }
            string location = parameters[0];

            if (location == "me" | location == "ground")
            {
                WorldObject loot = LootGenerationFactory.CreateTrainingWand(session.Player);
                switch (location)
                {
                case "me":
                {
                    LootGenerationFactory.AddToContainer(loot, session.Player);
                    session.Player.TrackObject(loot);
                    // TODO: Have to send game message CFS
                    break;
                }

                case "ground":
                {
                    LootGenerationFactory.Spawn(loot, session.Player.Location.InFrontOf(1.0f));
                    LandblockManager.AddObject(loot);
                    break;
                }
                }
            }
            else
            {
                ChatPacket.SendServerMessage(session, "Usage: @ctw me or @ctw ground",
                                             ChatMessageType.Broadcast);
            }
        }
Exemplo n.º 4
0
        /// <summary>
        /// Synchronizes the WorldObject Location with the Physics Location
        /// </summary>
        public void UpdatePosition_SyncLocation()
        {
            // was the position successfully moved to?
            // use the physics position as the source-of-truth?
            var newPos = PhysicsObj.Position;

            if (Location.LandblockId.Raw != newPos.ObjCellID)
            {
                var prevBlockCell = Location.LandblockId.Raw;
                var prevBlock     = Location.LandblockId.Raw >> 16;
                var prevCell      = Location.LandblockId.Raw & 0xFFFF;

                var newBlockCell = newPos.ObjCellID;
                var newBlock     = newPos.ObjCellID >> 16;
                var newCell      = newPos.ObjCellID & 0xFFFF;

                Location.LandblockId = new LandblockId(newPos.ObjCellID);

                if (prevBlock != newBlock)
                {
                    LandblockManager.RelocateObjectForPhysics(this, true);
                    //Console.WriteLine($"Relocating {Name} from {prevBlockCell:X8} to {newBlockCell:X8}");
                    //Console.WriteLine("Old position: " + Location.Pos);
                    //Console.WriteLine("New position: " + newPos.Frame.Origin);
                }
                //else
                //Console.WriteLine("Moving " + Name + " to " + Location.LandblockId.Raw.ToString("X8"));
            }

            Location.Pos      = newPos.Frame.Origin;
            Location.Rotation = newPos.Frame.Orientation;

            if (DebugMove)
            {
                DebugDistance();
            }
        }
Exemplo n.º 5
0
        /// <summary>
        /// Do the player log out work.<para />
        /// If you want to force a player to logout, use Session.LogOffPlayer().
        /// </summary>
        public void Logout(bool clientSessionTerminatedAbruptly = false)
        {
            if (!IsOnline)
            {
                return;
            }

            InWorld  = false;
            IsOnline = false;

            SendFriendStatusUpdates();

            // remove the player from landblock management
            LandblockManager.RemoveObject(this);

            // NOTE: Adding this here for now because some chracter options do not trigger the GameActionSetCharacterOptions packet to fire when apply is clicked (which is where we are currently saving to the db).
            // Once we get a CharacterSave method, we might consider removing this and putting it in that method instead.
            DatabaseManager.Character.SaveCharacterOptions(character);

            DatabaseManager.Character.UpdateCharacter(character);

            if (!clientSessionTerminatedAbruptly)
            {
                // TODO: Evt_Movement__MovementEvent_ID = F74C

                SetPhysicsState(PhysicsState.ReportCollision | PhysicsState.Gravity | PhysicsState.EdgeSlide);

                // Thie retail server sends a ChatRoomTracker 0x0295 first, then the status message, 0x028B. It does them one at a time for each individual channel.
                // The ChatRoomTracker message doesn't seem to change at all.
                // For the purpose of ACE, we simplify this process.
                var general  = new GameEventDisplayParameterizedStatusMessage(Session, StatusMessageType2.YouHaveLeftThe_Channel, "General");
                var trade    = new GameEventDisplayParameterizedStatusMessage(Session, StatusMessageType2.YouHaveLeftThe_Channel, "Trade");
                var lfg      = new GameEventDisplayParameterizedStatusMessage(Session, StatusMessageType2.YouHaveLeftThe_Channel, "LFG");
                var roleplay = new GameEventDisplayParameterizedStatusMessage(Session, StatusMessageType2.YouHaveLeftThe_Channel, "Roleplay");
                Session.Network.EnqueueSend(general, trade, lfg, roleplay);
            }
        }
Exemplo n.º 6
0
        public void ProjectileImpact()
        {
            //Console.WriteLine($"{Name}.ProjectileImpact()");

            ActionChain selfDestructChain = new ActionChain();

            selfDestructChain.AddAction(this, () =>
            {
                ReportCollisions = false;
                Ethereal         = true;
                IgnoreCollisions = true;
                NoDraw           = true;
                Cloaked          = true;
                LightsStatus     = false;

                PhysicsObj.set_active(false);

                EnqueueBroadcastPhysicsState();
                EnqueueBroadcast(new GameMessageScript(Guid, ACE.Entity.Enum.PlayScript.Explode, GetProjectileScriptIntensity(SpellType)));
            });
            selfDestructChain.AddDelaySeconds(5.0);
            selfDestructChain.AddAction(this, () => LandblockManager.RemoveObject(this));
            selfDestructChain.EnqueueChain();
        }
Exemplo n.º 7
0
        /// <summary>
        /// Used by physics engine to actually update a player position
        /// Automatically notifies clients of updated position
        /// </summary>
        /// <param name="newPosition">The new position being requested, before verification through physics engine</param>
        /// <returns>TRUE if object moves to a different landblock</returns>
        public bool UpdatePlayerPhysics(ACE.Entity.Position newPosition, bool forceUpdate = false)
        {
            //Console.WriteLine($"UpdatePlayerPhysics: {newPosition.Cell:X8}, {newPosition.Pos}");

            var player = this as Player;

            // only handles player movement
            if (player == null)
            {
                return(false);
            }

            // possible bug: while teleporting, client can still send AutoPos packets from old landblock
            if (Teleporting && !forceUpdate)
            {
                return(false);
            }

            if (PhysicsObj != null)
            {
                var dist = (newPosition.Pos - PhysicsObj.Position.Frame.Origin).Length();
                if (dist > PhysicsGlobals.EPSILON)
                {
                    var curCell = LScape.get_landcell(newPosition.Cell);
                    if (curCell != null)
                    {
                        //if (PhysicsObj.CurCell == null || curCell.ID != PhysicsObj.CurCell.ID)
                        //PhysicsObj.change_cell_server(curCell);

                        PhysicsObj.set_request_pos(newPosition.Pos, newPosition.Rotation, curCell, Location.LandblockId.Raw);
                        PhysicsObj.update_object_server();

                        if (PhysicsObj.CurCell == null)
                        {
                            PhysicsObj.CurCell = curCell;
                        }

                        player.CheckMonsters();

                        if (curCell.ID != prevCell)
                        {
                            //prevCell = curCell.ID;
                            //Console.WriteLine("Player cell: " + curCell.ID.ToString("X8"));
                            //var envCell = curCell as Physics.Common.EnvCell;
                            //var seenOutside = envCell != null ? envCell.SeenOutside : true;
                            //Console.WriteLine($"CurCell: {curCell.ID:X8}, SeenOutside: {seenOutside}");
                        }
                    }
                }
            }

            // double update path: landblock physics update -> updateplayerphysics() -> update_object_server() -> Teleport() -> updateplayerphysics() -> return to end of original branch
            if (Teleporting && !forceUpdate)
            {
                return(true);
            }

            var landblockUpdate = Location.Cell >> 16 != newPosition.Cell >> 16;

            Location = newPosition;

            SendUpdatePosition();

            if (!InUpdate)
            {
                LandblockManager.RelocateObjectForPhysics(this, true);
            }

            return(landblockUpdate);
        }
Exemplo n.º 8
0
 public static void CreateLifeStone(Session session, params string[] parameters)
 {
     LandblockManager.AddObject(LifestoneObjectFactory.CreateLifestone(509, session.Player.Location.InFrontOf(3.0f), LifestoneType.Original));
 }
Exemplo n.º 9
0
 public static void CreatePortal(Session session, params string[] parameters)
 {
     LandblockManager.AddObject(PortalObjectFactory.CreatePortal(1234, session.Player.Location.InFrontOf(3.0f), "Test Portal", PortalType.Purple));
 }
Exemplo n.º 10
0
        public void Tick(double currentUnixTime)
        {
            if (monitorsRequireEventStart)
            {
                Monitor5m.RegisterEventStart();
                Monitor1h.RegisterEventStart();
            }
            else
            {
                Monitor5m.ResumeEvent();
                Monitor1h.ResumeEvent();
            }

            ServerPerformanceMonitor.ResumeEvent(ServerPerformanceMonitor.MonitorType.Landblock_Tick_RunActions);
            actionQueue.RunActions();
            ServerPerformanceMonitor.PauseEvent(ServerPerformanceMonitor.MonitorType.Landblock_Tick_RunActions);

            ProcessPendingWorldObjectAdditionsAndRemovals();

            ServerPerformanceMonitor.ResumeEvent(ServerPerformanceMonitor.MonitorType.Landblock_Tick_Player_Tick);
            foreach (var player in players)
            {
                player.Player_Tick(currentUnixTime);
            }
            ServerPerformanceMonitor.PauseEvent(ServerPerformanceMonitor.MonitorType.Landblock_Tick_Player_Tick);

            // When a WorldObject Ticks, it can end up adding additional WorldObjects to this landblock
            if (!IsDormant)
            {
                ServerPerformanceMonitor.ResumeEvent(ServerPerformanceMonitor.MonitorType.Landblock_Tick_Monster_Tick);
                while (sortedCreaturesByNextTick.Count > 0) // Monster_Tick()
                {
                    var first = sortedCreaturesByNextTick.First.Value;

                    // If they wanted to run before or at now
                    if (first.NextMonsterTickTime <= currentUnixTime)
                    {
                        sortedCreaturesByNextTick.RemoveFirst();
                        first.Monster_Tick(currentUnixTime);
                        sortedCreaturesByNextTick.AddLast(first); // All creatures tick at a fixed interval
                    }
                    else
                    {
                        break;
                    }
                }
                ServerPerformanceMonitor.PauseEvent(ServerPerformanceMonitor.MonitorType.Landblock_Tick_Monster_Tick);
            }

            ServerPerformanceMonitor.ResumeEvent(ServerPerformanceMonitor.MonitorType.Landblock_Tick_WorldObject_Heartbeat);
            while (sortedWorldObjectsByNextHeartbeat.Count > 0) // Heartbeat()
            {
                var first = sortedWorldObjectsByNextHeartbeat.First.Value;

                // If they wanted to run before or at now
                if (first.NextHeartbeatTime <= currentUnixTime)
                {
                    sortedWorldObjectsByNextHeartbeat.RemoveFirst();
                    first.Heartbeat(currentUnixTime);
                    InsertWorldObjectIntoSortedHeartbeatList(first); // WorldObjects can have heartbeats at different intervals
                }
                else
                {
                    break;
                }
            }
            ServerPerformanceMonitor.PauseEvent(ServerPerformanceMonitor.MonitorType.Landblock_Tick_WorldObject_Heartbeat);

            ServerPerformanceMonitor.ResumeEvent(ServerPerformanceMonitor.MonitorType.Landblock_Tick_GeneratorUpdate);
            while (sortedGeneratorsByNextGeneratorUpdate.Count > 0)
            {
                var first = sortedGeneratorsByNextGeneratorUpdate.First.Value;

                // If they wanted to run before or at now
                if (first.NextGeneratorUpdateTime <= currentUnixTime)
                {
                    sortedGeneratorsByNextGeneratorUpdate.RemoveFirst();
                    first.GeneratorUpdate(currentUnixTime);
                    //InsertWorldObjectIntoSortedGeneratorUpdateList(first);
                    sortedGeneratorsByNextGeneratorUpdate.AddLast(first);
                }
                else
                {
                    break;
                }
            }
            ServerPerformanceMonitor.PauseEvent(ServerPerformanceMonitor.MonitorType.Landblock_Tick_GeneratorUpdate);

            ServerPerformanceMonitor.ResumeEvent(ServerPerformanceMonitor.MonitorType.Landblock_Tick_GeneratorRegeneration);
            while (sortedGeneratorsByNextRegeneration.Count > 0) // GeneratorRegeneration()
            {
                var first = sortedGeneratorsByNextRegeneration.First.Value;

                //Console.WriteLine($"{first.Name}.Landblock_Tick_GeneratorRegeneration({currentUnixTime})");

                // If they wanted to run before or at now
                if (first.NextGeneratorRegenerationTime <= currentUnixTime)
                {
                    sortedGeneratorsByNextRegeneration.RemoveFirst();
                    first.GeneratorRegeneration(currentUnixTime);
                    InsertWorldObjectIntoSortedGeneratorRegenerationList(first); // Generators can have regnerations at different intervals
                }
                else
                {
                    break;
                }
            }
            ServerPerformanceMonitor.PauseEvent(ServerPerformanceMonitor.MonitorType.Landblock_Tick_GeneratorRegeneration);

            // Heartbeat
            ServerPerformanceMonitor.ResumeEvent(ServerPerformanceMonitor.MonitorType.Landblock_Tick_Heartbeat);
            if (lastHeartBeat + heartbeatInterval <= DateTime.UtcNow)
            {
                var thisHeartBeat = DateTime.UtcNow;

                ProcessPendingWorldObjectAdditionsAndRemovals();

                // Decay world objects
                if (lastHeartBeat != DateTime.MinValue)
                {
                    foreach (var wo in worldObjects.Values)
                    {
                        if (wo.IsDecayable())
                        {
                            wo.Decay(thisHeartBeat - lastHeartBeat);
                        }
                    }
                }

                if (!Permaload)
                {
                    if (lastActiveTime + dormantInterval < thisHeartBeat)
                    {
                        IsDormant = true;
                    }
                    if (lastActiveTime + unloadInterval < thisHeartBeat)
                    {
                        LandblockManager.AddToDestructionQueue(this);
                    }
                }

                //log.Info($"Landblock {Id.ToString()}.Tick({currentUnixTime}).Landblock_Tick_Heartbeat: thisHeartBeat: {thisHeartBeat.ToString()} | lastHeartBeat: {lastHeartBeat.ToString()} | worldObjects.Count: {worldObjects.Count()}");
                lastHeartBeat = thisHeartBeat;
            }
            ServerPerformanceMonitor.PauseEvent(ServerPerformanceMonitor.MonitorType.Landblock_Tick_Heartbeat);

            // Database Save
            ServerPerformanceMonitor.ResumeEvent(ServerPerformanceMonitor.MonitorType.Landblock_Tick_Database_Save);
            if (lastDatabaseSave + databaseSaveInterval <= DateTime.UtcNow)
            {
                ProcessPendingWorldObjectAdditionsAndRemovals();

                SaveDB();
                lastDatabaseSave = DateTime.UtcNow;
            }
            ServerPerformanceMonitor.PauseEvent(ServerPerformanceMonitor.MonitorType.Landblock_Tick_Database_Save);

            Monitor5m.RegisterEventEnd();
            Monitor1h.RegisterEventEnd();
            monitorsRequireEventStart = true;

            if (DateTime.UtcNow - last5mClear >= last5mClearInteval)
            {
                Monitor5m.ClearEventHistory();
                last5mClear = DateTime.UtcNow;
            }

            if (DateTime.UtcNow - last1hClear >= last1hClearInteval)
            {
                Monitor1h.ClearEventHistory();
                last1hClear = DateTime.UtcNow;
            }
        }
Exemplo n.º 11
0
        public void LogOut_Inner(bool clientSessionTerminatedAbruptly = false)
        {
            if (Fellowship != null)
            {
                FellowshipQuit(false);
            }

            if (IsTrading && TradePartner != null)
            {
                var tradePartner = PlayerManager.GetOnlinePlayer(TradePartner);

                if (tradePartner != null)
                {
                    tradePartner.HandleActionCloseTradeNegotiations();
                }
            }

            if (!clientSessionTerminatedAbruptly)
            {
                // Thie retail server sends a ChatRoomTracker 0x0295 first, then the status message, 0x028B. It does them one at a time for each individual channel.
                // The ChatRoomTracker message doesn't seem to change at all.
                // For the purpose of ACE, we simplify this process.
                var general  = new GameEventWeenieErrorWithString(Session, WeenieErrorWithString.YouHaveLeftThe_Channel, "General");
                var trade    = new GameEventWeenieErrorWithString(Session, WeenieErrorWithString.YouHaveLeftThe_Channel, "Trade");
                var lfg      = new GameEventWeenieErrorWithString(Session, WeenieErrorWithString.YouHaveLeftThe_Channel, "LFG");
                var roleplay = new GameEventWeenieErrorWithString(Session, WeenieErrorWithString.YouHaveLeftThe_Channel, "Roleplay");
                Session.Network.EnqueueSend(general, trade, lfg, roleplay);
            }

            if (CurrentActivePet != null)
            {
                CurrentActivePet.Destroy();
            }

            if (CurrentLandblock != null)
            {
                var logout = new Motion(MotionStance.NonCombat, MotionCommand.LogOut);
                EnqueueBroadcastMotion(logout);

                EnqueueBroadcastPhysicsState();

                var logoutChain = new ActionChain();

                var   motionTable           = DatManager.PortalDat.ReadFromDat <MotionTable>((uint)MotionTableId);
                float logoutAnimationLength = motionTable.GetAnimationLength(MotionCommand.LogOut);
                logoutChain.AddDelaySeconds(logoutAnimationLength);

                // remove the player from landblock management -- after the animation has run
                logoutChain.AddAction(this, () =>
                {
                    if (CurrentLandblock == null)
                    {
                        log.Debug($"0x{Guid}:{Name}.LogOut_Inner.logoutChain: CurrentLandblock is null, unable to remove from a landblock...");
                        if (Location != null)
                        {
                            log.Debug($"0x{Guid}:{Name}.LogOut_Inner.logoutChain: Location is not null, Location = {Location.ToLOCString()}");
                        }
                    }

                    CurrentLandblock?.RemoveWorldObject(Guid, false);
                    SetPropertiesAtLogOut();
                    SavePlayerToDatabase();
                    PlayerManager.SwitchPlayerFromOnlineToOffline(this);
                });

                // close any open landblock containers (chests / corpses)
                if (LastOpenedContainerId != ObjectGuid.Invalid)
                {
                    var container = CurrentLandblock.GetObject(LastOpenedContainerId) as Container;

                    if (container != null)
                    {
                        container.Close(this);
                    }
                }

                logoutChain.EnqueueChain();
            }
            else
            {
                log.Debug($"0x{Guid}:{Name}.LogOut_Inner: CurrentLandblock is null");
                if (Location != null)
                {
                    log.Debug($"0x{Guid}:{Name}.LogOut_Inner: Location is not null, Location = {Location.ToLOCString()}");
                    var validLoadedLandblock = LandblockManager.GetLandblock(Location.LandblockId, false);
                    if (validLoadedLandblock.GetObject(Guid.Full) != null)
                    {
                        log.Debug($"0x{Guid}:{Name}.LogOut_Inner: Player is still on landblock, removing...");
                        validLoadedLandblock.RemoveWorldObject(Guid, false);
                    }
                    else
                    {
                        log.Debug($"0x{Guid}:{Name}.LogOut_Inner: Player is not found on the landblock Location references.");
                    }
                }
                else
                {
                    log.Debug($"0x{Guid}:{Name}.LogOut_Inner: Location is null");
                }
                SetPropertiesAtLogOut();
                SavePlayerToDatabase();
                PlayerManager.SwitchPlayerFromOnlineToOffline(this);
            }
        }
Exemplo n.º 12
0
        /// <summary>
        /// Launches a projectile from player to target
        /// </summary>
        public WorldObject LaunchProjectile(WorldObject weapon, WorldObject ammo, WorldObject target, out float time)
        {
            var proj = WorldObjectFactory.CreateNewWorldObject(ammo.WeenieClassId);

            proj.ProjectileSource = this;
            proj.ProjectileTarget = target;

            proj.ProjectileLauncher = weapon;

            var matchIndoors = Location.Indoors == target.Location.Indoors;
            var origin       = matchIndoors ? Location.ToGlobal() : Location.Pos;

            origin.Z += Height;

            var dest = matchIndoors ? target.Location.ToGlobal() : target.Location.Pos;

            dest.Z += target.Height / GetAimHeight(target);

            var speed = 35.0f;  // TODO: get correct speed
            var dir   = GetDir2D(origin, dest);

            origin += dir * 2.0f;

            var velocity = GetProjectileVelocity(target, origin, dir, dest, speed, out time);

            var player = this as Player;

            if (!velocity.IsValid())
            {
                if (player != null)
                {
                    player.SendWeenieError(WeenieError.YourAttackMisfired);
                }

                return(null);
            }

            proj.Velocity = velocity;

            proj.Location = matchIndoors ? Location.FromGlobal(origin) : new Position(Location.Cell, origin, Location.Rotation);
            if (!matchIndoors)
            {
                proj.Location.LandblockId = new LandblockId(proj.Location.GetCell());
            }

            SetProjectilePhysicsState(proj, target);

            var result = LandblockManager.AddObject(proj);

            if (proj.PhysicsObj == null)
            {
                return(null);
            }

            var pkStatus = player?.PlayerKillerStatus ?? PlayerKillerStatus.Creature;

            proj.EnqueueBroadcast(new GameMessagePublicUpdatePropertyInt(proj, PropertyInt.PlayerKillerStatus, (int)pkStatus));
            proj.EnqueueBroadcast(new GameMessageScript(proj.Guid, PlayScript.Launch, 0f));

            // detonate point-blank projectiles immediately

            /*var radsum = target.PhysicsObj.GetRadius() + proj.PhysicsObj.GetRadius();
             * var dist = Vector3.Distance(origin, dest);
             * if (dist < radsum)
             * {
             *  Console.WriteLine($"Point blank");
             *  proj.OnCollideObject(target);
             * }*/

            return(proj);
        }
Exemplo n.º 13
0
        /// <summary>
        /// Loads the backing store landblock structure<para />
        /// This function is thread safe
        /// </summary>
        /// <param name="iBlockCellID">Any instance + landblock + cell ID within the landblock</param>
        public static Landblock get_landblock(ulong iBlockCellID)
        {
            /*if ((iBlockCellID | 0xFFFF) == 0x1D9FFFF)
             * {
             *  Console.WriteLine(System.Environment.StackTrace);
             *  var debug = true;
             * }*/

            if (PhysicsEngine.Instance.Server)
            {
                var lbmLandblock = LandblockManager.GetLandblock(iBlockCellID, false, false);

                return(lbmLandblock.PhysicsLandblock);
            }

            // client implementation

            /*if (Landblocks == null || Landblocks.Count == 0)
             *  return null;
             *
             * if (!LandDefs.inbound_valid_cellid(cellID) || cellID >= 0x100)
             *  return null;
             *
             * var local_lcoord = LandDefs.blockid_to_lcoord(LoadedCellID);
             * var global_lcoord = LandDefs.gid_to_lcoord(cellID);
             *
             * var xDiff = ((int)global_lcoord.Value.X + 8 * MidRadius - (int)local_lcoord.Value.X) / 8;
             * var yDiff = ((int)global_lcoord.Value.Y + 8 * MidRadius - (int)local_lcoord.Value.Y) / 8;
             *
             * if (xDiff < 0 || yDiff < 0 || xDiff < MidWidth || yDiff < MidWidth)
             *  return null;
             *
             * return Landblocks[yDiff + xDiff * MidWidth];*/

            var iLandblockID = iBlockCellID | 0xFFFF;

            // check if landblock is already cached
            if (Landblocks.TryGetValue(iLandblockID, out var landblock))
            {
                return(landblock);
            }

            lock (landblockMutex)
            {
                // check if landblock is already cached, this time under the lock.
                if (Landblocks.TryGetValue(iLandblockID, out landblock))
                {
                    return(landblock);
                }

                // if not, load into cache
                var instance = BlockCell.GetInstance(iLandblockID);

                landblock = new Landblock(DBObj.GetCellLandblock((uint)iLandblockID), instance);
                if (Landblocks.TryAdd(iLandblockID, landblock))
                {
                    landblock.PostInit();
                }
                else
                {
                    Landblocks.TryGetValue(iLandblockID, out landblock);
                }

                return(landblock);
            }
        }
Exemplo n.º 14
0
        /// <summary>
        /// Launches a projectile from player to target
        /// </summary>
        public WorldObject LaunchProjectile(WorldObject target, out float time)
        {
            var ammo  = GetEquippedAmmo();
            var arrow = WorldObjectFactory.CreateNewWorldObject(ammo.WeenieClassId);

            arrow.ProjectileSource = this;
            arrow.ProjectileTarget = target;

            var origin = Location.ToGlobal();

            origin.Z += Height;

            var dest = target.Location.ToGlobal();

            //var dest = target.Location.Pos;
            dest.Z += target.Height / GetAimHeight(target);

            var speed = 35.0f;
            var dir   = GetDir2D(origin, dest);

            origin += dir * 2.0f;

            var velocity = GetProjectileVelocity(target, origin, dir, dest, speed, out time);

            arrow.Velocity = new AceVector3(velocity.X, velocity.Y, velocity.Z);

            origin = Location.FromGlobal(origin).Pos;
            var rotation = Location.Rotation;

            arrow.Location = new Position(Location.LandblockId.Raw, origin.X, origin.Y, origin.Z, rotation.X, rotation.Y, rotation.Z, rotation.W);
            SetProjectilePhysicsState(arrow, target);

            // TODO: Get correct aim level based on arrow velocity and add aim motion delay.
            var motion = new UniversalMotion(CurrentMotionState.Stance);

            motion.MovementData.CurrentStyle   = (uint)CurrentMotionState.Stance;
            motion.MovementData.ForwardCommand = (uint)MotionCommand.AimLevel;
            CurrentMotionState = motion;

            DoMotion(motion);
            //actionChain.AddDelaySeconds(animLength);

            LandblockManager.AddObject(arrow);
            arrow.EnqueueBroadcast(new GameMessagePickupEvent(ammo));

            var player = this as Player;

            // TODO: Add support for monster ammo depletion. For now only players will use up ammo.
            if (player != null)
            {
                UpdateAmmoAfterLaunch(ammo);
            }
            // Not sure why this would be needed but it is sent in retail pcaps.
            arrow.EnqueueBroadcast(new GameMessageSetStackSize(arrow));

            var pkStatus = player == null ? PlayerKillerStatus.Creature : player.PlayerKillerStatus;

            arrow.EnqueueBroadcast(new GameMessagePublicUpdatePropertyInt(arrow, PropertyInt.PlayerKillerStatus, (int)pkStatus));
            arrow.EnqueueBroadcast(new GameMessageScript(arrow.Guid, ACE.Entity.Enum.PlayScript.Launch, 0f));

            // detonate point-blank projectiles immediately
            var radsum = target.PhysicsObj.GetRadius() + arrow.PhysicsObj.GetRadius();
            var dist   = Vector3.Distance(origin, dest);

            if (dist < radsum)
            {
                arrow.OnCollideObject(target);
            }

            return(arrow);
        }
Exemplo n.º 15
0
        public void LogOut_Inner(bool clientSessionTerminatedAbruptly = false)
        {
            IsBusy       = true;
            IsLoggingOut = true;

            if (Fellowship != null)
            {
                FellowshipQuit(false);
            }

            if (IsTrading && TradePartner != ObjectGuid.Invalid)
            {
                var tradePartner = PlayerManager.GetOnlinePlayer(TradePartner);

                if (tradePartner != null)
                {
                    tradePartner.HandleActionCloseTradeNegotiations();
                }
            }

            if (!clientSessionTerminatedAbruptly)
            {
                if (PropertyManager.GetBool("use_turbine_chat").Item)
                {
                    if (GetCharacterOption(CharacterOption.ListenToGeneralChat))
                    {
                        LeaveTurbineChatChannel("General");
                    }
                    if (GetCharacterOption(CharacterOption.ListenToTradeChat))
                    {
                        LeaveTurbineChatChannel("Trade");
                    }
                    if (GetCharacterOption(CharacterOption.ListenToLFGChat))
                    {
                        LeaveTurbineChatChannel("LFG");
                    }
                    if (GetCharacterOption(CharacterOption.ListenToRoleplayChat))
                    {
                        LeaveTurbineChatChannel("Roleplay");
                    }
                    if (GetCharacterOption(CharacterOption.ListenToAllegianceChat) && Allegiance != null)
                    {
                        LeaveTurbineChatChannel("Allegiance");
                    }
                    if (GetCharacterOption(CharacterOption.ListenToSocietyChat) && Society != FactionBits.None)
                    {
                        LeaveTurbineChatChannel("Society");
                    }
                }
            }

            if (CurrentActivePet != null)
            {
                CurrentActivePet.Destroy();
            }

            if (CurrentLandblock != null)
            {
                var logout = new Motion(MotionStance.NonCombat, MotionCommand.LogOut);
                EnqueueBroadcastMotion(logout);

                EnqueueBroadcastPhysicsState();

                var logoutChain = new ActionChain();

                var   motionTable           = DatManager.PortalDat.ReadFromDat <MotionTable>((uint)MotionTableId);
                float logoutAnimationLength = motionTable.GetAnimationLength(MotionCommand.LogOut);
                logoutChain.AddDelaySeconds(logoutAnimationLength);

                // remove the player from landblock management -- after the animation has run
                logoutChain.AddAction(this, () =>
                {
                    if (CurrentLandblock == null)
                    {
                        log.Debug($"0x{Guid}:{Name}.LogOut_Inner.logoutChain: CurrentLandblock is null, unable to remove from a landblock...");
                        if (Location != null)
                        {
                            log.Debug($"0x{Guid}:{Name}.LogOut_Inner.logoutChain: Location is not null, Location = {Location.ToLOCString()}");
                        }
                    }

                    CurrentLandblock?.RemoveWorldObject(Guid, false);
                    SetPropertiesAtLogOut();
                    SavePlayerToDatabase();
                    PlayerManager.SwitchPlayerFromOnlineToOffline(this);
                });

                // close any open landblock containers (chests / corpses)
                if (LastOpenedContainerId != ObjectGuid.Invalid)
                {
                    var container = CurrentLandblock.GetObject(LastOpenedContainerId) as Container;

                    if (container != null)
                    {
                        container.Close(this);
                    }
                }

                logoutChain.EnqueueChain();
            }
            else
            {
                log.Debug($"0x{Guid}:{Name}.LogOut_Inner: CurrentLandblock is null");
                if (Location != null)
                {
                    log.Debug($"0x{Guid}:{Name}.LogOut_Inner: Location is not null, Location = {Location.ToLOCString()}");
                    var validLoadedLandblock = LandblockManager.GetLandblock(Location.LandblockId, false);
                    if (validLoadedLandblock.GetObject(Guid.Full) != null)
                    {
                        log.Debug($"0x{Guid}:{Name}.LogOut_Inner: Player is still on landblock, removing...");
                        validLoadedLandblock.RemoveWorldObject(Guid, false);
                    }
                    else
                    {
                        log.Debug($"0x{Guid}:{Name}.LogOut_Inner: Player is not found on the landblock Location references.");
                    }
                }
                else
                {
                    log.Debug($"0x{Guid}:{Name}.LogOut_Inner: Location is null");
                }
                SetPropertiesAtLogOut();
                SavePlayerToDatabase();
                PlayerManager.SwitchPlayerFromOnlineToOffline(this);
            }
        }
Exemplo n.º 16
0
        /// <summary>
        /// Creates the Magic projectile spells for Life, War, and Void Magic
        /// </summary>
        /// <param name="caster"></param>
        /// <param name="target"></param>
        /// <param name="spellId"></param>
        /// <param name="projectileWcid"></param>
        /// <param name="lifeProjectileDamage"></param>
        private void CreateSpellProjectile(WorldObject caster, WorldObject target, uint spellId, uint projectileWcid, uint lifeProjectileDamage = 0)
        {
            SpellProjectile spellProjectile = WorldObjectFactory.CreateNewWorldObject(projectileWcid) as SpellProjectile;

            spellProjectile.Setup(spellId);

            var origin = caster.Location.ToGlobal();

            if (spellProjectile.SpellType == SpellProjectile.ProjectileSpellType.Arc)
            {
                origin.Z += caster.Height;
            }
            else
            {
                origin.Z += caster.Height * 2.0f / 3.0f;
            }

            var dest = target.Location.ToGlobal();

            dest.Z += target.Height / 2.0f;

            var direction = Vector3.Normalize(dest - origin);

            // This is not perfect but is close to values that retail used. TODO: revisit this later.
            origin += direction * (caster.PhysicsObj.GetRadius() + spellProjectile.PhysicsObj.GetRadius());

            float time;
            var   dist  = (dest - origin).Length();
            float speed = 15f;

            if (spellProjectile.SpellType == SpellProjectile.ProjectileSpellType.Bolt)
            {
                speed = GetStationaryVelocity(15f, dist);
            }
            else if (spellProjectile.SpellType == SpellProjectile.ProjectileSpellType.Streak)
            {
                speed = GetStationaryVelocity(45f, dist);
            }
            else if (spellProjectile.SpellType == SpellProjectile.ProjectileSpellType.Arc)
            {
                speed = GetStationaryVelocity(40f, dist);
            }

            // TODO: Implement target leading for non arc spells
            // Also: velocity seems to increase when target is moving away from the caster and decrease when
            // the target is moving toward the caster. This still needs more research.

            var velocity = direction * speed;

            if (spellProjectile.SpellType == SpellProjectile.ProjectileSpellType.Arc)
            {
                spellProjectile.Velocity = GetSpellProjectileVelocity(origin, dest, speed, out time);
            }
            else
            {
                spellProjectile.Velocity = new AceVector3(velocity.X, velocity.Y, velocity.Z);
                var velocityLength = spellProjectile.Velocity.Get().Length();
                time = dist / velocityLength;
            }
            spellProjectile.FlightTime = time;

            var loc = caster.Location;

            origin = loc.Pos;
            if (spellProjectile.SpellType == SpellProjectile.ProjectileSpellType.Arc)
            {
                origin.Z += caster.Height;
            }
            else
            {
                origin.Z += caster.Height * 2.0f / 3.0f;
            }
            origin += direction * (caster.PhysicsObj.GetRadius() + spellProjectile.PhysicsObj.GetRadius());

            spellProjectile.Location             = new ACE.Entity.Position(loc.LandblockId.Raw, origin.X, origin.Y, origin.Z, loc.Rotation.X, loc.Rotation.Y, loc.Rotation.Z, loc.RotationW);
            spellProjectile.ParentWorldObject    = (Creature)this;
            spellProjectile.TargetGuid           = target.Guid;
            spellProjectile.LifeProjectileDamage = lifeProjectileDamage;

            LandblockManager.AddObject(spellProjectile);
            CurrentLandblock.EnqueueBroadcast(spellProjectile.Location, new GameMessageScript(spellProjectile.Guid, ACE.Entity.Enum.PlayScript.Launch, spellProjectile.PlayscriptIntensity));

            // TODO : removed when real server projectile tracking and collisions are implemented
            var actionChain = new ActionChain();

            actionChain.AddDelaySeconds(spellProjectile.FlightTime);
            actionChain.AddAction(spellProjectile, () => spellProjectile.HandleOnCollide(spellProjectile.TargetGuid));
            actionChain.EnqueueChain();
        }
Exemplo n.º 17
0
        /// <summary>
        /// main game loop
        /// </summary>
        public void UseTime()
        {
            while (running)
            {
                // here we'd move server objects in motion (subject to landscape) and do physics collision detection

                List <WorldObject> allworldobj    = null;
                List <Player>      allplayers     = null;
                List <Creature>    allcreatures   = null;
                List <WorldObject> movedObjects   = null;
                List <WorldObject> despawnObjects = null;
                List <Creature>    deadCreatures  = null;

                lock (objectCacheLocker)
                {
                    allworldobj = worldObjects.Values.ToList();
                }

                // all players on this land block
                allplayers   = allworldobj.OfType <Player>().ToList();
                allcreatures = allworldobj.OfType <Creature>().ToList();

                despawnObjects = allworldobj.ToList();
                despawnObjects = despawnObjects.Where(x => x.DespawnTime > -1).ToList();

                deadCreatures = allworldobj.OfType <Creature>().ToList();
                deadCreatures = deadCreatures.Where(x => x.IsAlive == false).ToList();

                // flag them as updated now in order to reduce chance of missing an update
                // this is only for moving objects across landblocks.
                movedObjects = allworldobj.ToList();
                movedObjects = movedObjects.Where(p => p.LastUpdatedTicks >= p.LastMovementBroadcastTicks).ToList();
                movedObjects.ForEach(m => m.LastMovementBroadcastTicks = WorldManager.PortalYearTicks);

                if (id.MapScope == Enum.MapScope.Outdoors)
                {
                    // check to see if a player or other mutable object "roamed" to an adjacent landblock
                    var objectsToRelocate = movedObjects.Where(m => m.Location.LandblockId.IsAdjacentTo(id) && m.Location.LandblockId != id).ToList();

                    // so, these objects moved to an adjacent block.  they could have recalled to that block, died and bounced to a lifestone on that block, or
                    // just simply walked accross the border line.  in any case, we won't delete them, we'll just transfer them.  the trick, though, is to
                    // figure out how to treat it in adjacent landblocks.  if the player walks across the southern border, the north adjacency needs to remove
                    // them, but the south is actually getting them.  we need to avoid sending Delete+Create to clients that already know about it, though.

                    objectsToRelocate.ForEach(o => Log($"attempting to relocate object {o.Name} ({o.Guid.Full.ToString("X")})"));

                    // RelocateObject will put them in the right landblock
                    objectsToRelocate.ForEach(o => LandblockManager.RelocateObject(o));

                    // Remove has logic to make sure it doesn't double up the delete+create when "true" is passed.
                    objectsToRelocate.ForEach(o => RemoveWorldObject(o.Guid, true));
                }

                // for all players on landblock.
                Parallel.ForEach(allplayers, player =>
                {
                    // Process Action Queue for player.
                    QueuedGameAction action = player.ActionQueuePop();
                    if (action != null)
                    {
                        HandleGameAction(action, player);
                    }

                    // Process Examination Queue for player
                    QueuedGameAction examination = player.ExaminationQueuePop();
                    if (examination != null)
                    {
                        HandleGameAction(examination, player);
                    }
                });
                UpdateStatus(allplayers.Count);

                double tickTime = WorldManager.PortalYearTicks;
                // per-creature update on landblock.
                Parallel.ForEach(allworldobj, wo =>
                {
                    // Process the creatures
                    wo.Tick(tickTime);
                });

                // broadcast moving objects to the world..
                // players and creatures can move.
                Parallel.ForEach(movedObjects, mo =>
                {
                    // detect all world objects in ghost range
                    List <WorldObject> woproxghost = new List <WorldObject>();
                    woproxghost.AddRange(GetWorldObjectsInRange(mo, maxobjectGhostRange, true));

                    // for all objects in rang of this moving object or in ghost range of moving object update them.
                    Parallel.ForEach(woproxghost, wo =>
                    {
                        if (mo.Guid.IsPlayer())
                        {
                            // if world object is in active zone then.
                            if (wo.Location.SquaredDistanceTo(mo.Location) <= maxobjectRange)
                            {
                                // if world object is in active zone.
                                if (!(mo as Player).GetTrackedObjectGuids().Contains(wo.Guid))
                                {
                                    (mo as Player).TrackObject(wo);
                                }
                            }
                            // if world object is in ghost zone and outside of active zone
                            else
                            if ((mo as Player).GetTrackedObjectGuids().Contains(wo.Guid))
                            {
                                (mo as Player).StopTrackingObject(wo, true);
                            }
                        }
                    });

                    if (mo.Location.LandblockId == id)
                    {
                        // update if it's still here
                        Broadcast(BroadcastEventArgs.CreateAction(BroadcastAction.AddOrUpdate, mo), true, Quadrant.All);
                    }
                    else
                    {
                        // remove and readd if it's not
                        RemoveWorldObject(mo.Guid, false);
                        LandblockManager.AddObject(mo);
                    }
                });

                // despawn objects
                despawnObjects.ForEach(deo =>
                {
                    if (deo.DespawnTime < WorldManager.PortalYearTicks)
                    {
                        RemoveWorldObject(deo.Guid, false);
                    }
                });

                // respawn creatures
                deadCreatures.ForEach(dc =>
                {
                    if (dc.RespawnTime < WorldManager.PortalYearTicks)
                    {
                        dc.IsAlive = true;
                        // HandleParticleEffectEvent(dc, PlayScript.Create);
                        AddWorldObject(dc);
                    }
                });

                Thread.Sleep(1);
            }

            // TODO: release resources
        }
Exemplo n.º 18
0
        /// <summary>
        /// Called when a player dies, in conjunction with Die()
        /// </summary>
        /// <param name="lastDamager">The last damager that landed the death blow</param>
        /// <param name="damageType">The damage type for the death message</param>
        public override DeathMessage OnDeath(WorldObject lastDamager, DamageType damageType, bool criticalHit = false)
        {
            if (DamageHistory.TopDamager is Player pkPlayer)
            {
                if (IsPKDeath(pkPlayer))
                {
                    pkPlayer.PkTimestamp = Time.GetUnixTime();
                    pkPlayer.PlayerKillsPk++;

                    var globalPKDe = $"{lastDamager.Name} has defeated {Name}!";

                    if ((Location.Cell & 0xFFFF) < 0x100)
                    {
                        globalPKDe += $" The kill occured at {Location.GetMapCoordStr()}";
                    }

                    globalPKDe += "\n[PKDe]";

                    PlayerManager.BroadcastToAll(new GameMessageSystemChat(globalPKDe, ChatMessageType.Broadcast));
                }
                else if (IsPKLiteDeath(pkPlayer))
                {
                    pkPlayer.PlayerKillsPkl++;
                }
            }

            var deathMessage = base.OnDeath(lastDamager, damageType, criticalHit);

            if (lastDamager != null)
            {
                lastDamager.EmoteManager.OnKill(this);
            }

            var playerMsg = "";

            if (lastDamager != null)
            {
                playerMsg = string.Format(deathMessage.Victim, Name, lastDamager.Name);
            }
            else
            {
                playerMsg = deathMessage.Victim;
            }

            var msgYourDeath = new GameEventYourDeath(Session, playerMsg);

            Session.Network.EnqueueSend(msgYourDeath);

            // broadcast to nearby players
            var nearbyMsg = "";

            if (lastDamager != null)
            {
                nearbyMsg = string.Format(deathMessage.Broadcast, Name, lastDamager.Name);
            }
            else
            {
                nearbyMsg = deathMessage.Broadcast;
            }

            var broadcastMsg = new GameMessageSystemChat(nearbyMsg, ChatMessageType.Broadcast);

            log.Debug("[CORPSE] " + nearbyMsg);

            var excludePlayers = new List <Player>();

            if (lastDamager is Player lastDamagerPlayer)
            {
                excludePlayers.Add(lastDamagerPlayer);
            }

            var nearbyPlayers = EnqueueBroadcast(excludePlayers, false, broadcastMsg);

            excludePlayers.AddRange(nearbyPlayers);
            excludePlayers.Add(this); // exclude self

            if (Fellowship != null)
            {
                Fellowship.OnDeath(this);
            }

            // if the player's lifestone is in a different landblock, also broadcast their demise to that landblock
            if (Sanctuary != null && Location.Landblock != Sanctuary.Landblock)
            {
                // ActionBroadcastKill might not work if other players around lifestone aren't aware of this player yet...
                // this existing broadcast method is also based on the current visible objects to the player,
                // and the player hasn't entered portal space or teleported back to the lifestone yet, so this doesn't work
                //ActionBroadcastKill(nearbyMsg, Guid, lastDamager.Guid);

                // instead, we get all of the players in the lifestone landblock + adjacent landblocks,
                // and possibly limit that to some radius around the landblock?
                var lifestoneBlock = LandblockManager.GetLandblock(new LandblockId(Sanctuary.Landblock << 16 | 0xFFFF), true);
                lifestoneBlock.EnqueueBroadcast(excludePlayers, true, broadcastMsg);
            }

            return(deathMessage);
        }
        public AdminAuthenticatedModule()
        {
            this.RequiresAuthentication();

            this.RequiresClaims(k => k.Type == AccessLevel.Admin.ToString());

            Get("/api/command", async(_) =>
            {
                AdminCommandRequestModel request = this.BindAndValidate <AdminCommandRequestModel>();
                if (!ModelValidationResult.IsValid)
                {
                    return(Negotiate.WithModel(ModelValidationResult).WithStatusCode(HttpStatusCode.BadRequest));
                }
                CommandManager.ConsoleCommandOverallResult result = null;

                Gate.RunGatedAction(() =>
                {
                    result = CommandManager.DigestCommand(request.Command);
                });

                return(new AdminCommandResponseModel()
                {
                    Success = result.ConsoleCommandResult == CommandManager.ConsoleCommandDigestionResult.Success,
                    CommandDigestionResult = result.ConsoleCommandResult.ToString(),
                    CommandHandlerResponse = result.CommandHandlerResponse?.ToString(),
                    SubmittedCommand = request.Command
                }.AsJsonWebResponse());
            });

            Get("/api/playerLocations", async(_) =>
            {
                PlayerLocationsResponseModel resp        = new PlayerLocationsResponseModel();
                Gate.RunGatedAction(() => resp.Locations = PlayerManager.GetAllOnline().Select(k => new PlayerNameAndLocation()
                {
                    Location = k.Location.ToString(), Name = k.Name
                }).ToList());
                return(resp.AsJsonWebResponse());
            });

            Get("/api/landblockStatus", async(_) =>
            {
                LandblockStatusResponseModel resp = new LandblockStatusResponseModel();
                Gate.RunGatedAction(() =>
                {
                    List <Server.Entity.Landblock> activeLandblocks = LandblockManager.GetActiveLandblocks();
                    List <LandblockStatus> lbsl = new List <LandblockStatus>();
                    foreach (Server.Entity.Landblock landblock in activeLandblocks)
                    {
                        LandblockStatus lbs = new LandblockStatus()
                        {
                            Id        = landblock.Id.ToString(),
                            Creatures = new List <WorldObjectStatus>(),
                            Missiles  = new List <WorldObjectStatus>(),
                            Other     = new List <WorldObjectStatus>(),
                            Players   = new List <WorldObjectStatus>()
                        };
                        foreach (WorldObject worldObject in landblock.GetAllWorldObjectsForDiagnostics())
                        {
                            if (worldObject is Player)
                            {
                                lbs.Players.Add(new WorldObjectStatus()
                                {
                                    Id = worldObject.Guid.Full, Name = worldObject.Name
                                });
                            }
                            else if (worldObject is Creature)
                            {
                                lbs.Creatures.Add(new WorldObjectStatus()
                                {
                                    Id = worldObject.Guid.Full, Name = worldObject.Name
                                });
                            }
                            else if (worldObject.Missile ?? false)
                            {
                                lbs.Missiles.Add(new WorldObjectStatus()
                                {
                                    Id = worldObject.Guid.Full, Name = worldObject.Name
                                });
                            }
                            else
                            {
                                lbs.Other.Add(new WorldObjectStatus()
                                {
                                    Id = worldObject.Guid.Full, Name = worldObject.Name
                                });
                            }
                        }
                        lbsl.Add(lbs);
                    }
                    resp.Active = lbsl;
                });
                return(resp.AsJsonWebResponse());
            });
        }
Exemplo n.º 20
0
        private void HandleGameAction(QueuedGameAction action, Player player)
        {
            switch (action.ActionType)
            {
            case GameActionType.TalkDirect:
            {
                // TODO: remove this hack (using TalkDirect) ASAP
                var         g   = new ObjectGuid(action.ObjectId);
                WorldObject obj = (WorldObject)player;
                if (worldObjects.ContainsKey(g))
                {
                    obj = worldObjects[g];
                }
                DeathMessageArgs d = new DeathMessageArgs(action.ActionBroadcastMessage, new ObjectGuid(action.ObjectId), new ObjectGuid(action.SecondaryObjectId));
                HandleDeathMessage(obj, d);
                break;
            }

            case GameActionType.TeleToHouse:
            case GameActionType.TeleToLifestone:
            case GameActionType.TeleToMansion:
            case GameActionType.TeleToMarketPlace:
            case GameActionType.TeleToPkArena:
            case GameActionType.TeleToPklArena:
            {
                player.Teleport(action.ActionLocation);
                break;
            }

            case GameActionType.ApplyVisualEffect:
            {
                var         g   = new ObjectGuid(action.ObjectId);
                WorldObject obj = (WorldObject)player;
                if (worldObjects.ContainsKey(g))
                {
                    obj = worldObjects[g];
                }
                var particleEffect = (PlayScript)action.SecondaryObjectId;
                HandleParticleEffectEvent(obj, particleEffect);
                break;
            }

            case GameActionType.ApplySoundEffect:
            {
                var         g   = new ObjectGuid(action.ObjectId);
                WorldObject obj = (WorldObject)player;
                if (worldObjects.ContainsKey(g))
                {
                    obj = worldObjects[g];
                }
                var soundEffect = (Sound)action.SecondaryObjectId;
                HandleSoundEvent(obj, soundEffect);
                break;
            }

            case GameActionType.PutItemInContainer:
            {
                var playerId    = new ObjectGuid(action.ObjectId);
                var inventoryId = new ObjectGuid(action.SecondaryObjectId);
                if (playerId.IsPlayer())
                {
                    Player      aPlayer       = null;
                    WorldObject inventoryItem = null;

                    if (worldObjects.ContainsKey(playerId) && worldObjects.ContainsKey(inventoryId))
                    {
                        aPlayer       = (Player)worldObjects[playerId];
                        inventoryItem = worldObjects[inventoryId];
                    }

                    if ((aPlayer != null) && (inventoryItem != null))
                    {
                        var motion = new GeneralMotion(MotionStance.Standing);
                        motion.MovementData.ForwardCommand = (ushort)MotionCommand.Pickup;
                        aPlayer.Session.Network.EnqueueSend(new GameMessageUpdatePosition(aPlayer),
                                                            new GameMessageUpdateMotion(aPlayer, aPlayer.Session, motion),
                                                            new GameMessageSound(aPlayer.Guid, Sound.PickUpItem, (float)1.0));

                        // Add to the inventory list.
                        aPlayer.AddToInventory(inventoryItem);
                        LandblockManager.RemoveObject(inventoryItem);

                        motion = new GeneralMotion(MotionStance.Standing);
                        aPlayer.Session.Network.EnqueueSend(new GameMessagePrivateUpdatePropertyInt(aPlayer.Session,
                                                                                                    PropertyInt.EncumbVal,
                                                                                                    aPlayer.GameData.Burden),
                                                            new GameMessagePutObjectInContainer(aPlayer.Session, aPlayer, inventoryId),
                                                            new GameMessageUpdateMotion(aPlayer, aPlayer.Session, motion),
                                                            new GameMessageUpdateInstanceId(inventoryId, playerId),
                                                            new GameMessagePickupEvent(aPlayer.Session, inventoryItem));

                        aPlayer.TrackObject(inventoryItem);
                    }
                }
                break;
            }

            case GameActionType.DropItem:
            {
                var g = new ObjectGuid(action.ObjectId);
                // ReSharper disable once InconsistentlySynchronizedField
                if (worldObjects.ContainsKey(g))
                {
                    var playerId    = new ObjectGuid(action.ObjectId);
                    var inventoryId = new ObjectGuid(action.SecondaryObjectId);
                    if (playerId.IsPlayer())
                    {
                        Player      aPlayer       = null;
                        WorldObject inventoryItem = null;

                        if (worldObjects.ContainsKey(playerId))
                        {
                            aPlayer       = (Player)worldObjects[playerId];
                            inventoryItem = aPlayer.GetInventoryItem(inventoryId);
                            aPlayer.RemoveFromInventory(inventoryId);
                        }

                        if ((aPlayer != null) && (inventoryItem != null))
                        {
                            var targetContainer = new ObjectGuid(0);
                            aPlayer.Session.Network.EnqueueSend(
                                new GameMessagePrivateUpdatePropertyInt(
                                    aPlayer.Session,
                                    PropertyInt.EncumbVal,
                                    (uint)aPlayer.Session.Player.GameData.Burden));

                            var motion = new GeneralMotion(MotionStance.Standing);
                            motion.MovementData.ForwardCommand = (ushort)MotionCommand.Pickup;
                            aPlayer.Session.Network.EnqueueSend(
                                new GameMessageUpdateMotion(aPlayer, aPlayer.Session, motion),
                                new GameMessageUpdateInstanceId(inventoryId, targetContainer));

                            motion = new GeneralMotion(MotionStance.Standing);
                            aPlayer.Session.Network.EnqueueSend(
                                new GameMessageUpdateMotion(aPlayer, aPlayer.Session, motion),
                                new GameMessagePutObjectIn3d(aPlayer.Session, aPlayer, inventoryId),
                                new GameMessageSound(aPlayer.Guid, Sound.DropItem, (float)1.0),
                                new GameMessageUpdateInstanceId(inventoryId, targetContainer));

                            // This is the sequence magic - adds back into 3d space seem to be treated like teleport.
                            inventoryItem.Sequences.GetNextSequence(SequenceType.ObjectTeleport);
                            inventoryItem.Sequences.GetNextSequence(SequenceType.ObjectVector);
                            LandblockManager.AddObject(inventoryItem);
                            aPlayer.Session.Network.EnqueueSend(new GameMessageUpdatePosition(inventoryItem));
                        }
                    }
                }
                break;
            }

            case GameActionType.MovementEvent:
            {
                var         g   = new ObjectGuid(action.ObjectId);
                WorldObject obj = (WorldObject)player;
                if (worldObjects.ContainsKey(g))
                {
                    obj = worldObjects[g];
                }
                var motion = action.Motion;
                HandleMovementEvent(obj, motion);
                break;
            }

            case GameActionType.ObjectCreate:
            {
                this.AddWorldObject(action.WorldObject);
                break;
            }

            case GameActionType.ObjectDelete:
            {
                this.RemoveWorldObject(action.WorldObject.Guid, false);
                break;
            }

            case GameActionType.QueryHealth:
            {
                if (action.ObjectId == 0)
                {
                    // Deselect the formerly selected Target
                    player.SelectedTarget = 0;
                    break;
                }

                object target   = null;
                var    targetId = new ObjectGuid(action.ObjectId);

                // Remember the selected Target
                player.SelectedTarget = action.ObjectId;

                // TODO: once items are implemented check if there are items that can trigger
                //       the QueryHealth event. So far I believe it only gets triggered for players and creatures
                if (targetId.IsPlayer() || targetId.IsCreature())
                {
                    if (this.worldObjects.ContainsKey(targetId))
                    {
                        target = this.worldObjects[targetId];
                    }

                    if (target == null)
                    {
                        // check adjacent landblocks for the targetId
                        foreach (var block in adjacencies)
                        {
                            if (block.Value != null)
                            {
                                if (block.Value.worldObjects.ContainsKey(targetId))
                                {
                                    target = block.Value.worldObjects[targetId];
                                }
                            }
                        }
                    }
                    if (target != null)
                    {
                        float healthPercentage = 0;

                        if (targetId.IsPlayer())
                        {
                            Player tmpTarget = (Player)target;
                            healthPercentage = (float)tmpTarget.Health.Current / (float)tmpTarget.Health.MaxValue;
                        }
                        if (targetId.IsCreature())
                        {
                            Creature tmpTarget = (Creature)target;
                            healthPercentage = (float)tmpTarget.Health.Current / (float)tmpTarget.Health.MaxValue;
                        }
                        var updateHealth = new GameEventUpdateHealth(player.Session, targetId.Full, healthPercentage);
                        player.Session.Network.EnqueueSend(updateHealth);
                    }
                }

                break;
            }

            case GameActionType.Use:
            {
                var g = new ObjectGuid(action.ObjectId);
                if (worldObjects.ContainsKey(g))
                {
                    WorldObject obj = worldObjects[g];

                    switch (obj.Type)
                    {
                    case Enum.ObjectType.Portal:
                    {
                        // validate within use range :: set to a fixed value as static Portals are normally OnCollide usage
                        float rangeCheck = 5.0f;

                        if (player.Location.SquaredDistanceTo(obj.Location) < rangeCheck)
                        {
                            PortalDestination portalDestination = DatabaseManager.World.GetPortalDestination(obj.WeenieClassid);

                            if (portalDestination != null)
                            {
                                player.Session.Player.Teleport(portalDestination.Position);
                                // always send useDone event
                                var sendUseDoneEvent = new GameEventUseDone(player.Session);
                                player.Session.Network.EnqueueSend(sendUseDoneEvent);
                            }
                            else
                            {
                                string serverMessage    = "Portal destination for portal ID " + obj.WeenieClassid + " not yet implemented!";
                                var    usePortalMessage = new GameMessageSystemChat(serverMessage, ChatMessageType.System);
                                // always send useDone event
                                var sendUseDoneEvent = new GameEventUseDone(player.Session);
                                player.Session.Network.EnqueueSend(usePortalMessage, sendUseDoneEvent);
                            }
                        }
                        else
                        {
                            // always send useDone event
                            var sendUseDoneEvent = new GameEventUseDone(player.Session);
                            player.Session.Network.EnqueueSend(sendUseDoneEvent);
                        }

                        break;
                    }

                    case Enum.ObjectType.LifeStone:
                    {
                        string serverMessage = null;
                        // validate within use range
                        float radiusSquared = obj.GameData.UseRadius * obj.GameData.UseRadius;

                        var motionSanctuary = new GeneralMotion(MotionStance.Standing, new MotionItem(MotionCommand.Sanctuary));

                        var animationEvent = new GameMessageUpdateMotion(player, player.Session, motionSanctuary);

                        // This event was present for a pcap in the training dungeon.. Why? The sound comes with animationEvent...
                        var soundEvent = new GameMessageSound(obj.Guid, Sound.LifestoneOn, 1);

                        if (player.Location.SquaredDistanceTo(obj.Location) >= radiusSquared)
                        {
                            serverMessage = "You wandered too far to attune with the Lifestone!";
                        }
                        else
                        {
                            player.SetCharacterPosition(PositionType.Sanctuary, player.Location);

                            // create the outbound server message
                            serverMessage = "You have attuned your spirit to this Lifestone. You will resurrect here after you die.";
                            player.EnqueueMovementEvent(motionSanctuary, player.Guid);
                            player.Session.Network.EnqueueSend(soundEvent);
                        }

                        var lifestoneBindMessage = new GameMessageSystemChat(serverMessage, ChatMessageType.Magic);
                        // always send useDone event
                        var sendUseDoneEvent = new GameEventUseDone(player.Session);
                        player.Session.Network.EnqueueSend(lifestoneBindMessage, sendUseDoneEvent);

                        break;
                    }
                    }
                }
                break;
            }
            }
        }
Exemplo n.º 21
0
        /// <summary>
        /// Called when a player dies, in conjunction with Die()
        /// </summary>
        /// <param name="lastDamager">The last damager that landed the death blow</param>
        /// <param name="damageType">The damage type for the death message</param>
        public override DeathMessage OnDeath(DamageHistoryInfo lastDamager, DamageType damageType, bool criticalHit = false)
        {
            var topDamager = DamageHistory.GetTopDamager(false);

            HandlePKDeathBroadcast(lastDamager, topDamager);

            var deathMessage = base.OnDeath(lastDamager, damageType, criticalHit);

            var lastDamagerObj = lastDamager?.TryGetAttacker();

            if (lastDamagerObj != null)
            {
                lastDamagerObj.EmoteManager.OnKill(this);
            }

            var playerMsg = "";

            if (lastDamager != null)
            {
                playerMsg = string.Format(deathMessage.Victim, Name, lastDamager.Name);
            }
            else
            {
                playerMsg = deathMessage.Victim;
            }

            var msgYourDeath = new GameEventVictimNotification(Session, playerMsg);

            Session.Network.EnqueueSend(msgYourDeath);

            // broadcast to nearby players
            var nearbyMsg = "";

            if (lastDamager != null)
            {
                nearbyMsg = string.Format(deathMessage.Broadcast, Name, lastDamager.Name);
            }
            else
            {
                nearbyMsg = deathMessage.Broadcast;
            }

            var broadcastMsg = new GameMessagePlayerKilled(nearbyMsg, Guid, lastDamager?.Guid ?? ObjectGuid.Invalid);

            log.Debug("[CORPSE] " + nearbyMsg);

            var excludePlayers = new List <Player>();

            var nearbyPlayers = EnqueueBroadcast(excludePlayers, false, broadcastMsg);

            excludePlayers.AddRange(nearbyPlayers);

            if (Fellowship != null)
            {
                Fellowship.OnDeath(this);
            }

            // if the player's lifestone is in a different landblock, also broadcast their demise to that landblock
            if (PropertyManager.GetBool("lifestone_broadcast_death").Item&& Sanctuary != null && Location.Landblock != Sanctuary.Landblock)
            {
                // ActionBroadcastKill might not work if other players around lifestone aren't aware of this player yet...
                // this existing broadcast method is also based on the current visible objects to the player,
                // and the player hasn't entered portal space or teleported back to the lifestone yet, so this doesn't work
                //ActionBroadcastKill(nearbyMsg, Guid, lastDamager.Guid);

                // instead, we get all of the players in the lifestone landblock + adjacent landblocks,
                // and possibly limit that to some radius around the landblock?
                var lifestoneBlock = LandblockManager.GetLandblock(new LandblockId(Sanctuary.Landblock << 16 | 0xFFFF), true);
                lifestoneBlock.EnqueueBroadcast(excludePlayers, true, Sanctuary, LocalBroadcastRangeSq, broadcastMsg);
            }

            return(deathMessage);
        }
Exemplo n.º 22
0
        /// <summary>
        /// Used by physics engine to actually update a player position
        /// Automatically notifies clients of updated position
        /// </summary>
        /// <param name="newPosition">The new position being requested, before verification through physics engine</param>
        /// <returns>TRUE if object moves to a different landblock</returns>
        public bool UpdatePlayerPhysics(ACE.Entity.Position newPosition, bool forceUpdate = false)
        {
            //Console.WriteLine($"{Name}.UpdatePlayerPhysics({newPosition}, {forceUpdate}, {Teleporting})");

            // possible bug: while teleporting, client can still send AutoPos packets from old landblock
            if (Teleporting && !forceUpdate)
            {
                return(false);
            }

            // pre-validate movement
            if (!ValidateMovement(newPosition))
            {
                log.Error($"{Name}.UpdatePlayerPhysics() - movement pre-validation failed from {Location} to {newPosition}");
                return(false);
            }

            try
            {
                if (!forceUpdate) // This is needed beacuse this function might be called recursively
                {
                    stopwatch.Restart();
                }

                var success = true;

                if (PhysicsObj != null)
                {
                    var distSq = Location.SquaredDistanceTo(newPosition);

                    if (distSq > PhysicsGlobals.EpsilonSq)
                    {
                        var curCell = LScape.get_landcell(newPosition.Cell);
                        if (curCell != null)
                        {
                            //if (PhysicsObj.CurCell == null || curCell.ID != PhysicsObj.CurCell.ID)
                            //PhysicsObj.change_cell_server(curCell);

                            PhysicsObj.set_request_pos(newPosition.Pos, newPosition.Rotation, curCell, Location.LandblockId.Raw);
                            success = PhysicsObj.update_object_server();

                            if (PhysicsObj.CurCell == null && curCell.ID >> 16 != 0x18A)
                            {
                                PhysicsObj.CurCell = curCell;
                            }

                            CheckMonsters();
                        }
                    }
                }

                // double update path: landblock physics update -> updateplayerphysics() -> update_object_server() -> Teleport() -> updateplayerphysics() -> return to end of original branch
                if (Teleporting && !forceUpdate)
                {
                    return(true);
                }

                if (!success)
                {
                    return(false);
                }

                var landblockUpdate = Location.Cell >> 16 != newPosition.Cell >> 16;
                Location = newPosition;

                SendUpdatePosition();

                if (!InUpdate)
                {
                    LandblockManager.RelocateObjectForPhysics(this, true);
                }

                return(landblockUpdate);
            }
            finally
            {
                if (!forceUpdate) // This is needed beacuse this function might be called recursively
                {
                    var elapsedSeconds = stopwatch.Elapsed.TotalSeconds;
                    ServerPerformanceMonitor.AddToCumulativeEvent(ServerPerformanceMonitor.CumulativeEventHistoryType.WorldObject_Tick_UpdatePlayerPhysics, elapsedSeconds);
                    if (elapsedSeconds >= 1) // Yea, that ain't good....
                    {
                        log.Warn($"[PERFORMANCE][PHYSICS] {Guid}:{Name} took {(elapsedSeconds * 1000):N1} ms to process UpdatePlayerPhysics() at loc: {Location}");
                    }
                    else if (elapsedSeconds >= 0.010)
                    {
                        log.Debug($"[PERFORMANCE][PHYSICS] {Guid}:{Name} took {(elapsedSeconds * 1000):N1} ms to process UpdatePlayerPhysics() at loc: {Location}");
                    }
                }
            }
        }
Exemplo n.º 23
0
        /// <summary>
        /// Launches a projectile from player to target
        /// </summary>
        public WorldObject LaunchProjectile(WorldObject weapon, WorldObject ammo, WorldObject target, Vector3 origin, Quaternion orientation, Vector3 velocity)
        {
            var player = this as Player;

            if (!velocity.IsValid())
            {
                if (player != null)
                {
                    player.SendWeenieError(WeenieError.YourAttackMisfired);
                }

                return(null);
            }

            var proj = WorldObjectFactory.CreateNewWorldObject(ammo.WeenieClassId);

            proj.ProjectileSource = this;
            proj.ProjectileTarget = target;

            proj.ProjectileLauncher = weapon;

            proj.Location          = new Position(Location);
            proj.Location.Pos      = origin;
            proj.Location.Rotation = orientation;

            proj.Velocity = velocity;

            SetProjectilePhysicsState(proj, target);

            var success = LandblockManager.AddObject(proj);

            if (!success || proj.PhysicsObj == null)
            {
                if (!proj.HitMsg && player != null)
                {
                    player.Session.Network.EnqueueSend(new GameMessageSystemChat("Your missile attack hit the environment.", ChatMessageType.Broadcast));
                }

                return(null);
            }

            if (!IsProjectileVisible(proj))
            {
                proj.OnCollideEnvironment();
                return(null);
            }

            var pkStatus = player?.PlayerKillerStatus ?? PlayerKillerStatus.Creature;

            proj.EnqueueBroadcast(new GameMessagePublicUpdatePropertyInt(proj, PropertyInt.PlayerKillerStatus, (int)pkStatus));
            proj.EnqueueBroadcast(new GameMessageScript(proj.Guid, PlayScript.Launch, 0f));

            // detonate point-blank projectiles immediately

            /*var radsum = target.PhysicsObj.GetRadius() + proj.PhysicsObj.GetRadius();
             * var dist = Vector3.Distance(origin, dest);
             * if (dist < radsum)
             * {
             *  Console.WriteLine($"Point blank");
             *  proj.OnCollideObject(target);
             * }*/

            return(proj);
        }
Exemplo n.º 24
0
 /// <summary>
 /// Called when a monster changes landblocks
 /// </summary>
 public void UpdateLandblock()
 {
     LandblockManager.RelocateObjectForPhysics(this, true);
     //Console.WriteLine("Relocating " + Name + " to " + Location.LandblockId);
 }
Exemplo n.º 25
0
        public static void HandleLandblockPerformance(Session session, params string[] parameters)
        {
            var sb = new StringBuilder();

            var loadedLandblocks = LandblockManager.GetLoadedLandblocks();

            // Filter out landblocks that haven't recorded a certain amount of events
            var sortedBy5mAverage  = loadedLandblocks.Where(r => r.Monitor5m.EventHistory.TotalEvents >= 10).OrderByDescending(r => r.Monitor5m.EventHistory.AverageEventDuration).Take(10).ToList();
            var sortedBy1hrAverage = loadedLandblocks.Where(r => r.Monitor1h.EventHistory.TotalEvents >= 1000).OrderByDescending(r => r.Monitor1h.EventHistory.AverageEventDuration).Take(10).ToList();

            var combinedByAverage = sortedBy5mAverage.Concat(sortedBy1hrAverage).Distinct().OrderByDescending(r => Math.Max(r.Monitor5m.EventHistory.AverageEventDuration, r.Monitor1h.EventHistory.AverageEventDuration)).Take(10);

            sb.Append($"Most Busy Landblock - By Average{'\n'}");
            sb.Append($"~5m Hits   Avg  Long  Last - ~1h Hits   Avg  Long  Last - Location   Players  Creatures{'\n'}");

            foreach (var entry in combinedByAverage)
            {
                int players = 0, creatures = 0;
                foreach (var worldObject in entry.GetAllWorldObjectsForDiagnostics())
                {
                    if (worldObject is Player)
                    {
                        players++;
                    }
                    else if (worldObject is Creature)
                    {
                        creatures++;
                    }
                }

                sb.Append($"{entry.Monitor5m.EventHistory.TotalEvents.ToString().PadLeft(7)} {entry.Monitor5m.EventHistory.AverageEventDuration:N4} {entry.Monitor5m.EventHistory.LongestEvent:N3} {entry.Monitor5m.EventHistory.LastEvent:N3} - " +
                          $"{entry.Monitor1h.EventHistory.TotalEvents.ToString().PadLeft(7)} {entry.Monitor1h.EventHistory.AverageEventDuration:N4} {entry.Monitor1h.EventHistory.LongestEvent:N3} {entry.Monitor1h.EventHistory.LastEvent:N3} - " +
                          $"0x{entry.Id.Raw:X8} {players.ToString().PadLeft(7)}  {creatures.ToString().PadLeft(9)}{'\n'}");
            }

            var sortedBy5mLong  = loadedLandblocks.OrderByDescending(r => r.Monitor5m.EventHistory.LongestEvent).Take(10);
            var sortedBy1hrLong = loadedLandblocks.OrderByDescending(r => r.Monitor1h.EventHistory.LongestEvent).Take(10);

            var combinedByLong = sortedBy5mLong.Concat(sortedBy1hrLong).Distinct().OrderByDescending(r => Math.Max(r.Monitor5m.EventHistory.LongestEvent, r.Monitor1h.EventHistory.LongestEvent)).Take(10);

            sb.Append($"Most Busy Landblock - By Longest{'\n'}");
            sb.Append($"~5m Hits   Avg  Long  Last - ~1h Hits   Avg  Long  Last - Location   Players  Creatures{'\n'}");

            foreach (var entry in combinedByLong)
            {
                int players = 0, creatures = 0;
                foreach (var worldObject in entry.GetAllWorldObjectsForDiagnostics())
                {
                    if (worldObject is Player)
                    {
                        players++;
                    }
                    else if (worldObject is Creature)
                    {
                        creatures++;
                    }
                }

                sb.Append($"{entry.Monitor5m.EventHistory.TotalEvents.ToString().PadLeft(7)} {entry.Monitor5m.EventHistory.AverageEventDuration:N4} {entry.Monitor5m.EventHistory.LongestEvent:N3} {entry.Monitor5m.EventHistory.LastEvent:N3} - " +
                          $"{entry.Monitor1h.EventHistory.TotalEvents.ToString().PadLeft(7)} {entry.Monitor1h.EventHistory.AverageEventDuration:N4} {entry.Monitor1h.EventHistory.LongestEvent:N3} {entry.Monitor1h.EventHistory.LastEvent:N3} - " +
                          $"0x{entry.Id.Raw:X8} {players.ToString().PadLeft(7)}  {creatures.ToString().PadLeft(9)}{'\n'}");
            }

            CommandHandlerHelper.WriteOutputInfo(session, sb.ToString());
        }
 public static Landblock TryGetLandblock(this Position p)
 {
     return(LandblockManager.TryGetLandblock(p.LongLandblockID));
 }
Exemplo n.º 27
0
        /// <summary>
        /// Used by physics engine to actually update a player position
        /// Automatically notifies clients of updated position
        /// </summary>
        /// <param name="newPosition">The new position being requested, before verification through physics engine</param>
        /// <returns>TRUE if object moves to a different landblock</returns>
        public bool UpdatePlayerPosition(ACE.Entity.Position newPosition, bool forceUpdate = false)
        {
            //Console.WriteLine($"{Name}.UpdatePlayerPhysics({newPosition}, {forceUpdate}, {Teleporting})");
            bool verifyContact = false;

            // possible bug: while teleporting, client can still send AutoPos packets from old landblock
            if (Teleporting && !forceUpdate)
            {
                return(false);
            }

            // pre-validate movement
            if (!ValidateMovement(newPosition))
            {
                log.Error($"{Name}.UpdatePlayerPosition() - movement pre-validation failed from {Location} to {newPosition}");
                return(false);
            }

            try
            {
                if (!forceUpdate) // This is needed beacuse this function might be called recursively
                {
                    stopwatch.Restart();
                }

                var success = true;

                if (PhysicsObj != null)
                {
                    var distSq = Location.SquaredDistanceTo(newPosition);

                    if (distSq > PhysicsGlobals.EpsilonSq)
                    {
                        /*var p = new Physics.Common.Position(newPosition);
                         * var dist = PhysicsObj.Position.Distance(p);
                         * Console.WriteLine($"Dist: {dist}");*/

                        if (newPosition.Landblock == 0x18A && Location.Landblock != 0x18A)
                        {
                            log.Info($"{Name} is getting swanky");
                        }

                        if (!Teleporting)
                        {
                            var blockDist = PhysicsObj.GetBlockDist(Location.ObjCellID, newPosition.ObjCellID);

                            // verify movement
                            if (distSq > MaxSpeedSq && blockDist > 1)
                            {
                                //Session.Network.EnqueueSend(new GameMessageSystemChat("Movement error", ChatMessageType.Broadcast));
                                log.Warn($"MOVEMENT SPEED: {Name} trying to move from {Location} to {newPosition}, speed: {Math.Sqrt(distSq)}");
                                return(false);
                            }

                            // verify z-pos
                            if (blockDist == 0 && LastGroundPos != null && newPosition.Pos.Z - LastGroundPos.Pos.Z > 10 && DateTime.UtcNow - LastJumpTime > TimeSpan.FromSeconds(1) && GetCreatureSkill(Skill.Jump).Current < 1000)
                            {
                                verifyContact = true;
                            }
                        }

                        var curCell = LScape.get_landcell(newPosition.LongObjCellID);
                        if (curCell != null)
                        {
                            //if (PhysicsObj.CurCell == null || curCell.ID != PhysicsObj.CurCell.ID)
                            //PhysicsObj.change_cell_server(curCell);

                            PhysicsObj.set_request_pos(newPosition.Pos, newPosition.Rotation, newPosition.Instance, curCell, Location.ObjCellID);
                            if (FastTick)
                            {
                                success = PhysicsObj.update_object_server_new(true, newPosition.Instance);
                            }
                            else
                            {
                                success = PhysicsObj.update_object_server(true, newPosition.Instance);
                            }

                            if (PhysicsObj.CurCell == null && curCell.ID >> 16 != 0x18A)
                            {
                                PhysicsObj.CurCell = curCell;
                            }

                            if (verifyContact && IsJumping)
                            {
                                log.Warn($"z-pos hacking detected for {Name}, lastGroundPos: {LastGroundPos.ToLOCString()} - requestPos: {newPosition.ToLOCString()}");
                                Location = new ACE.Entity.Position(LastGroundPos);
                                Sequences.GetNextSequence(SequenceType.ObjectForcePosition);
                                SendUpdatePosition();
                                return(false);
                            }

                            CheckMonsters();
                        }
                    }
                    else
                    {
                        PhysicsObj.Position.Frame.Orientation = newPosition.Rotation;
                    }
                }

                // double update path: landblock physics update -> updateplayerphysics() -> update_object_server() -> Teleport() -> updateplayerphysics() -> return to end of original branch
                if (Teleporting && !forceUpdate)
                {
                    return(true);
                }

                if (!success)
                {
                    return(false);
                }

                var landblockUpdate = Location.Landblock != newPosition.Landblock;

                Location = newPosition;

                if (RecordCast.Enabled)
                {
                    RecordCast.Log($"CurPos: {Location.ToLOCString()}");
                }

                if (RequestedLocationBroadcast || DateTime.UtcNow - LastUpdatePosition >= MoveToState_UpdatePosition_Threshold)
                {
                    SendUpdatePosition();
                }
                else
                {
                    Session.Network.EnqueueSend(new GameMessageUpdatePosition(this));
                }

                if (!InUpdate)
                {
                    // todo: improve this logic

                    /*if (CurrentLandblock.Instance > 0)
                     * {
                     *  ClearInstance(CurrentLandblock.LongId);
                     * }*/

                    LandblockManager.RelocateObjectForPhysics(this, true);
                }

                return(landblockUpdate);
            }
            finally
            {
                if (!forceUpdate) // This is needed beacuse this function might be called recursively
                {
                    var elapsedSeconds = stopwatch.Elapsed.TotalSeconds;
                    ServerPerformanceMonitor.AddToCumulativeEvent(ServerPerformanceMonitor.CumulativeEventHistoryType.Player_Tick_UpdateObjectPhysics, elapsedSeconds);
                    if (elapsedSeconds >= 0.100) // Yea, that ain't good....
                    {
                        log.Warn($"[PERFORMANCE][PHYSICS] {Guid}:{Name} took {(elapsedSeconds * 1000):N1} ms to process UpdatePlayerPosition() at loc: {Location}");
                    }
                    else if (elapsedSeconds >= 0.010)
                    {
                        log.Debug($"[PERFORMANCE][PHYSICS] {Guid}:{Name} took {(elapsedSeconds * 1000):N1} ms to process UpdatePlayerPosition() at loc: {Location}");
                    }
                }
            }
        }
Exemplo n.º 28
0
        /// <summary>
        /// main game loop
        /// </summary>
        public void UseTime()
        {
            while (running)
            {
                // here we'd move server objects in motion (subject to landscape) and do physics collision detection

                // for now, we'll move players around
                List <WorldObject> movedObjects = null;
                List <Player>      players      = null;

                lock (objectCacheLocker)
                {
                    movedObjects = this.worldObjects.Values.OfType <WorldObject>().ToList();
                    players      = this.worldObjects.Values.OfType <Player>().ToList();
                }

                movedObjects = movedObjects.Where(p => p.LastUpdatedTicks >= p.LastMovementBroadcastTicks).ToList();

                // flag them as updated now in order to reduce chance of missing an update
                movedObjects.ForEach(m => m.LastMovementBroadcastTicks = WorldManager.PortalYearTicks);

                if (this.id.MapScope == Enum.MapScope.Outdoors)
                {
                    // check to see if a player or other mutable object "roamed" to an adjacent landblock
                    var objectsToRelocate = movedObjects.Where(m => m.Location.LandblockId.IsAdjacentTo(this.id) && m.Location.LandblockId != this.id).ToList();

                    // so, these objects moved to an adjacent block.  they could have recalled to that block, died and bounced to a lifestone on that block, or
                    // just simply walked accross the border line.  in any case, we won't delete them, we'll just transfer them.  the trick, though, is to
                    // figure out how to treat it in adjacent landblocks.  if the player walks across the southern border, the north adjacency needs to remove
                    // them, but the south is actually getting them.  we need to avoid sending Delete+Create to clients that already know about it, though.

                    objectsToRelocate.ForEach(o => Log($"attempting to relocate object {o.Name} ({o.Guid.Full.ToString("X")})"));

                    // RelocateObject will put them in the right landblock
                    objectsToRelocate.ForEach(o => LandblockManager.RelocateObject(o));

                    // Remove has logic to make sure it doesn't double up the delete+create when "true" is passed.
                    objectsToRelocate.ForEach(o => RemoveWorldObject(o.Guid, true));
                }

                // broadcast
                Parallel.ForEach(movedObjects, mo =>
                {
                    if (mo.Location.LandblockId == this.id)
                    {
                        // update if it's still here
                        Broadcast(BroadcastEventArgs.CreateAction(BroadcastAction.AddOrUpdate, mo), true, Quadrant.All);
                    }
                    else
                    {
                        // remove and readd if it's not
                        this.RemoveWorldObject(mo.Guid, false);
                        LandblockManager.AddObject(mo);
                    }
                });

                // TODO: figure out if this landblock can be unloaded

                // process player action queues
                foreach (Player p in players)
                {
                    QueuedGameAction action = p.ActionQueuePop();

                    if (action != null)
                    {
                        HandleGameAction(action, p);
                    }
                }

                Thread.Sleep(1);
            }

            // TODO: release resources
        }
Exemplo n.º 29
0
        public static void HandleServerStatus(Session session, params string[] parameters)
        {
            // This is formatted very similarly to GDL.

            var sb = new StringBuilder();

            var proc = Process.GetCurrentProcess();

            sb.Append($"Server Status:{'\n'}");

            sb.Append($"Host Info: {Environment.OSVersion}, vCPU: {Environment.ProcessorCount}{'\n'}");

            var runTime = DateTime.Now - proc.StartTime;

            sb.Append($"Server Runtime: {(int)runTime.TotalHours}h {runTime.Minutes}m {runTime.Seconds}s{'\n'}");

            sb.Append($"Total CPU Time: {(int)proc.TotalProcessorTime.TotalHours}h {proc.TotalProcessorTime.Minutes}m {proc.TotalProcessorTime.Seconds}s, Threads: {proc.Threads.Count}{'\n'}");

            // todo, add actual system memory used/avail
            sb.Append($"{(proc.PrivateMemorySize64 >> 20):N0} MB used{'\n'}");  // sb.Append($"{(proc.PrivateMemorySize64 >> 20)} MB used, xxxx / yyyy MB physical mem free.{'\n'}");

            sb.Append($"{NetworkManager.GetSessionCount():N0} connections, {NetworkManager.GetUniqueSessionEndpointCount():N0} unique connections, {PlayerManager.GetAllOnline().Count:N0} players online{'\n'}");
            sb.Append($"Total Accounts Created: {DatabaseManager.Authentication.GetAccountCount():N0}, Total Characters Created: {(PlayerManager.GetAllOffline().Count + PlayerManager.GetAllOnline().Count):N0}{'\n'}");

            // 330 active objects, 1931 total objects(16777216 buckets.)

            // todo, expand this
            var loadedLandblocks = LandblockManager.GetLoadedLandblocks();
            int dormantLandblocks = 0, activeDungeonLandblocks = 0, dormantDungeonLandblocks = 0;
            int players = 0, creatures = 0, missiles = 0, other = 0, total = 0;

            foreach (var landblock in loadedLandblocks)
            {
                if (landblock.IsDormant)
                {
                    dormantLandblocks++;
                }

                if (landblock.IsDungeon)
                {
                    if (landblock.IsDormant)
                    {
                        dormantDungeonLandblocks++;
                    }
                    else
                    {
                        activeDungeonLandblocks++;
                    }
                }

                foreach (var worldObject in landblock.GetAllWorldObjectsForDiagnostics())
                {
                    if (worldObject is Player)
                    {
                        players++;
                    }
                    else if (worldObject is Creature)
                    {
                        creatures++;
                    }
                    else if (worldObject.Missile ?? false)
                    {
                        missiles++;
                    }
                    else
                    {
                        other++;
                    }

                    total++;
                }
            }
            sb.Append($"Landblocks: {(loadedLandblocks.Count - dormantLandblocks):N0} active ({activeDungeonLandblocks:N0} dungeons), {dormantLandblocks:N0} dormant ({dormantDungeonLandblocks:N0} dungeons) - Players: {players:N0}, Creatures: {creatures:N0}, Missiles: {missiles:N0}, Other: {other:N0}, Total: {total:N0}.{'\n'}"); // 11 total blocks loaded. 11 active. 0 pending dormancy. 0 dormant. 314 unloaded.
            // 11 total blocks loaded. 11 active. 0 pending dormancy. 0 dormant. 314 unloaded.

            if (ServerPerformanceMonitor.IsRunning)
            {
                sb.Append($"Server Performance Monitor - UpdateGameWorld ~5m {ServerPerformanceMonitor.GetEventHistory5m(ServerPerformanceMonitor.MonitorType.UpdateGameWorld_Entire).AverageEventDuration:N3}, ~1h {ServerPerformanceMonitor.GetEventHistory1h(ServerPerformanceMonitor.MonitorType.UpdateGameWorld_Entire).AverageEventDuration:N3} s{'\n'}");
            }
            else
            {
                sb.Append($"Server Performance Monitor - Not running. To start use /serverperformance start{'\n'}");
            }

            sb.Append($"Physics Cache Counts - BSPCache: {BSPCache.Count:N0}, GfxObjCache: {GfxObjCache.Count:N0}, PolygonCache: {PolygonCache.Count:N0}, VertexCache: {VertexCache.Count:N0}{'\n'}");

            sb.Append($"Total Server Objects: {ServerObjectManager.ServerObjects.Count:N0}{'\n'}");

            sb.Append($"World DB Cache Counts - Weenies: {DatabaseManager.World.GetWeenieCacheCount():N0}, LandblockInstances: {DatabaseManager.World.GetLandblockInstancesCacheCount():N0}, PointsOfInterest: {DatabaseManager.World.GetPointsOfInterestCacheCount():N0}, Cookbooks: {DatabaseManager.World.GetCookbookCacheCount():N0}, Spells: {DatabaseManager.World.GetSpellCacheCount():N0}, Encounters: {DatabaseManager.World.GetEncounterCacheCount():N0}, Events: {DatabaseManager.World.GetEventsCacheCount():N0}{'\n'}");
            sb.Append($"Shard DB Counts - Biotas: {DatabaseManager.Shard.GetBiotaCount():N0}{'\n'}");

            sb.Append(GuidManager.GetDynamicGuidDebugInfo() + '\n');

            sb.Append($"Portal.dat has {DatManager.PortalDat.FileCache.Count:N0} files cached of {DatManager.PortalDat.AllFiles.Count:N0} total{'\n'}");
            sb.Append($"Cell.dat has {DatManager.CellDat.FileCache.Count:N0} files cached of {DatManager.CellDat.AllFiles.Count:N0} total{'\n'}");

            CommandHandlerHelper.WriteOutputInfo(session, $"{sb}");
        }
Exemplo n.º 30
0
        private void HandleGameAction(QueuedGameAction action, Player player)
        {
            switch (action.ActionType)
            {
            case GameActionType.TalkDirect:
            {
                // TODO: remove this hack (using TalkDirect) ASAP
                var         g   = new ObjectGuid(action.ObjectId);
                WorldObject obj = (WorldObject)player;
                if (worldObjects.ContainsKey(g))
                {
                    obj = worldObjects[g];
                }
                DeathMessageArgs d = new DeathMessageArgs(action.ActionBroadcastMessage, new ObjectGuid(action.ObjectId), new ObjectGuid(action.SecondaryObjectId));
                HandleDeathMessage(obj, d);
                break;
            }

            case GameActionType.TeleToHouse:
            case GameActionType.TeleToLifestone:
            case GameActionType.TeleToMansion:
            case GameActionType.TeleToMarketPlace:
            case GameActionType.TeleToPkArena:
            case GameActionType.TeleToPklArena:
            {
                player.Teleport(action.ActionLocation);
                break;
            }

            case GameActionType.ApplyVisualEffect:
            {
                var         g   = new ObjectGuid(action.ObjectId);
                WorldObject obj = (WorldObject)player;
                if (worldObjects.ContainsKey(g))
                {
                    obj = worldObjects[g];
                }
                var particleEffect = (PlayScript)action.SecondaryObjectId;
                HandleParticleEffectEvent(obj, particleEffect);
                break;
            }

            case GameActionType.ApplySoundEffect:
            {
                var         g   = new ObjectGuid(action.ObjectId);
                WorldObject obj = (WorldObject)player;
                if (worldObjects.ContainsKey(g))
                {
                    obj = worldObjects[g];
                }
                var soundEffect = (Sound)action.SecondaryObjectId;
                HandleSoundEvent(obj, soundEffect);
                break;
            }

            case GameActionType.IdentifyObject:
            {
                // TODO: Throttle this request. The live servers did this, likely for a very good reason, so we should, too.
                var         g   = new ObjectGuid(action.ObjectId);
                WorldObject obj = (WorldObject)player;
                if (worldObjects.ContainsKey(g))
                {
                    obj = worldObjects[g];
                }
                var identifyResponse = new GameEventIdentifyObjectResponse(player.Session, action.ObjectId, obj);
                player.Session.Network.EnqueueSend(identifyResponse);
                break;
            }

            case GameActionType.PutItemInContainer:
            {
                var playerId    = new ObjectGuid(action.ObjectId);
                var inventoryId = new ObjectGuid(action.SecondaryObjectId);
                if (playerId.IsPlayer())
                {
                    Player      aPlayer       = null;
                    WorldObject inventoryItem = null;

                    if (worldObjects.ContainsKey(playerId) && worldObjects.ContainsKey(inventoryId))
                    {
                        aPlayer       = (Player)worldObjects[playerId];
                        inventoryItem = worldObjects[inventoryId];
                    }

                    if ((aPlayer != null) && (inventoryItem != null))
                    {
                        if (aPlayer.PhysicsData.Position.SquaredDistanceTo(inventoryItem.PhysicsData.Position)
                            > Math.Pow(inventoryItem.GameData.UseRadius, 2))
                        {
                            // This is where I need to hook in the move to object code.
                            // TODO: Og II work on this soon.
                        }
                        var motion = new UniversalMotion(MotionStance.Standing);
                        motion.MovementData.ForwardCommand = (ushort)MotionCommand.Pickup;
                        aPlayer.Session.Network.EnqueueSend(new GameMessageUpdatePosition(aPlayer),
                                                            new GameMessageUpdateMotion(aPlayer, aPlayer.Session, motion),
                                                            new GameMessageSound(aPlayer.Guid, Sound.PickUpItem, (float)1.0));

                        // Add to the inventory list.
                        aPlayer.AddToInventory(inventoryItem);
                        LandblockManager.RemoveObject(inventoryItem);

                        motion = new UniversalMotion(MotionStance.Standing);
                        aPlayer.Session.Network.EnqueueSend(new GameMessagePrivateUpdatePropertyInt(aPlayer.Session,
                                                                                                    PropertyInt.EncumbVal,
                                                                                                    aPlayer.GameData.Burden),
                                                            new GameMessagePutObjectInContainer(aPlayer.Session, aPlayer, inventoryId),
                                                            new GameMessageUpdateMotion(aPlayer, aPlayer.Session, motion),
                                                            new GameMessageUpdateInstanceId(inventoryId, playerId),
                                                            new GameMessagePickupEvent(aPlayer.Session, inventoryItem));

                        aPlayer.TrackObject(inventoryItem);
                        // This may not be needed when we fix landblock update object -
                        // TODO: Og II - check this later to see if it is still required.
                        aPlayer.Session.Network.EnqueueSend(new GameMessageUpdateObject(inventoryItem));
                    }
                }
                break;
            }

            case GameActionType.DropItem:
            {
                var g = new ObjectGuid(action.ObjectId);
                // ReSharper disable once InconsistentlySynchronizedField
                if (worldObjects.ContainsKey(g))
                {
                    var playerId    = new ObjectGuid(action.ObjectId);
                    var inventoryId = new ObjectGuid(action.SecondaryObjectId);
                    if (playerId.IsPlayer())
                    {
                        Player      aPlayer       = null;
                        WorldObject inventoryItem = null;

                        if (worldObjects.ContainsKey(playerId))
                        {
                            aPlayer       = (Player)worldObjects[playerId];
                            inventoryItem = aPlayer.GetInventoryItem(inventoryId);
                            aPlayer.RemoveFromInventory(inventoryId);
                        }

                        if ((aPlayer != null) && (inventoryItem != null))
                        {
                            var targetContainer = new ObjectGuid(0);
                            aPlayer.Session.Network.EnqueueSend(
                                new GameMessagePrivateUpdatePropertyInt(
                                    aPlayer.Session,
                                    PropertyInt.EncumbVal,
                                    (uint)aPlayer.Session.Player.GameData.Burden));

                            var motion = new UniversalMotion(MotionStance.Standing);
                            motion.MovementData.ForwardCommand = (ushort)MotionCommand.Pickup;
                            aPlayer.Session.Network.EnqueueSend(
                                new GameMessageUpdateMotion(aPlayer, aPlayer.Session, motion),
                                new GameMessageUpdateInstanceId(inventoryId, targetContainer));

                            motion = new UniversalMotion(MotionStance.Standing);
                            aPlayer.Session.Network.EnqueueSend(
                                new GameMessageUpdateMotion(aPlayer, aPlayer.Session, motion),
                                new GameMessagePutObjectIn3d(aPlayer.Session, aPlayer, inventoryId),
                                new GameMessageSound(aPlayer.Guid, Sound.DropItem, (float)1.0),
                                new GameMessageUpdateInstanceId(inventoryId, targetContainer));

                            // This is the sequence magic - adds back into 3d space seem to be treated like teleport.
                            inventoryItem.Sequences.GetNextSequence(SequenceType.ObjectTeleport);
                            inventoryItem.Sequences.GetNextSequence(SequenceType.ObjectVector);
                            LandblockManager.AddObject(inventoryItem);

                            // This may not be needed when we fix landblock update object -
                            // TODO: Og II - check this later to see if it is still required.
                            aPlayer.Session.Network.EnqueueSend(new GameMessageUpdateObject(inventoryItem));

                            aPlayer.Session.Network.EnqueueSend(new GameMessageUpdatePosition(inventoryItem));
                        }
                    }
                }
                break;
            }

            case GameActionType.MovementEvent:
            {
                var         g   = new ObjectGuid(action.ObjectId);
                WorldObject obj = (WorldObject)player;
                if (worldObjects.ContainsKey(g))
                {
                    obj = worldObjects[g];
                }
                var motion = action.Motion;
                HandleMovementEvent(obj, motion);
                break;
            }

            case GameActionType.ObjectCreate:
            {
                this.AddWorldObject(action.WorldObject);
                break;
            }

            case GameActionType.ObjectDelete:
            {
                this.RemoveWorldObject(action.WorldObject.Guid, false);
                break;
            }

            case GameActionType.QueryHealth:
            {
                if (action.ObjectId == 0)
                {
                    // Deselect the formerly selected Target
                    player.SelectedTarget = 0;
                    break;
                }

                object target   = null;
                var    targetId = new ObjectGuid(action.ObjectId);

                // Remember the selected Target
                player.SelectedTarget = action.ObjectId;

                // TODO: once items are implemented check if there are items that can trigger
                //       the QueryHealth event. So far I believe it only gets triggered for players and creatures
                if (targetId.IsPlayer() || targetId.IsCreature())
                {
                    if (this.worldObjects.ContainsKey(targetId))
                    {
                        target = this.worldObjects[targetId];
                    }

                    if (target == null)
                    {
                        // check adjacent landblocks for the targetId
                        foreach (var block in adjacencies)
                        {
                            if (block.Value != null)
                            {
                                if (block.Value.worldObjects.ContainsKey(targetId))
                                {
                                    target = block.Value.worldObjects[targetId];
                                }
                            }
                        }
                    }
                    if (target != null)
                    {
                        float healthPercentage = 0;

                        if (targetId.IsPlayer())
                        {
                            Player tmpTarget = (Player)target;
                            healthPercentage = (float)tmpTarget.Health.Current / (float)tmpTarget.Health.MaxValue;
                        }
                        if (targetId.IsCreature())
                        {
                            Creature tmpTarget = (Creature)target;
                            healthPercentage = (float)tmpTarget.Health.Current / (float)tmpTarget.Health.MaxValue;
                        }
                        var updateHealth = new GameEventUpdateHealth(player.Session, targetId.Full, healthPercentage);
                        player.Session.Network.EnqueueSend(updateHealth);
                    }
                }

                break;
            }

            case GameActionType.Use:
            {
                var g = new ObjectGuid(action.ObjectId);
                if (worldObjects.ContainsKey(g))
                {
                    WorldObject obj = worldObjects[g];

                    if ((obj.DescriptionFlags & ObjectDescriptionFlag.LifeStone) != 0)
                    {
                        (obj as Lifestone).OnUse(player);
                    }
                    else if ((obj.DescriptionFlags & ObjectDescriptionFlag.Portal) != 0)
                    {
                        // TODO: When Physics collisions are implemented, this logic should be switched there, as normal portals are not onUse.
                        (obj as Portal).OnCollide(player);
                    }
                    else if ((obj.DescriptionFlags & ObjectDescriptionFlag.Door) != 0)
                    {
                        (obj as Door).OnUse(player);
                    }

                    // switch (obj.Type)
                    // {
                    //    case Enum.ObjectType.Portal:
                    //        {
                    //            // TODO: When Physics collisions are implemented, this logic should be switched there, as normal portals are not onUse.
                    //
                    //            (obj as Portal).OnCollide(player);
                    //
                    //            break;
                    //        }
                    //    case Enum.ObjectType.LifeStone:
                    //        {
                    //            (obj as Lifestone).OnUse(player);
                    //            break;
                    //        }
                    // }
                }
                break;
            }
            }
        }