1
0

Statistic Manager

This commit is contained in:
andrew 2014-05-11 14:57:06 +03:00
parent a38d8b6513
commit c7c3724a3e
16 changed files with 448 additions and 43 deletions

View File

@ -76,6 +76,7 @@ $cfile "../CompositeChat.h"
$cfile "../Map.h"
$cfile "../MapManager.h"
$cfile "../Scoreboard.h"
$cfile "../Statistics.h"

View File

@ -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);

View File

@ -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);

View File

@ -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);
}

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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

View File

@ -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;
}

View File

@ -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;
} ;