diff options
Diffstat (limited to '')
-rw-r--r-- | Server/Plugins/APIDump/APIDesc.lua | 4 | ||||
-rw-r--r-- | src/Entities/Player.cpp | 83 | ||||
-rw-r--r-- | src/Entities/Player.h | 30 |
3 files changed, 116 insertions, 1 deletions
diff --git a/Server/Plugins/APIDump/APIDesc.lua b/Server/Plugins/APIDump/APIDesc.lua index 6be3795a8..75d8b0eb7 100644 --- a/Server/Plugins/APIDump/APIDesc.lua +++ b/Server/Plugins/APIDump/APIDesc.lua @@ -1928,6 +1928,7 @@ a_Player:OpenWindow(Window); Feed = { Params = "AddFood, AddSaturation", Return = "bool", Notes = "Tries to add the specified amounts to food level and food saturation level (only positive amounts expected). Returns true if player was hungry and the food was consumed, false if too satiated." }, FoodPoison = { Params = "NumTicks", Return = "", Notes = "Starts the food poisoning for the specified amount of ticks; if already foodpoisoned, sets FoodPoisonedTicksRemaining to the larger of the two" }, ForceSetSpeed = { Params = "{{Vector3d|Direction}}", Notes = "Forces the player to move to the given direction." }, + Freeze = { Params = "{{Vector3d|Location}}", Return = "", Notes = "Teleports the player to \"Location\" and prevents them from moving, locking them in place until unfreeze() is called" }, GetClientHandle = { Params = "", Return = "{{cClientHandle}}", Notes = "Returns the client handle representing the player's connection. May be nil (AI players)." }, GetColor = { Return = "string", Notes = "Returns the full color code to be used for this player's messages (based on their rank). Prefix player messages with this code." }, GetCurrentXp = { Params = "", Return = "number", Notes = "Returns the current amount of XP" }, @@ -1943,6 +1944,7 @@ a_Player:OpenWindow(Window); GetFoodPoisonedTicksRemaining = { Params = "", Return = "", Notes = "Returns the number of ticks left for the food posoning effect" }, GetFoodSaturationLevel = { Params = "", Return = "number", Notes = "Returns the food saturation (overcharge of the food level, is depleted before food level)" }, GetFoodTickTimer = { Params = "", Return = "", Notes = "Returns the number of ticks past the last food-based heal or damage action; when this timer reaches 80, a new heal / damage is applied." }, + GetFrozenDuration = { Params = "", Return = "number", Notes = "Returns the number of ticks since the player was frozen" }, GetGameMode = { Return = "{{Globals#GameMode|GameMode}}", Notes = "Returns the player's gamemode. The player may have their gamemode unassigned, in which case they inherit the gamemode from the current {{cWorld|world}}.<br /> <b>NOTE:</b> Instead of comparing the value returned by this function to the gmXXX constants, use the IsGameModeXXX() functions. These functions handle the gamemode inheritance automatically."}, GetIP = { Return = "string", Notes = "Returns the IP address of the player, if available. Returns an empty string if there's no IP to report."}, GetInventory = { Return = "{{cInventory|Inventory}}", Notes = "Returns the player's inventory"}, @@ -1975,6 +1977,7 @@ a_Player:OpenWindow(Window); IsGameModeSurvival = { Params = "", Return = "bool", Notes = "Returns true if the player is in the gmSurvival gamemode, or has their gamemode unset and the world is a gmSurvival world." }, IsInBed = { Params = "", Return = "bool", Notes = "Returns true if the player is currently lying in a bed." }, IsSatiated = { Params = "", Return = "bool", Notes = "Returns true if the player is satiated (cannot eat)." }, + IsFrozen = { Params = "", Return = "bool", Notes = "Returns true if the player is frozen. See Freeze()" }, IsVisible = { Params = "", Return = "bool", Notes = "Returns true if the player is visible to other players" }, LoadRank = { Params = "", Return = "", Notes = "Reloads the player's rank, message visuals and permissions from the {{cRankManager}}, based on the player's current rank." }, MoveTo = { Params = "{{Vector3d|NewPosition}}", Return = "Tries to move the player into the specified position." }, @@ -2017,6 +2020,7 @@ a_Player:OpenWindow(Window); TossEquippedItem = { Params = "[Amount]", Return = "", Notes = "Tosses the item that the player has selected in their hotbar. Amount defaults to 1." }, TossHeldItem = { Params = "[Amount]", Return = "", Notes = "Tosses the item held by the cursor, then the player is in a UI window. Amount defaults to 1." }, TossPickup = { Params = "{{cItem|Item}}", Return = "", Notes = "Tosses a pickup newly created from the specified item." }, + Unfreeze = { Params = "", Return = "", Notes = "Allows the player to move again, canceling the effects of Freeze()" }, XpForLevel = { Params = "XPLevel", Return = "number", Notes = "(STATIC) Returns the total amount of XP needed for the specified XP level. Inverse of CalcLevelFromXp()." }, }, Constants = diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index 33ded6ab9..bede603e2 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -63,6 +63,7 @@ cPlayer::cPlayer(cClientHandlePtr a_Client, const AString & a_PlayerName) : m_GameMode(eGameMode_NotSet), m_IP(""), m_ClientHandle(a_Client), + m_FreezeCounter(-1), m_NormalMaxSpeed(1.0), m_SprintingMaxSpeed(1.3), m_FlyingMaxSpeed(1.0), @@ -112,6 +113,7 @@ cPlayer::cPlayer(cClientHandlePtr a_Client, const AString & a_PlayerName) : m_LastGroundHeight = static_cast<float>(GetPosY()); m_Stance = GetPosY() + 1.62; + FreezeInternal(GetPosition(), false); // Freeze. Will be unfrozen once the chunk is loaded if (m_GameMode == gmNotSet) { @@ -220,8 +222,35 @@ void cPlayer::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) m_Stats.AddValue(statMinutesPlayed, 1); + // Handle a frozen player + if (m_IsFrozen) + { + m_FreezeCounter += 1; + if (!m_IsManuallyFrozen && a_Chunk.IsValid()) + { + // If the player was automatically frozen, unfreeze if the chunk the player is inside is loaded + Unfreeze(); + } + else + { + // If the player was externally / manually frozen (plugin, etc.) or if the chunk isn't loaded yet: + // 1. Set the location to m_FrozenPosition every tick. + // 2. Zero out the speed every tick. + // 3. Send location updates every 60 ticks. + SetPosition(m_FrozenPosition); + SetSpeed(0, 0, 0); + if (m_FreezeCounter % 60 == 0) + { + BroadcastMovementUpdate(m_ClientHandle.get()); + m_ClientHandle->SendPlayerPosition(); + } + return; + } + } + if (!a_Chunk.IsValid()) { + FreezeInternal(GetPosition(), false); // This may happen if the cPlayer is created before the chunks have the chance of being loaded / generated (#83) return; } @@ -1263,6 +1292,46 @@ void cPlayer::TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ) +void cPlayer::Freeze(const Vector3d & a_Location) +{ + FreezeInternal(a_Location, true); +} + + + + + +bool cPlayer::IsFrozen() +{ + return m_IsFrozen; +} + + + + + +int cPlayer::GetFrozenDuration() +{ + return m_FreezeCounter; +} + + + + + +void cPlayer::Unfreeze() +{ + m_FreezeCounter = -1; + m_IsFrozen = false; + SetPosition(m_FrozenPosition); + BroadcastMovementUpdate(m_ClientHandle.get()); + m_ClientHandle->SendPlayerPosition(); +} + + + + + void cPlayer::SendRotation(double a_YawDegrees, double a_PitchDegrees) { SetYaw(a_YawDegrees); @@ -1533,6 +1602,20 @@ void cPlayer::TossItems(const cItems & a_Items) } + + + +void cPlayer::FreezeInternal(const Vector3d & a_Location, bool a_ManuallyFrozen) +{ + m_IsFrozen = true; + m_FrozenPosition = a_Location; + m_IsManuallyFrozen = a_ManuallyFrozen; +} + + + + + bool cPlayer::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn, Vector3d a_NewPosition) { ASSERT(a_World != nullptr); diff --git a/src/Entities/Player.h b/src/Entities/Player.h index bff9599f7..10c9106a3 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -138,6 +138,18 @@ public: // tolua_begin + /** Prevent the player from moving and lock him into a_Location. */ + void Freeze(const Vector3d & a_Location); + + /** Is the player frozen? */ + bool IsFrozen(); + + /** How long has the player been frozen? */ + int GetFrozenDuration(); + + /** Cancels Freeze(...) and allows the player to move naturally. */ + void Unfreeze(); + /** Sends the "look" packet to the player, forcing them to set their rotation to the specified values. a_YawDegrees is clipped to range [-180, +180), a_PitchDegrees is clipped to range [-180, +180) but the client only uses [-90, +90] @@ -240,7 +252,7 @@ public: void SendMessageFatal (const AString & a_Message) { m_ClientHandle->SendChat(a_Message, mtFailure); } void SendMessagePrivateMsg (const AString & a_Message, const AString & a_Sender) { m_ClientHandle->SendChat(a_Message, mtPrivateMessage, a_Sender); } void SendMessage (const cCompositeChat & a_Message) { m_ClientHandle->SendChat(a_Message); } - + void SendSystemMessage (const AString & a_Message) { m_ClientHandle->SendChatSystem(a_Message, mtCustom); } void SendAboveActionBarMessage(const AString & a_Message) { m_ClientHandle->SendChatAboveActionBar(a_Message, mtCustom); } void SendSystemMessage (const cCompositeChat & a_Message) { m_ClientHandle->SendChatSystem(a_Message); } @@ -576,6 +588,18 @@ protected: cSlotNums m_InventoryPaintSlots; + /** if m_IsFrozen is true, we lock m_Location to this position. */ + Vector3d m_FrozenPosition; + + /** If true, we are locking m_Position to m_FrozenPosition. */ + bool m_IsFrozen; + + /** */ + int m_FreezeCounter; + + /** Was the player frozen manually by a plugin or automatically by the server? */ + bool m_IsManuallyFrozen; + /** Max speed, relative to the game default. 1 means regular speed, 2 means twice as fast, 0.5 means half-speed. Default value is 1. */ @@ -661,6 +685,10 @@ protected: /** Tosses a list of items. */ void TossItems(const cItems & a_Items); + /** Pins the player to a_Location until Unfreeze() is called. + If ManuallyFrozen is false, the player will unfreeze when the chunk is loaded. */ + void FreezeInternal(const Vector3d & a_Location, bool a_ManuallyFrozen); + /** Returns the filename for the player data based on the UUID given. This can be used both for online and offline UUIDs. */ AString GetUUIDFileName(const AString & a_UUID); |