Пример #1
0
        /// <summary>
        /// Gets the cell ID for a position within a landblock
        /// </summary>
        public static uint GetCell(this Position p)
        {
            var landblock = LScape.get_landblock(p.LandblockId.Raw);

            // dungeons
            // TODO: investigate dungeons that are below actual traversable overworld terrain
            // ex., 010AFFFF
            //if (landblock.IsDungeon)
            if (p.Indoors)
            {
                return(GetIndoorCell(p));
            }

            // outside - could be on landscape, in building, or underground cave
            var cellID   = GetOutdoorCell(p);
            var landcell = LScape.get_landcell(cellID) as LandCell;

            if (landcell == null)
            {
                return(cellID);
            }

            if (landcell.has_building())
            {
                var envCells = landcell.Building.get_building_cells();
                foreach (var envCell in envCells)
                {
                    if (envCell.point_in_cell(p.Pos))
                    {
                        return(envCell.ID);
                    }
                }
            }

            // handle underground areas ie. caves
            // get the terrain Z-height for this X/Y
            Physics.Polygon walkable    = null;
            var             terrainPoly = landcell.find_terrain_poly(p.Pos, ref walkable);

            if (walkable != null)
            {
                Vector3 terrainPos = p.Pos;
                walkable.Plane.set_height(ref terrainPos);

                // are we below ground? if so, search all of the indoor cells for this landblock
                if (terrainPos.Z > p.Pos.Z)
                {
                    var envCells = landblock.get_envcells();
                    foreach (var envCell in envCells)
                    {
                        if (envCell.point_in_cell(p.Pos))
                        {
                            return(envCell.ID);
                        }
                    }
                }
            }

            return(cellID);
        }
Пример #2
0
        /// <summary>
        /// Called when a monster changes cells
        /// </summary>
        public void UpdateCell()
        {
            var curCell = LScape.get_landcell(Location.LandblockId.Raw);

            //Console.WriteLine("Moving " + Name + " to " + curCell.ID.ToString("X8"));
            PhysicsObj.change_cell_server(curCell);
            //PhysicsObj.remove_shadows_from_cells();
            PhysicsObj.add_shadows_to_cell(curCell);
        }
Пример #3
0
        /// <summary>
        /// Spawns the semi-randomized monsters scattered around the outdoors<para />
        /// This will be called from a separate task from our constructor. Use thread safety when interacting with this landblock.
        /// </summary>
        private void SpawnEncounters()
        {
            // get the encounter spawns for this landblock
            var encounters = DatabaseManager.World.GetCachedEncountersByLandblock(Id.Landblock);

            foreach (var encounter in encounters)
            {
                var wo = WorldObjectFactory.CreateNewWorldObject(encounter.WeenieClassId);

                if (wo == null)
                {
                    continue;
                }

                var xPos = Math.Clamp(encounter.CellX * 24.0f, 0.5f, 191.5f);
                var yPos = Math.Clamp(encounter.CellY * 24.0f, 0.5f, 191.5f);

                var pos = new Physics.Common.Position();
                pos.ObjCellID = (uint)(Id.Landblock << 16) | 1;
                pos.Frame     = new Physics.Animation.AFrame(new Vector3(xPos, yPos, 0), Quaternion.Identity);
                pos.adjust_to_outside();

                pos.Frame.Origin.Z = _landblock.GetZ(pos.Frame.Origin);

                wo.Location = new Position(pos.ObjCellID, pos.Frame.Origin, pos.Frame.Orientation);

                var sortCell = LScape.get_landcell(pos.ObjCellID) as SortCell;
                if (sortCell != null && sortCell.has_building())
                {
                    continue;
                }

                if (PropertyManager.GetBool("override_encounter_spawn_rates").Item)
                {
                    wo.RegenerationInterval = PropertyManager.GetDouble("encounter_regen_interval").Item;

                    wo.ReinitializeHeartbeats();

                    foreach (var profile in wo.Biota.BiotaPropertiesGenerator)
                    {
                        profile.Delay = (float)PropertyManager.GetDouble("encounter_delay").Item;
                    }
                }

                actionQueue.EnqueueAction(new ActionEventDelegate(() =>
                {
                    AddWorldObject(wo);
                }));
            }
        }
