1
0

Player data filenames are based on UUID.

This commit is contained in:
madmaxoft 2014-07-11 13:13:10 +02:00
parent 6cea81e383
commit ebea2b7efc
4 changed files with 155 additions and 67 deletions

View File

@ -34,46 +34,47 @@
cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName) cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName) :
: super(etPlayer, 0.6, 1.8) super(etPlayer, 0.6, 1.8),
, m_bVisible(true) m_bVisible(true),
, m_FoodLevel(MAX_FOOD_LEVEL) m_FoodLevel(MAX_FOOD_LEVEL),
, m_FoodSaturationLevel(5.0) m_FoodSaturationLevel(5.0),
, m_FoodTickTimer(0) m_FoodTickTimer(0),
, m_FoodExhaustionLevel(0.0) m_FoodExhaustionLevel(0.0),
, m_FoodPoisonedTicksRemaining(0) m_FoodPoisonedTicksRemaining(0),
, m_LastJumpHeight(0) m_LastJumpHeight(0),
, m_LastGroundHeight(0) m_LastGroundHeight(0),
, m_bTouchGround(false) m_bTouchGround(false),
, m_Stance(0.0) m_Stance(0.0),
, m_Inventory(*this) m_Inventory(*this),
, m_EnderChestContents(9, 3) m_EnderChestContents(9, 3),
, m_CurrentWindow(NULL) m_CurrentWindow(NULL),
, m_InventoryWindow(NULL) m_InventoryWindow(NULL),
, m_Color('-') m_Color('-'),
, m_GameMode(eGameMode_NotSet) m_GameMode(eGameMode_NotSet),
, m_IP("") m_IP(""),
, m_ClientHandle(a_Client) m_ClientHandle(a_Client),
, m_NormalMaxSpeed(1.0) m_NormalMaxSpeed(1.0),
, m_SprintingMaxSpeed(1.3) m_SprintingMaxSpeed(1.3),
, m_FlyingMaxSpeed(1.0) m_FlyingMaxSpeed(1.0),
, m_IsCrouched(false) m_IsCrouched(false),
, m_IsSprinting(false) m_IsSprinting(false),
, m_IsFlying(false) m_IsFlying(false),
, m_IsSwimming(false) m_IsSwimming(false),
, m_IsSubmerged(false) m_IsSubmerged(false),
, m_IsFishing(false) m_IsFishing(false),
, m_CanFly(false) m_CanFly(false),
, m_EatingFinishTick(-1) m_EatingFinishTick(-1),
, m_LifetimeTotalXp(0) m_LifetimeTotalXp(0),
, m_CurrentXp(0) m_CurrentXp(0),
, m_bDirtyExperience(false) m_bDirtyExperience(false),
, m_IsChargingBow(false) m_IsChargingBow(false),
, m_BowCharge(0) m_BowCharge(0),
, m_FloaterID(-1) m_FloaterID(-1),
, m_Team(NULL) m_Team(NULL),
, m_TicksUntilNextSave(PLAYER_INVENTORY_SAVE_INTERVAL) m_TicksUntilNextSave(PLAYER_INVENTORY_SAVE_INTERVAL),
, m_bIsTeleporting(false) m_bIsTeleporting(false),
m_UUID((a_Client != NULL) ? a_Client->GetUUID() : "")
{ {
m_InventoryWindow = new cInventoryWindow(*this); m_InventoryWindow = new cInventoryWindow(*this);
m_CurrentWindow = m_InventoryWindow; m_CurrentWindow = m_InventoryWindow;
@ -1690,15 +1691,42 @@ bool cPlayer::LoadFromDisk(void)
{ {
LoadPermissionsFromDisk(); LoadPermissionsFromDisk();
AString SourceFile; // Load from the UUID file:
Printf(SourceFile, "players/%s.json", GetName().c_str()); bool res = LoadFromFile(GetUUIDFileName(m_UUID));
bool res = LoadFromFile(SourceFile);
if (res) if (res)
{ {
return true; return true;
} }
// Load from the offline UUID file, if allowed:
AString OfflineUUID = cClientHandle::GenerateOfflineUUID(GetName());
if (cRoot::Get()->GetServer()->ShouldLoadOfflinePlayerData())
{
res = LoadFromFile(GetUUIDFileName(OfflineUUID));
if (res)
{
return true;
}
}
// Load from the old-style name-based file, if allowed:
if (cRoot::Get()->GetServer()->ShouldLoadNamedPlayerData())
{
AString OldStyleFileName = Printf("players/%s.json", GetName().c_str());
res = LoadFromFile(OldStyleFileName);
if (res)
{
// Save in new format and remove the old file
SaveToDisk();
cFile::Delete(OldStyleFileName);
return true;
}
}
// None of the files loaded successfully
LOGD("Player data file not found for %s (%s, offline %s), will be reset to defaults.",
GetName().c_str(), m_UUID.c_str(), OfflineUUID.c_str()
);
return false; return false;
} }
@ -1791,6 +1819,7 @@ bool cPlayer::LoadFromFile(const AString & a_FileName)
bool cPlayer::SaveToDisk() bool cPlayer::SaveToDisk()
{ {
cFile::CreateFolder(FILE_IO_PREFIX + AString("players")); cFile::CreateFolder(FILE_IO_PREFIX + AString("players"));
cFile::CreateFolder(FILE_IO_PREFIX + AString("players/") + m_UUID.substr(0, 2));
// create the JSON data // create the JSON data
Json::Value JSON_PlayerPosition; Json::Value JSON_PlayerPosition;
@ -1822,10 +1851,12 @@ bool cPlayer::SaveToDisk()
root["foodSaturation"] = m_FoodSaturationLevel; root["foodSaturation"] = m_FoodSaturationLevel;
root["foodTickTimer"] = m_FoodTickTimer; root["foodTickTimer"] = m_FoodTickTimer;
root["foodExhaustion"] = m_FoodExhaustionLevel; root["foodExhaustion"] = m_FoodExhaustionLevel;
root["world"] = GetWorld()->GetName();
root["isflying"] = IsFlying(); root["isflying"] = IsFlying();
root["lastknownname"] = GetName();
if (m_GameMode == GetWorld()->GetGameMode()) if (m_World != NULL)
{
root["world"] = m_World->GetName();
if (m_GameMode == m_World->GetGameMode())
{ {
root["gamemode"] = (int) eGameMode_NotSet; root["gamemode"] = (int) eGameMode_NotSet;
} }
@ -1833,22 +1864,32 @@ bool cPlayer::SaveToDisk()
{ {
root["gamemode"] = (int) m_GameMode; root["gamemode"] = (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"] = (int) eGameMode_NotSet;
}
Json::StyledWriter writer; Json::StyledWriter writer;
std::string JsonData = writer.write(root); std::string JsonData = writer.write(root);
AString SourceFile; AString SourceFile = GetUUIDFileName(m_UUID);
Printf(SourceFile, "players/%s.json", GetName().c_str() );
cFile f; cFile f;
if (!f.Open(SourceFile, cFile::fmWrite)) if (!f.Open(SourceFile, cFile::fmWrite))
{ {
LOGERROR("ERROR WRITING PLAYER \"%s\" TO FILE \"%s\" - cannot open file", GetName().c_str(), SourceFile.c_str()); 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 false;
} }
if (f.Write(JsonData.c_str(), JsonData.size()) != (int)JsonData.size()) if (f.Write(JsonData.c_str(), JsonData.size()) != (int)JsonData.size())
{ {
LOGERROR("ERROR WRITING PLAYER JSON TO FILE \"%s\"", SourceFile.c_str()); 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 false;
} }
@ -1857,7 +1898,7 @@ bool cPlayer::SaveToDisk()
cStatSerializer StatSerializer(cRoot::Get()->GetDefaultWorld()->GetName(), GetName(), &m_Stats); cStatSerializer StatSerializer(cRoot::Get()->GetDefaultWorld()->GetName(), GetName(), &m_Stats);
if (!StatSerializer.Save()) if (!StatSerializer.Save())
{ {
LOGERROR("Could not save stats for player %s", GetName().c_str()); LOGWARNING("Could not save stats for player %s", GetName().c_str());
return false; return false;
} }
@ -2170,3 +2211,19 @@ void cPlayer::Detach()
AString cPlayer::GetUUIDFileName(const AString & a_UUID)
{
ASSERT(a_UUID.size() == 36);
AString res("players/");
res.append(a_UUID, 0, 2);
res.push_back('/');
res.append(a_UUID, 2, AString::npos);
res.append(".json");
return res;
}

View File

@ -528,6 +528,24 @@ protected:
cStatManager m_Stats; cStatManager m_Stats;
/** Flag representing whether the player is currently in a bed
Set by a right click on unoccupied bed, unset by a time fast forward or teleport */
bool m_bIsInBed;
/** How long till the player's inventory will be saved
Default save interval is #defined in PLAYER_INVENTORY_SAVE_INTERVAL */
unsigned int m_TicksUntilNextSave;
/** Flag used by food handling system to determine whether a teleport has just happened
Will not apply food penalties if found to be true; will set to false after processing
*/
bool m_bIsTeleporting;
/** The UUID of the player, as read from the ClientHandle.
If no ClientHandle is given, the UUID is initialized to empty. */
AString m_UUID;
/** Sets the speed and sends it to the client, so that they are forced to move so. */ /** Sets the speed and sends it to the client, so that they are forced to move so. */
virtual void DoSetSpeed(double a_SpeedX, double a_SpeedY, double a_SpeedZ) override; virtual void DoSetSpeed(double a_SpeedX, double a_SpeedY, double a_SpeedZ) override;
@ -554,19 +572,9 @@ protected:
/** Adds food exhaustion based on the difference between Pos and LastPos, sprinting status and swimming (in water block) */ /** Adds food exhaustion based on the difference between Pos and LastPos, sprinting status and swimming (in water block) */
void ApplyFoodExhaustionFromMovement(); void ApplyFoodExhaustionFromMovement();
/** Flag representing whether the player is currently in a bed /** Returns the filename for the player data based on the UUID given.
Set by a right click on unoccupied bed, unset by a time fast forward or teleport */ This can be used both for online and offline UUIDs. */
bool m_bIsInBed; AString GetUUIDFileName(const AString & a_UUID);
/** How long till the player's inventory will be saved
Default save interval is #defined in PLAYER_INVENTORY_SAVE_INTERVAL */
unsigned int m_TicksUntilNextSave;
/** Flag used by food handling system to determine whether a teleport has just happened
Will not apply food penalties if found to be true; will set to false after processing
*/
bool m_bIsTeleporting;
} ; // tolua_export } ; // tolua_export

View File

@ -258,6 +258,9 @@ bool cServer::InitServer(cIniFile & a_SettingsIni)
m_ServerID.resize(16, '0'); m_ServerID.resize(16, '0');
} }
m_ShouldLoadOfflinePlayerData = a_SettingsIni.GetValueSetB("PlayerData", "LoadOfflinePlayerData", false);
m_ShouldLoadNamedPlayerData = a_SettingsIni.GetValueSetB("PlayerData", "LoadNamedPlayerData", true);
m_ClientViewDistance = a_SettingsIni.GetValueSetI("Server", "DefaultViewDistance", cClientHandle::DEFAULT_VIEW_DISTANCE); m_ClientViewDistance = a_SettingsIni.GetValueSetI("Server", "DefaultViewDistance", cClientHandle::DEFAULT_VIEW_DISTANCE);
if (m_ClientViewDistance < cClientHandle::MIN_VIEW_DISTANCE) if (m_ClientViewDistance < cClientHandle::MIN_VIEW_DISTANCE)
{ {

View File

@ -112,8 +112,18 @@ public: // tolua_export
cRsaPrivateKey & GetPrivateKey(void) { return m_PrivateKey; } cRsaPrivateKey & GetPrivateKey(void) { return m_PrivateKey; }
const AString & GetPublicKeyDER(void) const { return m_PublicKeyDER; } const AString & GetPublicKeyDER(void) const { return m_PublicKeyDER; }
/** Returns true if authentication has been turned on in server settings. */
bool ShouldAuthenticate(void) const { return m_ShouldAuthenticate; } bool ShouldAuthenticate(void) const { return m_ShouldAuthenticate; }
/** Returns true if offline UUIDs should be used to load data for players whose normal UUIDs cannot be found.
Loaded from the settings.ini [PlayerData].LoadOfflinePlayerData setting. */
bool ShouldLoadOfflinePlayerData(void) const { return m_ShouldLoadOfflinePlayerData; }
/** Returns true if old-style playernames should be used to load data for players whose regular datafiles cannot be found.
This allows a seamless transition from name-based to UUID-based player storage.
Loaded from the settings.ini [PlayerData].LoadNamedPlayerData setting. */
bool ShouldLoadNamedPlayerData(void) const { return m_ShouldLoadNamedPlayerData; }
private: private:
friend class cRoot; // so cRoot can create and destroy cServer friend class cRoot; // so cRoot can create and destroy cServer
@ -204,6 +214,16 @@ private:
This setting is the same as the "online-mode" setting in Vanilla. */ This setting is the same as the "online-mode" setting in Vanilla. */
bool m_ShouldAuthenticate; bool m_ShouldAuthenticate;
/** True if offline UUIDs should be used to load data for players whose normal UUIDs cannot be found.
This allows transitions from an offline (no-auth) server to an online one.
Loaded from the settings.ini [PlayerData].LoadOfflinePlayerData setting. */
bool m_ShouldLoadOfflinePlayerData;
/** True if old-style playernames should be used to load data for players whose regular datafiles cannot be found.
This allows a seamless transition from name-based to UUID-based player storage.
Loaded from the settings.ini [PlayerData].LoadNamedPlayerData setting. */
bool m_ShouldLoadNamedPlayerData;
cServer(void); cServer(void);