public void HandleActionJump(JumpPack jump) { StartJump = new ACE.Entity.Position(Location); //Console.WriteLine($"JumpPack: Velocity: {jump.Velocity}, Extent: {jump.Extent}"); var strength = Strength.Current; var capacity = EncumbranceSystem.EncumbranceCapacity((int)strength, AugmentationIncreasedCarryingCapacity); var burden = EncumbranceSystem.GetBurden(capacity, EncumbranceVal ?? 0); // calculate stamina cost for this jump var extent = Math.Clamp(jump.Extent, 0.0f, 1.0f); var staminaCost = MovementSystem.JumpStaminaCost(extent, burden, PKTimerActive); //Console.WriteLine($"Strength: {strength}, Capacity: {capacity}, Encumbrance: {EncumbranceVal ?? 0}, Burden: {burden}, StaminaCost: {staminaCost}"); // ensure player has enough stamina to jump /*if (staminaCost > Stamina.Current) * { * // get adjusted power * extent = MovementSystem.GetJumpPower(Stamina.Current, burden, false); * * staminaCost = (int)Stamina.Current; * * // adjust jump velocity * var velocityZ = MovementSystem.GetJumpHeight(burden, GetCreatureSkill(Skill.Jump).Current, extent, 1.0f); * * jump.Velocity.Z = velocityZ; * }*/ LastJumpTime = DateTime.UtcNow; UpdateVitalDelta(Stamina, -staminaCost); //Console.WriteLine($"Jump velocity: {jump.Velocity}"); // TODO: have server verify / scale magnitude if (FastTick) { if (!PhysicsObj.IsMovingOrAnimating) { //PhysicsObj.UpdateTime = PhysicsTimer.CurrentTime - Physics.PhysicsGlobals.MinQuantum; PhysicsObj.UpdateTime = PhysicsTimer.CurrentTime; } // perform jump in physics engine PhysicsObj.TransientState &= ~(Physics.TransientStateFlags.Contact | Physics.TransientStateFlags.WaterContact); PhysicsObj.calc_acceleration(); PhysicsObj.set_on_walkable(false); PhysicsObj.set_local_velocity(jump.Velocity, false); if (CombatMode == CombatMode.Magic && MagicState.IsCasting) { FailCast(); } } else { PhysicsObj.UpdateTime = PhysicsTimer.CurrentTime; // set jump velocity //var glob_velocity = Vector3.Transform(jump.Velocity, Location.Rotation); //PhysicsObj.set_velocity(glob_velocity, true); // perform jump in physics engine PhysicsObj.TransientState &= ~(Physics.TransientStateFlags.Contact | Physics.TransientStateFlags.WaterContact); PhysicsObj.calc_acceleration(); PhysicsObj.set_on_walkable(false); PhysicsObj.set_local_velocity(jump.Velocity, false); } // this shouldn't be needed, but without sending this update motion / simulated movement event beforehand, // running forward and then performing a charged jump does an uncharged shallow arc jump instead // this hack fixes that... var movementData = new MovementData(this); movementData.IsAutonomous = true; movementData.MovementType = MovementType.Invalid; movementData.Invalid = new MovementInvalid(movementData); EnqueueBroadcast(new GameMessageUpdateMotion(this, movementData)); // broadcast jump EnqueueBroadcast(new GameMessageVectorUpdate(this)); if (MagicState.IsCasting && RecordCast.Enabled) { RecordCast.OnJump(jump); } }
private void SetEphemeralValues() { ObjectDescriptionFlags |= ObjectDescriptionFlag.Player; // This is the default send upon log in and the most common. Anything with a velocity will need to add that flag. // This should be handled automatically... //PositionFlags |= PositionFlags.OrientationHasNoX | PositionFlags.OrientationHasNoY | PositionFlags.IsGrounded | PositionFlags.HasPlacementID; FirstEnterWorldDone = false; SetStance(MotionStance.NonCombat, false); // radius for object updates ListeningRadius = 5f; if (Session != null && Common.ConfigManager.Config.Server.Accounts.OverrideCharacterPermissions) { if (Session.AccessLevel == AccessLevel.Admin) { IsAdmin = true; } if (Session.AccessLevel == AccessLevel.Developer) { IsArch = true; } if (Session.AccessLevel == AccessLevel.Envoy || Session.AccessLevel == AccessLevel.Sentinel) { IsSentinel = true; } if (Session.AccessLevel == AccessLevel.Advocate) { IsAdvocate = true; } } ContainerCapacity = (byte)(7 + AugmentationExtraPackSlot); if (Session != null && AdvocateQuest && IsAdvocate) // Advocate permissions are per character regardless of override { if (Session.AccessLevel == AccessLevel.Player) { Session.SetAccessLevel(AccessLevel.Advocate); // Elevate to Advocate permissions } if (AdvocateLevel > 4) { IsPsr = true; // Enable AdvocateTeleport via MapClick } } CombatTable = DatManager.PortalDat.ReadFromDat <CombatManeuverTable>(CombatTableDID.Value); _questManager = new QuestManager(this); ContractManager = new ContractManager(this); ConfirmationManager = new ConfirmationManager(this); LootPermission = new Dictionary <ObjectGuid, DateTime>(); SquelchManager = new SquelchManager(this); MagicState = new MagicState(this); RecordCast = new RecordCast(this); AttackQueue = new AttackQueue(this); return; // todo // ======================================= // This code was taken from the old Load() // ======================================= /*AceCharacter character; * * if (Common.ConfigManager.Config.Server.Accounts.OverrideCharacterPermissions) * { * if (Session.AccessLevel == AccessLevel.Admin) * character.IsAdmin = true; * if (Session.AccessLevel == AccessLevel.Developer) * character.IsArch = true; * if (Session.AccessLevel == AccessLevel.Envoy) * character.IsEnvoy = true; * // TODO: Need to setup and account properly for IsSentinel and IsAdvocate. * // if (Session.AccessLevel == AccessLevel.Sentinel) * // character.IsSentinel = true; * // if (Session.AccessLevel == AccessLevel.Advocate) * // character.IsAdvocate= true; * }*/ // FirstEnterWorldDone = false; // IsAlive = true; }
/// <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}.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 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.Cell, newPosition.Cell); // 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.PositionZ - LastGroundPos.PositionZ > 10 && DateTime.UtcNow - LastJumpTime > TimeSpan.FromSeconds(1) && GetCreatureSkill(Skill.Jump).Current < 1000) { verifyContact = true; } } 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); if (FastTick) { success = PhysicsObj.update_object_server_new(); } else { success = PhysicsObj.update_object_server(); } if (PhysicsObj.CurCell == null && curCell.ID >> 16 != 0x18A) { PhysicsObj.CurCell = curCell; } if (verifyContact && !PhysicsObj.TransientState.HasFlag(TransientStateFlags.OnWalkable)) { 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(); } } } // 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; 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) { 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 >= 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}"); } } } }