Пример #4
0
        /// <summary>
        /// Updates the position using the simple physics method
        /// </summary>
        public void UpdatePosition_PhysicsInner(Vector3 requestPos, Vector3 dir)
        {
            Location.Rotate(dir);
            PhysicsObj.Position.Frame.Orientation = Location.Rotation;

            var cell = LScape.get_landcell(Location.Cell);

            PhysicsObj.set_request_pos(requestPos, Location.Rotation, null, Location.LandblockId.Raw);

            // simulate running forward for this amount of time
            PhysicsObj.update_object_server(false);

            UpdatePosition_SyncLocation();
        }
Пример #5
0
        /// <summary>
        /// Returns TRUE if outdoor position is located on walkable slope
        /// </summary>
        public static bool IsWalkable(this Position p)
        {
            if (p.Indoors)
            {
                return(true);
            }

            var landcell = (LandCell)LScape.get_landcell(p.Cell);

            Physics.Polygon walkable    = null;
            var             terrainPoly = landcell.find_terrain_poly(p.Pos, ref walkable);

            if (walkable == null)
            {
                return(false);
            }

            return(Physics.PhysicsObj.is_valid_walkable(walkable.Plane.Normal));
        }
Пример #6
0
        public void UpdatePosition_PhysicsInner(Vector3 newPos, Vector3 dir)
        {
            Location.Rotate(dir);
            PhysicsObj.Position.Frame.Orientation = Location.Rotation;

            var cell = LScape.get_landcell(Location.Cell);

            PhysicsObj.set_request_pos(newPos, Location.Rotation, cell);

            // simulate running forward for this amount of time
            PhysicsObj.update_object_server(false);

            // was the position successfully moved to?
            // use the physics position as the source-of-truth?
            Location.Pos = PhysicsObj.Position.Frame.Origin;
            if (PhysicsObj.CurCell != null && PhysicsObj.CurCell.ID != Location.Cell)
            {
                Location.LandblockId = new LandblockId(PhysicsObj.CurCell.ID);
            }
        }
Пример #7
0
        public static void AdjustMapCoords(this Position pos)
        {
            // adjust Z to terrain height
            pos.PositionZ = pos.GetTerrainZ();

            // adjust to building height, if applicable
            var sortCell = LScape.get_landcell(pos.Cell) as SortCell;

            if (sortCell != null && sortCell.has_building())
            {
                var building = sortCell.Building;

                var minZ = building.GetMinZ();

                if (minZ > 0 && minZ < float.MaxValue)
                {
                    pos.PositionZ += minZ;
                }

                pos.LandblockId = new LandblockId(pos.GetCell());
            }
        }
Пример #8
0
        public void BuildEnvCells()
        {
            EnvCells = new List <R_EnvCell>();

            if (Landblock.Info == null || Landblock.Info.NumCells == 0)
            {
                return;
            }

            var numCells = Landblock.Info.NumCells;

            var landblockID = Landblock.ID & 0xFFFF0000;

            for (uint i = 0; i < numCells; i++)
            {
                var envCellID = landblockID | (0x100 + i);

                var envCell = (EnvCell)LScape.get_landcell(envCellID);

                EnvCells.Add(new R_EnvCell(envCell));
            }
        }
Пример #9
0
        public static float GetTerrainZ(this Position p)
        {
            var cellID   = GetOutdoorCell(p);
            var landcell = (LandCell)LScape.get_landcell(cellID);

            if (landcell == null)
            {
                return(p.Pos.Z);
            }

            Physics.Polygon walkable = null;
            if (!landcell.find_terrain_poly(p.Pos, ref walkable))
            {
                return(p.Pos.Z);
            }

            Vector3 terrainPos = p.Pos;

            walkable.Plane.set_height(ref terrainPos);

            return(terrainPos.Z);
        }
Пример #10
0
        /// <summary>
        /// Spawns the semi-randomized monsters scattered around the outdoors<para />
        /// This will be called from a separate task from our constructor. Use thread safety when interacting with this landblock.
        /// </summary>
        private void SpawnEncounters()
        {
            // get the encounter spawns for this landblock
            var encounters = DatabaseManager.World.GetCachedEncountersByLandblock(Id.Landblock);

            foreach (var encounter in encounters)
            {
                var wo = WorldObjectFactory.CreateNewWorldObject(encounter.WeenieClassId);

                if (wo == null)
                {
                    continue;
                }

                var xPos = Math.Clamp(encounter.CellX * 24.0f, 0.5f, 191.5f);
                var yPos = Math.Clamp(encounter.CellY * 24.0f, 0.5f, 191.5f);

                var pos = new Physics.Common.Position();
                pos.ObjCellID = (uint)(Id.Landblock << 16) | 1;
                pos.Frame     = new Physics.Animation.AFrame(new Vector3(xPos, yPos, 0), Quaternion.Identity);
                pos.adjust_to_outside();

                pos.Frame.Origin.Z = _landblock.GetZ(pos.Frame.Origin);

                wo.Location = new Position(pos.ObjCellID, pos.Frame.Origin, pos.Frame.Orientation);

                var sortCell = LScape.get_landcell(pos.ObjCellID) as SortCell;
                if (sortCell != null && sortCell.has_building())
                {
                    continue;
                }

                actionQueue.EnqueueAction(new ActionEventDelegate(() =>
                {
                    AddWorldObject(wo);
                }));
            }
        }
