public void HandleActionEnterPkLite() { // ensure permanent npk if (PlayerKillerStatus != PlayerKillerStatus.NPK || MinimumTimeSincePk != null) { Session.Network.EnqueueSend(new GameEventWeenieError(Session, WeenieError.OnlyNonPKsMayEnterPKLite)); return; } if (TooBusyToRecall) { Session.Network.EnqueueSend(new GameEventWeenieError(Session, WeenieError.YoureTooBusy)); return; } var animTime = 0.0f; if (CombatMode != CombatMode.NonCombat) { Session.Network.EnqueueSend(new GameMessagePrivateUpdatePropertyInt(this, PropertyInt.CombatMode, (int)CombatMode.NonCombat)); animTime += SetCombatMode(CombatMode.NonCombat); } var actionChain = new ActionChain(); actionChain.AddDelaySeconds(animTime); actionChain.AddAction(this, () => { IsBusy = true; EnqueueBroadcast(new GameMessageSystemChat($"{Name} is looking for a fight!", ChatMessageType.Broadcast), LocalBroadcastRange); // perform pk lite entry motion / effect SendMotionAsCommands(MotionCommand.EnterPKLite, MotionStance.NonCombat); var innerChain = new ActionChain(); // wait for animation to complete animTime = DatManager.PortalDat.ReadFromDat <MotionTable>(MotionTableId).GetAnimationLength(MotionCommand.EnterPKLite); innerChain.AddDelaySeconds(animTime); innerChain.AddAction(this, () => { IsBusy = false; if (PropertyManager.GetBool("allow_pkl_bump").Item) { // check for collisions PlayerKillerStatus = PlayerKillerStatus.PKLite; var colliding = PhysicsObj.ethereal_check_for_collisions(); if (colliding) { // try initial placement var result = PhysicsObj.SetPositionSimple(PhysicsObj.Position, true); if (result == SetPositionError.OK) { // handle landblock update? SyncLocation(); // force broadcast Sequences.GetNextSequence(SequenceType.ObjectForcePosition); SendUpdatePosition(); } } } UpdateProperty(this, PropertyInt.PlayerKillerStatus, (int)PlayerKillerStatus.PKLite, true); Session.Network.EnqueueSend(new GameEventWeenieError(Session, WeenieError.YouAreNowPKLite)); }); innerChain.EnqueueChain(); }); actionChain.EnqueueChain(); }
/// <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}"); } } } }
public void HandleActionEnterPkLite() { // ensure permanent npk if (PlayerKillerStatus != PlayerKillerStatus.NPK || MinimumTimeSincePk != null) { Session.Network.EnqueueSend(new GameEventWeenieError(Session, WeenieError.OnlyNonPKsMayEnterPKLite)); return; } if (TooBusyToRecall) { Session.Network.EnqueueSend(new GameEventWeenieError(Session, WeenieError.YoureTooBusy)); return; } EnqueueBroadcast(new GameMessageSystemChat($"{Name} is looking for a fight!", ChatMessageType.Broadcast), LocalBroadcastRange); // perform pk lite entry motion / effect IsBusy = true; var prevStance = CurrentMotionState.Stance; var actionChain = new ActionChain(); var animTime = 0.0f; animTime += EnqueueMotion_Force(actionChain, MotionStance.NonCombat, MotionCommand.EnterPKLite); actionChain.AddAction(this, () => { if (PropertyManager.GetBool("allow_pkl_bump").Item) { // check for collisions PlayerKillerStatus = PlayerKillerStatus.PKLite; var colliding = PhysicsObj.ethereal_check_for_collisions(); if (colliding) { // try initial placement var result = PhysicsObj.SetPositionSimple(PhysicsObj.Position, true); if (result == SetPositionError.OK) { // handle landblock update? SyncLocation(); // force broadcast Sequences.GetNextSequence(SequenceType.ObjectForcePosition); SendUpdatePosition(); } } } UpdateProperty(this, PropertyInt.PlayerKillerStatus, (int)PlayerKillerStatus.PKLite, true); Session.Network.EnqueueSend(new GameEventWeenieError(Session, WeenieError.YouAreNowPKLite)); }); // return to previous stance, if applicable if (prevStance != MotionStance.NonCombat) { animTime += EnqueueMotion_Force(actionChain, prevStance, MotionCommand.Ready, MotionCommand.NonCombat); } actionChain.AddAction(this, () => IsBusy = false); actionChain.EnqueueChain(); }