summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/Bindings/AllToLua.pkg1
-rw-r--r--src/ClientHandle.cpp19
-rw-r--r--src/ClientHandle.h2
-rw-r--r--src/Entities/Player.cpp21
-rw-r--r--src/Entities/Player.h7
-rw-r--r--src/Protocol/Protocol.h2
-rw-r--r--src/Protocol/Protocol125.cpp28
-rw-r--r--src/Protocol/Protocol125.h1
-rw-r--r--src/Protocol/Protocol17x.cpp48
-rw-r--r--src/Protocol/Protocol17x.h1
-rw-r--r--src/Protocol/ProtocolRecognizer.cpp10
-rw-r--r--src/Protocol/ProtocolRecognizer.h1
-rw-r--r--src/Statistics.cpp123
-rw-r--r--src/Statistics.h48
-rw-r--r--src/WorldStorage/StatSerializer.cpp126
-rw-r--r--src/WorldStorage/StatSerializer.h53
16 files changed, 448 insertions, 43 deletions
diff --git a/src/Bindings/AllToLua.pkg b/src/Bindings/AllToLua.pkg
index 1cd7c74f8..4fe86e1c5 100644
--- a/src/Bindings/AllToLua.pkg
+++ b/src/Bindings/AllToLua.pkg
@@ -76,6 +76,7 @@ $cfile "../CompositeChat.h"
$cfile "../Map.h"
$cfile "../MapManager.h"
$cfile "../Scoreboard.h"
+$cfile "../Statistics.h"
diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp
index 94f031ed6..03b1b0dc2 100644
--- a/src/ClientHandle.cpp
+++ b/src/ClientHandle.cpp
@@ -31,6 +31,8 @@
#include "CompositeChat.h"
#include "Items/ItemSword.h"
+#include "WorldStorage/StatSerializer.h"
+
#include "md5/md5.h"
@@ -336,7 +338,13 @@ void cClientHandle::Authenticate(const AString & a_Name, const AString & a_UUID)
// Send scoreboard data
World->GetScoreBoard().SendTo(*this);
-
+
+#if 0
+ // Load stats
+ cStatSerializer StatSerializer(World->GetName(), m_Player->GetName(), &m_Player->GetStatManager());
+ StatSerializer.Load();
+#endif
+
// Delay the first ping until the client "settles down"
// This should fix #889, "BadCast exception, cannot convert bit to fm" error in client
cTimer t1;
@@ -2437,6 +2445,15 @@ void cClientHandle::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleTy
+void cClientHandle::SendStatistics(const cStatManager & a_Manager)
+{
+ m_Protocol->SendStatistics(a_Manager);
+}
+
+
+
+
+
void cClientHandle::SendTabCompletionResults(const AStringVector & a_Results)
{
m_Protocol->SendTabCompletionResults(a_Results);
diff --git a/src/ClientHandle.h b/src/ClientHandle.h
index 4dc6ab074..0236d38fb 100644
--- a/src/ClientHandle.h
+++ b/src/ClientHandle.h
@@ -39,6 +39,7 @@ class cFallingBlock;
class cItemHandler;
class cWorld;
class cCompositeChat;
+class cStatManager;
@@ -160,6 +161,7 @@ public:
void SendSpawnMob (const cMonster & a_Mob);
void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch);
void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType = 0);
+ void SendStatistics (const cStatManager & a_Manager);
void SendTabCompletionResults(const AStringVector & a_Results);
void SendTeleportEntity (const cEntity & a_Entity);
void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ);
diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp
index 6ac11c270..a42fe89cf 100644
--- a/src/Entities/Player.cpp
+++ b/src/Entities/Player.cpp
@@ -16,6 +16,8 @@
#include "../Items/ItemHandler.h"
#include "../Vector3.h"
+#include "../WorldStorage/StatSerializer.h"
+
#include "inifile/iniFile.h"
#include "json/json.h"
@@ -131,6 +133,15 @@ cPlayer::~cPlayer(void)
SaveToDisk();
+#if 0
+ /* Save statistics. */
+ cStatSerializer StatSerializer(m_World->GetName(), m_PlayerName, &m_Stats);
+ if (!StatSerializer.Save())
+ {
+ LOGERROR("Could not save stats for player %s", m_PlayerName.c_str());
+ }
+#endif
+
m_World->RemovePlayer( this );
m_ClientHandle = NULL;
@@ -871,9 +882,13 @@ void cPlayer::KilledBy(cEntity * a_Killer)
}
else if (a_Killer->IsPlayer())
{
- GetWorld()->BroadcastChatDeath(Printf("%s was killed by %s", GetName().c_str(), ((cPlayer *)a_Killer)->GetName().c_str()));
+ cPlayer* Killer = (cPlayer*)a_Killer;
- m_World->GetScoreBoard().AddPlayerScore(((cPlayer *)a_Killer)->GetName(), cObjective::otPlayerKillCount, 1);
+ GetWorld()->BroadcastChatDeath(Printf("%s was killed by %s", GetName().c_str(), Killer->GetName().c_str()));
+
+ Killer->GetStatManager().AddValue(statPlayerKills);
+
+ m_World->GetScoreBoard().AddPlayerScore(Killer->GetName(), cObjective::otPlayerKillCount, 1);
}
else
{
@@ -883,6 +898,8 @@ void cPlayer::KilledBy(cEntity * a_Killer)
GetWorld()->BroadcastChatDeath(Printf("%s was killed by a %s", GetName().c_str(), KillerClass.c_str()));
}
+ m_Stats.AddValue(statDeaths);
+
m_World->GetScoreBoard().AddPlayerScore(GetName(), cObjective::otDeathCount, 1);
}
diff --git a/src/Entities/Player.h b/src/Entities/Player.h
index 6fc7e2875..82a138290 100644
--- a/src/Entities/Player.h
+++ b/src/Entities/Player.h
@@ -7,6 +7,8 @@
#include "../World.h"
#include "../ClientHandle.h"
+#include "../Statistics.h"
+
@@ -174,6 +176,9 @@ public:
cTeam * UpdateTeam(void);
// tolua_end
+
+ /** Return the associated statistic and achievement manager. */
+ cStatManager & GetStatManager() { return m_Stats; }
void SetIP(const AString & a_IP);
@@ -487,6 +492,8 @@ protected:
cTeam * m_Team;
+ cStatManager m_Stats;
+
void ResolvePermissions(void);
diff --git a/src/Protocol/Protocol.h b/src/Protocol/Protocol.h
index 8f152ad37..a543c6361 100644
--- a/src/Protocol/Protocol.h
+++ b/src/Protocol/Protocol.h
@@ -31,6 +31,7 @@ class cMonster;
class cChunkDataSerializer;
class cFallingBlock;
class cCompositeChat;
+class cStatManager;
@@ -111,6 +112,7 @@ public:
virtual void SendSpawnMob (const cMonster & a_Mob) = 0;
virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) = 0;
virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) = 0;
+ virtual void SendStatistics (const cStatManager & a_Manager) = 0;
virtual void SendTabCompletionResults(const AStringVector & a_Results) = 0;
virtual void SendTeleportEntity (const cEntity & a_Entity) = 0;
virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) = 0;
diff --git a/src/Protocol/Protocol125.cpp b/src/Protocol/Protocol125.cpp
index e7873cf7a..f3bdae3ac 100644
--- a/src/Protocol/Protocol125.cpp
+++ b/src/Protocol/Protocol125.cpp
@@ -99,6 +99,7 @@ enum
PACKET_ENCHANT_ITEM = 0x6C,
PACKET_UPDATE_SIGN = 0x82,
PACKET_ITEM_DATA = 0x83,
+ PACKET_INCREMENT_STATISTIC = 0xC8,
PACKET_PLAYER_LIST_ITEM = 0xC9,
PACKET_PLAYER_ABILITIES = 0xca,
PACKET_PLUGIN_MESSAGE = 0xfa,
@@ -992,6 +993,33 @@ void cProtocol125::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleTyp
+void cProtocol125::SendStatistics(const cStatManager & a_Manager)
+{
+ /* NOTE:
+ * Versions prior to minecraft 1.7 use an incremental statistic sync
+ * method. The current setup does not allow us to implement that, because
+ * of performance considerations.
+ */
+#if 0
+ for (unsigned int i = 0; i < (unsigned int)statCount; ++i)
+ {
+ StatValue Value = m_Manager->GetValue((eStatistic) i);
+
+ unsigned int StatID = cStatInfo::GetID((eStatistic) i);
+
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_INCREMENT_STATISTIC);
+ WriteInt(StatID);
+ WriteByte(Value); /* Can overflow! */
+ Flush();
+ }
+#endif
+}
+
+
+
+
+
void cProtocol125::SendTabCompletionResults(const AStringVector & a_Results)
{
// This protocol version doesn't support tab completion
diff --git a/src/Protocol/Protocol125.h b/src/Protocol/Protocol125.h
index 423e58d67..18a626a2d 100644
--- a/src/Protocol/Protocol125.h
+++ b/src/Protocol/Protocol125.h
@@ -84,6 +84,7 @@ public:
virtual void SendSpawnMob (const cMonster & a_Mob) override;
virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) override;
virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) override;
+ virtual void SendStatistics (const cStatManager & a_Manager) override;
virtual void SendTabCompletionResults(const AStringVector & a_Results) override;
virtual void SendTeleportEntity (const cEntity & a_Entity) override;
virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) override;
diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp
index 443723e40..3b21f7821 100644
--- a/src/Protocol/Protocol17x.cpp
+++ b/src/Protocol/Protocol17x.cpp
@@ -11,13 +11,19 @@ Implements the 1.7.x protocol classes:
#include "json/json.h"
#include "Protocol17x.h"
#include "ChunkDataSerializer.h"
+#include "PolarSSL++/Sha1Checksum.h"
+
#include "../ClientHandle.h"
#include "../Root.h"
#include "../Server.h"
#include "../World.h"
+#include "../StringCompression.h"
+#include "../CompositeChat.h"
+#include "../Statistics.h"
+
#include "../WorldStorage/FastNBT.h"
#include "../WorldStorage/EnchantmentSerializer.h"
-#include "../StringCompression.h"
+
#include "../Entities/ExpOrb.h"
#include "../Entities/Minecart.h"
#include "../Entities/FallingBlock.h"
@@ -25,15 +31,15 @@ Implements the 1.7.x protocol classes:
#include "../Entities/Pickup.h"
#include "../Entities/Player.h"
#include "../Entities/ItemFrame.h"
+#include "../Entities/ArrowEntity.h"
+#include "../Entities/FireworkEntity.h"
+
#include "../Mobs/IncludeAllMonsters.h"
#include "../UI/Window.h"
+
#include "../BlockEntities/CommandBlockEntity.h"
#include "../BlockEntities/MobHeadEntity.h"
#include "../BlockEntities/FlowerPotEntity.h"
-#include "../CompositeChat.h"
-#include "../Entities/ArrowEntity.h"
-#include "../Entities/FireworkEntity.h"
-#include "PolarSSL++/Sha1Checksum.h"
@@ -1169,6 +1175,28 @@ void cProtocol172::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleTyp
+void cProtocol172::SendStatistics(const cStatManager & a_Manager)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x37);
+ Pkt.WriteVarInt(statCount); // TODO 2014-05-11 xdot: Optimization: Send "dirty" statistics only
+
+ for (unsigned int i = 0; i < (unsigned int)statCount; ++i)
+ {
+ StatValue Value = a_Manager.GetValue((eStatistic) i);
+
+ const AString & StatName = cStatInfo::GetName((eStatistic) i);
+
+ Pkt.WriteString(StatName);
+ Pkt.WriteVarInt(Value);
+ }
+}
+
+
+
+
+
void cProtocol172::SendTabCompletionResults(const AStringVector & a_Results)
{
ASSERT(m_State == 3); // In game mode?
@@ -1843,13 +1871,19 @@ void cProtocol172::HandlePacketClientStatus(cByteBuffer & a_ByteBuffer)
case 1:
{
// Request stats
- // TODO
+ const cStatManager & Manager = m_Client->GetPlayer()->GetStatManager();
+ SendStatistics(Manager);
+
break;
}
case 2:
{
// Open Inventory achievement
- // TODO
+ cStatManager & Manager = m_Client->GetPlayer()->GetStatManager();
+ Manager.AddValue(achOpenInv);
+
+ SendStatistics(Manager);
+
break;
}
}
diff --git a/src/Protocol/Protocol17x.h b/src/Protocol/Protocol17x.h
index dc111e737..3c6a8c085 100644
--- a/src/Protocol/Protocol17x.h
+++ b/src/Protocol/Protocol17x.h
@@ -116,6 +116,7 @@ public:
virtual void SendSpawnMob (const cMonster & a_Mob) override;
virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) override;
virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) override;
+ virtual void SendStatistics (const cStatManager & a_Manager) override;
virtual void SendTabCompletionResults(const AStringVector & a_Results) override;
virtual void SendTeleportEntity (const cEntity & a_Entity) override;
virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) override;
diff --git a/src/Protocol/ProtocolRecognizer.cpp b/src/Protocol/ProtocolRecognizer.cpp
index 667fb5cef..b0cbb6def 100644
--- a/src/Protocol/ProtocolRecognizer.cpp
+++ b/src/Protocol/ProtocolRecognizer.cpp
@@ -675,6 +675,16 @@ void cProtocolRecognizer::SendSpawnVehicle(const cEntity & a_Vehicle, char a_Veh
+void cProtocolRecognizer::SendStatistics(const cStatManager & a_Manager)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendStatistics(a_Manager);
+}
+
+
+
+
+
void cProtocolRecognizer::SendTabCompletionResults(const AStringVector & a_Results)
{
ASSERT(m_Protocol != NULL);
diff --git a/src/Protocol/ProtocolRecognizer.h b/src/Protocol/ProtocolRecognizer.h
index 37f47379d..3a291bf7a 100644
--- a/src/Protocol/ProtocolRecognizer.h
+++ b/src/Protocol/ProtocolRecognizer.h
@@ -119,6 +119,7 @@ public:
virtual void SendSpawnMob (const cMonster & a_Mob) override;
virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) override;
virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) override;
+ virtual void SendStatistics (const cStatManager & a_Manager) override;
virtual void SendTabCompletionResults(const AStringVector & a_Results) override;
virtual void SendTeleportEntity (const cEntity & a_Entity) override;
virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) override;
diff --git a/src/Statistics.cpp b/src/Statistics.cpp
index 2c980d98e..94076f828 100644
--- a/src/Statistics.cpp
+++ b/src/Statistics.cpp
@@ -13,39 +13,39 @@ cStatInfo cStatInfo::ms_Info[statCount] = {
// http://minecraft.gamepedia.com/Achievements
/* Type | Name | Prerequisite */
- cStatInfo(achOpenInv, "openInventory"),
- cStatInfo(achMineWood, "mineWood", achOpenInv),
- cStatInfo(achCraftWorkbench, "buildWorkBench", achMineWood),
- cStatInfo(achCraftPickaxe, "buildPickaxe", achCraftWorkbench),
- cStatInfo(achCraftFurnace, "buildFurnace", achCraftPickaxe),
- cStatInfo(achAcquireIron, "acquireIron", achCraftFurnace),
- cStatInfo(achCraftHoe, "buildHoe", achCraftWorkbench),
- cStatInfo(achMakeBread, "makeBread", achCraftHoe),
- cStatInfo(achBakeCake, "bakeCake", achCraftHoe),
- cStatInfo(achCraftBetterPick, "buildBetterPickaxe", achCraftPickaxe),
- cStatInfo(achCookFish, "cookFish", achAcquireIron),
- cStatInfo(achOnARail, "onARail", achAcquireIron),
- cStatInfo(achCraftSword, "buildSword", achCraftWorkbench),
- cStatInfo(achKillMonster, "killEnemy", achCraftSword),
- cStatInfo(achKillCow, "killCow", achCraftSword),
- cStatInfo(achFlyPig, "flyPig", achKillCow),
- cStatInfo(achSnipeSkeleton, "snipeSkeleton", achKillMonster),
- cStatInfo(achDiamonds, "diamonds", achAcquireIron),
- cStatInfo(achEnterPortal, "portal", achDiamonds),
- cStatInfo(achReturnToSender, "ghast", achEnterPortal),
- cStatInfo(achBlazeRod, "blazeRod", achEnterPortal),
- cStatInfo(achBrewPotion, "potion", achBlazeRod),
- cStatInfo(achEnterTheEnd, "theEnd", achBlazeRod),
- cStatInfo(achDefeatDragon, "theEnd2", achEnterTheEnd),
- cStatInfo(achCraftEnchantTable, "enchantments", achDiamonds),
- cStatInfo(achOverkill, "overkill", achCraftEnchantTable),
- cStatInfo(achBookshelf, "bookcase", achCraftEnchantTable),
- cStatInfo(achExploreAllBiomes, "exploreAllBiomes", achEnterTheEnd),
- cStatInfo(achSpawnWither, "spawnWither", achDefeatDragon),
- cStatInfo(achKillWither, "killWither", achSpawnWither),
- cStatInfo(achFullBeacon, "fullBeacon", achKillWither),
- cStatInfo(achBreedCow, "breedCow", achKillCow),
- cStatInfo(achThrowDiamonds, "diamondsToYou", achDiamonds),
+ cStatInfo(achOpenInv, "achievement.openInventory"),
+ cStatInfo(achMineWood, "achievement.mineWood", achOpenInv),
+ cStatInfo(achCraftWorkbench, "achievement.buildWorkBench", achMineWood),
+ cStatInfo(achCraftPickaxe, "achievement.buildPickaxe", achCraftWorkbench),
+ cStatInfo(achCraftFurnace, "achievement.buildFurnace", achCraftPickaxe),
+ cStatInfo(achAcquireIron, "achievement.acquireIron", achCraftFurnace),
+ cStatInfo(achCraftHoe, "achievement.buildHoe", achCraftWorkbench),
+ cStatInfo(achMakeBread, "achievement.makeBread", achCraftHoe),
+ cStatInfo(achBakeCake, "achievement.bakeCake", achCraftHoe),
+ cStatInfo(achCraftBetterPick, "achievement.buildBetterPickaxe", achCraftPickaxe),
+ cStatInfo(achCookFish, "achievement.cookFish", achAcquireIron),
+ cStatInfo(achOnARail, "achievement.onARail", achAcquireIron),
+ cStatInfo(achCraftSword, "achievement.buildSword", achCraftWorkbench),
+ cStatInfo(achKillMonster, "achievement.killEnemy", achCraftSword),
+ cStatInfo(achKillCow, "achievement.killCow", achCraftSword),
+ cStatInfo(achFlyPig, "achievement.flyPig", achKillCow),
+ cStatInfo(achSnipeSkeleton, "achievement.snipeSkeleton", achKillMonster),
+ cStatInfo(achDiamonds, "achievement.diamonds", achAcquireIron),
+ cStatInfo(achEnterPortal, "achievement.portal", achDiamonds),
+ cStatInfo(achReturnToSender, "achievement.ghast", achEnterPortal),
+ cStatInfo(achBlazeRod, "achievement.blazeRod", achEnterPortal),
+ cStatInfo(achBrewPotion, "achievement.potion", achBlazeRod),
+ cStatInfo(achEnterTheEnd, "achievement.theEnd", achBlazeRod),
+ cStatInfo(achDefeatDragon, "achievement.theEnd2", achEnterTheEnd),
+ cStatInfo(achCraftEnchantTable, "achievement.enchantments", achDiamonds),
+ cStatInfo(achOverkill, "achievement.overkill", achCraftEnchantTable),
+ cStatInfo(achBookshelf, "achievement.bookcase", achCraftEnchantTable),
+ cStatInfo(achExploreAllBiomes, "achievement.exploreAllBiomes", achEnterTheEnd),
+ cStatInfo(achSpawnWither, "achievement.spawnWither", achDefeatDragon),
+ cStatInfo(achKillWither, "achievement.killWither", achSpawnWither),
+ cStatInfo(achFullBeacon, "achievement.fullBeacon", achKillWither),
+ cStatInfo(achBreedCow, "achievement.breedCow", achKillCow),
+ cStatInfo(achThrowDiamonds, "achievement.diamondsToYou", achDiamonds),
// http://minecraft.gamepedia.com/Statistics
@@ -57,6 +57,7 @@ cStatInfo cStatInfo::ms_Info[statCount] = {
cStatInfo(statDistFallen, "stat.fallOneCm"),
cStatInfo(statDistClimbed, "stat.climbOneCm"),
cStatInfo(statDistFlown, "stat.flyOneCm"),
+ cStatInfo(statDistDove, "stat.diveOneCm"),
cStatInfo(statDistMinecart, "stat.minecartOneCm"),
cStatInfo(statDistBoat, "stat.boatOneCm"),
cStatInfo(statDistPig, "stat.pigOneCm"),
@@ -137,3 +138,59 @@ eStatistic cStatInfo::GetPrerequisite(const eStatistic a_Type)
+cStatManager::cStatManager()
+{
+ Reset();
+}
+
+
+
+
+
+StatValue cStatManager::GetValue(const eStatistic a_Stat) const
+{
+ ASSERT((a_Stat > statInvalid) && (a_Stat < statCount));
+
+ return m_MainStats[a_Stat];
+}
+
+
+
+
+
+void cStatManager::SetValue(const eStatistic a_Stat, const StatValue a_Value)
+{
+ ASSERT((a_Stat > statInvalid) && (a_Stat < statCount));
+
+ m_MainStats[a_Stat] = a_Value;
+}
+
+
+
+
+
+StatValue cStatManager::AddValue(const eStatistic a_Stat, const StatValue a_Delta)
+{
+ ASSERT((a_Stat > statInvalid) && (a_Stat < statCount));
+
+ m_MainStats[a_Stat] += a_Delta;
+
+ return m_MainStats[a_Stat];
+}
+
+
+
+
+
+void cStatManager::Reset(void)
+{
+ for (unsigned int i = 0; i < (unsigned int)statCount; ++i)
+ {
+ m_MainStats[i] = 0;
+ }
+}
+
+
+
+
+
diff --git a/src/Statistics.h b/src/Statistics.h
index 540df38cc..f37f32e1e 100644
--- a/src/Statistics.h
+++ b/src/Statistics.h
@@ -9,6 +9,7 @@
+// tolua_begin
enum eStatistic
{
// The order must match the order of cStatInfo::ms_Info
@@ -77,6 +78,7 @@ enum eStatistic
statCount
};
+// tolua_end
@@ -114,3 +116,49 @@ private:
+
+/* Signed (?) integral value. */
+typedef int StatValue; // tolua_export
+
+
+
+
+/** Class that manages the statistics and achievements of a single player. */
+// tolua_begin
+class cStatManager
+{
+public:
+ // tolua_end
+
+ cStatManager();
+
+ // tolua_begin
+
+ /** Return the value of the specified stat. */
+ StatValue GetValue(const eStatistic a_Stat) const;
+
+ /** Set the value of the specified stat. */
+ void SetValue(const eStatistic a_Stat, const StatValue a_Value);
+
+ /** Reset everything. */
+ void Reset();
+
+ /** Increment the specified stat.
+ *
+ * Returns the new value.
+ */
+ StatValue AddValue(const eStatistic a_Stat, const StatValue a_Delta = 1);
+
+ // tolua_end
+
+private:
+
+ StatValue m_MainStats[statCount];
+
+ // TODO 10-05-2014 xdot: Use, mine, craft statistics
+
+
+}; // tolua_export
+
+
+
diff --git a/src/WorldStorage/StatSerializer.cpp b/src/WorldStorage/StatSerializer.cpp
new file mode 100644
index 000000000..5c6724c60
--- /dev/null
+++ b/src/WorldStorage/StatSerializer.cpp
@@ -0,0 +1,126 @@
+
+// StatSerializer.cpp
+
+
+#include "Globals.h"
+#include "StatSerializer.h"
+
+#include "../Statistics.h"
+
+#include <fstream>
+
+
+
+
+
+cStatSerializer::cStatSerializer(const AString& a_WorldName, const AString& a_PlayerName, cStatManager* a_Manager)
+ : m_Manager(a_Manager)
+{
+ AString StatsPath;
+ Printf(StatsPath, "%s/stats", a_WorldName.c_str());
+
+ m_Path = StatsPath + "/" + a_PlayerName + ".dat";
+
+ /* Ensure that the directory exists. */
+ cFile::CreateFolder(FILE_IO_PREFIX + StatsPath);
+}
+
+
+
+
+
+bool cStatSerializer::Load(void)
+{
+ AString Data = cFile::ReadWholeFile(FILE_IO_PREFIX + m_Path);
+ if (Data.empty())
+ {
+ return false;
+ }
+
+ Json::Value Root;
+ Json::Reader Reader;
+
+ if (Reader.parse(Data, Root, false))
+ {
+ return LoadStatFromJSON(Root);
+ }
+
+ return false;
+}
+
+
+
+
+
+bool cStatSerializer::Save(void)
+{
+ Json::Value Root;
+ SaveStatToJSON(Root);
+
+ cFile File;
+ if (!File.Open(FILE_IO_PREFIX + m_Path, cFile::fmWrite))
+ {
+ return false;
+ }
+
+ Json::StyledWriter Writer;
+ AString JsonData = Writer.write(Root);
+
+ File.Write(JsonData.data(), JsonData.size());
+ File.Close();
+
+ return true;
+}
+
+
+
+
+
+void cStatSerializer::SaveStatToJSON(Json::Value & a_Out)
+{
+ for (unsigned int i = 0; i < (unsigned int)statCount; ++i)
+ {
+ StatValue Value = m_Manager->GetValue((eStatistic) i);
+
+ if (Value != 0)
+ {
+ const AString & StatName = cStatInfo::GetName((eStatistic) i);
+
+ a_Out[StatName] = Value;
+ }
+ }
+}
+
+
+
+
+
+bool cStatSerializer::LoadStatFromJSON(const Json::Value & a_In)
+{
+ m_Manager->Reset();
+
+ for (Json::ValueIterator it = a_In.begin() ; it != a_In.end() ; ++it)
+ {
+ AString StatName = it.key().asString();
+
+ eStatistic StatType = cStatInfo::GetType(StatName);
+
+ if (StatType == statInvalid)
+ {
+ LOGWARNING("Invalid statistic type %s", StatName.c_str());
+ continue;
+ }
+
+ m_Manager->SetValue(StatType, (*it).asInt());
+ }
+
+ return true;
+}
+
+
+
+
+
+
+
+
diff --git a/src/WorldStorage/StatSerializer.h b/src/WorldStorage/StatSerializer.h
new file mode 100644
index 000000000..43514465b
--- /dev/null
+++ b/src/WorldStorage/StatSerializer.h
@@ -0,0 +1,53 @@
+
+// StatSerializer.h
+
+// Declares the cStatSerializer class that is used for saving stats into JSON
+
+
+
+
+
+#pragma once
+
+#include "json/json.h"
+
+
+
+
+
+// fwd:
+class cStatManager;
+
+
+
+
+class cStatSerializer
+{
+public:
+
+ cStatSerializer(const AString& a_WorldName, const AString& a_PlayerName, cStatManager* a_Manager);
+
+ bool Load(void);
+
+ bool Save(void);
+
+
+protected:
+
+ void SaveStatToJSON(Json::Value & a_Out);
+
+ bool LoadStatFromJSON(const Json::Value & a_In);
+
+
+private:
+
+ cStatManager* m_Manager;
+
+ AString m_Path;
+
+
+} ;
+
+
+
+