Пример #11
0
        private void AddWorldObjectInternal(WorldObject wo)
        {
            Log($"adding {wo.Guid.Full:X}");

            if (!worldObjects.ContainsKey(wo.Guid))
            {
                worldObjects[wo.Guid] = wo;
            }

            wo.SetParent(this);

            wo.PhysicsObj.Position.Frame.Origin      = wo.Location.Pos;
            wo.PhysicsObj.Position.Frame.Orientation = wo.Location.Rotation;

            //wo.AdjustDungeonCells(wo.Location);

            var cell = LScape.get_landcell(wo.Location.Cell);

            if (cell != null)
            {
                wo.PhysicsObj.enter_cell(cell);
                wo.PhysicsObj.add_shadows_to_cell(cell);
            }

            // var args = BroadcastEventArgs.CreateAction(BroadcastAction.AddOrUpdate, wo);
            // Broadcast(args, true, Quadrant.All);
            // Alert all nearby players of the object
            EnqueueActionBroadcast(wo.Location, MaxObjectRange, (Player p) => p.TrackObject(wo));

            // if this is a player, tell them about everything else we have in range of them.
            if (wo is Player)
            {
                List <WorldObject> wolist = null;
                wolist = GetWorldObjectsInRange(wo, MaxObjectRange);
                AddPlayerTracking(wolist, ((Player)wo));
            }
        }
Пример #12
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}");
                    }
                }
            }
        }
Пример #13
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}");
                    }
                }
            }
        }
Пример #14
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);
        }
Пример #15
0
        public static void LoadEncounters()
        {
            Initting = true;
            MainWindow.Instance.SuppressStatusText = true;

            SetDatabaseConfig();

            Encounters = new List <WorldObject>();

            var timer = Stopwatch.StartNew();

            // get the list of loaded landblocks
            foreach (var lbid in LScape.Landblocks.Keys)
            {
                var encounters = DatabaseManager.World.GetCachedEncountersByLandblock((ushort)(lbid >> 16));

                Console.WriteLine($"Found {encounters.Count:N0} encounters for {lbid:X8}");

                var landblock = LScape.get_landblock(lbid);

                foreach (var encounter in encounters)
                {
                    var wo = WorldObjectFactory.CreateNewWorldObject(encounter.WeenieClassId);

                    if (wo == null)
                    {
                        continue;
                    }

                    wo.InitPhysicsObj();

                    var xPos = Math.Clamp(encounter.CellX * 24.0f, 0.5f, 191.5f);
                    var yPos = Math.Clamp(encounter.CellY * 24.0f, 0.5f, 191.5f);

                    var pos = new Position();
                    pos.ObjCellID = lbid & 0xFFFF0000 | 1;
                    pos.Frame     = new AFrame(new Vector3(xPos, yPos, 0), Quaternion.Identity);
                    pos.adjust_to_outside();

                    pos.Frame.Origin.Z = landblock.GetZ(pos.Frame.Origin);

                    wo.Location = new ACE.Entity.Position(pos.ObjCellID, pos.Frame.Origin, pos.Frame.Orientation);

                    var sortCell = LScape.get_landcell(pos.ObjCellID) as SortCell;
                    if (sortCell != null && sortCell.has_building())
                    {
                        continue;
                    }

                    var success = wo.AddPhysicsObj(pos);

                    if (!success)
                    {
                        Console.WriteLine($"LoadEncounters({lbid:X8}).AddPhysicsObj({wo.Name}, {pos}) - failed to spawn");
                        continue;
                    }

                    Console.WriteLine($"Spawned {encounter.WeenieClassId} - {wo.Name} @ {pos}");

                    AddEncounter(wo);
                }
            }

            TickGenerators(GeneratorTickMode.Encounters);

            timer.Stop();

            Console.WriteLine($"Completed in {timer.Elapsed.TotalSeconds}s");
        }