From a6d30a72544a626680eec6facf5b7a67ebb8fda5 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Thu, 10 Jul 2014 12:20:55 +0200 Subject: Removed lilypad from plains village prefabs. --- src/Generating/Prefabs/PlainsVillagePrefabs.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/Generating/Prefabs/PlainsVillagePrefabs.cpp b/src/Generating/Prefabs/PlainsVillagePrefabs.cpp index bba493bf1..bad8dae74 100644 --- a/src/Generating/Prefabs/PlainsVillagePrefabs.cpp +++ b/src/Generating/Prefabs/PlainsVillagePrefabs.cpp @@ -356,7 +356,8 @@ const cPrefab::sDef g_PlainsVillagePrefabs[] = "e: 8: 0\n" /* water */ "f: 50: 5\n" /* torch */ "g: 59: 7\n" /* crops */ - "h:111: 0\n" /* lilypad */ + "h: 59: 0\n" /* crops */ + "i: 59: 1\n" /* crops */ "m: 19: 0\n" /* sponge */, // Block data: @@ -404,12 +405,12 @@ const cPrefab::sDef g_PlainsVillagePrefabs[] = /* * 012345678901234 */ /* 0 */ "f.....f.f.....f" /* 1 */ ".gg.gg...gg.gg." - /* 2 */ ".g...g...gg.gg." - /* 3 */ ".g.......gg.gg." - /* 4 */ ".gg..g...gg.gg." - /* 5 */ ".gg..g...gg.gg." - /* 6 */ "..g..g...gghgg." - /* 7 */ "..g.g....gg.gg." + /* 2 */ ".gh.hg...gg.gg." + /* 3 */ ".gh.ih...gg.gg." + /* 4 */ ".gg.hg...gg.gg." + /* 5 */ ".gg.hg...gg.gg." + /* 6 */ ".ig.hg...gg.gg." + /* 7 */ ".hg.gh...gg.gg." /* 8 */ "f.....f.f.....f" // Level 4 -- cgit v1.2.3 From ce670accc9740ad5f096658be9b0a39b4aabf0cc Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Thu, 10 Jul 2014 12:37:21 +0200 Subject: Fixed Vector3.h compilation in MSVC2008. --- src/Vector3.h | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src') diff --git a/src/Vector3.h b/src/Vector3.h index 9e855b8af..0a0968c59 100644 --- a/src/Vector3.h +++ b/src/Vector3.h @@ -316,6 +316,15 @@ protected: +template <> Vector3 Vector3::Floor(void) const +{ + return *this; +} + + + + + template const double Vector3::EPS = 0.000001; -- cgit v1.2.3 From db759891578c1cf4ce7f6d577b73fd1981ed502e Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Thu, 10 Jul 2014 12:46:09 +0200 Subject: Fixed a missing "inline" keyword. --- src/Vector3.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/Vector3.h b/src/Vector3.h index 0a0968c59..c175bf135 100644 --- a/src/Vector3.h +++ b/src/Vector3.h @@ -316,7 +316,7 @@ protected: -template <> Vector3 Vector3::Floor(void) const +template <> inline Vector3 Vector3::Floor(void) const { return *this; } -- cgit v1.2.3 From 9e22f46b15d4c92bfbfdd5fb23d7530348f1d534 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Thu, 10 Jul 2014 18:18:32 +0200 Subject: Implemented support for forced chunk ticking. Fixes #1160. --- src/Chunk.cpp | 30 ++++++++++++++++++++++++++++-- src/Chunk.h | 30 ++++++++++++++++++++++++++---- src/ChunkMap.cpp | 20 ++++++++++++++++++-- src/ChunkMap.h | 7 +++++++ src/World.cpp | 9 +++++++++ src/World.h | 7 +++++++ 6 files changed, 95 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/Chunk.cpp b/src/Chunk.cpp index 0fee40cac..3f5165b7b 100644 --- a/src/Chunk.cpp +++ b/src/Chunk.cpp @@ -87,7 +87,8 @@ cChunk::cChunk( m_NeighborZM(a_NeighborZM), m_NeighborZP(a_NeighborZP), m_WaterSimulatorData(a_World->GetWaterSimulator()->CreateChunkData()), - m_LavaSimulatorData (a_World->GetLavaSimulator ()->CreateChunkData()) + m_LavaSimulatorData (a_World->GetLavaSimulator ()->CreateChunkData()), + m_AlwaysTicked(0) { if (a_NeighborXM != NULL) { @@ -1641,6 +1642,31 @@ cBlockEntity * cChunk::GetBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ) +bool cChunk::ShouldBeTicked(void) const +{ + return (HasAnyClients() || (m_AlwaysTicked > 0)); +} + + + + + +void cChunk::SetAlwaysTicked(bool a_AlwaysTicked) +{ + if (a_AlwaysTicked) + { + m_AlwaysTicked += 1; + } + else + { + m_AlwaysTicked -= 1; + } +} + + + + + void cChunk::UseBlockEntity(cPlayer * a_Player, int a_X, int a_Y, int a_Z) { cBlockEntity * be = GetBlockEntity(a_X, a_Y, a_Z); @@ -1852,7 +1878,7 @@ bool cChunk::HasClient( cClientHandle* a_Client ) -bool cChunk::HasAnyClients(void) +bool cChunk::HasAnyClients(void) const { return !m_LoadedByClient.empty(); } diff --git a/src/Chunk.h b/src/Chunk.h index e9d964e05..ededf62cd 100644 --- a/src/Chunk.h +++ b/src/Chunk.h @@ -200,11 +200,16 @@ public: void SendBlockTo(int a_RelX, int a_RelY, int a_RelZ, cClientHandle * a_Client); /** Adds a client to the chunk; returns true if added, false if already there */ - bool AddClient (cClientHandle* a_Client ); + bool AddClient(cClientHandle * a_Client); - void RemoveClient (cClientHandle* a_Client ); - bool HasClient (cClientHandle* a_Client ); - bool HasAnyClients(void); // Returns true if theres any client in the chunk; false otherwise + /** Removes the specified client from the chunk; ignored if client not in chunk. */ + void RemoveClient(cClientHandle * a_Client); + + /** Returns true if the specified client is present in this chunk. */ + bool HasClient(cClientHandle * a_Client); + + /** Returns true if theres any client in the chunk; false otherwise */ + bool HasAnyClients(void) const; void AddEntity(cEntity * a_Entity); void RemoveEntity(cEntity * a_Entity); @@ -390,6 +395,17 @@ public: cBlockEntity * GetBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ); cBlockEntity * GetBlockEntity(const Vector3i & a_BlockPos) { return GetBlockEntity(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z); } + + /** Returns true if the chunk should be ticked in the tick-thread. + Checks if there are any clients and if the always-tick flag is set */ + bool ShouldBeTicked(void) const; + + /** Increments (a_AlwaysTicked == true) or decrements (false) the m_AlwaysTicked counter. + If the m_AlwaysTicked counter is greater than zero, the chunk is ticked in the tick-thread regardless of + whether it has any clients or not. + This function allows nesting and task-concurrency (multiple separate tasks can request ticking and as long + as at least one requests is active the chunk will be ticked). */ + void SetAlwaysTicked(bool a_AlwaysTicked); private: @@ -462,6 +478,12 @@ private: /** Indicates if simulate-once blocks should be updated by the redstone simulator */ bool m_IsRedstoneDirty; + + /** If greater than zero, the chunk is ticked even if it has no clients. + Manipulated by the SetAlwaysTicked() function, allows for nested calls of the function. + This is the support for plugin-accessible chunk tick forcing. */ + int m_AlwaysTicked; + // Pick up a random block of this chunk void getRandomBlockCoords(int& a_X, int& a_Y, int& a_Z); diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp index c9fb0b59e..687c0824f 100644 --- a/src/ChunkMap.cpp +++ b/src/ChunkMap.cpp @@ -2674,6 +2674,20 @@ void cChunkMap::QueueTickBlock(int a_BlockX, int a_BlockY, int a_BlockZ) +void cChunkMap::SetChunkAlwaysTicked(int a_ChunkX, int a_ChunkZ, bool a_AlwaysTicked) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + if (Chunk != NULL) + { + Chunk->SetAlwaysTicked(a_AlwaysTicked); + } +} + + + + + //////////////////////////////////////////////////////////////////////////////// // cChunkMap::cChunkLayer: @@ -2788,12 +2802,14 @@ void cChunkMap::cChunkLayer::SpawnMobs(cMobSpawner& a_MobSpawner) + + void cChunkMap::cChunkLayer::Tick(float a_Dt) { for (size_t i = 0; i < ARRAYCOUNT(m_Chunks); i++) { - // Only tick chunks that are valid and have clients: - if ((m_Chunks[i] != NULL) && m_Chunks[i]->IsValid() && m_Chunks[i]->HasAnyClients()) + // Only tick chunks that are valid and should be ticked: + if ((m_Chunks[i] != NULL) && m_Chunks[i]->IsValid() && m_Chunks[i]->ShouldBeTicked()) { m_Chunks[i]->Tick(a_Dt); } diff --git a/src/ChunkMap.h b/src/ChunkMap.h index 433516490..b8870dfca 100644 --- a/src/ChunkMap.h +++ b/src/ChunkMap.h @@ -339,6 +339,13 @@ public: /** Returns the CS for locking the chunkmap; only cWorld::cLock may use this function! */ cCriticalSection & GetCS(void) { return m_CSLayers; } + + /** Increments (a_AlwaysTicked == true) or decrements (false) the m_AlwaysTicked counter for the specified chunk. + If the m_AlwaysTicked counter is greater than zero, the chunk is ticked in the tick-thread regardless of + whether it has any clients or not. + This function allows nesting and task-concurrency (multiple separate tasks can request ticking and as long + as at least one requests is active the chunk will be ticked). */ + void SetChunkAlwaysTicked(int a_ChunkX, int a_ChunkZ, bool a_AlwaysTicked); private: diff --git a/src/World.cpp b/src/World.cpp index a6607de12..f91a15260 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -3033,6 +3033,15 @@ void cWorld::TabCompleteUserName(const AString & a_Text, AStringVector & a_Resul +void cWorld::SetChunkAlwaysTicked(int a_ChunkX, int a_ChunkZ, bool a_AlwaysTicked) +{ + m_ChunkMap->SetChunkAlwaysTicked(a_ChunkX, a_ChunkZ, a_AlwaysTicked); +} + + + + + cRedstoneSimulator * cWorld::InitializeRedstoneSimulator(cIniFile & a_IniFile) { AString SimulatorName = a_IniFile.GetValueSet("Physics", "RedstoneSimulator", ""); diff --git a/src/World.h b/src/World.h index 476a49739..f903ff5be 100644 --- a/src/World.h +++ b/src/World.h @@ -772,6 +772,13 @@ public: /** Get the current darkness level based on the time */ NIBBLETYPE GetSkyDarkness() { return m_SkyDarkness; } + + /** Increments (a_AlwaysTicked == true) or decrements (false) the m_AlwaysTicked counter for the specified chunk. + If the m_AlwaysTicked counter is greater than zero, the chunk is ticked in the tick-thread regardless of + whether it has any clients or not. + This function allows nesting and task-concurrency (multiple separate tasks can request ticking and as long + as at least one requests is active the chunk will be ticked). */ + void SetChunkAlwaysTicked(int a_ChunkX, int a_ChunkZ, bool a_AlwaysTicked = true); // tolua_export private: -- cgit v1.2.3 From 729cc7f6ffd34724e6c9d5e3a4367ba6b2c48241 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Thu, 10 Jul 2014 23:04:33 +0200 Subject: Fixed style consistency. --- src/Chunk.cpp | 121 ++++++++++++++++++++++++++++++---------------------------- src/Chunk.h | 4 +- 2 files changed, 64 insertions(+), 61 deletions(-) (limited to 'src') diff --git a/src/Chunk.cpp b/src/Chunk.cpp index 3f5165b7b..1e80eb61b 100644 --- a/src/Chunk.cpp +++ b/src/Chunk.cpp @@ -464,7 +464,7 @@ void cChunk::CollectMobCensus(cMobCensus& toFill) -void cChunk::getThreeRandomNumber(int& a_X, int& a_Y, int& a_Z,int a_MaxX, int a_MaxY, int a_MaxZ) +void cChunk::GetThreeRandomNumbers(int & a_X, int & a_Y, int & a_Z,int a_MaxX, int a_MaxY, int a_MaxZ) { ASSERT(a_MaxX * a_MaxY * a_MaxZ * 8 < 0x00ffffff); int Random = m_World->GetTickRandomNumber(0x00ffffff); @@ -480,12 +480,12 @@ void cChunk::getThreeRandomNumber(int& a_X, int& a_Y, int& a_Z,int a_MaxX, int a -void cChunk::getRandomBlockCoords(int& a_X, int& a_Y, int& a_Z) +void cChunk::GetRandomBlockCoords(int & a_X, int & a_Y, int & a_Z) { // MG TODO : check if this kind of optimization (only one random call) is still needed // MG TODO : if so propagate it - getThreeRandomNumber(a_X, a_Y, a_Z, Width, Height-2, Width); + GetThreeRandomNumbers(a_X, a_Y, a_Z, Width, Height - 2, Width); a_Y++; } @@ -495,65 +495,68 @@ void cChunk::getRandomBlockCoords(int& a_X, int& a_Y, int& a_Z) void cChunk::SpawnMobs(cMobSpawner& a_MobSpawner) { - int Center_X,Center_Y,Center_Z; - getRandomBlockCoords(Center_X,Center_Y,Center_Z); - - BLOCKTYPE PackCenterBlock = GetBlock(Center_X, Center_Y, Center_Z); - if (a_MobSpawner.CheckPackCenter(PackCenterBlock)) - { - a_MobSpawner.NewPack(); - int NumberOfTries = 0; - int NumberOfSuccess = 0; - int MaxNbOfSuccess = 4; // this can be changed during the process for Wolves and Ghass - while (NumberOfTries < 12 && NumberOfSuccess < MaxNbOfSuccess) - { - const int HorizontalRange = 20; // MG TODO : relocate - const int VerticalRange = 0; // MG TODO : relocate - int Try_X, Try_Y, Try_Z; - getThreeRandomNumber(Try_X, Try_Y, Try_Z, 2*HorizontalRange+1 , 2*VerticalRange+1 , 2*HorizontalRange+1); - Try_X -= HorizontalRange; - Try_Y -= VerticalRange; - Try_Z -= HorizontalRange; - Try_X += Center_X; - Try_Y += Center_Y; - Try_Z += Center_Z; - - ASSERT(Try_Y > 0); - ASSERT(Try_Y < cChunkDef::Height-1); - - EMCSBiome Biome = m_ChunkMap->GetBiomeAt (Try_X, Try_Z); - // MG TODO : - // Moon cycle (for slime) - // check player and playerspawn presence < 24 blocks - // check mobs presence on the block - - // MG TODO : check that "Level" really means Y - - /* - NIBBLETYPE SkyLight = 0; - - NIBBLETYPE BlockLight = 0; - */ + int CenterX, CenterY, CenterZ; + GetRandomBlockCoords(CenterX, CenterY, CenterZ); - if (IsLightValid()) - { - cEntity* newMob = a_MobSpawner.TryToSpawnHere(this, Try_X, Try_Y, Try_Z, Biome, MaxNbOfSuccess); - if (newMob) - { - int WorldX, WorldY, WorldZ; - PositionToWorldPosition(Try_X, Try_Y, Try_Z, WorldX, WorldY, WorldZ); - double ActualX = WorldX + 0.5; - double ActualZ = WorldZ + 0.5; - newMob->SetPosition(ActualX, WorldY, ActualZ); - LOGD("Spawning %s #%i at %d,%d,%d",newMob->GetClass(),newMob->GetUniqueID(),WorldX, WorldY, WorldZ); - NumberOfSuccess++; - } - } - - NumberOfTries++; - } + BLOCKTYPE PackCenterBlock = GetBlock(CenterX, CenterY, CenterZ); + if (!a_MobSpawner.CheckPackCenter(PackCenterBlock)) + { + return; } + + a_MobSpawner.NewPack(); + int NumberOfTries = 0; + int NumberOfSuccess = 0; + int MaxNbOfSuccess = 4; // This can be changed during the process for Wolves and Ghasts + while ((NumberOfTries < 12) && (NumberOfSuccess < MaxNbOfSuccess)) + { + const int HorizontalRange = 20; // MG TODO : relocate + const int VerticalRange = 0; // MG TODO : relocate + int TryX, TryY, TryZ; + GetThreeRandomNumbers(TryX, TryY, TryZ, 2 * HorizontalRange + 1, 2 * VerticalRange + 1, 2 * HorizontalRange + 1); + TryX -= HorizontalRange; + TryY -= VerticalRange; + TryZ -= HorizontalRange; + TryX += CenterX; + TryY += CenterY; + TryZ += CenterZ; + + ASSERT(TryY > 0); + ASSERT(TryY < cChunkDef::Height - 1); + + EMCSBiome Biome = m_ChunkMap->GetBiomeAt(TryX, TryZ); + // MG TODO : + // Moon cycle (for slime) + // check player and playerspawn presence < 24 blocks + // check mobs presence on the block + // MG TODO : check that "Level" really means Y + + /* + NIBBLETYPE SkyLight = 0; + + NIBBLETYPE BlockLight = 0; + */ + + NumberOfTries++; + if (!IsLightValid()) + { + continue; + } + + cEntity * newMob = a_MobSpawner.TryToSpawnHere(this, TryX, TryY, TryZ, Biome, MaxNbOfSuccess); + if (newMob == NULL) + { + continue; + } + int WorldX, WorldY, WorldZ; + PositionToWorldPosition(TryX, TryY, TryZ, WorldX, WorldY, WorldZ); + double ActualX = WorldX + 0.5; + double ActualZ = WorldZ + 0.5; + newMob->SetPosition(ActualX, WorldY, ActualZ); + LOGD("Spawning %s #%i at {%d, %d, %d}", newMob->GetClass(), newMob->GetUniqueID(), WorldX, WorldY, WorldZ); + NumberOfSuccess++; + } // while (retry) } diff --git a/src/Chunk.h b/src/Chunk.h index ededf62cd..90664b513 100644 --- a/src/Chunk.h +++ b/src/Chunk.h @@ -486,8 +486,8 @@ private: // Pick up a random block of this chunk - void getRandomBlockCoords(int& a_X, int& a_Y, int& a_Z); - void getThreeRandomNumber(int& a_X, int& a_Y, int& a_Z,int a_MaxX, int a_MaxY, int a_MaxZ); + void GetRandomBlockCoords(int & a_X, int & a_Y, int & a_Z); + void GetThreeRandomNumbers(int & a_X, int & a_Y, int & a_Z, int a_MaxX, int a_MaxY, int a_MaxZ); void RemoveBlockEntity(cBlockEntity * a_BlockEntity); void AddBlockEntity (cBlockEntity * a_BlockEntity); -- cgit v1.2.3 From 2bd486660ab4140c42da8f0f92304d64afc604e2 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Fri, 11 Jul 2014 00:06:05 +0200 Subject: Preparation for player UUID-based storage: LoadFromFile() --- src/Entities/Player.cpp | 54 ++++++++++++++++++++++++++++++------------------- src/Entities/Player.h | 9 +++++++++ 2 files changed, 42 insertions(+), 21 deletions(-) (limited to 'src') diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index dbb8cd26c..2cd4d4b31 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -75,11 +75,6 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName) , m_TicksUntilNextSave(PLAYER_INVENTORY_SAVE_INTERVAL) , m_bIsTeleporting(false) { - LOGD("Created a player object for \"%s\" @ \"%s\" at %p, ID %d", - a_PlayerName.c_str(), a_Client->GetIPString().c_str(), - this, GetUniqueID() - ); - m_InventoryWindow = new cInventoryWindow(*this); m_CurrentWindow = m_InventoryWindow; m_InventoryWindow->OpenedByPlayer(*this); @@ -1690,53 +1685,70 @@ void cPlayer::LoadPermissionsFromDisk() -bool cPlayer::LoadFromDisk() + +bool cPlayer::LoadFromDisk(void) { LoadPermissionsFromDisk(); AString SourceFile; - Printf(SourceFile, "players/%s.json", GetName().c_str() ); + Printf(SourceFile, "players/%s.json", GetName().c_str()); + + bool res = LoadFromFile(SourceFile); + if (res) + { + return true; + } +} + + + + +bool cPlayer::LoadFromFile(const AString & a_FileName) +{ + // Load the data from the file: cFile f; - if (!f.Open(SourceFile, cFile::fmRead)) + if (!f.Open(a_FileName, cFile::fmRead)) { // This is a new player whom we haven't seen yet, bail out, let them have the defaults return false; } - AString buffer; if (f.ReadRestOfFile(buffer) != f.GetSize()) { - LOGWARNING("Cannot read player data from file \"%s\"", SourceFile.c_str()); + LOGWARNING("Cannot read player data from file \"%s\"", a_FileName.c_str()); return false; } - f.Close(); //cool kids play nice + f.Close(); + // Parse the JSON format: Json::Value root; Json::Reader reader; if (!reader.parse(buffer, root, false)) { - LOGWARNING("Cannot parse player data in file \"%s\", player will be reset", SourceFile.c_str()); + LOGWARNING("Cannot parse player data in file \"%s\"", a_FileName.c_str()); + return false; } + // Load the player data: Json::Value & JSON_PlayerPosition = root["position"]; if (JSON_PlayerPosition.size() == 3) { - SetPosX(JSON_PlayerPosition[(unsigned int)0].asDouble()); - SetPosY(JSON_PlayerPosition[(unsigned int)1].asDouble()); - SetPosZ(JSON_PlayerPosition[(unsigned int)2].asDouble()); + SetPosX(JSON_PlayerPosition[(unsigned)0].asDouble()); + SetPosY(JSON_PlayerPosition[(unsigned)1].asDouble()); + SetPosZ(JSON_PlayerPosition[(unsigned)2].asDouble()); m_LastPos = GetPosition(); } Json::Value & JSON_PlayerRotation = root["rotation"]; if (JSON_PlayerRotation.size() == 3) { - SetYaw ((float)JSON_PlayerRotation[(unsigned int)0].asDouble()); - SetPitch ((float)JSON_PlayerRotation[(unsigned int)1].asDouble()); - SetRoll ((float)JSON_PlayerRotation[(unsigned int)2].asDouble()); + SetYaw ((float)JSON_PlayerRotation[(unsigned)0].asDouble()); + SetPitch ((float)JSON_PlayerRotation[(unsigned)1].asDouble()); + SetRoll ((float)JSON_PlayerRotation[(unsigned)2].asDouble()); } - m_Health = root.get("health", 0).asInt(); + m_Health = root.get("health", 0).asInt(); 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(); @@ -1763,8 +1775,8 @@ bool cPlayer::LoadFromDisk() cStatSerializer StatSerializer(cRoot::Get()->GetDefaultWorld()->GetName(), GetName(), &m_Stats); StatSerializer.Load(); - LOGD("Player \"%s\" was read from file, spawning at {%.2f, %.2f, %.2f} in world \"%s\"", - GetName().c_str(), GetPosX(), GetPosY(), GetPosZ(), m_LoadedWorldName.c_str() + LOGD("Player \"%s\" was read from file \"%s\", spawning at {%.2f, %.2f, %.2f} in world \"%s\"", + GetName().c_str(), a_FileName.c_str(), GetPosX(), GetPosY(), GetPosZ(), m_LoadedWorldName.c_str() ); return true; diff --git a/src/Entities/Player.h b/src/Entities/Player.h index f247ac2f9..7641b0c31 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -41,6 +41,7 @@ public: cPlayer(cClientHandle * a_Client, const AString & a_PlayerName); + virtual ~cPlayer(); virtual void SpawnOn(cClientHandle & a_Client) override; @@ -337,7 +338,15 @@ public: bool MoveToWorld(const char * a_WorldName); // tolua_export bool SaveToDisk(void); + + /** Loads the player data from the disk file. + Returns true on success, false on failure. */ bool LoadFromDisk(void); + + /** Loads the player data from the specified file. + Returns true on success, false on failure. */ + bool LoadFromFile(const AString & a_FileName); + void LoadPermissionsFromDisk(void); // tolua_export const AString & GetLoadedWorldName() { return m_LoadedWorldName; } -- cgit v1.2.3 From 6cea81e3833bafbf2916ab2a06edf31c0f9a536e Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Fri, 11 Jul 2014 00:06:58 +0200 Subject: Fixed a missing return value. --- src/Entities/Player.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index 2cd4d4b31..5a459d421 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -1698,6 +1698,8 @@ bool cPlayer::LoadFromDisk(void) { return true; } + + return false; } -- cgit v1.2.3 From ebea2b7efc1775de66e0c6b6ee66da6f9ad04422 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Fri, 11 Jul 2014 13:13:10 +0200 Subject: Player data filenames are based on UUID. --- src/Entities/Player.cpp | 165 ++++++++++++++++++++++++++++++++---------------- src/Entities/Player.h | 34 ++++++---- src/Server.cpp | 3 + src/Server.h | 20 ++++++ 4 files changed, 155 insertions(+), 67 deletions(-) (limited to 'src') diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index 5a459d421..4e1314209 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -34,46 +34,47 @@ -cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName) - : super(etPlayer, 0.6, 1.8) - , m_bVisible(true) - , m_FoodLevel(MAX_FOOD_LEVEL) - , m_FoodSaturationLevel(5.0) - , m_FoodTickTimer(0) - , m_FoodExhaustionLevel(0.0) - , m_FoodPoisonedTicksRemaining(0) - , m_LastJumpHeight(0) - , m_LastGroundHeight(0) - , m_bTouchGround(false) - , m_Stance(0.0) - , m_Inventory(*this) - , m_EnderChestContents(9, 3) - , m_CurrentWindow(NULL) - , m_InventoryWindow(NULL) - , m_Color('-') - , m_GameMode(eGameMode_NotSet) - , m_IP("") - , m_ClientHandle(a_Client) - , m_NormalMaxSpeed(1.0) - , m_SprintingMaxSpeed(1.3) - , m_FlyingMaxSpeed(1.0) - , m_IsCrouched(false) - , m_IsSprinting(false) - , m_IsFlying(false) - , m_IsSwimming(false) - , m_IsSubmerged(false) - , m_IsFishing(false) - , m_CanFly(false) - , m_EatingFinishTick(-1) - , m_LifetimeTotalXp(0) - , m_CurrentXp(0) - , m_bDirtyExperience(false) - , m_IsChargingBow(false) - , m_BowCharge(0) - , m_FloaterID(-1) - , m_Team(NULL) - , m_TicksUntilNextSave(PLAYER_INVENTORY_SAVE_INTERVAL) - , m_bIsTeleporting(false) +cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName) : + super(etPlayer, 0.6, 1.8), + m_bVisible(true), + m_FoodLevel(MAX_FOOD_LEVEL), + m_FoodSaturationLevel(5.0), + m_FoodTickTimer(0), + m_FoodExhaustionLevel(0.0), + m_FoodPoisonedTicksRemaining(0), + m_LastJumpHeight(0), + m_LastGroundHeight(0), + m_bTouchGround(false), + m_Stance(0.0), + m_Inventory(*this), + m_EnderChestContents(9, 3), + m_CurrentWindow(NULL), + m_InventoryWindow(NULL), + m_Color('-'), + m_GameMode(eGameMode_NotSet), + m_IP(""), + m_ClientHandle(a_Client), + m_NormalMaxSpeed(1.0), + m_SprintingMaxSpeed(1.3), + m_FlyingMaxSpeed(1.0), + m_IsCrouched(false), + m_IsSprinting(false), + m_IsFlying(false), + m_IsSwimming(false), + m_IsSubmerged(false), + m_IsFishing(false), + m_CanFly(false), + m_EatingFinishTick(-1), + m_LifetimeTotalXp(0), + m_CurrentXp(0), + m_bDirtyExperience(false), + m_IsChargingBow(false), + m_BowCharge(0), + m_FloaterID(-1), + m_Team(NULL), + m_TicksUntilNextSave(PLAYER_INVENTORY_SAVE_INTERVAL), + m_bIsTeleporting(false), + m_UUID((a_Client != NULL) ? a_Client->GetUUID() : "") { m_InventoryWindow = new cInventoryWindow(*this); m_CurrentWindow = m_InventoryWindow; @@ -1690,15 +1691,42 @@ bool cPlayer::LoadFromDisk(void) { LoadPermissionsFromDisk(); - AString SourceFile; - Printf(SourceFile, "players/%s.json", GetName().c_str()); - - bool res = LoadFromFile(SourceFile); + // Load from the UUID file: + bool res = LoadFromFile(GetUUIDFileName(m_UUID)); if (res) { 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; } @@ -1791,6 +1819,7 @@ bool cPlayer::LoadFromFile(const AString & a_FileName) bool cPlayer::SaveToDisk() { cFile::CreateFolder(FILE_IO_PREFIX + AString("players")); + cFile::CreateFolder(FILE_IO_PREFIX + AString("players/") + m_UUID.substr(0, 2)); // create the JSON data Json::Value JSON_PlayerPosition; @@ -1822,33 +1851,45 @@ bool cPlayer::SaveToDisk() root["foodSaturation"] = m_FoodSaturationLevel; root["foodTickTimer"] = m_FoodTickTimer; root["foodExhaustion"] = m_FoodExhaustionLevel; - root["world"] = GetWorld()->GetName(); root["isflying"] = IsFlying(); - - if (m_GameMode == GetWorld()->GetGameMode()) + root["lastknownname"] = GetName(); + if (m_World != NULL) { - root["gamemode"] = (int) eGameMode_NotSet; + root["world"] = m_World->GetName(); + if (m_GameMode == m_World->GetGameMode()) + { + root["gamemode"] = (int) eGameMode_NotSet; + } + else + { + root["gamemode"] = (int) m_GameMode; + } } else { - root["gamemode"] = (int) m_GameMode; + // 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; std::string JsonData = writer.write(root); - AString SourceFile; - Printf(SourceFile, "players/%s.json", GetName().c_str() ); + AString SourceFile = GetUUIDFileName(m_UUID); cFile f; 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; } 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; } @@ -1857,7 +1898,7 @@ bool cPlayer::SaveToDisk() cStatSerializer StatSerializer(cRoot::Get()->GetDefaultWorld()->GetName(), GetName(), &m_Stats); 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; } @@ -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; +} + + + + diff --git a/src/Entities/Player.h b/src/Entities/Player.h index 7641b0c31..8f9b46e0f 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -528,6 +528,24 @@ protected: 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. */ 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) */ void ApplyFoodExhaustionFromMovement(); - /** 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; - + /** 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); } ; // tolua_export diff --git a/src/Server.cpp b/src/Server.cpp index 2c695cc70..5d1e51036 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -258,6 +258,9 @@ bool cServer::InitServer(cIniFile & a_SettingsIni) 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); if (m_ClientViewDistance < cClientHandle::MIN_VIEW_DISTANCE) { diff --git a/src/Server.h b/src/Server.h index 3d76c8ccf..5227799e8 100644 --- a/src/Server.h +++ b/src/Server.h @@ -112,8 +112,18 @@ public: // tolua_export cRsaPrivateKey & GetPrivateKey(void) { return m_PrivateKey; } 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; } + /** 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: 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. */ 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); -- cgit v1.2.3 From f73042fb02ead13aa870f061a08f56d84ed1dc99 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Fri, 11 Jul 2014 23:12:57 +0200 Subject: Simplified the player data loading. --- src/Entities/Player.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index 4e1314209..944ed643e 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -1692,8 +1692,7 @@ bool cPlayer::LoadFromDisk(void) LoadPermissionsFromDisk(); // Load from the UUID file: - bool res = LoadFromFile(GetUUIDFileName(m_UUID)); - if (res) + if (LoadFromFile(GetUUIDFileName(m_UUID))) { return true; } @@ -1702,8 +1701,7 @@ bool cPlayer::LoadFromDisk(void) AString OfflineUUID = cClientHandle::GenerateOfflineUUID(GetName()); if (cRoot::Get()->GetServer()->ShouldLoadOfflinePlayerData()) { - res = LoadFromFile(GetUUIDFileName(OfflineUUID)); - if (res) + if (LoadFromFile(GetUUIDFileName(OfflineUUID))) { return true; } @@ -1713,18 +1711,19 @@ bool cPlayer::LoadFromDisk(void) if (cRoot::Get()->GetServer()->ShouldLoadNamedPlayerData()) { AString OldStyleFileName = Printf("players/%s.json", GetName().c_str()); - res = LoadFromFile(OldStyleFileName); - if (res) + if (LoadFromFile(OldStyleFileName)) { // Save in new format and remove the old file - SaveToDisk(); - cFile::Delete(OldStyleFileName); + if (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.", + LOG("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; -- cgit v1.2.3