summaryrefslogtreecommitdiffstats
path: root/src/Entities
diff options
context:
space:
mode:
Diffstat (limited to 'src/Entities')
-rw-r--r--src/Entities/Player.cpp226
-rw-r--r--src/Entities/Player.h26
2 files changed, 88 insertions, 164 deletions
diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp
index 59291e40d..9497d326b 100644
--- a/src/Entities/Player.cpp
+++ b/src/Entities/Player.cpp
@@ -37,23 +37,6 @@
namespace
{
-/** Returns the old Offline UUID generated before becoming vanilla compliant. */
-cUUID GetOldStyleOfflineUUID(const AString & a_PlayerName)
-{
- // Use lowercase username
- auto BaseUUID = cUUID::GenerateVersion3(StrToLower(a_PlayerName)).ToRaw();
- // Clobber a full nibble around the variant bits
- BaseUUID[8] = (BaseUUID[8] & 0x0f) | 0x80;
-
- cUUID Ret;
- Ret.FromRaw(BaseUUID);
- return Ret;
-}
-
-
-
-
-
/** Returns the folder for the player data based on the UUID given.
This can be used both for online and offline UUIDs. */
AString GetUUIDFolderName(const cUUID & a_Uuid)
@@ -115,7 +98,6 @@ cPlayer::cPlayer(const std::shared_ptr<cClientHandle> & a_Client) :
m_bIsInBed(false),
m_TicksUntilNextSave(PLAYER_INVENTORY_SAVE_INTERVAL),
m_bIsTeleporting(false),
- m_UUID((a_Client != nullptr) ? a_Client->GetUUID() : cUUID{}),
m_SkinParts(0),
m_MainHand(mhRight)
{
@@ -129,22 +111,7 @@ cPlayer::cPlayer(const std::shared_ptr<cClientHandle> & a_Client) :
m_Health = MAX_HEALTH;
cWorld * World = nullptr;
- if (!LoadFromDisk(World))
- {
- m_Inventory.Clear();
- SetPosX(World->GetSpawnX());
- SetPosY(World->GetSpawnY());
- SetPosZ(World->GetSpawnZ());
-
- // 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);
-
- 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}",
- GetName(), GetPosition()
- );
- }
+ LoadFromDisk(World);
m_LastGroundHeight = static_cast<float>(GetPosY());
m_Stance = GetPosY() + 1.62;
@@ -1734,70 +1701,30 @@ void cPlayer::TossPickup(const cItem & a_Item)
-bool cPlayer::LoadFromDisk(cWorldPtr & a_World)
+void cPlayer::LoadFromDisk(cWorldPtr & a_World)
{
LoadRank();
- // Load from the UUID file:
- if (LoadFromFile(GetUUIDFileName(m_UUID), a_World))
- {
- return true;
- }
+ const auto & UUID = GetUUID();
- // Check for old offline UUID filename, if it exists migrate to new filename
- cUUID OfflineUUID = cClientHandle::GenerateOfflineUUID(GetName());
- auto OldFilename = GetUUIDFileName(GetOldStyleOfflineUUID(GetName()));
- auto NewFilename = GetUUIDFileName(m_UUID);
- // Only move if there isn't already a new file
- if (!cFile::IsFile(NewFilename) && cFile::IsFile(OldFilename))
+ // Load from the UUID file:
+ if (LoadFromFile(GetUUIDFileName(UUID), a_World))
{
- cFile::CreateFolderRecursive(GetUUIDFolderName(m_UUID)); // Ensure folder exists to move to
- if (
- cFile::Rename(OldFilename, NewFilename) &&
- (m_UUID == OfflineUUID) &&
- LoadFromFile(NewFilename, a_World)
- )
- {
- return true;
- }
+ return;
}
- // Load from the offline UUID file, if allowed:
- const char * OfflineUsage = " (unused)";
- if (cRoot::Get()->GetServer()->ShouldLoadOfflinePlayerData())
- {
- OfflineUsage = "";
- if (LoadFromFile(GetUUIDFileName(OfflineUUID), a_World))
- {
- return true;
- }
- }
+ // Player not found:
+ a_World = cRoot::Get()->GetDefaultWorld();
+ LOG("Player \"%s\" (%s) data not found, resetting to defaults", GetName().c_str(), UUID.ToShortString().c_str());
- // Load from the old-style name-based file, if allowed:
- if (cRoot::Get()->GetServer()->ShouldLoadNamedPlayerData())
- {
- AString OldStyleFileName = Printf("players/%s.json", GetName().c_str());
- if (LoadFromFile(OldStyleFileName, a_World))
- {
- // Save in new format and remove the old file
- if (SaveToDisk())
- {
- cFile::Delete(OldStyleFileName);
- }
- return true;
- }
- }
+ const Vector3i WorldSpawn(static_cast<int>(a_World->GetSpawnX()), static_cast<int>(a_World->GetSpawnY()), static_cast<int>(a_World->GetSpawnZ()));
+ SetPosition(WorldSpawn);
+ SetBedPos(WorldSpawn, a_World);
- // None of the files loaded successfully
- LOG("Player data file not found for %s (%s, offline %s%s), will be reset to defaults.",
- GetName().c_str(), m_UUID.ToShortString().c_str(), OfflineUUID.ToShortString().c_str(), OfflineUsage
- );
+ m_Inventory.Clear();
+ m_EnchantmentSeed = GetRandomProvider().RandInt<unsigned int>(); // Use a random number to seed the enchantment generator
- if (a_World == nullptr)
- {
- a_World = cRoot::Get()->GetDefaultWorld();
- }
- return false;
+ FLOGD("Player \"{0}\" is connecting for the first time, spawning at default world spawn {1:.2f}", GetName(), GetPosition());
}
@@ -1806,35 +1733,31 @@ bool cPlayer::LoadFromDisk(cWorldPtr & a_World)
bool cPlayer::LoadFromFile(const AString & a_FileName, cWorldPtr & a_World)
{
- // Load the data from the file:
- cFile f;
- if (!f.Open(a_FileName, cFile::fmRead))
+ Json::Value Root;
+
+ try
{
- // This is a new player whom we haven't seen yet, bail out, let them have the defaults
- return false;
+ // Load the data from the file and parse:
+ InputFileStream(a_FileName) >> Root;
}
- AString buffer;
- if (f.ReadRestOfFile(buffer) != f.GetSize())
+ catch (const Json::Exception & Oops)
{
- LOGWARNING("Cannot read player data from file \"%s\"", a_FileName.c_str());
- return false;
+ // Parse failure:
+ throw std::runtime_error(Oops.what());
}
- f.Close();
-
- // Parse the JSON format:
- Json::Value root;
- AString ParseError;
- if (!JsonUtils::ParseString(buffer, root, &ParseError))
+ catch (const InputFileStream::failure &)
{
- FLOGWARNING(
- "Cannot parse player data in file \"{0}\":\n {1}",
- a_FileName, ParseError
- );
- return false;
+ if (errno == ENOENT)
+ {
+ // This is a new player whom we haven't seen yet, bail out, let them have the defaults:
+ return false;
+ }
+
+ throw;
}
// Load the player data:
- Json::Value & JSON_PlayerPosition = root["position"];
+ Json::Value & JSON_PlayerPosition = Root["position"];
if (JSON_PlayerPosition.size() == 3)
{
SetPosX(JSON_PlayerPosition[0].asDouble());
@@ -1843,7 +1766,7 @@ bool cPlayer::LoadFromFile(const AString & a_FileName, cWorldPtr & a_World)
m_LastPosition = GetPosition();
}
- Json::Value & JSON_PlayerRotation = root["rotation"];
+ Json::Value & JSON_PlayerRotation = Root["rotation"];
if (JSON_PlayerRotation.size() == 3)
{
SetYaw (static_cast<float>(JSON_PlayerRotation[0].asDouble()));
@@ -1851,18 +1774,18 @@ bool cPlayer::LoadFromFile(const AString & a_FileName, cWorldPtr & a_World)
SetRoll (static_cast<float>(JSON_PlayerRotation[2].asDouble()));
}
- m_Health = root.get("health", 0).asFloat();
- m_AirLevel = root.get("air", MAX_AIR_LEVEL).asInt();
- m_FoodLevel = root.get("food", MAX_FOOD_LEVEL).asInt();
- m_FoodSaturationLevel = root.get("foodSaturation", MAX_FOOD_LEVEL).asDouble();
- m_FoodTickTimer = root.get("foodTickTimer", 0).asInt();
- m_FoodExhaustionLevel = root.get("foodExhaustion", 0).asDouble();
- m_LifetimeTotalXp = root.get("xpTotal", 0).asInt();
- m_CurrentXp = root.get("xpCurrent", 0).asInt();
- m_IsFlying = root.get("isflying", 0).asBool();
- m_EnchantmentSeed = root.get("enchantmentSeed", GetRandomProvider().RandInt<unsigned int>()).asUInt();
+ m_Health = Root.get("health", 0).asFloat();
+ m_AirLevel = Root.get("air", MAX_AIR_LEVEL).asInt();
+ m_FoodLevel = Root.get("food", MAX_FOOD_LEVEL).asInt();
+ m_FoodSaturationLevel = Root.get("foodSaturation", MAX_FOOD_LEVEL).asDouble();
+ m_FoodTickTimer = Root.get("foodTickTimer", 0).asInt();
+ m_FoodExhaustionLevel = Root.get("foodExhaustion", 0).asDouble();
+ m_LifetimeTotalXp = Root.get("xpTotal", 0).asInt();
+ m_CurrentXp = Root.get("xpCurrent", 0).asInt();
+ m_IsFlying = Root.get("isflying", 0).asBool();
+ m_EnchantmentSeed = Root.get("enchantmentSeed", GetRandomProvider().RandInt<unsigned int>()).asUInt();
- Json::Value & JSON_KnownItems = root["knownItems"];
+ Json::Value & JSON_KnownItems = Root["knownItems"];
for (UInt32 i = 0; i < JSON_KnownItems.size(); i++)
{
cItem Item;
@@ -1872,7 +1795,7 @@ bool cPlayer::LoadFromFile(const AString & a_FileName, cWorldPtr & a_World)
const auto & RecipeNameMap = cRoot::Get()->GetCraftingRecipes()->GetRecipeNameMap();
- Json::Value & JSON_KnownRecipes = root["knownRecipes"];
+ Json::Value & JSON_KnownRecipes = Root["knownRecipes"];
for (UInt32 i = 0; i < JSON_KnownRecipes.size(); i++)
{
auto RecipeId = RecipeNameMap.find(JSON_KnownRecipes[i].asString());
@@ -1882,21 +1805,21 @@ bool cPlayer::LoadFromFile(const AString & a_FileName, cWorldPtr & a_World)
}
}
- m_GameMode = static_cast<eGameMode>(root.get("gamemode", eGameMode_NotSet).asInt());
+ m_GameMode = static_cast<eGameMode>(Root.get("gamemode", eGameMode_NotSet).asInt());
if (m_GameMode == eGameMode_Creative)
{
m_CanFly = true;
}
- m_Inventory.LoadFromJson(root["inventory"]);
+ m_Inventory.LoadFromJson(Root["inventory"]);
- int equippedSlotNum = root.get("equippedItemSlot", 0).asInt();
+ int equippedSlotNum = Root.get("equippedItemSlot", 0).asInt();
m_Inventory.SetEquippedSlotNum(equippedSlotNum);
- cEnderChestEntity::LoadFromJson(root["enderchestinventory"], m_EnderChestContents);
+ cEnderChestEntity::LoadFromJson(Root["enderchestinventory"], m_EnderChestContents);
- m_CurrentWorldName = root.get("world", "world").asString();
+ m_CurrentWorldName = Root.get("world", "world").asString();
a_World = cRoot::Get()->GetWorld(GetLoadedWorldName());
if (a_World == nullptr)
{
@@ -1904,10 +1827,10 @@ 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();
- m_SpawnWorldName = root.get("SpawnWorld", cRoot::Get()->GetDefaultWorld()->GetName()).asString();
+ 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();
+ m_SpawnWorldName = Root.get("SpawnWorld", cRoot::Get()->GetDefaultWorld()->GetName()).asString();
try
{
@@ -1960,9 +1883,10 @@ void cPlayer::OpenHorseInventory()
-bool cPlayer::SaveToDisk()
+void cPlayer::SaveToDisk()
{
- cFile::CreateFolderRecursive(GetUUIDFolderName(m_UUID));
+ const auto & UUID = GetUUID();
+ cFile::CreateFolderRecursive(GetUUIDFolderName(UUID));
// create the JSON data
Json::Value JSON_PlayerPosition;
@@ -2023,22 +1947,22 @@ bool cPlayer::SaveToDisk()
root["gamemode"] = static_cast<int>(m_GameMode);
auto JsonData = JsonUtils::WriteStyledString(root);
- AString SourceFile = GetUUIDFileName(m_UUID);
+ AString SourceFile = GetUUIDFileName(UUID);
cFile f;
if (!f.Open(SourceFile, cFile::fmWrite))
{
- LOGWARNING("Error writing player \"%s\" to file \"%s\" - cannot open file. Player will lose their progress.",
+ LOGWARNING("Error writing player \"%s\" to file \"%s\": cannot open file. Player will lose their progress",
GetName().c_str(), SourceFile.c_str()
);
- return false;
+ return;
}
if (f.Write(JsonData.c_str(), JsonData.size()) != static_cast<int>(JsonData.size()))
{
- LOGWARNING("Error writing player \"%s\" to file \"%s\" - cannot save data. Player will lose their progress. ",
+ LOGWARNING("Error writing player \"%s\" to file \"%s\": cannot save data. Player will lose their progress",
GetName().c_str(), SourceFile.c_str()
);
- return false;
+ return;
}
try
@@ -2050,11 +1974,8 @@ bool cPlayer::SaveToDisk()
}
catch (...)
{
- LOGWARNING("Could not save stats for player %s", GetName().c_str());
- return false;
+ LOGWARNING("Error writing player \"%s\" statistics to file", GetName().c_str());
}
-
- return true;
}
@@ -2316,9 +2237,11 @@ void cPlayer::UpdateMovementStats(const Vector3d & a_DeltaPos, bool a_PreviousIs
void cPlayer::LoadRank(void)
{
- // Load the values from cRankManager:
+ const auto & UUID = GetUUID();
cRankManager * RankMgr = cRoot::Get()->GetRankManager();
- m_Rank = RankMgr->GetPlayerRankName(m_UUID);
+
+ // Load the values from cRankManager:
+ m_Rank = RankMgr->GetPlayerRankName(UUID);
if (m_Rank.empty())
{
m_Rank = RankMgr->GetDefaultRank();
@@ -2326,10 +2249,10 @@ void cPlayer::LoadRank(void)
else
{
// Update the name:
- RankMgr->UpdatePlayerName(m_UUID, GetName());
+ RankMgr->UpdatePlayerName(UUID, GetName());
}
- m_Permissions = RankMgr->GetPlayerPermissions(m_UUID);
- m_Restrictions = RankMgr->GetPlayerRestrictions(m_UUID);
+ m_Permissions = RankMgr->GetPlayerPermissions(UUID);
+ m_Restrictions = RankMgr->GetPlayerRestrictions(UUID);
RankMgr->GetRankVisuals(m_Rank, m_MsgPrefix, m_MsgSuffix, m_MsgNameColorCode);
// Break up the individual permissions on each dot, into m_SplitPermissions:
@@ -2469,6 +2392,15 @@ bool cPlayer::DoesPlacingBlocksIntersectEntity(const sSetBlockVector & a_Blocks)
+const cUUID & cPlayer::GetUUID(void) const
+{
+ return m_ClientHandle->GetUUID();
+}
+
+
+
+
+
bool cPlayer::PlaceBlocks(const sSetBlockVector & a_Blocks)
{
if (DoesPlacingBlocksIntersectEntity(a_Blocks))
diff --git a/src/Entities/Player.h b/src/Entities/Player.h
index 2d31ef84b..4d6a41a35 100644
--- a/src/Entities/Player.h
+++ b/src/Entities/Player.h
@@ -379,21 +379,18 @@ public:
void SetVisible( bool a_bVisible); // tolua_export
bool IsVisible(void) const { return m_bVisible; } // tolua_export
- /** Saves all player data, such as inventory, to JSON */
- bool SaveToDisk(void);
+ /** Saves all player data, such as inventory, to JSON. */
+ void SaveToDisk(void);
typedef cWorld * cWorldPtr;
- /** Loads the player data from the disk file
- Sets a_World to the world where the player will spawn, based on the stored world name or the default world by calling LoadFromFile()
- Returns true on success, false on failure
- */
- bool LoadFromDisk(cWorldPtr & a_World);
+ /** Loads the player data from the disk file.
+ Sets a_World to the world where the player will spawn, based on the stored world name or the default world by calling LoadFromFile(). */
+ void LoadFromDisk(cWorldPtr & a_World);
- /** Loads the player data from the specified file
- Sets a_World to the world where the player will spawn, based on the stored world name or the default world
- Returns true on success, false on failure
- */
+ /** Loads the player data from the specified file.
+ Sets a_World to the world where the player will spawn, based on the stored world name or the default world.
+ Returns true on success, false if the player wasn't found, and excepts with base std::runtime_error if the data couldn't be read or parsed. */
bool LoadFromFile(const AString & a_FileName, cWorldPtr & a_World);
const AString & GetLoadedWorldName() const { return m_CurrentWorldName; }
@@ -499,7 +496,7 @@ public:
bool DoesPlacingBlocksIntersectEntity(const sSetBlockVector & a_Blocks);
/** Returns the UUID that has been read from the client, or nil if not available. */
- const cUUID & GetUUID(void) const { return m_UUID; } // Exported in ManualBindings.cpp
+ const cUUID & GetUUID(void) const; // Exported in ManualBindings.cpp
// tolua_begin
@@ -507,7 +504,6 @@ public:
virtual bool CanFly(void) const { return m_CanFly; }
/** (Re)loads the rank and permissions from the cRankManager.
- Expects the m_UUID member to be valid.
Loads the m_Rank, m_Permissions, m_MsgPrefix, m_MsgSuffix and m_MsgNameColorCode members. */
void LoadRank(void);
@@ -718,10 +714,6 @@ private:
*/
bool m_bIsTeleporting;
- /** The UUID of the player, as read from the ClientHandle.
- If no ClientHandle is given, the UUID is nil. */
- cUUID m_UUID;
-
AString m_CustomName;
/** Displayed skin part bit mask */