summaryrefslogtreecommitdiffstats
path: root/src/Entities/Player.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/Entities/Player.cpp')
-rw-r--r--src/Entities/Player.cpp458
1 files changed, 207 insertions, 251 deletions
diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp
index 391a5ce71..d20795643 100644
--- a/src/Entities/Player.cpp
+++ b/src/Entities/Player.cpp
@@ -86,7 +86,7 @@ const int cPlayer::EATING_TICKS = 30;
-cPlayer::cPlayer(const cClientHandlePtr & a_Client, const AString & a_PlayerName) :
+cPlayer::cPlayer(const cClientHandlePtr & a_Client) :
Super(etPlayer, 0.6, 1.8),
m_bVisible(true),
m_FoodLevel(MAX_FOOD_LEVEL),
@@ -96,10 +96,8 @@ cPlayer::cPlayer(const cClientHandlePtr & a_Client, const AString & a_PlayerName
m_Stance(0.0),
m_Inventory(*this),
m_EnderChestContents(9, 3),
- m_CurrentWindow(nullptr),
- m_InventoryWindow(nullptr),
+ m_DefaultWorldPath(cRoot::Get()->GetDefaultWorld()->GetDataPath()),
m_GameMode(eGameMode_NotSet),
- m_IP(""),
m_ClientHandle(a_Client),
m_IsFrozen(false),
m_NormalMaxSpeed(1.0),
@@ -113,7 +111,6 @@ cPlayer::cPlayer(const cClientHandlePtr & a_Client, const AString & a_PlayerName
m_EatingFinishTick(-1),
m_LifetimeTotalXp(0),
m_CurrentXp(0),
- m_bDirtyExperience(false),
m_IsChargingBow(false),
m_BowCharge(0),
m_FloaterID(cEntity::INVALID_ID),
@@ -122,11 +119,10 @@ cPlayer::cPlayer(const cClientHandlePtr & a_Client, const AString & a_PlayerName
m_TicksUntilNextSave(PLAYER_INVENTORY_SAVE_INTERVAL),
m_bIsTeleporting(false),
m_UUID((a_Client != nullptr) ? a_Client->GetUUID() : cUUID{}),
- m_CustomName(""),
m_SkinParts(0),
m_MainHand(mhRight)
{
- ASSERT(a_PlayerName.length() <= 16); // Otherwise this player could crash many clients...
+ ASSERT(GetName().length() <= 16); // Otherwise this player could crash many clients...
m_InventoryWindow = new cInventoryWindow(*this);
m_CurrentWindow = m_InventoryWindow;
@@ -136,7 +132,6 @@ cPlayer::cPlayer(const cClientHandlePtr & a_Client, const AString & a_PlayerName
m_Health = MAX_HEALTH;
m_LastPlayerListTime = std::chrono::steady_clock::now();
- m_PlayerName = a_PlayerName;
cWorld * World = nullptr;
if (!LoadFromDisk(World))
@@ -149,19 +144,16 @@ cPlayer::cPlayer(const cClientHandlePtr & a_Client, const AString & a_PlayerName
// This is a new player. Set the player spawn point to the spawn point of the default world
SetBedPos(Vector3i(static_cast<int>(World->GetSpawnX()), static_cast<int>(World->GetSpawnY()), static_cast<int>(World->GetSpawnZ())), World);
- SetWorld(World); // Use default world
-
m_EnchantmentSeed = GetRandomProvider().RandInt<unsigned int>(); // Use a random number to seed the enchantment generator
FLOGD("Player \"{0}\" is connecting for the first time, spawning at default world spawn {1:.2f}",
- a_PlayerName, GetPosition()
+ GetName(), GetPosition()
);
}
m_LastGroundHeight = static_cast<float>(GetPosY());
m_Stance = GetPosY() + 1.62;
-
if (m_GameMode == gmNotSet)
{
if (World->IsGameModeCreative())
@@ -187,34 +179,6 @@ cPlayer::cPlayer(const cClientHandlePtr & a_Client, const AString & a_PlayerName
-bool cPlayer::Initialize(OwnedEntity a_Self, cWorld & a_World)
-{
- UNUSED(a_World);
- ASSERT(GetWorld() != nullptr);
- ASSERT(GetParentChunk() == nullptr);
- GetWorld()->AddPlayer(std::unique_ptr<cPlayer>(static_cast<cPlayer *>(a_Self.release())));
-
- cPluginManager::Get()->CallHookSpawnedEntity(*GetWorld(), *this);
-
- if (m_KnownRecipes.empty())
- {
- m_ClientHandle->SendInitRecipes(0);
- }
- else
- {
- for (const auto KnownRecipe : m_KnownRecipes)
- {
- m_ClientHandle->SendInitRecipes(KnownRecipe);
- }
- }
-
- return true;
-}
-
-
-
-
-
void cPlayer::AddKnownItem(const cItem & a_Item)
{
if (a_Item.m_ItemType < 0)
@@ -258,20 +222,14 @@ void cPlayer::AddKnownRecipe(UInt32 a_RecipeId)
cPlayer::~cPlayer(void)
{
- if (!cRoot::Get()->GetPluginManager()->CallHookPlayerDestroyed(*this))
- {
- cRoot::Get()->BroadcastChatLeave(Printf("%s has left the game", GetName().c_str()));
- LOGINFO("Player %s has left the game", GetName().c_str());
- }
-
LOGD("Deleting cPlayer \"%s\" at %p, ID %d", GetName().c_str(), static_cast<void *>(this), GetUniqueID());
- SaveToDisk();
+ // "Times ragequit":
+ m_Stats.AddValue(Statistic::LeaveGame);
- m_ClientHandle = nullptr;
+ SaveToDisk();
delete m_InventoryWindow;
- m_InventoryWindow = nullptr;
LOGD("Player %p deleted", static_cast<void *>(this));
}
@@ -280,9 +238,105 @@ cPlayer::~cPlayer(void)
+void cPlayer::OnAddToWorld(cWorld & a_World)
+{
+ Super::OnAddToWorld(a_World);
+
+ // Update world name tracking:
+ m_CurrentWorldName = m_World->GetName();
+
+ // Fix to stop the player falling through the world, until we get serversided collision detection:
+ FreezeInternal(GetPosition(), false);
+
+ // Set capabilities based on new world:
+ SetCapabilities();
+
+ // Send contents of the inventory window:
+ m_ClientHandle->SendWholeInventory(*m_CurrentWindow);
+
+ // Send health (the respawn packet, which understandably resets health, is also used for world travel...):
+ m_ClientHandle->SendHealth();
+
+ // Send experience, similar story with the respawn packet:
+ m_ClientHandle->SendExperience();
+
+ // Send hotbar active slot (also reset by respawn):
+ m_ClientHandle->SendHeldItemChange(m_Inventory.GetEquippedSlotNum());
+
+ // Update player team:
+ UpdateTeam();
+
+ // Send scoreboard data:
+ m_World->GetScoreBoard().SendTo(*m_ClientHandle);
+
+ // Update the view distance:
+ m_ClientHandle->SetViewDistance(m_ClientHandle->GetRequestedViewDistance());
+
+ // Send current weather of target world:
+ m_ClientHandle->SendWeather(a_World.GetWeather());
+
+ // Send time:
+ m_ClientHandle->SendTimeUpdate(a_World.GetWorldAge(), a_World.GetTimeOfDay(), a_World.IsDaylightCycleEnabled());
+
+ // Finally, deliver the notification hook:
+ cRoot::Get()->GetPluginManager()->CallHookPlayerSpawned(*this);
+}
+
+
+
+
+
void cPlayer::OnRemoveFromWorld(cWorld & a_World)
{
+ Super::OnRemoveFromWorld(a_World);
+
+ // Remove any references to this player pointer by windows in the old world:
CloseWindow(false);
+
+ // Remove the client handle from the world:
+ m_World->RemoveClientFromChunks(m_ClientHandle.get());
+
+ if (m_ClientHandle->IsDestroyed()) // Note: checking IsWorldChangeScheduled not appropriate here since we can disconnecting while having a scheduled warp
+ {
+ // Disconnecting, do the necessary cleanup.
+ // This isn't in the destructor to avoid crashing accessing destroyed objects during shutdown.
+
+ if (!cRoot::Get()->GetPluginManager()->CallHookPlayerDestroyed(*this))
+ {
+ cRoot::Get()->BroadcastChatLeave(Printf("%s has left the game", GetName().c_str()));
+ LOGINFO("Player %s has left the game", GetName().c_str());
+ }
+
+ // Remove ourself from everyone's lists:
+ cRoot::Get()->BroadcastPlayerListsRemovePlayer(*this);
+
+ // Atomically decrement player count (in world thread):
+ cRoot::Get()->GetServer()->PlayerDestroyed();
+
+ // We're just disconnecting. The remaining code deals with going through portals, so bail:
+ return;
+ }
+
+ const auto DestinationDimension = m_WorldChangeInfo.m_NewWorld->GetDimension();
+
+ // Award relevant achievements:
+ if (DestinationDimension == dimEnd)
+ {
+ AwardAchievement(Statistic::AchTheEnd);
+ }
+ else if (DestinationDimension == dimNether)
+ {
+ AwardAchievement(Statistic::AchPortal);
+ }
+
+ // Clear sent chunk lists from the clienthandle:
+ m_ClientHandle->RemoveFromWorld();
+
+ // The clienthandle caches the coords of the chunk we're standing at. Invalidate this.
+ m_ClientHandle->InvalidateCachedSentChunk();
+
+ // Clientside warp start:
+ m_ClientHandle->SendRespawn(DestinationDimension, false);
}
@@ -313,30 +367,23 @@ void cPlayer::SpawnOn(cClientHandle & a_Client)
void cPlayer::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
- if (m_ClientHandle != nullptr)
- {
- if (m_ClientHandle->IsDestroyed())
- {
- // This should not happen, because destroying a client will remove it from the world, but just in case
- ASSERT(!"Player ticked whilst in the process of destruction!");
- m_ClientHandle = nullptr;
- return;
- }
+ m_ClientHandle->Tick(a_Dt.count());
- if (!m_ClientHandle->IsPlaying())
- {
- // We're not yet in the game, ignore everything
- return;
- }
- }
- else
+ if (m_ClientHandle->IsDestroyed())
{
- ASSERT(!"Player ticked whilst in the process of destruction!");
+ Destroy();
+ return;
}
+ if (!m_ClientHandle->IsPlaying())
+ {
+ // We're not yet in the game, ignore everything:
+ return;
+ }
m_Stats.AddValue(Statistic::PlayOneMinute);
m_Stats.AddValue(Statistic::TimeSinceDeath);
+
if (IsCrouched())
{
m_Stats.AddValue(Statistic::SneakTime);
@@ -356,16 +403,27 @@ void cPlayer::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
Detach();
}
- // Handle a frozen player
- TickFreezeCode();
- if (m_IsFrozen)
+ if (!a_Chunk.IsValid())
{
+ // Players are ticked even if the parent chunk is invalid.
+ // We've processed as much as we can, bail:
return;
}
- ASSERT((GetParentChunk() != nullptr) && (GetParentChunk()->IsValid()));
+ ASSERT((GetParentChunk() != nullptr) && (GetParentChunk()->IsValid()));
ASSERT(a_Chunk.IsValid());
+ // Handle a frozen player:
+ TickFreezeCode();
+
+ if (
+ m_IsFrozen || // Don't do Tick updates if frozen
+ IsWorldChangeScheduled() // If we're about to change worlds (e.g. respawn), abort processing all world interactions (GH #3939)
+ )
+ {
+ return;
+ }
+
Super::Tick(a_Dt, a_Chunk);
// Handle charging the bow:
@@ -374,12 +432,6 @@ void cPlayer::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
m_BowCharge += 1;
}
- // Handle updating experience
- if (m_bDirtyExperience)
- {
- SendExperience();
- }
-
BroadcastMovementUpdate(m_ClientHandle.get());
if (m_Health > 0) // make sure player is alive
@@ -504,6 +556,15 @@ int cPlayer::CalcLevelFromXp(int a_XpTotal)
+const std::set<UInt32> & cPlayer::GetKnownRecipes() const
+{
+ return m_KnownRecipes;
+}
+
+
+
+
+
int cPlayer::XpForLevel(int a_Level)
{
// level 0 to 15
@@ -558,8 +619,8 @@ bool cPlayer::SetCurrentExperience(int a_CurrentXp)
m_CurrentXp = a_CurrentXp;
- // Set experience to be updated
- m_bDirtyExperience = true;
+ // Update experience:
+ m_ClientHandle->SendExperience();
return true;
}
@@ -591,8 +652,8 @@ int cPlayer::DeltaExperience(int a_Xp_delta)
LOGD("Player \"%s\" gained / lost %d experience, total is now: %d", GetName().c_str(), a_Xp_delta, m_CurrentXp);
- // Set experience to be updated
- m_bDirtyExperience = true;
+ // Set experience to be updated:
+ m_ClientHandle->SendExperience();
return m_CurrentXp;
}
@@ -661,7 +722,7 @@ void cPlayer::SetTouchGround(bool a_bTouchGround)
void cPlayer::Heal(int a_Health)
{
Super::Heal(a_Health);
- SendHealth();
+ m_ClientHandle->SendHealth();
}
@@ -679,7 +740,7 @@ void cPlayer::SetFoodLevel(int a_FoodLevel)
}
m_FoodLevel = FoodLevel;
- SendHealth();
+ m_ClientHandle->SendHealth();
}
@@ -807,43 +868,6 @@ void cPlayer::AbortEating(void)
-void cPlayer::SendHealth(void)
-{
- if (m_ClientHandle != nullptr)
- {
- m_ClientHandle->SendHealth();
- }
-}
-
-
-
-
-
-void cPlayer::SendHotbarActiveSlot(void)
-{
- if (m_ClientHandle != nullptr)
- {
- m_ClientHandle->SendHeldItemChange(m_Inventory.GetEquippedSlotNum());
- }
-}
-
-
-
-
-
-void cPlayer::SendExperience(void)
-{
- if (m_ClientHandle != nullptr)
- {
- m_ClientHandle->SendExperience();
- m_bDirtyExperience = false;
- }
-}
-
-
-
-
-
void cPlayer::ClearInventoryPaintSlots(void)
{
// Clear the list of slots that are being inventory-painted. Used by cWindow only
@@ -1007,7 +1031,7 @@ void cPlayer::SetCustomName(const AString & a_CustomName)
}
m_World->BroadcastPlayerListAddPlayer(*this);
- m_World->BroadcastSpawnEntity(*this, GetClientHandle());
+ m_World->BroadcastSpawnEntity(*this, m_ClientHandle.get());
}
@@ -1017,7 +1041,7 @@ void cPlayer::SetCustomName(const AString & a_CustomName)
void cPlayer::SetBedPos(const Vector3i & a_Pos)
{
m_LastBedPos = a_Pos;
- m_SpawnWorld = m_World;
+ m_SpawnWorldName = m_World->GetName();
}
@@ -1028,7 +1052,7 @@ void cPlayer::SetBedPos(const Vector3i & a_Pos, cWorld * a_World)
{
m_LastBedPos = a_Pos;
ASSERT(a_World != nullptr);
- m_SpawnWorld = a_World;
+ m_SpawnWorldName = a_World->GetName();
}
@@ -1037,7 +1061,12 @@ void cPlayer::SetBedPos(const Vector3i & a_Pos, cWorld * a_World)
cWorld * cPlayer::GetBedWorld()
{
- return m_SpawnWorld;
+ if (const auto World = cRoot::Get()->GetWorld(m_SpawnWorldName); World != nullptr)
+ {
+ return World;
+ }
+
+ return cRoot::Get()->GetDefaultWorld();
}
@@ -1106,7 +1135,7 @@ bool cPlayer::DoTakeDamage(TakeDamageInfo & a_TDI)
{
// Any kind of damage adds food exhaustion
AddFoodExhaustion(0.3f);
- SendHealth();
+ m_ClientHandle->SendHealth();
// Tell the wolves
if (a_TDI.Attacker != nullptr)
@@ -1296,17 +1325,16 @@ void cPlayer::Respawn(void)
m_LifetimeTotalXp = 0;
// ToDo: send score to client? How?
- m_ClientHandle->SendRespawn(m_SpawnWorld->GetDimension(), true);
-
// Extinguish the fire:
StopBurning();
- if (GetWorld() != m_SpawnWorld)
+ if (const auto BedWorld = GetBedWorld(); m_World != BedWorld)
{
- MoveToWorld(*m_SpawnWorld, GetLastBedPos(), false, false);
+ MoveToWorld(*BedWorld, GetLastBedPos(), false, false);
}
else
{
+ m_ClientHandle->SendRespawn(m_World->GetDimension(), true);
TeleportToCoords(GetLastBedPos().x, GetLastBedPos().y, GetLastBedPos().z);
}
@@ -1380,6 +1408,15 @@ bool cPlayer::CanMobsTarget(void) const
+AString cPlayer::GetIP(void) const
+{
+ return m_ClientHandle->GetIPString();
+}
+
+
+
+
+
void cPlayer::SetTeam(cTeam * a_Team)
{
if (m_Team == a_Team)
@@ -1600,6 +1637,15 @@ void cPlayer::SendAboveActionBarMessage(const cCompositeChat & a_Message)
+const AString & cPlayer::GetName(void) const
+{
+ return m_ClientHandle->GetUsername();
+}
+
+
+
+
+
void cPlayer::SetGameMode(eGameMode a_GameMode)
{
if ((a_GameMode < gmMin) || (a_GameMode >= gmMax))
@@ -1671,15 +1717,6 @@ void cPlayer::SetCapabilities()
-void cPlayer::SetIP(const AString & a_IP)
-{
- m_IP = a_IP;
-}
-
-
-
-
-
void cPlayer::AwardAchievement(const Statistic a_Ach)
{
// Check if the prerequisites are met:
@@ -1753,7 +1790,7 @@ void cPlayer::Unfreeze()
GetClientHandle()->SendPlayerMaxSpeed();
m_IsFrozen = false;
- BroadcastMovementUpdate(GetClientHandle());
+ BroadcastMovementUpdate(m_ClientHandle.get());
GetClientHandle()->SendPlayerPosition();
}
@@ -1819,6 +1856,29 @@ Vector3d cPlayer::GetThrowSpeed(double a_SpeedCoeff) const
+eGameMode cPlayer::GetEffectiveGameMode(void) const
+{
+ // Since entities' m_World aren't set until Initialize, but cClientHandle sends the player's gamemode early
+ // the below block deals with m_World being nullptr when called.
+
+ auto World = m_World;
+
+ if (World == nullptr)
+ {
+ World = cRoot::Get()->GetDefaultWorld();
+ }
+ else if (IsWorldChangeScheduled())
+ {
+ World = m_WorldChangeInfo.m_NewWorld;
+ }
+
+ return (m_GameMode == gmNotSet) ? World->GetGameMode() : m_GameMode;
+}
+
+
+
+
+
void cPlayer::ForceSetSpeed(const Vector3d & a_Speed)
{
SetSpeed(a_Speed);
@@ -2099,79 +2159,6 @@ void cPlayer::TossPickup(const cItem & a_Item)
-void cPlayer::DoMoveToWorld(const cEntity::sWorldChangeInfo & a_WorldChangeInfo)
-{
- ASSERT(a_WorldChangeInfo.m_NewWorld != nullptr);
-
- // Reset portal cooldown
- if (a_WorldChangeInfo.m_SetPortalCooldown)
- {
- m_PortalCooldownData.m_TicksDelayed = 0;
- m_PortalCooldownData.m_ShouldPreventTeleportation = true;
- }
-
- if (m_World == a_WorldChangeInfo.m_NewWorld)
- {
- // Moving to same world, don't need to remove from world
- SetPosition(a_WorldChangeInfo.m_NewPosition);
- return;
- }
-
- LOGD("Warping player \"%s\" from world \"%s\" to \"%s\". Source chunk: (%d, %d) ",
- GetName(), GetWorld()->GetName(), a_WorldChangeInfo.m_NewWorld->GetName(),
- GetChunkX(), GetChunkZ()
- );
-
- // Stop all mobs from targeting this player
- StopEveryoneFromTargetingMe();
-
- // If player is attached to entity, detach, to prevent client side effects
- Detach();
-
- // Prevent further ticking in this world
- SetIsTicking(false);
-
- // Remove from the old world
- auto & OldWorld = *GetWorld();
- auto Self = OldWorld.RemovePlayer(*this);
-
- ResetPosition(a_WorldChangeInfo.m_NewPosition);
- FreezeInternal(a_WorldChangeInfo.m_NewPosition, false);
- SetWorld(a_WorldChangeInfo.m_NewWorld); // Chunks may be streamed before cWorld::AddPlayer() sets the world to the new value
-
- // Set capabilities based on new world
- SetCapabilities();
-
- cClientHandle * ch = GetClientHandle();
- if (ch != nullptr)
- {
- // The clienthandle caches the coords of the chunk we're standing at. Invalidate this.
- ch->InvalidateCachedSentChunk();
-
- // Send the respawn packet:
- if (a_WorldChangeInfo.m_SendRespawn)
- {
- ch->SendRespawn(a_WorldChangeInfo.m_NewWorld->GetDimension());
- }
-
- // Update the view distance.
- ch->SetViewDistance(ch->GetRequestedViewDistance());
-
- // Send current weather of target world to player
- if (a_WorldChangeInfo.m_NewWorld->GetDimension() == dimOverworld)
- {
- ch->SendWeather(a_WorldChangeInfo.m_NewWorld->GetWeather());
- }
- }
-
- // New world will take over and announce client at its next tick
- a_WorldChangeInfo.m_NewWorld->AddPlayer(std::move(Self), &OldWorld);
-}
-
-
-
-
-
bool cPlayer::LoadFromDisk(cWorldPtr & a_World)
{
LoadRank();
@@ -2334,7 +2321,7 @@ bool cPlayer::LoadFromFile(const AString & a_FileName, cWorldPtr & a_World)
cEnderChestEntity::LoadFromJson(root["enderchestinventory"], m_EnderChestContents);
- m_LoadedWorldName = root.get("world", "world").asString();
+ m_CurrentWorldName = root.get("world", "world").asString();
a_World = cRoot::Get()->GetWorld(GetLoadedWorldName());
if (a_World == nullptr)
{
@@ -2345,18 +2332,13 @@ bool cPlayer::LoadFromFile(const AString & a_FileName, cWorldPtr & a_World)
m_LastBedPos.x = root.get("SpawnX", a_World->GetSpawnX()).asInt();
m_LastBedPos.y = root.get("SpawnY", a_World->GetSpawnY()).asInt();
m_LastBedPos.z = root.get("SpawnZ", a_World->GetSpawnZ()).asInt();
- AString SpawnWorldName = root.get("SpawnWorld", cRoot::Get()->GetDefaultWorld()->GetName()).asString();
- m_SpawnWorld = cRoot::Get()->GetWorld(SpawnWorldName);
- if (m_SpawnWorld == nullptr)
- {
- m_SpawnWorld = cRoot::Get()->GetDefaultWorld();
- }
+ m_SpawnWorldName = root.get("SpawnWorld", cRoot::Get()->GetDefaultWorld()->GetName()).asString();
try
{
// Load the player stats.
// We use the default world name (like bukkit) because stats are shared between dimensions / worlds.
- StatSerializer::Load(m_Stats, cRoot::Get()->GetDefaultWorld()->GetDataPath(), GetUUID().ToLongString());
+ StatSerializer::Load(m_Stats, m_DefaultWorldPath, GetUUID().ToLongString());
}
catch (...)
{
@@ -2460,27 +2442,10 @@ bool cPlayer::SaveToDisk()
root["SpawnX"] = GetLastBedPos().x;
root["SpawnY"] = GetLastBedPos().y;
root["SpawnZ"] = GetLastBedPos().z;
- root["SpawnWorld"] = m_SpawnWorld->GetName();
+ root["SpawnWorld"] = m_SpawnWorldName;
root["enchantmentSeed"] = m_EnchantmentSeed;
-
- if (m_World != nullptr)
- {
- root["world"] = m_World->GetName();
- if (m_GameMode == m_World->GetGameMode())
- {
- root["gamemode"] = static_cast<int>(eGameMode_NotSet);
- }
- else
- {
- root["gamemode"] = static_cast<int>(m_GameMode);
- }
- }
- else
- {
- // This happens if the player is saved to new format after loading from the old format
- root["world"] = m_LoadedWorldName;
- root["gamemode"] = static_cast<int>(eGameMode_NotSet);
- }
+ root["world"] = m_CurrentWorldName;
+ root["gamemode"] = static_cast<int>(m_GameMode);
auto JsonData = JsonUtils::WriteStyledString(root);
AString SourceFile = GetUUIDFileName(m_UUID);
@@ -2505,7 +2470,8 @@ bool cPlayer::SaveToDisk()
{
// Save the player stats.
// We use the default world name (like bukkit) because stats are shared between dimensions / worlds.
- StatSerializer::Save(m_Stats, cRoot::Get()->GetDefaultWorld()->GetDataPath(), GetUUID().ToLongString());
+ // TODO: save together with player.dat, not in some other place.
+ StatSerializer::Save(m_Stats, m_DefaultWorldPath, GetUUID().ToLongString());
}
catch (...)
{
@@ -2787,7 +2753,7 @@ void cPlayer::LoadRank(void)
else
{
// Update the name:
- RankMgr->UpdatePlayerName(m_UUID, m_PlayerName);
+ RankMgr->UpdatePlayerName(m_UUID, GetName());
}
m_Permissions = RankMgr->GetPlayerPermissions(m_UUID);
m_Restrictions = RankMgr->GetPlayerRestrictions(m_UUID);
@@ -3062,16 +3028,6 @@ void cPlayer::Detach()
-void cPlayer::RemoveClientHandle(void)
-{
- ASSERT(m_ClientHandle != nullptr);
- m_ClientHandle.reset();
-}
-
-
-
-
-
AString cPlayer::GetUUIDFileName(const cUUID & a_UUID)
{
AString UUID = a_UUID.ToLongString();