From 2b943610598c193a349107fd9345d321724aff98 Mon Sep 17 00:00:00 2001 From: andrew Date: Sun, 19 Jan 2014 14:20:57 +0200 Subject: [PATCH 01/12] Basic scoreboard implementation --- src/Entities/Player.cpp | 52 +++++++ src/Entities/Player.h | 11 +- src/Scoreboard.cpp | 301 ++++++++++++++++++++++++++++++++++++++++ src/Scoreboard.h | 207 +++++++++++++++++++++++++++ src/World.h | 6 + 5 files changed, 576 insertions(+), 1 deletion(-) create mode 100644 src/Scoreboard.cpp create mode 100644 src/Scoreboard.h diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index c1f2456eb..4f3c6138b 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -74,6 +74,7 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName) , m_IsChargingBow(false) , m_BowCharge(0) , m_FloaterID(-1) + , m_Team(NULL) { LOGD("Created a player object for \"%s\" @ \"%s\" at %p, ID %d", a_PlayerName.c_str(), a_Client->GetIPString().c_str(), @@ -790,6 +791,20 @@ void cPlayer::DoTakeDamage(TakeDamageInfo & a_TDI) return; } } + + if ((a_TDI.Attacker != NULL) && (a_TDI.Attacker->IsPlayer())) + { + cPlayer* Attacker = (cPlayer*) a_TDI.Attacker; + + if ((m_Team != NULL) && (m_Team == Attacker->m_Team)) + { + if (!m_Team->GetFriendlyFire()) + { + // Friendly fire is disabled + return; + } + } + } super::DoTakeDamage(a_TDI); @@ -836,6 +851,24 @@ void cPlayer::KilledBy(cEntity * a_Killer) GetWorld()->BroadcastChat(Printf("%s[DEATH] %s%s was killed by a %s", cChatColor::Red.c_str(), cChatColor::White.c_str(), GetName().c_str(), KillerClass.c_str())); } + + class cIncrementCounterCB + : public cObjectiveCallback + { + AString m_Name; + public: + cIncrementCounterCB(const AString & a_Name) : m_Name(a_Name) {} + + virtual bool Item(cObjective * a_Objective) override + { + a_Objective->AddScore(m_Name, 1); + } + } IncrementCounter (GetName()); + + cScoreboard* Scoreboard = m_World->GetScoreBoard(); + + // Update scoreboard objectives + Scoreboard->ForEachObjectiveWith(E_OBJECTIVE_DEATH_COUNT, IncrementCounter); } @@ -916,6 +949,25 @@ bool cPlayer::IsGameModeAdventure(void) const +void cPlayer::SetTeam(cTeam* a_Team) +{ + if (m_Team) + { + m_Team->RemovePlayer(this); + } + + m_Team = a_Team; + + if (m_Team) + { + m_Team->AddPlayer(this); + } +} + + + + + void cPlayer::OpenWindow(cWindow * a_Window) { if (a_Window != m_CurrentWindow) diff --git a/src/Entities/Player.h b/src/Entities/Player.h index bf3ca08e8..52e629dc3 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -13,6 +13,7 @@ class cGroup; class cWindow; class cClientHandle; +class cTeam; @@ -153,6 +154,12 @@ public: AString GetIP(void) const { return m_IP; } // tolua_export + /// Returns the associated team, NULL if none + cTeam* GetTeam(void) { return m_Team; } // tolua_export + + /// Sets the player team, NULL if none + void SetTeam(cTeam* a_Team); + // tolua_end void SetIP(const AString & a_IP); @@ -456,6 +463,8 @@ protected: int m_FloaterID; + cTeam* m_Team; + void ResolvePermissions(void); @@ -463,7 +472,7 @@ protected: virtual void Destroyed(void); - /// Filters out damage for creative mode + /// Filters out damage for creative mode/friendly fire virtual void DoTakeDamage(TakeDamageInfo & TDI) override; /// Called in each tick to handle food-related processing diff --git a/src/Scoreboard.cpp b/src/Scoreboard.cpp new file mode 100644 index 000000000..1d2711ca0 --- /dev/null +++ b/src/Scoreboard.cpp @@ -0,0 +1,301 @@ + +// Scoreboard.cpp + +// Implementation of a scoreboard that keeps track of specified objectives + +#include "Globals.h" + +#include "Scoreboard.h" + + + + + +cObjective::cObjective(eObjectiveType a_Type) : m_Type(a_Type) +{} + + + + + +void cObjective::SetDisplaySlot(eDisplaySlot a_Display) +{ + m_Display = a_Display; +} + + + + + +void cObjective::Reset(void) +{ + m_Scores.clear(); +} + + + + + +cObjective::Score cObjective::GetScore(const AString & a_Name) const +{ + ScoreMap::const_iterator it = m_Scores.find(a_Name); + + if (it == m_Scores.end()) + { + return 0; + } + else + { + return it->second; + } +} + + + + + +void cObjective::SetScore(const AString & a_Name, cObjective::Score a_Score) +{ + m_Scores[a_Name] = a_Score; +} + + + + + +void cObjective::ResetScore(const AString & a_Name) +{ + m_Scores.erase(a_Name); +} + + + + + +cObjective::Score cObjective::AddScore(const AString & a_Name, cObjective::Score a_Delta) +{ + // TODO 2014-01-19 xdot: Potential optimization - Reuse iterator + Score NewScore = m_Scores[a_Name] + a_Delta; + + m_Scores[a_Name] = NewScore; + + return NewScore; +} + + + + + +cObjective::Score cObjective::SubScore(const AString & a_Name, cObjective::Score a_Delta) +{ + // TODO 2014-01-19 xdot: Potential optimization - Reuse iterator + Score NewScore = m_Scores[a_Name] - a_Delta; + + m_Scores[a_Name] = NewScore; + + return NewScore; +} + + + + + +cTeam::cTeam(const AString & a_Name, const AString & a_DisplayName, + const AString & a_Prefix, const AString & a_Suffix) + : m_FriendlyFire(true) + , m_SeeFriendlyInvisible(false) + , m_Name(a_Name) + , m_DisplayName(a_DisplayName) + , m_Prefix(a_Prefix) + , m_Suffix(a_Suffix) +{} + + + + + +bool cTeam::AddPlayer(cPlayer * a_Player) +{ + return m_Players.insert(a_Player).second; +} + + + + + +bool cTeam::RemovePlayer(cPlayer * a_Player) +{ + return m_Players.erase(a_Player) > 0; +} + + + + + +void cTeam::Reset(void) +{ + m_Players.clear(); +} + + + + +unsigned int cTeam::GetNumPlayers(void) const +{ + return m_Players.size(); +} + + + + + +cScoreboard::~cScoreboard() +{ + for (ObjectiveMap::iterator it = m_Objectives.begin(); it != m_Objectives.end(); ++it) + { + delete it->second; + } + + for (TeamMap::iterator it = m_Teams.begin(); it != m_Teams.end(); ++it) + { + delete it->second; + } +} + + + + + +cObjective* cScoreboard::RegisterObjective(const AString & a_Name, eObjectiveType a_Type) +{ + cObjective* Objective = new cObjective(a_Type); + + bool Status = m_Objectives.insert(NamedObjective(a_Name, Objective)).second; + + if (Status) + { + return Objective; + } + else + { + delete Objective; + return NULL; + } +} + + + + + +bool cScoreboard::RemoveObjective(const AString & a_Name) +{ + ObjectiveMap::iterator it = m_Objectives.find(a_Name); + + if (it == m_Objectives.end()) + { + return false; + } + + m_Objectives.erase(it); + + return true; +} + + + + + +cObjective* cScoreboard::GetObjective(const AString & a_Name) +{ + ObjectiveMap::iterator it = m_Objectives.find(a_Name); + + if (it == m_Objectives.end()) + { + return NULL; + } + else + { + return it->second; + } +} + + + + + +cTeam* cScoreboard::RegisterTeam(const AString & a_Name, const AString & a_DisplayName, + const AString & a_Prefix, const AString & a_Suffix) +{ + cTeam* Team = new cTeam(a_Name, a_DisplayName, a_Prefix, a_Suffix); + + bool Status = m_Teams.insert(NamedTeam(a_Name, Team)).second; + + if (Status) + { + return Team; + } + else + { + delete Team; + return NULL; + } +} + + + + + +bool cScoreboard::RemoveTeam(const AString & a_Name) +{ + TeamMap::iterator it = m_Teams.find(a_Name); + + if (it == m_Teams.end()) + { + return false; + } + + m_Teams.erase(it); + + return true; +} + + + + + +cTeam* cScoreboard::GetTeam(const AString & a_Name) +{ + TeamMap::iterator it = m_Teams.find(a_Name); + + if (it == m_Teams.end()) + { + return NULL; + } + else + { + return it->second; + } +} + + + + + +void cScoreboard::ForEachObjectiveWith(eObjectiveType a_Type, cObjectiveCallback& a_Callback) +{ + for (ObjectiveMap::iterator it = m_Objectives.begin(); it != m_Objectives.end(); ++it) + { + if (it->second->GetType() == a_Type) + { + // Call callback + if (a_Callback.Item(it->second)) + { + return; + } + } + } +} + + + + diff --git a/src/Scoreboard.h b/src/Scoreboard.h new file mode 100644 index 000000000..8ab298a07 --- /dev/null +++ b/src/Scoreboard.h @@ -0,0 +1,207 @@ + +// Scoreboard.h + +// Implementation of a scoreboard that keeps track of specified objectives + + + + + +#pragma once + + + + + +class cPlayer; +class cObjective; + +typedef std::set< cPlayer * > cPlayerSet; +typedef cItemCallback cObjectiveCallback; + + + + + +enum eObjectiveType +{ + E_OBJECTIVE_DUMMY, + + E_OBJECTIVE_DEATH_COUNT, + E_OBJECTIVE_PLAYER_KILL_COUNT, + E_OBJECTIVE_TOTAL_KILL_COUNT, + E_OBJECTIVE_HEALTH, + + E_OBJECTIVE_ACHIEVEMENT, + + E_OBJECTIVE_STAT, + E_OBJECTIVE_STAT_ITEM_CRAFT, + E_OBJECTIVE_STAT_ITEM_USE, + E_OBJECTIVE_STAT_ITEM_BREAK, + + E_OBJECTIVE_STAT_BLOCK_MINE, + E_OBJECTIVE_STAT_ENTITY_KILL, + E_OBJECTIVE_STAT_ENTITY_KILLED_BY +}; + + + + + +enum eDisplaySlot +{ + E_DISPLAY_SLOT_LIST, + E_DISPLAY_SLOT_SIDEBAR, + E_DISPLAY_SLOT_NAME +}; + + + + + +class cObjective +{ +public: + typedef int Score; + +public: + cObjective(eObjectiveType a_Type); + + eObjectiveType GetType(void) const { return m_Type; } + + eDisplaySlot GetDisplaySlot(void) const { return m_Display; } + + void SetDisplaySlot(eDisplaySlot a_Display); + + /// Resets the objective + void Reset(void); + + /// Returns the score of the specified player + Score GetScore(const AString & a_Name) const; + + /// Sets the score of the specified player + void SetScore(const AString & a_Name, Score a_Score); + + /// Resets the score of the specified player + void ResetScore(const AString & a_Name); + + /// Adds a_Delta and returns the new score + Score AddScore(const AString & a_Name, Score a_Delta); + + /// Subtracts a_Delta and returns the new score + Score SubScore(const AString & a_Name, Score a_Delta); + +private: + typedef std::pair TrackedPlayer; + + typedef std::map ScoreMap; + + ScoreMap m_Scores; + + eObjectiveType m_Type; + + eDisplaySlot m_Display; +}; + + + + + +class cTeam +{ +public: + cTeam(const AString & a_Name, const AString & a_DisplayName, + const AString & a_Prefix, const AString & a_Suffix); + + /// Adds a new player to the team + bool AddPlayer(cPlayer * a_Player); + + /// Removes a player from the team + bool RemovePlayer(cPlayer * a_Player); + + /// Removes all registered players + void Reset(void); + + /// Returns the number of registered players + unsigned int GetNumPlayers(void) const; + + bool GetFriendlyFire(void) const { return m_FriendlyFire; } + bool GetCanSeeFriendlyInvisible(void) const { return m_SeeFriendlyInvisible; } + + const AString & GetDisplayName(void) const { return m_Name; } + const AString & GetName(void) const { return m_DisplayName; } + + const AString & GetPrefix(void) const { return m_Prefix; } + const AString & GetSuffix(void) const { return m_Suffix; } + + void SetFriendlyFire(bool a_Flag); + void SetCanSeeFriendlyInvisible(bool a_Flag); + + void SetDisplayName(const AString & a_Name); + + void SetPrefix(const AString & a_Prefix); + void SetSuffix(const AString & a_Suffix); + +private: + + bool m_FriendlyFire; + bool m_SeeFriendlyInvisible; + + AString m_DisplayName; + AString m_Name; + + AString m_Prefix; + AString m_Suffix; + + // TODO 2014-01-19 xdot: Potential optimization - vector/list + cPlayerSet m_Players; +}; + + + + + +class cScoreboard +{ +public: + cScoreboard() {} + virtual ~cScoreboard(); + + /// Registers a new scoreboard objective, returns the cObjective instance + cObjective* RegisterObjective(const AString & a_Name, eObjectiveType a_Type); + + /// Removes a registered objective, returns true if operation was successful + bool RemoveObjective(const AString & a_Name); + + /// Retrieves the objective with the specified name, NULL if not found + cObjective* GetObjective(const AString & a_Name); + + /// Registers a new team, returns the cTeam instance + cTeam* RegisterTeam(const AString & a_Name, const AString & a_DisplayName, + const AString & a_Prefix, const AString & a_Suffix); + + /// Removes a registered team, returns true if operation was successful + bool RemoveTeam(const AString & a_Name); + + /// Retrieves the team with the specified name, NULL if not found + cTeam* GetTeam(const AString & a_Name); + + /// Execute callback for each objective with the specified type + void ForEachObjectiveWith(eObjectiveType a_Type, cObjectiveCallback& a_Callback); + +private: + typedef std::pair NamedObjective; + typedef std::pair NamedTeam; + + typedef std::map ObjectiveMap; + typedef std::map TeamMap; + + // TODO 2014-01-19 xdot: Potential optimization - Sort objectives based on type + ObjectiveMap m_Objectives; + + TeamMap m_Teams; +} ; + + + + diff --git a/src/World.h b/src/World.h index 1a7ad0cb1..1dcbac8e4 100644 --- a/src/World.h +++ b/src/World.h @@ -22,6 +22,7 @@ #include "Item.h" #include "Mobs/Monster.h" #include "Entities/ProjectileEntity.h" +#include "Scoreboard.h" @@ -513,6 +514,9 @@ public: /// Returns the name of the world.ini file used by this world const AString & GetIniFileName(void) const {return m_IniFileName; } + + /// Returns the associated scoreboard instance + cScoreboard* GetScoreBoard(void) { return &m_Scoreboard; } // tolua_end @@ -757,6 +761,8 @@ private: sSetBlockList m_FastSetBlockQueue; cChunkGenerator m_Generator; + + cScoreboard m_Scoreboard; /** The callbacks that the ChunkGenerator uses to store new chunks and interface to plugins */ cChunkGeneratorCallbacks m_GeneratorCallbacks; From f321b5d224cb4a6d562cfb32850bf752ddd69f61 Mon Sep 17 00:00:00 2001 From: andrew Date: Sun, 19 Jan 2014 16:02:37 +0200 Subject: [PATCH 02/12] Scoreboard improvements --- src/Entities/Player.cpp | 8 +-- src/Scoreboard.cpp | 79 +++++++++------------------ src/Scoreboard.h | 116 +++++++++++++++++++--------------------- 3 files changed, 83 insertions(+), 120 deletions(-) diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index 4f3c6138b..d2fdba909 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -798,7 +798,7 @@ void cPlayer::DoTakeDamage(TakeDamageInfo & a_TDI) if ((m_Team != NULL) && (m_Team == Attacker->m_Team)) { - if (!m_Team->GetFriendlyFire()) + if (!m_Team->AllowsFriendlyFire()) { // Friendly fire is disabled return; @@ -868,7 +868,7 @@ void cPlayer::KilledBy(cEntity * a_Killer) cScoreboard* Scoreboard = m_World->GetScoreBoard(); // Update scoreboard objectives - Scoreboard->ForEachObjectiveWith(E_OBJECTIVE_DEATH_COUNT, IncrementCounter); + Scoreboard->ForEachObjectiveWith(cObjective::E_TYPE_DEATH_COUNT, IncrementCounter); } @@ -953,14 +953,14 @@ void cPlayer::SetTeam(cTeam* a_Team) { if (m_Team) { - m_Team->RemovePlayer(this); + m_Team->RemovePlayer(GetName()); } m_Team = a_Team; if (m_Team) { - m_Team->AddPlayer(this); + m_Team->AddPlayer(GetName()); } } diff --git a/src/Scoreboard.cpp b/src/Scoreboard.cpp index 1d2711ca0..539316356 100644 --- a/src/Scoreboard.cpp +++ b/src/Scoreboard.cpp @@ -11,14 +11,14 @@ -cObjective::cObjective(eObjectiveType a_Type) : m_Type(a_Type) +cObjective::cObjective(cObjective::eType a_Type) : m_Type(a_Type) {} -void cObjective::SetDisplaySlot(eDisplaySlot a_Display) +void cObjective::SetDisplaySlot(cObjective::eDisplaySlot a_Display) { m_Display = a_Display; } @@ -102,8 +102,8 @@ cObjective::Score cObjective::SubScore(const AString & a_Name, cObjective::Score cTeam::cTeam(const AString & a_Name, const AString & a_DisplayName, const AString & a_Prefix, const AString & a_Suffix) - : m_FriendlyFire(true) - , m_SeeFriendlyInvisible(false) + : m_AllowsFriendlyFire(true) + , m_CanSeeFriendlyInvisible(false) , m_Name(a_Name) , m_DisplayName(a_DisplayName) , m_Prefix(a_Prefix) @@ -114,18 +114,18 @@ cTeam::cTeam(const AString & a_Name, const AString & a_DisplayName, -bool cTeam::AddPlayer(cPlayer * a_Player) +bool cTeam::AddPlayer(const AString & a_Name) { - return m_Players.insert(a_Player).second; + return m_Players.insert(a_Name).second; } -bool cTeam::RemovePlayer(cPlayer * a_Player) +bool cTeam::RemovePlayer(const AString & a_Name) { - return m_Players.erase(a_Player) > 0; + return m_Players.erase(a_Name) > 0; } @@ -149,38 +149,13 @@ unsigned int cTeam::GetNumPlayers(void) const -cScoreboard::~cScoreboard() +cObjective* cScoreboard::RegisterObjective(const AString & a_Name, cObjective::eType a_Type) { - for (ObjectiveMap::iterator it = m_Objectives.begin(); it != m_Objectives.end(); ++it) - { - delete it->second; - } + cObjective Objective(a_Type); - for (TeamMap::iterator it = m_Teams.begin(); it != m_Teams.end(); ++it) - { - delete it->second; - } -} + std::pair Status = m_Objectives.insert(NamedObjective(a_Name, Objective)); - - - - -cObjective* cScoreboard::RegisterObjective(const AString & a_Name, eObjectiveType a_Type) -{ - cObjective* Objective = new cObjective(a_Type); - - bool Status = m_Objectives.insert(NamedObjective(a_Name, Objective)).second; - - if (Status) - { - return Objective; - } - else - { - delete Objective; - return NULL; - } + return Status.second ? &Status.first->second : NULL; } @@ -215,7 +190,7 @@ cObjective* cScoreboard::GetObjective(const AString & a_Name) } else { - return it->second; + return &it->second; } } @@ -223,22 +198,16 @@ cObjective* cScoreboard::GetObjective(const AString & a_Name) -cTeam* cScoreboard::RegisterTeam(const AString & a_Name, const AString & a_DisplayName, - const AString & a_Prefix, const AString & a_Suffix) +cTeam* cScoreboard::RegisterTeam( + const AString & a_Name, const AString & a_DisplayName, + const AString & a_Prefix, const AString & a_Suffix +) { - cTeam* Team = new cTeam(a_Name, a_DisplayName, a_Prefix, a_Suffix); + cTeam Team(a_Name, a_DisplayName, a_Prefix, a_Suffix); - bool Status = m_Teams.insert(NamedTeam(a_Name, Team)).second; + std::pair Status = m_Teams.insert(NamedTeam(a_Name, Team)); - if (Status) - { - return Team; - } - else - { - delete Team; - return NULL; - } + return Status.second ? &Status.first->second : NULL; } @@ -273,7 +242,7 @@ cTeam* cScoreboard::GetTeam(const AString & a_Name) } else { - return it->second; + return &it->second; } } @@ -281,14 +250,14 @@ cTeam* cScoreboard::GetTeam(const AString & a_Name) -void cScoreboard::ForEachObjectiveWith(eObjectiveType a_Type, cObjectiveCallback& a_Callback) +void cScoreboard::ForEachObjectiveWith(cObjective::eType a_Type, cObjectiveCallback& a_Callback) { for (ObjectiveMap::iterator it = m_Objectives.begin(); it != m_Objectives.end(); ++it) { - if (it->second->GetType() == a_Type) + if (it->second.GetType() == a_Type) { // Call callback - if (a_Callback.Item(it->second)) + if (a_Callback.Item(&it->second)) { return; } diff --git a/src/Scoreboard.h b/src/Scoreboard.h index 8ab298a07..7993b1333 100644 --- a/src/Scoreboard.h +++ b/src/Scoreboard.h @@ -13,61 +13,51 @@ -class cPlayer; class cObjective; -typedef std::set< cPlayer * > cPlayerSet; typedef cItemCallback cObjectiveCallback; -enum eObjectiveType -{ - E_OBJECTIVE_DUMMY, - - E_OBJECTIVE_DEATH_COUNT, - E_OBJECTIVE_PLAYER_KILL_COUNT, - E_OBJECTIVE_TOTAL_KILL_COUNT, - E_OBJECTIVE_HEALTH, - - E_OBJECTIVE_ACHIEVEMENT, - - E_OBJECTIVE_STAT, - E_OBJECTIVE_STAT_ITEM_CRAFT, - E_OBJECTIVE_STAT_ITEM_USE, - E_OBJECTIVE_STAT_ITEM_BREAK, - - E_OBJECTIVE_STAT_BLOCK_MINE, - E_OBJECTIVE_STAT_ENTITY_KILL, - E_OBJECTIVE_STAT_ENTITY_KILLED_BY -}; - - - - - -enum eDisplaySlot -{ - E_DISPLAY_SLOT_LIST, - E_DISPLAY_SLOT_SIDEBAR, - E_DISPLAY_SLOT_NAME -}; - - - - - class cObjective { public: typedef int Score; -public: - cObjective(eObjectiveType a_Type); + enum eType + { + E_TYPE_DUMMY, - eObjectiveType GetType(void) const { return m_Type; } + E_TYPE_DEATH_COUNT, + E_TYPE_PLAYER_KILL_COUNT, + E_TYPE_TOTAL_KILL_COUNT, + E_TYPE_HEALTH, + + E_TYPE_ACHIEVEMENT, + + E_TYPE_STAT, + E_TYPE_STAT_ITEM_CRAFT, + E_TYPE_STAT_ITEM_USE, + E_TYPE_STAT_ITEM_BREAK, + + E_TYPE_STAT_BLOCK_MINE, + E_TYPE_STAT_ENTITY_KILL, + E_TYPE_STAT_ENTITY_KILLED_BY + }; + + enum eDisplaySlot + { + E_DISPLAY_SLOT_LIST, + E_DISPLAY_SLOT_SIDEBAR, + E_DISPLAY_SLOT_NAME + }; + +public: + cObjective(eType a_Type); + + eType GetType(void) const { return m_Type; } eDisplaySlot GetDisplaySlot(void) const { return m_Display; } @@ -98,7 +88,7 @@ private: ScoreMap m_Scores; - eObjectiveType m_Type; + eType m_Type; eDisplaySlot m_Display; }; @@ -110,14 +100,17 @@ private: class cTeam { public: - cTeam(const AString & a_Name, const AString & a_DisplayName, - const AString & a_Prefix, const AString & a_Suffix); + + cTeam( + const AString & a_Name, const AString & a_DisplayName, + const AString & a_Prefix, const AString & a_Suffix + ); /// Adds a new player to the team - bool AddPlayer(cPlayer * a_Player); + bool AddPlayer(const AString & a_Name); /// Removes a player from the team - bool RemovePlayer(cPlayer * a_Player); + bool RemovePlayer(const AString & a_Name); /// Removes all registered players void Reset(void); @@ -125,10 +118,10 @@ public: /// Returns the number of registered players unsigned int GetNumPlayers(void) const; - bool GetFriendlyFire(void) const { return m_FriendlyFire; } - bool GetCanSeeFriendlyInvisible(void) const { return m_SeeFriendlyInvisible; } + bool AllowsFriendlyFire(void) const { return m_AllowsFriendlyFire; } + bool CanSeeFriendlyInvisible(void) const { return m_CanSeeFriendlyInvisible; } - const AString & GetDisplayName(void) const { return m_Name; } + const AString & GetDisplayName(void) const { return m_DisplayName; } const AString & GetName(void) const { return m_DisplayName; } const AString & GetPrefix(void) const { return m_Prefix; } @@ -144,8 +137,8 @@ public: private: - bool m_FriendlyFire; - bool m_SeeFriendlyInvisible; + bool m_AllowsFriendlyFire; + bool m_CanSeeFriendlyInvisible; AString m_DisplayName; AString m_Name; @@ -154,7 +147,9 @@ private: AString m_Suffix; // TODO 2014-01-19 xdot: Potential optimization - vector/list - cPlayerSet m_Players; + typedef std::set PlayerNameSet; + + PlayerNameSet m_Players; }; @@ -165,10 +160,9 @@ class cScoreboard { public: cScoreboard() {} - virtual ~cScoreboard(); - /// Registers a new scoreboard objective, returns the cObjective instance - cObjective* RegisterObjective(const AString & a_Name, eObjectiveType a_Type); + /// Registers a new scoreboard objective, returns the cObjective instance, NULL on name collision + cObjective* RegisterObjective(const AString & a_Name, cObjective::eType a_Type); /// Removes a registered objective, returns true if operation was successful bool RemoveObjective(const AString & a_Name); @@ -176,7 +170,7 @@ public: /// Retrieves the objective with the specified name, NULL if not found cObjective* GetObjective(const AString & a_Name); - /// Registers a new team, returns the cTeam instance + /// Registers a new team, returns the cTeam instance, NULL on name collision cTeam* RegisterTeam(const AString & a_Name, const AString & a_DisplayName, const AString & a_Prefix, const AString & a_Suffix); @@ -187,14 +181,14 @@ public: cTeam* GetTeam(const AString & a_Name); /// Execute callback for each objective with the specified type - void ForEachObjectiveWith(eObjectiveType a_Type, cObjectiveCallback& a_Callback); + void ForEachObjectiveWith(cObjective::eType a_Type, cObjectiveCallback& a_Callback); private: - typedef std::pair NamedObjective; - typedef std::pair NamedTeam; + typedef std::pair NamedObjective; + typedef std::pair NamedTeam; - typedef std::map ObjectiveMap; - typedef std::map TeamMap; + typedef std::map ObjectiveMap; + typedef std::map TeamMap; // TODO 2014-01-19 xdot: Potential optimization - Sort objectives based on type ObjectiveMap m_Objectives; From 7728f4bcbee7fa61f005c7b972685deb4bf04f2a Mon Sep 17 00:00:00 2001 From: andrew Date: Mon, 20 Jan 2014 16:10:39 +0200 Subject: [PATCH 03/12] Scoreboard deserialization --- src/Entities/Player.cpp | 22 +- src/Entities/Player.h | 7 +- src/Scoreboard.cpp | 176 ++++++++++-- src/Scoreboard.h | 87 ++++-- src/WorldStorage/ScoreboardSerializer.cpp | 317 ++++++++++++++++++++++ src/WorldStorage/ScoreboardSerializer.h | 48 ++++ src/WorldStorage/WSSAnvil.cpp | 2 +- 7 files changed, 605 insertions(+), 54 deletions(-) create mode 100644 src/WorldStorage/ScoreboardSerializer.cpp create mode 100644 src/WorldStorage/ScoreboardSerializer.h diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index d2fdba909..285aefd25 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -111,6 +111,8 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName) m_LastJumpHeight = (float)(GetPosY()); m_LastGroundHeight = (float)(GetPosY()); m_Stance = GetPosY() + 1.62; + + // UpdateTeam(); cRoot::Get()->GetServer()->PlayerCreated(this); } @@ -949,8 +951,13 @@ bool cPlayer::IsGameModeAdventure(void) const -void cPlayer::SetTeam(cTeam* a_Team) +void cPlayer::SetTeam(cTeam * a_Team) { + if (m_Team == a_Team) + { + return; + } + if (m_Team) { m_Team->RemovePlayer(GetName()); @@ -968,6 +975,19 @@ void cPlayer::SetTeam(cTeam* a_Team) +cTeam * cPlayer::UpdateTeam(void) +{ + cScoreboard * Scoreboard = m_World->GetScoreBoard(); + + m_Team = Scoreboard->QueryPlayerTeam(GetName()); + + return m_Team; +} + + + + + void cPlayer::OpenWindow(cWindow * a_Window) { if (a_Window != m_CurrentWindow) diff --git a/src/Entities/Player.h b/src/Entities/Player.h index 52e629dc3..52ba2065c 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -155,10 +155,13 @@ public: AString GetIP(void) const { return m_IP; } // tolua_export /// Returns the associated team, NULL if none - cTeam* GetTeam(void) { return m_Team; } // tolua_export + cTeam * GetTeam(void) { return m_Team; } // tolua_export /// Sets the player team, NULL if none - void SetTeam(cTeam* a_Team); + void SetTeam(cTeam * a_Team); + + /// Forces the player to query the scoreboard for his team + cTeam * UpdateTeam(void); // tolua_end diff --git a/src/Scoreboard.cpp b/src/Scoreboard.cpp index 539316356..864837d3d 100644 --- a/src/Scoreboard.cpp +++ b/src/Scoreboard.cpp @@ -11,22 +11,74 @@ -cObjective::cObjective(cObjective::eType a_Type) : m_Type(a_Type) -{} - - - - - -void cObjective::SetDisplaySlot(cObjective::eDisplaySlot a_Display) +AString cObjective::TypeToString(eType a_Type) { - m_Display = a_Display; + switch (a_Type) + { + case E_TYPE_DUMMY: return "dummy"; + case E_TYPE_DEATH_COUNT: return "deathCount"; + case E_TYPE_PLAYER_KILL_COUNT: return "playerKillCount"; + case E_TYPE_TOTAL_KILL_COUNT: return "totalKillCount"; + case E_TYPE_HEALTH: return "health"; + case E_TYPE_ACHIEVEMENT: return "achievement"; + case E_TYPE_STAT: return "stat"; + case E_TYPE_STAT_ITEM_CRAFT: return "stat.craftItem"; + case E_TYPE_STAT_ITEM_USE: return "stat.useItem"; + case E_TYPE_STAT_ITEM_BREAK: return "stat.breakItem"; + case E_TYPE_STAT_BLOCK_MINE: return "stat.mineBlock"; + case E_TYPE_STAT_ENTITY_KILL: return "stat.killEntity"; + case E_TYPE_STAT_ENTITY_KILLED_BY: return "stat.entityKilledBy"; + + default: return ""; + } } +cObjective::eType cObjective::StringToType(const AString & a_Name) +{ + static struct { + eType m_Type; + const char * m_String; + } TypeMap [] = + { + {E_TYPE_DUMMY, "dummy"}, + {E_TYPE_DEATH_COUNT, "deathCount"}, + {E_TYPE_PLAYER_KILL_COUNT, "playerKillCount"}, + {E_TYPE_TOTAL_KILL_COUNT, "totalKillCount"}, + {E_TYPE_HEALTH, "health"}, + {E_TYPE_ACHIEVEMENT, "achievement"}, + {E_TYPE_STAT, "stat"}, + {E_TYPE_STAT_ITEM_CRAFT, "stat.craftItem"}, + {E_TYPE_STAT_ITEM_USE, "stat.useItem"}, + {E_TYPE_STAT_ITEM_BREAK, "stat.breakItem"}, + {E_TYPE_STAT_BLOCK_MINE, "stat.mineBlock"}, + {E_TYPE_STAT_ENTITY_KILL, "stat.killEntity"}, + {E_TYPE_STAT_ENTITY_KILLED_BY, "stat.entityKilledBy"} + }; + for (size_t i = 0; i < ARRAYCOUNT(TypeMap); i++) + { + if (NoCaseCompare(TypeMap[i].m_String, a_Name) == 0) + { + return TypeMap[i].m_Type; + } + } // for i - TypeMap[] + return E_TYPE_DUMMY; +} + + + + + +cObjective::cObjective(const AString & a_DisplayName, cObjective::eType a_Type) : m_DisplayName(a_DisplayName), m_Type(a_Type) +{} + + + + + void cObjective::Reset(void) { m_Scores.clear(); @@ -132,6 +184,17 @@ bool cTeam::RemovePlayer(const AString & a_Name) +bool cTeam::HasPlayer(const AString & a_Name) const +{ + cPlayerNameSet::const_iterator it = m_Players.find(a_Name); + + return it != m_Players.end(); +} + + + + + void cTeam::Reset(void) { m_Players.clear(); @@ -149,11 +212,23 @@ unsigned int cTeam::GetNumPlayers(void) const -cObjective* cScoreboard::RegisterObjective(const AString & a_Name, cObjective::eType a_Type) +cScoreboard::cScoreboard() { - cObjective Objective(a_Type); + for (int i = 0; i < (int) E_DISPLAY_SLOT_COUNT; ++i) + { + m_Display[i] = NULL; + } +} - std::pair Status = m_Objectives.insert(NamedObjective(a_Name, Objective)); + + + + +cObjective* cScoreboard::RegisterObjective(const AString & a_Name, const AString & a_DisplayName, cObjective::eType a_Type) +{ + cObjective Objective(a_DisplayName, a_Type); + + std::pair Status = m_Objectives.insert(cNamedObjective(a_Name, Objective)); return Status.second ? &Status.first->second : NULL; } @@ -164,7 +239,7 @@ cObjective* cScoreboard::RegisterObjective(const AString & a_Name, cObjective::e bool cScoreboard::RemoveObjective(const AString & a_Name) { - ObjectiveMap::iterator it = m_Objectives.find(a_Name); + cObjectiveMap::iterator it = m_Objectives.find(a_Name); if (it == m_Objectives.end()) { @@ -180,9 +255,9 @@ bool cScoreboard::RemoveObjective(const AString & a_Name) -cObjective* cScoreboard::GetObjective(const AString & a_Name) +cObjective * cScoreboard::GetObjective(const AString & a_Name) { - ObjectiveMap::iterator it = m_Objectives.find(a_Name); + cObjectiveMap::iterator it = m_Objectives.find(a_Name); if (it == m_Objectives.end()) { @@ -198,14 +273,14 @@ cObjective* cScoreboard::GetObjective(const AString & a_Name) -cTeam* cScoreboard::RegisterTeam( +cTeam * cScoreboard::RegisterTeam( const AString & a_Name, const AString & a_DisplayName, const AString & a_Prefix, const AString & a_Suffix ) { cTeam Team(a_Name, a_DisplayName, a_Prefix, a_Suffix); - std::pair Status = m_Teams.insert(NamedTeam(a_Name, Team)); + std::pair Status = m_Teams.insert(cNamedTeam(a_Name, Team)); return Status.second ? &Status.first->second : NULL; } @@ -216,7 +291,7 @@ cTeam* cScoreboard::RegisterTeam( bool cScoreboard::RemoveTeam(const AString & a_Name) { - TeamMap::iterator it = m_Teams.find(a_Name); + cTeamMap::iterator it = m_Teams.find(a_Name); if (it == m_Teams.end()) { @@ -232,9 +307,9 @@ bool cScoreboard::RemoveTeam(const AString & a_Name) -cTeam* cScoreboard::GetTeam(const AString & a_Name) +cTeam * cScoreboard::GetTeam(const AString & a_Name) { - TeamMap::iterator it = m_Teams.find(a_Name); + cTeamMap::iterator it = m_Teams.find(a_Name); if (it == m_Teams.end()) { @@ -250,9 +325,50 @@ cTeam* cScoreboard::GetTeam(const AString & a_Name) +cTeam * cScoreboard::QueryPlayerTeam(const AString & a_Name) +{ + for (cTeamMap::iterator it = m_Teams.begin(); it != m_Teams.end(); ++it) + { + if (it->second.HasPlayer(a_Name)) + { + return &it->second; + } + } + + return NULL; +} + + + + + +void cScoreboard::SetDisplay(const AString & a_Objective, eDisplaySlot a_Slot) +{ + ASSERT(a_Slot < E_DISPLAY_SLOT_COUNT); + + cObjective * Objective = GetObjective(a_Objective); + + m_Display[a_Slot] = Objective; +} + + + + + +cObjective* cScoreboard::GetObjectiveIn(eDisplaySlot a_Slot) +{ + ASSERT(a_Slot < E_DISPLAY_SLOT_COUNT); + + return m_Display[a_Slot]; +} + + + + + void cScoreboard::ForEachObjectiveWith(cObjective::eType a_Type, cObjectiveCallback& a_Callback) { - for (ObjectiveMap::iterator it = m_Objectives.begin(); it != m_Objectives.end(); ++it) + for (cObjectiveMap::iterator it = m_Objectives.begin(); it != m_Objectives.end(); ++it) { if (it->second.GetType() == a_Type) { @@ -268,3 +384,21 @@ void cScoreboard::ForEachObjectiveWith(cObjective::eType a_Type, cObjectiveCallb + +unsigned int cScoreboard::GetNumObjectives(void) const +{ + return m_Objectives.size(); +} + + + + + +unsigned int cScoreboard::GetNumTeams(void) const +{ + return m_Teams.size(); +} + + + + diff --git a/src/Scoreboard.h b/src/Scoreboard.h index 7993b1333..f7285a9cf 100644 --- a/src/Scoreboard.h +++ b/src/Scoreboard.h @@ -24,6 +24,7 @@ typedef cItemCallback cObjectiveCallback; class cObjective { public: + typedef int Score; enum eType @@ -47,21 +48,17 @@ public: E_TYPE_STAT_ENTITY_KILLED_BY }; - enum eDisplaySlot - { - E_DISPLAY_SLOT_LIST, - E_DISPLAY_SLOT_SIDEBAR, - E_DISPLAY_SLOT_NAME - }; + static AString TypeToString(eType a_Type); + + static eType StringToType(const AString & a_Name); public: - cObjective(eType a_Type); + + cObjective(const AString & a_DisplayName, eType a_Type); eType GetType(void) const { return m_Type; } - eDisplaySlot GetDisplaySlot(void) const { return m_Display; } - - void SetDisplaySlot(eDisplaySlot a_Display); + const AString & GetDisplayName(void) const { return m_DisplayName; } /// Resets the objective void Reset(void); @@ -82,15 +79,17 @@ public: Score SubScore(const AString & a_Name, Score a_Delta); private: + typedef std::pair TrackedPlayer; typedef std::map ScoreMap; ScoreMap m_Scores; + AString m_DisplayName; + eType m_Type; - eDisplaySlot m_Display; }; @@ -112,6 +111,9 @@ public: /// Removes a player from the team bool RemovePlayer(const AString & a_Name); + /// Returns whether the specified player is in this team + bool HasPlayer(const AString & a_Name) const; + /// Removes all registered players void Reset(void); @@ -127,8 +129,8 @@ public: const AString & GetPrefix(void) const { return m_Prefix; } const AString & GetSuffix(void) const { return m_Suffix; } - void SetFriendlyFire(bool a_Flag); - void SetCanSeeFriendlyInvisible(bool a_Flag); + void SetFriendlyFire(bool a_Flag) { m_AllowsFriendlyFire = a_Flag; } + void SetCanSeeFriendlyInvisible(bool a_Flag) { m_CanSeeFriendlyInvisible = a_Flag; } void SetDisplayName(const AString & a_Name); @@ -137,6 +139,8 @@ public: private: + typedef std::set cPlayerNameSet; + bool m_AllowsFriendlyFire; bool m_CanSeeFriendlyInvisible; @@ -146,10 +150,8 @@ private: AString m_Prefix; AString m_Suffix; - // TODO 2014-01-19 xdot: Potential optimization - vector/list - typedef std::set PlayerNameSet; + cPlayerNameSet m_Players; - PlayerNameSet m_Players; }; @@ -159,41 +161,68 @@ private: class cScoreboard { public: - cScoreboard() {} + + enum eDisplaySlot + { + E_DISPLAY_SLOT_LIST = 0, + E_DISPLAY_SLOT_SIDEBAR, + E_DISPLAY_SLOT_NAME, + + E_DISPLAY_SLOT_COUNT + }; + + +public: + + cScoreboard(); /// Registers a new scoreboard objective, returns the cObjective instance, NULL on name collision - cObjective* RegisterObjective(const AString & a_Name, cObjective::eType a_Type); + cObjective * RegisterObjective(const AString & a_Name, const AString & a_DisplayName, cObjective::eType a_Type); /// Removes a registered objective, returns true if operation was successful bool RemoveObjective(const AString & a_Name); /// Retrieves the objective with the specified name, NULL if not found - cObjective* GetObjective(const AString & a_Name); + cObjective * GetObjective(const AString & a_Name); /// Registers a new team, returns the cTeam instance, NULL on name collision - cTeam* RegisterTeam(const AString & a_Name, const AString & a_DisplayName, - const AString & a_Prefix, const AString & a_Suffix); + cTeam * RegisterTeam(const AString & a_Name, const AString & a_DisplayName, const AString & a_Prefix, const AString & a_Suffix); /// Removes a registered team, returns true if operation was successful bool RemoveTeam(const AString & a_Name); /// Retrieves the team with the specified name, NULL if not found - cTeam* GetTeam(const AString & a_Name); + cTeam * GetTeam(const AString & a_Name); + + cTeam * QueryPlayerTeam(const AString & a_Name); // WARNING: O(n logn) + + void SetDisplay(const AString & a_Objective, eDisplaySlot a_Slot); + + cObjective* GetObjectiveIn(eDisplaySlot a_Slot); /// Execute callback for each objective with the specified type void ForEachObjectiveWith(cObjective::eType a_Type, cObjectiveCallback& a_Callback); -private: - typedef std::pair NamedObjective; - typedef std::pair NamedTeam; + unsigned int GetNumObjectives(void) const; - typedef std::map ObjectiveMap; - typedef std::map TeamMap; + unsigned int GetNumTeams(void) const; + + +private: + + typedef std::pair cNamedObjective; + typedef std::pair cNamedTeam; + + typedef std::map cObjectiveMap; + typedef std::map cTeamMap; // TODO 2014-01-19 xdot: Potential optimization - Sort objectives based on type - ObjectiveMap m_Objectives; + cObjectiveMap m_Objectives; + + cTeamMap m_Teams; + + cObjective* m_Display[E_DISPLAY_SLOT_COUNT]; - TeamMap m_Teams; } ; diff --git a/src/WorldStorage/ScoreboardSerializer.cpp b/src/WorldStorage/ScoreboardSerializer.cpp new file mode 100644 index 000000000..c2f13a092 --- /dev/null +++ b/src/WorldStorage/ScoreboardSerializer.cpp @@ -0,0 +1,317 @@ + +// ScoreboardSerializer.cpp + + +#include "Globals.h" +#include "ScoreboardSerializer.h" +#include "../StringCompression.h" +#include "zlib/zlib.h" +#include "FastNBT.h" + +#include "../Scoreboard.h" + + + + +#define SCOREBOARD_INFLATE_MAX 16 KiB + + + + + +cScoreboardSerializer::cScoreboardSerializer(const AString & a_WorldName, cScoreboard* a_ScoreBoard) + : m_ScoreBoard(a_ScoreBoard) +{ + Printf(m_Path, "%s/data/scoreboard.dat", a_WorldName.c_str()); +} + + + + + +bool cScoreboardSerializer::Load(void) +{ + cFile File; + + if (!File.Open(m_Path, cFile::fmReadWrite)) + { + return false; + } + + AString Data; + + File.ReadRestOfFile(Data); + + File.Close(); + + char Uncompressed[SCOREBOARD_INFLATE_MAX]; + z_stream strm; + strm.zalloc = (alloc_func)NULL; + strm.zfree = (free_func)NULL; + strm.opaque = NULL; + inflateInit(&strm); + strm.next_out = (Bytef *)Uncompressed; + strm.avail_out = sizeof(Uncompressed); + strm.next_in = (Bytef *)Data.data(); + strm.avail_in = Data.size(); + int res = inflate(&strm, Z_FINISH); + inflateEnd(&strm); + if (res != Z_STREAM_END) + { + return false; + } + + // Parse the NBT data: + cParsedNBT NBT(Uncompressed, strm.total_out); + if (!NBT.IsValid()) + { + // NBT Parsing failed + return false; + } + + return LoadScoreboardFromNBT(NBT); +} + + + + + +bool cScoreboardSerializer::Save(void) +{ + cFastNBTWriter Writer; + + Writer.BeginCompound(""); + + SaveScoreboardToNBT(Writer); + + Writer.EndCompound(); + Writer.Finish(); + + #ifdef _DEBUG + cParsedNBT TestParse(Writer.GetResult().data(), Writer.GetResult().size()); + ASSERT(TestParse.IsValid()); + #endif // _DEBUG + + gzFile gz = gzopen((FILE_IO_PREFIX + m_Path).c_str(), "wb"); + if (gz != NULL) + { + gzwrite(gz, Writer.GetResult().data(), Writer.GetResult().size()); + } + gzclose(gz); + + return true; +} + + + + + +void cScoreboardSerializer::SaveScoreboardToNBT(cFastNBTWriter & a_Writer) +{ + a_Writer.BeginCompound("Data"); + a_Writer.BeginList("Objectives", TAG_Compound); + + a_Writer.EndList(); + + a_Writer.BeginList("PlayerScores", TAG_Compound); + + a_Writer.EndList(); + + a_Writer.BeginList("Teams", TAG_Compound); + + a_Writer.EndList(); + a_Writer.EndCompound(); + + a_Writer.BeginCompound("DisplaySlots"); + + a_Writer.EndCompound(); +} + + + + + +bool cScoreboardSerializer::LoadScoreboardFromNBT(const cParsedNBT & a_NBT) +{ + int Data = a_NBT.FindChildByName(0, "Data"); + if (Data < 0) + { + return false; + } + + int Objectives = a_NBT.FindChildByName(Data, "Objectives"); + if (Objectives < 0) + { + return false; + } + + for (int Child = a_NBT.GetFirstChild(Objectives); Child >= 0; Child = a_NBT.GetNextSibling(Child)) + { + AString CriteriaName, DisplayName, Name; + + int CurrLine = a_NBT.FindChildByName(Child, "CriteriaName"); + if (CurrLine >= 0) + { + CriteriaName = a_NBT.GetString(CurrLine); + } + + CurrLine = a_NBT.FindChildByName(Child, "DisplayName"); + if (CurrLine >= 0) + { + DisplayName = a_NBT.GetString(CurrLine); + } + + CurrLine = a_NBT.FindChildByName(Child, "Name"); + if (CurrLine >= 0) + { + Name = a_NBT.GetString(CurrLine); + } + + cObjective::eType Type = cObjective::StringToType(CriteriaName); + + m_ScoreBoard->RegisterObjective(Name, DisplayName, Type); + } + + int PlayerScores = a_NBT.FindChildByName(Data, "PlayerScores"); + if (PlayerScores < 0) + { + return false; + } + + for (int Child = a_NBT.GetFirstChild(PlayerScores); Child >= 0; Child = a_NBT.GetNextSibling(Child)) + { + AString Name, ObjectiveName; + + cObjective::Score Score; + + int CurrLine = a_NBT.FindChildByName(Child, "Score"); + if (CurrLine >= 0) + { + Score = a_NBT.GetInt(CurrLine); + } + + CurrLine = a_NBT.FindChildByName(Child, "Name"); + if (CurrLine >= 0) + { + Name = a_NBT.GetString(CurrLine); + } + + CurrLine = a_NBT.FindChildByName(Child, "Objective"); + if (CurrLine >= 0) + { + ObjectiveName = a_NBT.GetString(CurrLine); + } + + cObjective * Objective = m_ScoreBoard->GetObjective(ObjectiveName); + + if (Objective) + { + Objective->SetScore(Name, Score); + } + } + + int Teams = a_NBT.FindChildByName(Data, "Teams"); + if (Teams < 0) + { + return false; + } + + for (int Child = a_NBT.GetFirstChild(Teams); Child >= 0; Child = a_NBT.GetNextSibling(Child)) + { + AString Name, DisplayName, Prefix, Suffix; + + bool AllowsFriendlyFire, CanSeeFriendlyInvisible; + + int CurrLine = a_NBT.FindChildByName(Child, "Name"); + if (CurrLine >= 0) + { + Name = a_NBT.GetInt(CurrLine); + } + + CurrLine = a_NBT.FindChildByName(Child, "DisplayName"); + if (CurrLine >= 0) + { + DisplayName = a_NBT.GetInt(CurrLine); + } + + CurrLine = a_NBT.FindChildByName(Child, "Prefix"); + if (CurrLine >= 0) + { + Prefix = a_NBT.GetInt(CurrLine); + } + + CurrLine = a_NBT.FindChildByName(Child, "Suffix"); + if (CurrLine >= 0) + { + Suffix = a_NBT.GetInt(CurrLine); + } + + CurrLine = a_NBT.FindChildByName(Child, "AllowFriendlyFire"); + if (CurrLine >= 0) + { + AllowsFriendlyFire = a_NBT.GetInt(CurrLine); + } + + CurrLine = a_NBT.FindChildByName(Child, "SeeFriendlyInvisibles"); + if (CurrLine >= 0) + { + CanSeeFriendlyInvisible = a_NBT.GetInt(CurrLine); + } + + cTeam * Team = m_ScoreBoard->RegisterTeam(Name, DisplayName, Prefix, Suffix); + + Team->SetFriendlyFire(AllowsFriendlyFire); + Team->SetCanSeeFriendlyInvisible(CanSeeFriendlyInvisible); + + int Players = a_NBT.FindChildByName(Child, "Players"); + if (Players < 0) + { + continue; + } + + for (int ChildB = a_NBT.GetFirstChild(Players); ChildB >= 0; ChildB = a_NBT.GetNextSibling(ChildB)) + { + Team->AddPlayer(a_NBT.GetString(ChildB)); + } + } + + int DisplaySlots = a_NBT.FindChildByName(0, "DisplaySlots"); + if (DisplaySlots < 0) + { + return false; + } + + int CurrLine = a_NBT.FindChildByName(DisplaySlots, "slot_0"); + if (CurrLine >= 0) + { + AString Name = a_NBT.GetString(CurrLine); + + m_ScoreBoard->SetDisplay(Name, cScoreboard::E_DISPLAY_SLOT_LIST); + } + + CurrLine = a_NBT.FindChildByName(DisplaySlots, "slot_1"); + if (CurrLine >= 0) + { + AString Name = a_NBT.GetString(CurrLine); + + m_ScoreBoard->SetDisplay(Name, cScoreboard::E_DISPLAY_SLOT_SIDEBAR); + } + + CurrLine = a_NBT.FindChildByName(DisplaySlots, "slot_2"); + if (CurrLine >= 0) + { + AString Name = a_NBT.GetString(CurrLine); + + m_ScoreBoard->SetDisplay(Name, cScoreboard::E_DISPLAY_SLOT_NAME); + } + + return true; +} + + + + + + + + diff --git a/src/WorldStorage/ScoreboardSerializer.h b/src/WorldStorage/ScoreboardSerializer.h new file mode 100644 index 000000000..2a4e0767e --- /dev/null +++ b/src/WorldStorage/ScoreboardSerializer.h @@ -0,0 +1,48 @@ + +// ScoreboardSerializer.h + +// Declares the cScoreboardSerializer class that is used for saving scoreboards into NBT format used by Anvil + + + + + +#pragma once + + + + + +// fwd: +class cFastNBTWriter; +class cParsedNBT; +class cScoreboard; + + + + +class cScoreboardSerializer +{ +public: + cScoreboardSerializer(const AString & a_WorldName, cScoreboard* a_ScoreBoard); + + /// Try to load the scoreboard + bool Load(void); + + /// Try to save the scoreboard + bool Save(void); + +private: + + void SaveScoreboardToNBT(cFastNBTWriter & a_Writer); + + bool LoadScoreboardFromNBT(const cParsedNBT & a_NBT); + + cScoreboard* m_ScoreBoard; + + AString m_Path; +} ; + + + + diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp index 96a77152b..600eb0a51 100644 --- a/src/WorldStorage/WSSAnvil.cpp +++ b/src/WorldStorage/WSSAnvil.cpp @@ -1935,7 +1935,7 @@ bool cWSSAnvil::LoadEntityBaseFromNBT(cEntity & a_Entity, const cParsedNBT & a_N return false; } a_Entity.SetYaw(Rotation[0]); - a_Entity.SetRoll (Rotation[1]); + a_Entity.SetRoll(Rotation[1]); return true; } From ff2302ebd53453242175683587b19ae5de5d1aed Mon Sep 17 00:00:00 2001 From: andrew Date: Mon, 20 Jan 2014 16:45:40 +0200 Subject: [PATCH 04/12] Scoreboard serialization --- src/Scoreboard.cpp | 11 +-- src/Scoreboard.h | 18 +++-- src/WorldStorage/ScoreboardSerializer.cpp | 81 +++++++++++++++++++++-- 3 files changed, 94 insertions(+), 16 deletions(-) diff --git a/src/Scoreboard.cpp b/src/Scoreboard.cpp index 864837d3d..3ddf146a6 100644 --- a/src/Scoreboard.cpp +++ b/src/Scoreboard.cpp @@ -72,7 +72,10 @@ cObjective::eType cObjective::StringToType(const AString & a_Name) -cObjective::cObjective(const AString & a_DisplayName, cObjective::eType a_Type) : m_DisplayName(a_DisplayName), m_Type(a_Type) +cObjective::cObjective(const AString & a_Name, const AString & a_DisplayName, cObjective::eType a_Type) + : m_DisplayName(a_DisplayName) + , m_Name(a_Name) + , m_Type(a_Type) {} @@ -90,7 +93,7 @@ void cObjective::Reset(void) cObjective::Score cObjective::GetScore(const AString & a_Name) const { - ScoreMap::const_iterator it = m_Scores.find(a_Name); + cScoreMap::const_iterator it = m_Scores.find(a_Name); if (it == m_Scores.end()) { @@ -226,7 +229,7 @@ cScoreboard::cScoreboard() cObjective* cScoreboard::RegisterObjective(const AString & a_Name, const AString & a_DisplayName, cObjective::eType a_Type) { - cObjective Objective(a_DisplayName, a_Type); + cObjective Objective(a_Name, a_DisplayName, a_Type); std::pair Status = m_Objectives.insert(cNamedObjective(a_Name, Objective)); @@ -355,7 +358,7 @@ void cScoreboard::SetDisplay(const AString & a_Objective, eDisplaySlot a_Slot) -cObjective* cScoreboard::GetObjectiveIn(eDisplaySlot a_Slot) +cObjective * cScoreboard::GetObjectiveIn(eDisplaySlot a_Slot) { ASSERT(a_Slot < E_DISPLAY_SLOT_COUNT); diff --git a/src/Scoreboard.h b/src/Scoreboard.h index f7285a9cf..2ce614de7 100644 --- a/src/Scoreboard.h +++ b/src/Scoreboard.h @@ -54,10 +54,11 @@ public: public: - cObjective(const AString & a_DisplayName, eType a_Type); + cObjective(const AString & a_Name, const AString & a_DisplayName, eType a_Type); eType GetType(void) const { return m_Type; } + const AString & GetName(void) const { return m_Name; } const AString & GetDisplayName(void) const { return m_DisplayName; } /// Resets the objective @@ -80,16 +81,19 @@ public: private: - typedef std::pair TrackedPlayer; + typedef std::pair cTrackedPlayer; - typedef std::map ScoreMap; + typedef std::map cScoreMap; - ScoreMap m_Scores; + cScoreMap m_Scores; AString m_DisplayName; + AString m_Name; eType m_Type; + friend class cScoreboardSerializer; + }; @@ -152,6 +156,8 @@ private: cPlayerNameSet m_Players; + friend class cScoreboardSerializer; + }; @@ -198,7 +204,7 @@ public: void SetDisplay(const AString & a_Objective, eDisplaySlot a_Slot); - cObjective* GetObjectiveIn(eDisplaySlot a_Slot); + cObjective * GetObjectiveIn(eDisplaySlot a_Slot); /// Execute callback for each objective with the specified type void ForEachObjectiveWith(cObjective::eType a_Type, cObjectiveCallback& a_Callback); @@ -223,6 +229,8 @@ private: cObjective* m_Display[E_DISPLAY_SLOT_COUNT]; + friend class cScoreboardSerializer; + } ; diff --git a/src/WorldStorage/ScoreboardSerializer.cpp b/src/WorldStorage/ScoreboardSerializer.cpp index c2f13a092..bcac50bb6 100644 --- a/src/WorldStorage/ScoreboardSerializer.cpp +++ b/src/WorldStorage/ScoreboardSerializer.cpp @@ -109,21 +109,88 @@ bool cScoreboardSerializer::Save(void) void cScoreboardSerializer::SaveScoreboardToNBT(cFastNBTWriter & a_Writer) { a_Writer.BeginCompound("Data"); - a_Writer.BeginList("Objectives", TAG_Compound); + a_Writer.BeginList("Objectives", TAG_Compound); + + for (cScoreboard::cObjectiveMap::const_iterator it = m_ScoreBoard->m_Objectives.begin(); it != m_ScoreBoard->m_Objectives.end(); ++it) + { + const cObjective & Objective = it->second; + + a_Writer.BeginCompound(""); + + a_Writer.AddString("CriteriaName", cObjective::TypeToString(Objective.GetType())); + + a_Writer.AddString("DisplayName", Objective.GetDisplayName()); + a_Writer.AddString("Name", it->first); + + a_Writer.EndCompound(); + } + + a_Writer.EndList(); + + a_Writer.BeginList("PlayerScores", TAG_Compound); + + for (cScoreboard::cObjectiveMap::const_iterator it = m_ScoreBoard->m_Objectives.begin(); it != m_ScoreBoard->m_Objectives.end(); ++it) + { + const cObjective & Objective = it->second; + + for (cObjective::cScoreMap::const_iterator it2 = Objective.m_Scores.begin(); it2 != Objective.m_Scores.end(); ++it2) + { + a_Writer.BeginCompound(""); + + a_Writer.AddInt("Score", it2->second); + + a_Writer.AddString("Name", it2->first); + a_Writer.AddString("Objective", it->first); + + a_Writer.EndCompound(); + } + } + + a_Writer.EndList(); + + a_Writer.BeginList("Teams", TAG_Compound); + + for (cScoreboard::cTeamMap::const_iterator it = m_ScoreBoard->m_Teams.begin(); it != m_ScoreBoard->m_Teams.end(); ++it) + { + const cTeam & Team = it->second; + + a_Writer.BeginCompound(""); + + a_Writer.AddByte("AllowFriendlyFire", Team.AllowsFriendlyFire() ? 1 : 0); + a_Writer.AddByte("SeeFriendlyInvisibles", Team.CanSeeFriendlyInvisible() ? 1 : 0); + + a_Writer.AddString("DisplayName", Team.GetDisplayName()); + a_Writer.AddString("Name", it->first); + + a_Writer.AddString("Prefix", Team.GetPrefix()); + a_Writer.AddString("Suffix", Team.GetSuffix()); + + a_Writer.BeginList("Players", TAG_String); + + for (cTeam::cPlayerNameSet::const_iterator it2 = Team.m_Players.begin(); it2 != Team.m_Players.end(); ++it2) + { + a_Writer.AddString("", *it2); + } a_Writer.EndList(); - a_Writer.BeginList("PlayerScores", TAG_Compound); + a_Writer.EndCompound(); + } - a_Writer.EndList(); - - a_Writer.BeginList("Teams", TAG_Compound); - - a_Writer.EndList(); + a_Writer.EndList(); a_Writer.EndCompound(); a_Writer.BeginCompound("DisplaySlots"); + cObjective * Objective = m_ScoreBoard->GetObjectiveIn(cScoreboard::E_DISPLAY_SLOT_LIST); + a_Writer.AddString("slot_0", (Objective == NULL) ? "" : Objective->GetName()); + + Objective = m_ScoreBoard->GetObjectiveIn(cScoreboard::E_DISPLAY_SLOT_SIDEBAR); + a_Writer.AddString("slot_1", (Objective == NULL) ? "" : Objective->GetName()); + + Objective = m_ScoreBoard->GetObjectiveIn(cScoreboard::E_DISPLAY_SLOT_NAME); + a_Writer.AddString("slot_2", (Objective == NULL) ? "" : Objective->GetName()); + a_Writer.EndCompound(); } From 9c93ab15ab6ea4131de5af275fdc759bb49ec648 Mon Sep 17 00:00:00 2001 From: Alexander Harkness Date: Mon, 20 Jan 2014 19:02:37 +0000 Subject: [PATCH 05/12] Fix a crash but somewhere... --- src/Protocol/Protocol17x.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp index e5a380f8a..fefcb9396 100644 --- a/src/Protocol/Protocol17x.cpp +++ b/src/Protocol/Protocol17x.cpp @@ -1,4 +1,3 @@ - // Protocol17x.cpp /* @@ -124,7 +123,7 @@ void cProtocol172::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, cha void cProtocol172::SendBlockBreakAnim(int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) { cPacketizer Pkt(*this, 0x25); // Block Break Animation packet - Pkt.WriteInt(a_EntityID); + Pkt.WriteVarInt(a_EntityID); Pkt.WriteInt(a_BlockX); Pkt.WriteInt(a_BlockY); Pkt.WriteInt(a_BlockZ); From aa61f55b743a8ecf3cd8e1f99e1d9a0308f6d014 Mon Sep 17 00:00:00 2001 From: andrew Date: Tue, 21 Jan 2014 15:58:17 +0200 Subject: [PATCH 06/12] Scoreboard protocol support --- src/ClientHandle.cpp | 30 +++++++++++ src/ClientHandle.h | 4 ++ src/Entities/Player.cpp | 17 ++++--- src/Protocol/Protocol.h | 4 ++ src/Protocol/Protocol125.h | 7 ++- src/Protocol/Protocol15x.cpp | 54 +++++++++++++++++++- src/Protocol/Protocol15x.h | 3 ++ src/Protocol/Protocol17x.cpp | 40 +++++++++++++++ src/Protocol/Protocol17x.h | 3 ++ src/Protocol/ProtocolRecognizer.cpp | 32 +++++++++++- src/Protocol/ProtocolRecognizer.h | 3 ++ src/Scoreboard.cpp | 66 ++++++++++++++++++++++--- src/Scoreboard.h | 13 ++++- src/Server.cpp | 2 +- src/World.cpp | 57 ++++++++++++++++++++- src/World.h | 5 +- src/WorldStorage/ScoreboardSerializer.h | 4 ++ 17 files changed, 321 insertions(+), 23 deletions(-) diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index faf583fbb..30d1bdaa4 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -267,6 +267,9 @@ void cClientHandle::Authenticate(void) m_Player->Initialize(World); m_State = csAuthenticated; + // Query player team + m_Player->UpdateTeam(); + cRoot::Get()->GetPluginManager()->CallHookPlayerSpawned(*m_Player); } @@ -2105,6 +2108,33 @@ void cClientHandle::SendExperienceOrb(const cExpOrb & a_ExpOrb) +void cClientHandle::SendScoreboardObjective(const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) +{ + m_Protocol->SendScoreboardObjective(a_Name, a_DisplayName, a_Mode); +} + + + + + +void cClientHandle::SendScoreUpdate(const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode) +{ + m_Protocol->SendScoreUpdate(a_Objective, a_Player, a_Score, a_Mode); +} + + + + + +void cClientHandle::SendDisplayObjective(const AString & a_Objective, cScoreboard::eDisplaySlot a_Display) +{ + m_Protocol->SendDisplayObjective(a_Objective, a_Display); +} + + + + + void cClientHandle::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) { m_Protocol->SendSoundEffect(a_SoundName, a_SrcX, a_SrcY, a_SrcZ, a_Volume, a_Pitch); diff --git a/src/ClientHandle.h b/src/ClientHandle.h index 373ca9e2e..636934f6f 100644 --- a/src/ClientHandle.h +++ b/src/ClientHandle.h @@ -16,6 +16,7 @@ #include "OSSupport/SocketThreads.h" #include "ChunkDef.h" #include "ByteBuffer.h" +#include "Scoreboard.h" @@ -125,6 +126,9 @@ public: void SendRespawn (void); void SendExperience (void); void SendExperienceOrb (const cExpOrb & a_ExpOrb); + void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode); + void SendScoreUpdate (const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode); + void SendDisplayObjective (const AString & a_Objective, cScoreboard::eDisplaySlot a_Display); void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch); // a_Src coords are Block * 8 void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data); void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock); diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index 285aefd25..c6b24a465 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -111,8 +111,6 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName) m_LastJumpHeight = (float)(GetPosY()); m_LastGroundHeight = (float)(GetPosY()); m_Stance = GetPosY() + 1.62; - - // UpdateTeam(); cRoot::Get()->GetServer()->PlayerCreated(this); } @@ -867,10 +865,10 @@ void cPlayer::KilledBy(cEntity * a_Killer) } } IncrementCounter (GetName()); - cScoreboard* Scoreboard = m_World->GetScoreBoard(); + cScoreboard & Scoreboard = m_World->GetScoreBoard(); // Update scoreboard objectives - Scoreboard->ForEachObjectiveWith(cObjective::E_TYPE_DEATH_COUNT, IncrementCounter); + Scoreboard.ForEachObjectiveWith(cObjective::E_TYPE_DEATH_COUNT, IncrementCounter); } @@ -977,9 +975,16 @@ void cPlayer::SetTeam(cTeam * a_Team) cTeam * cPlayer::UpdateTeam(void) { - cScoreboard * Scoreboard = m_World->GetScoreBoard(); + if (m_World == NULL) + { + SetTeam(NULL); + } + else + { + cScoreboard & Scoreboard = m_World->GetScoreBoard(); - m_Team = Scoreboard->QueryPlayerTeam(GetName()); + SetTeam(Scoreboard.QueryPlayerTeam(GetName())); + } return m_Team; } diff --git a/src/Protocol/Protocol.h b/src/Protocol/Protocol.h index 3293da32c..0a213f476 100644 --- a/src/Protocol/Protocol.h +++ b/src/Protocol/Protocol.h @@ -12,6 +12,7 @@ #include "../Defines.h" #include "../Endianness.h" +#include "../Scoreboard.h" @@ -92,6 +93,9 @@ public: virtual void SendRespawn (void) = 0; virtual void SendExperience (void) = 0; virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) = 0; + virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) = 0; + virtual void SendScoreUpdate (const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode) = 0; + virtual void SendDisplayObjective (const AString & a_Objective, cScoreboard::eDisplaySlot a_Display) = 0; virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) = 0; // a_Src coords are Block * 8 virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) = 0; virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) = 0; diff --git a/src/Protocol/Protocol125.h b/src/Protocol/Protocol125.h index d0e5c9428..fb08fb120 100644 --- a/src/Protocol/Protocol125.h +++ b/src/Protocol/Protocol125.h @@ -68,6 +68,9 @@ public: virtual void SendRespawn (void) override; virtual void SendExperience (void) override; virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override; + virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) override {} // This protocol doesn't support such message + virtual void SendScoreUpdate (const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode) override {} // This protocol doesn't support such message + virtual void SendDisplayObjective (const AString & a_Objective, cScoreboard::eDisplaySlot a_Display) override {} // This protocol doesn't support such message virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override; // a_Src coords are Block * 8 virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override; virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) override; @@ -82,8 +85,8 @@ public: virtual void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) override; virtual void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) override; virtual void SendWeather (eWeather a_Weather) override; - virtual void SendWholeInventory (const cWindow & a_Window) override; - virtual void SendWindowClose (const cWindow & a_Window) override; + virtual void SendWholeInventory (const cWindow & a_Window) override; + virtual void SendWindowClose (const cWindow & a_Window) override; virtual void SendWindowOpen (const cWindow & a_Window) override; virtual void SendWindowProperty (const cWindow & a_Window, short a_Property, short a_Value) override; diff --git a/src/Protocol/Protocol15x.cpp b/src/Protocol/Protocol15x.cpp index 0f1e59f10..c33aec7d5 100644 --- a/src/Protocol/Protocol15x.cpp +++ b/src/Protocol/Protocol15x.cpp @@ -35,8 +35,11 @@ Implements the 1.5.x protocol classes: enum { - PACKET_WINDOW_OPEN = 0x64, - PACKET_PARTICLE_EFFECT = 0x3F, + PACKET_WINDOW_OPEN = 0x64, + PACKET_PARTICLE_EFFECT = 0x3F, + PACKET_SCOREBOARD_OBJECTIVE = 0x3B, + PACKET_SCORE_UPDATE = 0x3C, + PACKET_DISPLAY_OBJECTIVE = 0x3D } ; @@ -97,6 +100,53 @@ void cProtocol150::SendParticleEffect(const AString & a_ParticleName, float a_Sr +void cProtocol150::SendScoreboardObjective(const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_SCOREBOARD_OBJECTIVE); + WriteString(a_Name); + WriteString(a_DisplayName); + WriteByte(a_Mode); + Flush(); +} + + + + + +void cProtocol150::SendScoreUpdate(const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_SCORE_UPDATE); + WriteString(a_Player); + WriteByte(a_Mode); + + if (a_Mode != 1) + { + WriteString(a_Objective); + WriteInt((int) a_Score); + } + + Flush(); +} + + + + + +void cProtocol150::SendDisplayObjective(const AString & a_Objective, cScoreboard::eDisplaySlot a_Display) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_DISPLAY_OBJECTIVE); + WriteByte((int) a_Display); + WriteString(a_Objective); + Flush(); +} + + + + + int cProtocol150::ParseWindowClick(void) { HANDLE_PACKET_READ(ReadChar, char, WindowID); diff --git a/src/Protocol/Protocol15x.h b/src/Protocol/Protocol15x.h index 0074b3a83..0d171a67c 100644 --- a/src/Protocol/Protocol15x.h +++ b/src/Protocol/Protocol15x.h @@ -30,6 +30,9 @@ public: virtual void SendWindowOpen (const cWindow & a_Window) override; virtual void SendParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount) override; + virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) override; + virtual void SendScoreUpdate (const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode) override; + virtual void SendDisplayObjective (const AString & a_Objective, cScoreboard::eDisplaySlot a_Display) override; virtual int ParseWindowClick(void); } ; diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp index 5b3a79555..d5ed1a0aa 100644 --- a/src/Protocol/Protocol17x.cpp +++ b/src/Protocol/Protocol17x.cpp @@ -705,6 +705,46 @@ void cProtocol172::SendExperienceOrb(const cExpOrb & a_ExpOrb) +void cProtocol172::SendScoreboardObjective(const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) +{ + cPacketizer Pkt(*this, 0x3b); + Pkt.WriteString(a_Name); + Pkt.WriteString(a_DisplayName); + Pkt.WriteByte(a_Mode); +} + + + + + +void cProtocol172::SendScoreUpdate(const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode) +{ + cPacketizer Pkt(*this, 0x3c); + Pkt.WriteString(a_Player); + Pkt.WriteByte(a_Mode); + + if (a_Mode != 1) + { + Pkt.WriteString(a_Objective); + Pkt.WriteInt((int) a_Score); + } +} + + + + + +void cProtocol172::SendDisplayObjective(const AString & a_Objective, cScoreboard::eDisplaySlot a_Display) +{ + cPacketizer Pkt(*this, 0x3d); + Pkt.WriteByte((int) a_Display); + Pkt.WriteString(a_Objective); +} + + + + + void cProtocol172::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) // a_Src coords are Block * 8 { cPacketizer Pkt(*this, 0x29); // Sound Effect packet diff --git a/src/Protocol/Protocol17x.h b/src/Protocol/Protocol17x.h index 07dba834b..bbbf820a6 100644 --- a/src/Protocol/Protocol17x.h +++ b/src/Protocol/Protocol17x.h @@ -92,6 +92,9 @@ public: virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override; // a_Src coords are Block * 8 virtual void SendExperience (void) override; virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override; + virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) override; + virtual void SendScoreUpdate (const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode) override; + virtual void SendDisplayObjective (const AString & a_Objective, cScoreboard::eDisplaySlot a_Display) override; virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override; virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) override; virtual void SendSpawnMob (const cMonster & a_Mob) override; diff --git a/src/Protocol/ProtocolRecognizer.cpp b/src/Protocol/ProtocolRecognizer.cpp index a21f4f042..04db2a995 100644 --- a/src/Protocol/ProtocolRecognizer.cpp +++ b/src/Protocol/ProtocolRecognizer.cpp @@ -526,6 +526,36 @@ void cProtocolRecognizer::SendExperienceOrb(const cExpOrb & a_ExpOrb) +void cProtocolRecognizer::SendScoreboardObjective(const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendScoreboardObjective(a_Name, a_DisplayName, a_Mode); +} + + + + + +void cProtocolRecognizer::SendScoreUpdate(const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendScoreUpdate(a_Objective, a_Player, a_Score, a_Mode); +} + + + + + +void cProtocolRecognizer::SendDisplayObjective(const AString & a_Objective, cScoreboard::eDisplaySlot a_Display) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendDisplayObjective(a_Objective, a_Display); +} + + + + + void cProtocolRecognizer::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) { ASSERT(m_Protocol != NULL); @@ -797,7 +827,7 @@ bool cProtocolRecognizer::TryRecognizeLengthlessProtocol(void) } switch (ch) { - case PROTO_VERSION_1_3_2: + case PROTO_VERSION_1_3_2: { m_Protocol = new cProtocol132(m_Client); return true; diff --git a/src/Protocol/ProtocolRecognizer.h b/src/Protocol/ProtocolRecognizer.h index e94f4cde8..0b811f4c6 100644 --- a/src/Protocol/ProtocolRecognizer.h +++ b/src/Protocol/ProtocolRecognizer.h @@ -103,6 +103,9 @@ public: virtual void SendRespawn (void) override; virtual void SendExperience (void) override; virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override; + virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) override; + virtual void SendScoreUpdate (const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode) override; + virtual void SendDisplayObjective (const AString & a_Objective, cScoreboard::eDisplaySlot a_Display) override; virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override; virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override; virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) override; diff --git a/src/Scoreboard.cpp b/src/Scoreboard.cpp index 3ddf146a6..7fa1eab99 100644 --- a/src/Scoreboard.cpp +++ b/src/Scoreboard.cpp @@ -6,6 +6,7 @@ #include "Globals.h" #include "Scoreboard.h" +#include "World.h" @@ -72,11 +73,13 @@ cObjective::eType cObjective::StringToType(const AString & a_Name) -cObjective::cObjective(const AString & a_Name, const AString & a_DisplayName, cObjective::eType a_Type) +cObjective::cObjective(const AString & a_Name, const AString & a_DisplayName, cObjective::eType a_Type, cWorld * a_World) : m_DisplayName(a_DisplayName) , m_Name(a_Name) , m_Type(a_Type) -{} + , m_World(a_World) +{ +} @@ -84,6 +87,11 @@ cObjective::cObjective(const AString & a_Name, const AString & a_DisplayName, cO void cObjective::Reset(void) { + for (cScoreMap::iterator it = m_Scores.begin(); it != m_Scores.end(); ++it) + { + m_World->BroadcastScoreUpdate(m_Name, it->first, 0, 1); + } + m_Scores.clear(); } @@ -112,6 +120,8 @@ cObjective::Score cObjective::GetScore(const AString & a_Name) const void cObjective::SetScore(const AString & a_Name, cObjective::Score a_Score) { m_Scores[a_Name] = a_Score; + + m_World->BroadcastScoreUpdate(m_Name, a_Name, a_Score, 0); } @@ -121,6 +131,8 @@ void cObjective::SetScore(const AString & a_Name, cObjective::Score a_Score) void cObjective::ResetScore(const AString & a_Name) { m_Scores.erase(a_Name); + + m_World->BroadcastScoreUpdate(m_Name, a_Name, 0, 1); } @@ -132,7 +144,7 @@ cObjective::Score cObjective::AddScore(const AString & a_Name, cObjective::Score // TODO 2014-01-19 xdot: Potential optimization - Reuse iterator Score NewScore = m_Scores[a_Name] + a_Delta; - m_Scores[a_Name] = NewScore; + SetScore(a_Name, NewScore); return NewScore; } @@ -146,7 +158,7 @@ cObjective::Score cObjective::SubScore(const AString & a_Name, cObjective::Score // TODO 2014-01-19 xdot: Potential optimization - Reuse iterator Score NewScore = m_Scores[a_Name] - a_Delta; - m_Scores[a_Name] = NewScore; + SetScore(a_Name, NewScore); return NewScore; } @@ -155,6 +167,17 @@ cObjective::Score cObjective::SubScore(const AString & a_Name, cObjective::Score +void cObjective::SetDisplayName(const AString & a_Name) +{ + m_DisplayName = a_Name; + + m_World->BroadcastScoreboardObjective(m_Name, m_DisplayName, 2); +} + + + + + cTeam::cTeam(const AString & a_Name, const AString & a_DisplayName, const AString & a_Prefix, const AString & a_Suffix) : m_AllowsFriendlyFire(true) @@ -215,7 +238,7 @@ unsigned int cTeam::GetNumPlayers(void) const -cScoreboard::cScoreboard() +cScoreboard::cScoreboard(cWorld * a_World) : m_World(a_World) { for (int i = 0; i < (int) E_DISPLAY_SLOT_COUNT; ++i) { @@ -229,11 +252,21 @@ cScoreboard::cScoreboard() cObjective* cScoreboard::RegisterObjective(const AString & a_Name, const AString & a_DisplayName, cObjective::eType a_Type) { - cObjective Objective(a_Name, a_DisplayName, a_Type); + cObjective Objective(a_Name, a_DisplayName, a_Type, m_World); std::pair Status = m_Objectives.insert(cNamedObjective(a_Name, Objective)); - return Status.second ? &Status.first->second : NULL; + if (Status.second) + { + ASSERT(m_World != NULL); + m_World->BroadcastScoreboardObjective(a_Name, a_DisplayName, 0); + + return &Status.first->second; + } + else + { + return NULL; + } } @@ -242,6 +275,8 @@ cObjective* cScoreboard::RegisterObjective(const AString & a_Name, const AString bool cScoreboard::RemoveObjective(const AString & a_Name) { + cCSLock Lock(m_CSObjectives); + cObjectiveMap::iterator it = m_Objectives.find(a_Name); if (it == m_Objectives.end()) @@ -251,6 +286,9 @@ bool cScoreboard::RemoveObjective(const AString & a_Name) m_Objectives.erase(it); + ASSERT(m_World != NULL); + m_World->BroadcastScoreboardObjective(it->second.GetName(), it->second.GetDisplayName(), 1); + return true; } @@ -260,6 +298,8 @@ bool cScoreboard::RemoveObjective(const AString & a_Name) cObjective * cScoreboard::GetObjective(const AString & a_Name) { + cCSLock Lock(m_CSObjectives); + cObjectiveMap::iterator it = m_Objectives.find(a_Name); if (it == m_Objectives.end()) @@ -294,6 +334,8 @@ cTeam * cScoreboard::RegisterTeam( bool cScoreboard::RemoveTeam(const AString & a_Name) { + cCSLock Lock(m_CSTeams); + cTeamMap::iterator it = m_Teams.find(a_Name); if (it == m_Teams.end()) @@ -312,6 +354,8 @@ bool cScoreboard::RemoveTeam(const AString & a_Name) cTeam * cScoreboard::GetTeam(const AString & a_Name) { + cCSLock Lock(m_CSTeams); + cTeamMap::iterator it = m_Teams.find(a_Name); if (it == m_Teams.end()) @@ -330,6 +374,8 @@ cTeam * cScoreboard::GetTeam(const AString & a_Name) cTeam * cScoreboard::QueryPlayerTeam(const AString & a_Name) { + cCSLock Lock(m_CSTeams); + for (cTeamMap::iterator it = m_Teams.begin(); it != m_Teams.end(); ++it) { if (it->second.HasPlayer(a_Name)) @@ -352,6 +398,10 @@ void cScoreboard::SetDisplay(const AString & a_Objective, eDisplaySlot a_Slot) cObjective * Objective = GetObjective(a_Objective); m_Display[a_Slot] = Objective; + + ASSERT(m_World != NULL); + m_World->BroadcastDisplayObjective(Objective ? a_Objective : "", a_Slot); + } @@ -371,6 +421,8 @@ cObjective * cScoreboard::GetObjectiveIn(eDisplaySlot a_Slot) void cScoreboard::ForEachObjectiveWith(cObjective::eType a_Type, cObjectiveCallback& a_Callback) { + cCSLock Lock(m_CSObjectives); + for (cObjectiveMap::iterator it = m_Objectives.begin(); it != m_Objectives.end(); ++it) { if (it->second.GetType() == a_Type) diff --git a/src/Scoreboard.h b/src/Scoreboard.h index 2ce614de7..b92642a9a 100644 --- a/src/Scoreboard.h +++ b/src/Scoreboard.h @@ -14,6 +14,7 @@ class cObjective; +class cWorld; typedef cItemCallback cObjectiveCallback; @@ -54,7 +55,7 @@ public: public: - cObjective(const AString & a_Name, const AString & a_DisplayName, eType a_Type); + cObjective(const AString & a_Name, const AString & a_DisplayName, eType a_Type, cWorld * a_World); eType GetType(void) const { return m_Type; } @@ -79,6 +80,8 @@ public: /// Subtracts a_Delta and returns the new score Score SubScore(const AString & a_Name, Score a_Delta); + void SetDisplayName(const AString & a_Name); + private: typedef std::pair cTrackedPlayer; @@ -92,6 +95,8 @@ private: eType m_Type; + cWorld * m_World; + friend class cScoreboardSerializer; }; @@ -180,7 +185,7 @@ public: public: - cScoreboard(); + cScoreboard(cWorld * a_World); /// Registers a new scoreboard objective, returns the cObjective instance, NULL on name collision cObjective * RegisterObjective(const AString & a_Name, const AString & a_DisplayName, cObjective::eType a_Type); @@ -223,10 +228,14 @@ private: typedef std::map cTeamMap; // TODO 2014-01-19 xdot: Potential optimization - Sort objectives based on type + cCriticalSection m_CSObjectives; cObjectiveMap m_Objectives; + cCriticalSection m_CSTeams; cTeamMap m_Teams; + cWorld * m_World; + cObjective* m_Display[E_DISPLAY_SLOT_COUNT]; friend class cScoreboardSerializer; diff --git a/src/Server.cpp b/src/Server.cpp index 5280270b9..eb76fcaeb 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -501,7 +501,7 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallbac } } #endif - + if (cPluginManager::Get()->ExecuteConsoleCommand(split, a_Output)) { a_Output.Finished(); diff --git a/src/World.cpp b/src/World.cpp index 8e7b6171c..fb20e2242 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -242,7 +242,8 @@ cWorld::cWorld(const AString & a_WorldName) : m_Weather(eWeather_Sunny), m_WeatherInterval(24000), // Guaranteed 1 day of sunshine at server start :) m_GeneratorCallbacks(*this), - m_TickThread(*this) + m_TickThread(*this), + m_Scoreboard(this) { LOGD("cWorld::cWorld(\"%s\")", a_WorldName.c_str()); @@ -1982,6 +1983,60 @@ void cWorld::BroadcastRemoveEntityEffect(const cEntity & a_Entity, int a_EffectI +void cWorld::BroadcastScoreboardObjective(const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) +{ + cCSLock Lock(m_CSPlayers); + for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + cClientHandle * ch = (*itr)->GetClientHandle(); + if ((ch == NULL) || !ch->IsLoggedIn() || ch->IsDestroyed()) + { + continue; + } + ch->SendScoreboardObjective(a_Name, a_DisplayName, a_Mode); + } +} + + + + + +void cWorld::BroadcastScoreUpdate(const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode) +{ + cCSLock Lock(m_CSPlayers); + for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + cClientHandle * ch = (*itr)->GetClientHandle(); + if ((ch == NULL) || !ch->IsLoggedIn() || ch->IsDestroyed()) + { + continue; + } + ch->SendScoreUpdate(a_Objective, a_Player, a_Score, a_Mode); + } +} + + + + + +void cWorld::BroadcastDisplayObjective(const AString & a_Objective, cScoreboard::eDisplaySlot a_Display) +{ + cCSLock Lock(m_CSPlayers); + for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + cClientHandle * ch = (*itr)->GetClientHandle(); + if ((ch == NULL) || !ch->IsLoggedIn() || ch->IsDestroyed()) + { + continue; + } + ch->SendDisplayObjective(a_Objective, a_Display); + } +} + + + + + void cWorld::BroadcastSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude) { m_ChunkMap->BroadcastSoundEffect(a_SoundName, a_SrcX, a_SrcY, a_SrcZ, a_Volume, a_Pitch, a_Exclude); diff --git a/src/World.h b/src/World.h index 1dcbac8e4..51c3af8cb 100644 --- a/src/World.h +++ b/src/World.h @@ -179,6 +179,9 @@ public: void BroadcastParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount, cClientHandle * a_Exclude = NULL); void BroadcastPlayerListItem (const cPlayer & a_Player, bool a_IsOnline, const cClientHandle * a_Exclude = NULL); void BroadcastRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID, const cClientHandle * a_Exclude = NULL); + void BroadcastScoreboardObjective(const AString & a_Name, const AString & a_DisplayName, Byte a_Mode); + void BroadcastScoreUpdate (const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode); + void BroadcastDisplayObjective (const AString & a_Objective, cScoreboard::eDisplaySlot a_Display); void BroadcastSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude = NULL); // tolua_export a_Src coords are Block * 8 void BroadcastSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data, const cClientHandle * a_Exclude = NULL); // tolua_export void BroadcastSpawnEntity (cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); @@ -516,7 +519,7 @@ public: const AString & GetIniFileName(void) const {return m_IniFileName; } /// Returns the associated scoreboard instance - cScoreboard* GetScoreBoard(void) { return &m_Scoreboard; } + cScoreboard & GetScoreBoard(void) { return m_Scoreboard; } // tolua_end diff --git a/src/WorldStorage/ScoreboardSerializer.h b/src/WorldStorage/ScoreboardSerializer.h index 2a4e0767e..048fa3ab4 100644 --- a/src/WorldStorage/ScoreboardSerializer.h +++ b/src/WorldStorage/ScoreboardSerializer.h @@ -24,6 +24,7 @@ class cScoreboard; class cScoreboardSerializer { public: + cScoreboardSerializer(const AString & a_WorldName, cScoreboard* a_ScoreBoard); /// Try to load the scoreboard @@ -32,6 +33,7 @@ public: /// Try to save the scoreboard bool Save(void); + private: void SaveScoreboardToNBT(cFastNBTWriter & a_Writer); @@ -41,6 +43,8 @@ private: cScoreboard* m_ScoreBoard; AString m_Path; + + } ; From fa4750f015f1fed0937ba9fe80fc183c27d9e929 Mon Sep 17 00:00:00 2001 From: andrew Date: Tue, 21 Jan 2014 19:43:13 +0200 Subject: [PATCH 07/12] Scoreboard SendTo() --- src/ClientHandle.cpp | 3 ++ src/Protocol/Protocol17x.cpp | 6 ++-- src/Scoreboard.cpp | 54 ++++++++++++++++++++++++++++++++++-- src/Scoreboard.h | 17 ++++++++++++ 4 files changed, 74 insertions(+), 6 deletions(-) diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index 30d1bdaa4..b06dbc84a 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -270,6 +270,9 @@ void cClientHandle::Authenticate(void) // Query player team m_Player->UpdateTeam(); + // Send scoreboard data + World->GetScoreBoard().SendTo(*this); + cRoot::Get()->GetPluginManager()->CallHookPlayerSpawned(*m_Player); } diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp index d5ed1a0aa..926be6027 100644 --- a/src/Protocol/Protocol17x.cpp +++ b/src/Protocol/Protocol17x.cpp @@ -707,7 +707,7 @@ void cProtocol172::SendExperienceOrb(const cExpOrb & a_ExpOrb) void cProtocol172::SendScoreboardObjective(const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) { - cPacketizer Pkt(*this, 0x3b); + cPacketizer Pkt(*this, 0x3B); Pkt.WriteString(a_Name); Pkt.WriteString(a_DisplayName); Pkt.WriteByte(a_Mode); @@ -719,7 +719,7 @@ void cProtocol172::SendScoreboardObjective(const AString & a_Name, const AString void cProtocol172::SendScoreUpdate(const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode) { - cPacketizer Pkt(*this, 0x3c); + cPacketizer Pkt(*this, 0x3C); Pkt.WriteString(a_Player); Pkt.WriteByte(a_Mode); @@ -736,7 +736,7 @@ void cProtocol172::SendScoreUpdate(const AString & a_Objective, const AString & void cProtocol172::SendDisplayObjective(const AString & a_Objective, cScoreboard::eDisplaySlot a_Display) { - cPacketizer Pkt(*this, 0x3d); + cPacketizer Pkt(*this, 0x3D); Pkt.WriteByte((int) a_Display); Pkt.WriteString(a_Objective); } diff --git a/src/Scoreboard.cpp b/src/Scoreboard.cpp index 7fa1eab99..e6812d3d7 100644 --- a/src/Scoreboard.cpp +++ b/src/Scoreboard.cpp @@ -7,6 +7,7 @@ #include "Scoreboard.h" #include "World.h" +#include "ClientHandle.h" @@ -178,6 +179,20 @@ void cObjective::SetDisplayName(const AString & a_Name) +void cObjective::SendTo(cClientHandle & a_Client) +{ + a_Client.SendScoreboardObjective(m_Name, m_DisplayName, 0); + + for (cScoreMap::const_iterator it = m_Scores.begin(); it != m_Scores.end(); ++it) + { + a_Client.SendScoreUpdate(m_Name, it->first, it->second, 0); + } +} + + + + + cTeam::cTeam(const AString & a_Name, const AString & a_DisplayName, const AString & a_Prefix, const AString & a_Suffix) : m_AllowsFriendlyFire(true) @@ -397,11 +412,19 @@ void cScoreboard::SetDisplay(const AString & a_Objective, eDisplaySlot a_Slot) cObjective * Objective = GetObjective(a_Objective); - m_Display[a_Slot] = Objective; + SetDisplay(Objective, a_Slot); +} + + + + + +void cScoreboard::SetDisplay(cObjective * a_Objective, eDisplaySlot a_Slot) +{ + m_Display[a_Slot] = a_Objective; ASSERT(m_World != NULL); - m_World->BroadcastDisplayObjective(Objective ? a_Objective : "", a_Slot); - + m_World->BroadcastDisplayObjective(a_Objective ? a_Objective->GetName() : "", a_Slot); } @@ -440,6 +463,31 @@ void cScoreboard::ForEachObjectiveWith(cObjective::eType a_Type, cObjectiveCallb +void cScoreboard::SendTo(cClientHandle & a_Client) +{ + cCSLock Lock(m_CSObjectives); + + for (cObjectiveMap::iterator it = m_Objectives.begin(); it != m_Objectives.end(); ++it) + { + it->second.SendTo(a_Client); + } + + for (int i = 0; i < (int) E_DISPLAY_SLOT_COUNT; ++i) + { + // Avoid race conditions + cObjective * Objective = m_Display[i]; + + if (Objective) + { + a_Client.SendDisplayObjective(Objective->GetName(), (eDisplaySlot) i); + } + } +} + + + + + unsigned int cScoreboard::GetNumObjectives(void) const { return m_Objectives.size(); diff --git a/src/Scoreboard.h b/src/Scoreboard.h index b92642a9a..11b456739 100644 --- a/src/Scoreboard.h +++ b/src/Scoreboard.h @@ -22,10 +22,13 @@ typedef cItemCallback cObjectiveCallback; +// tolua_begin class cObjective { public: + // tolua_end + typedef int Score; enum eType @@ -82,6 +85,9 @@ public: void SetDisplayName(const AString & a_Name); + /// Send this objective to the specified client + void SendTo(cClientHandle & a_Client); + private: typedef std::pair cTrackedPlayer; @@ -105,10 +111,13 @@ private: +// tolua_begin class cTeam { public: + // tolua_end + cTeam( const AString & a_Name, const AString & a_DisplayName, const AString & a_Prefix, const AString & a_Suffix @@ -169,10 +178,13 @@ private: +// tolua_begin class cScoreboard { public: + // tolua_end + enum eDisplaySlot { E_DISPLAY_SLOT_LIST = 0, @@ -209,11 +221,16 @@ public: void SetDisplay(const AString & a_Objective, eDisplaySlot a_Slot); + void SetDisplay(cObjective * a_Objective, eDisplaySlot a_Slot); + cObjective * GetObjectiveIn(eDisplaySlot a_Slot); /// Execute callback for each objective with the specified type void ForEachObjectiveWith(cObjective::eType a_Type, cObjectiveCallback& a_Callback); + /// Send this scoreboard to the specified client + void SendTo(cClientHandle & a_Client); + unsigned int GetNumObjectives(void) const; unsigned int GetNumTeams(void) const; From 2a018cfa49e0a85d2984f6daf6ee3c3372bdafda Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Tue, 21 Jan 2014 22:59:08 +0100 Subject: [PATCH 08/12] Implemented cPluginManager:CallPlugin() API. This function supersedes cPlugin:Call(), is safer to use in regards to multithreading and once again removes the need for the cPlugin class being exported at all. --- MCServer/Plugins/APIDump/APIDesc.lua | 3 +- MCServer/Plugins/Debuggers/Debuggers.lua | 35 +++- src/Bindings/LuaState.cpp | 199 +++++++++++++++++++++-- src/Bindings/LuaState.h | 143 +++++++++------- src/Bindings/ManualBindings.cpp | 198 +++++++++++----------- src/Bindings/PluginLua.cpp | 34 ++++ src/Bindings/PluginLua.h | 42 +++-- src/Bindings/PluginManager.cpp | 15 ++ src/Bindings/PluginManager.h | 56 ++++--- 9 files changed, 508 insertions(+), 217 deletions(-) diff --git a/MCServer/Plugins/APIDump/APIDesc.lua b/MCServer/Plugins/APIDump/APIDesc.lua index 347299a50..07345d51e 100644 --- a/MCServer/Plugins/APIDump/APIDesc.lua +++ b/MCServer/Plugins/APIDump/APIDesc.lua @@ -1667,7 +1667,7 @@ a_Player:OpenWindow(Window); ]], Functions = { - Call = { Params = "Function name, [All the parameters divided with commas]", Notes = "This function allows you to call a function from another plugin. It can only use pass: integers, booleans, strings and usertypes (cPlayer, cEntity, cCuboid, etc.)." }, + Call = { Params = "Function name, [All the parameters divided with commas]", Notes = "(OBSOLETE) This function allows you to call a function from another plugin. It can only use pass: integers, booleans, strings and usertypes (cPlayer, cEntity, cCuboid, etc.).

This function is obsolete and unsafe, use {{cPluginManager}}:CallPlugin() instead!" }, GetDirectory = { Return = "string", Notes = "Returns the name of the folder where the plugin's files are. (APIDump)" }, GetLocalDirectory = { Notes = "OBSOLETE use GetLocalFolder instead." }, GetLocalFolder = { Return = "string", Notes = "Returns the path where the plugin's files are. (Plugins/APIDump)" }, @@ -1719,6 +1719,7 @@ cPluginManager.AddHook(cPluginManager.HOOK_CHAT, OnChatMessage); { Params = "Command, Callback, HelpString", Return = "", Notes = "(STATIC) Binds a console command with the specified callback function and help string. By common convention, providing an empty string for HelpString will hide the command from the \"help\" console command." }, { Params = "Command, Callback, HelpString", Return = "", Notes = "Binds a console command with the specified callback function and help string. By common convention, providing an empty string for HelpString will hide the command from the \"help\" console command." }, }, + CallPlugin = { Params = "PluginName, FunctionName, [FunctionArgs...]", Return = "[FunctionRets]", Notes = "(STATIC) Calls the specified function in the specified plugin, passing all the given arguments to it. If it succeeds, it returns all the values returned by that function. If it fails, returns no value at all. Note that only strings, numbers, bools, nils and classes can be used for parameters and return values; tables and functions cannot be copied across plugins." }, DisablePlugin = { Params = "PluginName", Return = "bool", Notes = "Disables a plugin specified by its name. Returns true if the plugin was disabled, false if it wasn't found or wasn't active." }, ExecuteCommand = { Params = "{{cPlayer|Player}}, CommandStr", Return = "bool", Notes = "Executes the command as if given by the specified Player. Checks permissions. Returns true if executed." }, FindPlugins = { Params = "", Return = "", Notes = "Refreshes the list of plugins to include all folders inside the Plugins folder (potentially new disabled plugins)" }, diff --git a/MCServer/Plugins/Debuggers/Debuggers.lua b/MCServer/Plugins/Debuggers/Debuggers.lua index 2d2d2736d..624261cbf 100644 --- a/MCServer/Plugins/Debuggers/Debuggers.lua +++ b/MCServer/Plugins/Debuggers/Debuggers.lua @@ -64,7 +64,8 @@ function Initialize(Plugin) -- TestBlockAreas(); -- TestSQLiteBindings(); -- TestExpatBindings(); - + TestPluginCalls(); + return true end; @@ -72,6 +73,38 @@ end; +function TestPluginCalls() + -- In order to test the inter-plugin communication, we're going to call Core's ReturnColorFromChar() function + -- It is a rather simple function that doesn't need any tables as its params and returns a value, too + -- Note the signature: function ReturnColorFromChar( Split, char ) ... return cChatColog.Gray ... end + -- The Split parameter should be a table, but it is not used in that function anyway, + -- so we can get away with passing nil to it. + + -- Use the old, deprecated and unsafe method: + local Core = cPluginManager:Get():GetPlugin("Core") + if (Core ~= nil) then + LOGINFO("Calling Core::ReturnColorFromChar() the old-fashioned way...") + local Gray = Core:Call("ReturnColorFromChar", nil, "8") + if (Gray ~= cChatColor.Gray) then + LOGWARNING("Call failed, exp " .. cChatColor.Gray .. ", got " .. (Gray or "")) + else + LOGINFO("Call succeeded") + end + end + + -- Use the new method: + LOGINFO("Calling Core::ReturnColorFromChar() the recommended way...") + local Gray = cPluginManager:CallPlugin("Core", "ReturnColorFromChar", nil, "8") + if (Gray ~= cChatColor.Gray) then + LOGWARNING("Call failed, exp " .. cChatColor.Gray .. ", got " .. (Gray or "")) + else + LOGINFO("Call succeeded") + end +end + + + + function TestBlockAreas() LOG("Testing block areas..."); diff --git a/src/Bindings/LuaState.cpp b/src/Bindings/LuaState.cpp index bfee1d037..2fca7142c 100644 --- a/src/Bindings/LuaState.cpp +++ b/src/Bindings/LuaState.cpp @@ -235,7 +235,7 @@ bool cLuaState::PushFunction(const char * a_FunctionName) if (!lua_isfunction(m_LuaState, -1)) { LOGWARNING("Error in %s: Could not find function %s()", m_SubsystemName.c_str(), a_FunctionName); - lua_pop(m_LuaState, 1); + lua_pop(m_LuaState, 2); return false; } m_CurrentFunctionName.assign(a_FunctionName); @@ -258,7 +258,7 @@ bool cLuaState::PushFunction(int a_FnRef) lua_rawgeti(m_LuaState, LUA_REGISTRYINDEX, a_FnRef); // same as lua_getref() if (!lua_isfunction(m_LuaState, -1)) { - lua_pop(m_LuaState, 1); + lua_pop(m_LuaState, 2); return false; } m_CurrentFunctionName = ""; @@ -282,7 +282,7 @@ bool cLuaState::PushFunction(const cTableRef & a_TableRef) if (!lua_istable(m_LuaState, -1)) { // Not a table, bail out - lua_pop(m_LuaState, 1); + lua_pop(m_LuaState, 2); return false; } lua_getfield(m_LuaState, -1, a_TableRef.GetFnName()); @@ -742,6 +742,10 @@ bool cLuaState::CallFunction(int a_NumResults) } m_NumCurrentFunctionArgs = -1; m_CurrentFunctionName.clear(); + + // Remove the error handler from the stack: + lua_remove(m_LuaState, -a_NumResults - 1); + return true; } @@ -1025,21 +1029,184 @@ void cLuaState::LogStackTrace(lua_State * a_LuaState) AString cLuaState::GetTypeText(int a_StackPos) { - int Type = lua_type(m_LuaState, a_StackPos); - switch (Type) + return lua_typename(m_LuaState, lua_type(m_LuaState, a_StackPos)); +} + + + + + +int cLuaState::CallFunctionWithForeignParams( + const AString & a_FunctionName, + cLuaState & a_SrcLuaState, + int a_SrcParamStart, + int a_SrcParamEnd +) +{ + ASSERT(IsValid()); + ASSERT(a_SrcLuaState.IsValid()); + + // Store the stack position before any changes + int OldTop = lua_gettop(m_LuaState); + + // Push the function to call, including the error handler: + if (!PushFunction(a_FunctionName.c_str())) { - case LUA_TNONE: return "TNONE"; - case LUA_TNIL: return "TNIL"; - case LUA_TBOOLEAN: return "TBOOLEAN"; - case LUA_TLIGHTUSERDATA: return "TLIGHTUSERDATA"; - case LUA_TNUMBER: return "TNUMBER"; - case LUA_TSTRING: return "TSTRING"; - case LUA_TTABLE: return "TTABLE"; - case LUA_TFUNCTION: return "TFUNCTION"; - case LUA_TUSERDATA: return "TUSERDATA"; - case LUA_TTHREAD: return "TTHREAD"; + LOGWARNING("Function '%s' not found", a_FunctionName.c_str()); + lua_pop(m_LuaState, 2); + return -1; } - return Printf("Unknown (%d)", Type); + + // Copy the function parameters to the target state + if (CopyStackFrom(a_SrcLuaState, a_SrcParamStart, a_SrcParamEnd) < 0) + { + // Something went wrong, fix the stack and exit + lua_pop(m_LuaState, 2); + return -1; + } + + // Call the function, with an error handler: + int s = lua_pcall(m_LuaState, a_SrcParamEnd - a_SrcParamStart + 1, LUA_MULTRET, OldTop); + if (ReportErrors(s)) + { + LOGWARN("Error while calling function '%s' in '%s'", a_FunctionName.c_str(), m_SubsystemName.c_str()); + // Fix the stack. + // We don't know how many values have been pushed, so just get rid of any that weren't there initially + int CurTop = lua_gettop(m_LuaState); + if (CurTop > OldTop) + { + lua_pop(m_LuaState, CurTop - OldTop); + } + return -1; + } + + // Reset the internal checking mechanisms: + m_NumCurrentFunctionArgs = -1; + + // Remove the error handler from the stack: + lua_remove(m_LuaState, OldTop + 1); + + // Return the number of return values: + return lua_gettop(m_LuaState) - OldTop; +} + + + + + +int cLuaState::CopyStackFrom(cLuaState & a_SrcLuaState, int a_SrcStart, int a_SrcEnd) +{ + /* + // DEBUG: + LOGD("Copying stack values from %d to %d", a_SrcStart, a_SrcEnd); + a_SrcLuaState.LogStack("Src stack before copying:"); + LogStack("Dst stack before copying:"); + */ + for (int i = a_SrcStart; i <= a_SrcEnd; ++i) + { + int t = lua_type(a_SrcLuaState, i); + switch (t) + { + case LUA_TNIL: + { + lua_pushnil(m_LuaState); + break; + } + case LUA_TSTRING: + { + AString s; + a_SrcLuaState.ToString(i, s); + Push(s); + break; + } + case LUA_TBOOLEAN: + { + bool b = (tolua_toboolean(a_SrcLuaState, i, false) != 0); + Push(b); + break; + } + case LUA_TNUMBER: + { + lua_Number d = tolua_tonumber(a_SrcLuaState, i, 0); + Push(d); + break; + } + case LUA_TUSERDATA: + { + // Get the class name: + const char * type = NULL; + if (lua_getmetatable(a_SrcLuaState, i) == 0) + { + LOGWARNING("%s: Unknown class in pos %d, cannot copy.", __FUNCTION__, i); + lua_pop(m_LuaState, i - a_SrcStart); + return -1; + } + lua_rawget(a_SrcLuaState, LUA_REGISTRYINDEX); // Stack +1 + type = lua_tostring(a_SrcLuaState, -1); + lua_pop(a_SrcLuaState, 1); // Stack -1 + + // Copy the value: + void * ud = tolua_touserdata(a_SrcLuaState, i, NULL); + tolua_pushusertype(m_LuaState, ud, type); + } + default: + { + LOGWARNING("%s: Unsupported value: '%s' at stack position %d. Can only copy numbers, strings, bools and classes!", + __FUNCTION__, lua_typename(a_SrcLuaState, t), i + ); + a_SrcLuaState.LogStack("Stack where copying failed:"); + lua_pop(m_LuaState, i - a_SrcStart); + return -1; + } + } + } + return a_SrcEnd - a_SrcStart + 1; +} + + + + + +void cLuaState::ToString(int a_StackPos, AString & a_String) +{ + size_t len; + const char * s = lua_tolstring(m_LuaState, a_StackPos, &len); + if (s != NULL) + { + a_String.assign(s, len); + } +} + + + + + +void cLuaState::LogStack(const char * a_Header) +{ + LogStack(m_LuaState, a_Header); +} + + + + + +void cLuaState::LogStack(lua_State * a_LuaState, const char * a_Header) +{ + LOGD((a_Header != NULL) ? a_Header : "Lua C API Stack contents:"); + for (int i = lua_gettop(a_LuaState); i >= 0; i--) + { + AString Value; + int Type = lua_type(a_LuaState, i); + switch (Type) + { + case LUA_TBOOLEAN: Value.assign((lua_toboolean(a_LuaState, i) != 0) ? "true" : "false"); break; + case LUA_TLIGHTUSERDATA: Printf(Value, "%p", lua_touserdata(a_LuaState, i)); break; + case LUA_TNUMBER: Printf(Value, "%f", (double)lua_tonumber(a_LuaState, i)); break; + case LUA_TSTRING: Printf(Value, "%s", lua_tostring(a_LuaState, i)); break; + default: break; + } + LOGD(" Idx %d: type %d (%s) %s", i, Type, lua_typename(a_LuaState, Type), Value.c_str()); + } // for i - stack idx } diff --git a/src/Bindings/LuaState.h b/src/Bindings/LuaState.h index f8b67f5cd..dda45bb28 100644 --- a/src/Bindings/LuaState.h +++ b/src/Bindings/LuaState.h @@ -60,23 +60,23 @@ class cBlockEntity; -/// Encapsulates a Lua state and provides some syntactic sugar for common operations +/** Encapsulates a Lua state and provides some syntactic sugar for common operations */ class cLuaState { public: - /// Used for storing references to object in the global registry + /** Used for storing references to object in the global registry */ class cRef { public: - /// Creates a reference in the specified LuaState for object at the specified StackPos + /** Creates a reference in the specified LuaState for object at the specified StackPos */ cRef(cLuaState & a_LuaState, int a_StackPos); ~cRef(); - /// Returns true if the reference is valid + /** Returns true if the reference is valid */ bool IsValid(void) const {return (m_Ref != LUA_REFNIL); } - /// Allows to use this class wherever an int (i. e. ref) is to be used + /** Allows to use this class wherever an int (i. e. ref) is to be used */ operator int(void) const { return m_Ref; } protected: @@ -102,7 +102,7 @@ public: } ; - /// A dummy class that's used only to delimit function args from return values for cLuaState::Call() + /** A dummy class that's used only to delimit function args from return values for cLuaState::Call() */ class cRet { } ; @@ -123,22 +123,22 @@ public: ~cLuaState(); - /// Allows this object to be used in the same way as a lua_State *, for example in the LuaLib functions + /** Allows this object to be used in the same way as a lua_State *, for example in the LuaLib functions */ operator lua_State * (void) { return m_LuaState; } - /// Creates the m_LuaState, if not closed already. This state will be automatically closed in the destructor + /** Creates the m_LuaState, if not closed already. This state will be automatically closed in the destructor */ void Create(void); - /// Closes the m_LuaState, if not closed already + /** Closes the m_LuaState, if not closed already */ void Close(void); - /// Attaches the specified state. Operations will be carried out on this state, but it will not be closed in the destructor + /** Attaches the specified state. Operations will be carried out on this state, but it will not be closed in the destructor */ void Attach(lua_State * a_State); - /// Detaches a previously attached state. + /** Detaches a previously attached state. */ void Detach(void); - /// Returns true if the m_LuaState is valid + /** Returns true if the m_LuaState is valid */ bool IsValid(void) const { return (m_LuaState != NULL); } /** Loads the specified file @@ -147,7 +147,7 @@ public: */ bool LoadFile(const AString & a_FileName); - /// Returns true if a_FunctionName is a valid Lua function that can be called + /** Returns true if a_FunctionName is a valid Lua function that can be called */ bool HasFunction(const char * a_FunctionName); // Push a value onto the stack @@ -182,7 +182,7 @@ public: void Push(cHopperEntity * a_Hopper); void Push(cBlockEntity * a_BlockEntity); - /// Call any 0-param 0-return Lua function in a single line: + /** Call any 0-param 0-return Lua function in a single line: */ template bool Call(FnT a_FnName) { @@ -193,7 +193,7 @@ public: return CallFunction(0); } - /// Call any 1-param 0-return Lua function in a single line: + /** Call any 1-param 0-return Lua function in a single line: */ template< typename FnT, typename ArgT1 @@ -208,7 +208,7 @@ public: return CallFunction(0); } - /// Call any 2-param 0-return Lua function in a single line: + /** Call any 2-param 0-return Lua function in a single line: */ template< typename FnT, typename ArgT1, typename ArgT2 > @@ -223,7 +223,7 @@ public: return CallFunction(0); } - /// Call any 3-param 0-return Lua function in a single line: + /** Call any 3-param 0-return Lua function in a single line: */ template< typename FnT, typename ArgT1, typename ArgT2, typename ArgT3 > @@ -239,7 +239,7 @@ public: return CallFunction(0); } - /// Call any 0-param 1-return Lua function in a single line: + /** Call any 0-param 1-return Lua function in a single line: */ template< typename FnT, typename RetT1 > @@ -259,12 +259,13 @@ public: return true; } - /// Call any 1-param 1-return Lua function in a single line: + /** Call any 1-param 1-return Lua function in a single line: */ template< typename FnT, typename ArgT1, typename RetT1 > bool Call(FnT a_FnName, ArgT1 a_Arg1, const cRet & a_Mark, RetT1 & a_Ret1) { + int InitialTop = lua_gettop(m_LuaState); UNUSED(a_Mark); if (!PushFunction(a_FnName)) { @@ -277,10 +278,11 @@ public: } GetReturn(-1, a_Ret1); lua_pop(m_LuaState, 1); + ASSERT(InitialTop == lua_gettop(m_LuaState)); return true; } - /// Call any 2-param 1-return Lua function in a single line: + /** Call any 2-param 1-return Lua function in a single line: */ template< typename FnT, typename ArgT1, typename ArgT2, typename RetT1 > @@ -302,7 +304,7 @@ public: return true; } - /// Call any 3-param 1-return Lua function in a single line: + /** Call any 3-param 1-return Lua function in a single line: */ template< typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename RetT1 > @@ -325,7 +327,7 @@ public: return true; } - /// Call any 4-param 1-return Lua function in a single line: + /** Call any 4-param 1-return Lua function in a single line: */ template< typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename RetT1 > @@ -349,7 +351,7 @@ public: return true; } - /// Call any 5-param 1-return Lua function in a single line: + /** Call any 5-param 1-return Lua function in a single line: */ template< typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename RetT1 > @@ -374,7 +376,7 @@ public: return true; } - /// Call any 6-param 1-return Lua function in a single line: + /** Call any 6-param 1-return Lua function in a single line: */ template< typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename ArgT6, typename RetT1 @@ -401,7 +403,7 @@ public: return true; } - /// Call any 7-param 1-return Lua function in a single line: + /** Call any 7-param 1-return Lua function in a single line: */ template< typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename ArgT6, typename ArgT7, typename RetT1 @@ -429,7 +431,7 @@ public: return true; } - /// Call any 8-param 1-return Lua function in a single line: + /** Call any 8-param 1-return Lua function in a single line: */ template< typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename ArgT6, typename ArgT7, typename ArgT8, typename RetT1 @@ -458,7 +460,7 @@ public: return true; } - /// Call any 9-param 1-return Lua function in a single line: + /** Call any 9-param 1-return Lua function in a single line: */ template< typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename ArgT6, typename ArgT7, typename ArgT8, typename ArgT9, typename RetT1 @@ -488,7 +490,7 @@ public: return true; } - /// Call any 10-param 1-return Lua function in a single line: + /** Call any 10-param 1-return Lua function in a single line: */ template< typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename ArgT6, typename ArgT7, typename ArgT8, typename ArgT9, typename ArgT10, typename RetT1 @@ -519,7 +521,7 @@ public: return true; } - /// Call any 1-param 2-return Lua function in a single line: + /** Call any 1-param 2-return Lua function in a single line: */ template< typename FnT, typename ArgT1, typename RetT1, typename RetT2 > @@ -541,7 +543,7 @@ public: return true; } - /// Call any 2-param 2-return Lua function in a single line: + /** Call any 2-param 2-return Lua function in a single line: */ template< typename FnT, typename ArgT1, typename ArgT2, typename RetT1, typename RetT2 > @@ -564,7 +566,7 @@ public: return true; } - /// Call any 3-param 2-return Lua function in a single line: + /** Call any 3-param 2-return Lua function in a single line: */ template< typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename RetT1, typename RetT2 @@ -589,7 +591,7 @@ public: return true; } - /// Call any 4-param 2-return Lua function in a single line: + /** Call any 4-param 2-return Lua function in a single line: */ template< typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename RetT1, typename RetT2 @@ -615,7 +617,7 @@ public: return true; } - /// Call any 5-param 2-return Lua function in a single line: + /** Call any 5-param 2-return Lua function in a single line: */ template< typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename RetT1, typename RetT2 @@ -642,7 +644,7 @@ public: return true; } - /// Call any 6-param 2-return Lua function in a single line: + /** Call any 6-param 2-return Lua function in a single line: */ template< typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename ArgT6, @@ -671,7 +673,7 @@ public: return true; } - /// Call any 7-param 2-return Lua function in a single line: + /** Call any 7-param 2-return Lua function in a single line: */ template< typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename ArgT6, typename ArgT7, @@ -701,7 +703,7 @@ public: return true; } - /// Call any 7-param 3-return Lua function in a single line: + /** Call any 7-param 3-return Lua function in a single line: */ template< typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename ArgT6, typename ArgT7, @@ -732,7 +734,7 @@ public: return true; } - /// Call any 8-param 3-return Lua function in a single line: + /** Call any 8-param 3-return Lua function in a single line: */ template< typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename ArgT6, typename ArgT7, typename ArgT8, @@ -764,7 +766,7 @@ public: return true; } - /// Call any 9-param 5-return Lua function in a single line: + /** Call any 9-param 5-return Lua function in a single line: */ template< typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename ArgT6, typename ArgT7, typename ArgT8, typename ArgT9, @@ -800,46 +802,71 @@ public: } - /// Returns true if the specified parameters on the stack are of the specified usertable type; also logs warning if not. Used for static functions + /** Returns true if the specified parameters on the stack are of the specified usertable type; also logs warning if not. Used for static functions */ bool CheckParamUserTable(int a_StartParam, const char * a_UserTable, int a_EndParam = -1); - /// Returns true if the specified parameters on the stack are of the specified usertype; also logs warning if not. Used for regular functions + /** Returns true if the specified parameters on the stack are of the specified usertype; also logs warning if not. Used for regular functions */ bool CheckParamUserType(int a_StartParam, const char * a_UserType, int a_EndParam = -1); - /// Returns true if the specified parameters on the stack are tables; also logs warning if not + /** Returns true if the specified parameters on the stack are tables; also logs warning if not */ bool CheckParamTable(int a_StartParam, int a_EndParam = -1); - /// Returns true if the specified parameters on the stack are numbers; also logs warning if not + /** Returns true if the specified parameters on the stack are numbers; also logs warning if not */ bool CheckParamNumber(int a_StartParam, int a_EndParam = -1); - /// Returns true if the specified parameters on the stack are strings; also logs warning if not + /** Returns true if the specified parameters on the stack are strings; also logs warning if not */ bool CheckParamString(int a_StartParam, int a_EndParam = -1); - /// Returns true if the specified parameters on the stack are functions; also logs warning if not + /** Returns true if the specified parameters on the stack are functions; also logs warning if not */ bool CheckParamFunction(int a_StartParam, int a_EndParam = -1); - /// Returns true if the specified parameter on the stack is nil (indicating an end-of-parameters) + /** Returns true if the specified parameter on the stack is nil (indicating an end-of-parameters) */ bool CheckParamEnd(int a_Param); - /// If the status is nonzero, prints the text on the top of Lua stack and returns true + /** If the status is nonzero, prints the text on the top of Lua stack and returns true */ bool ReportErrors(int status); - /// If the status is nonzero, prints the text on the top of Lua stack and returns true + /** If the status is nonzero, prints the text on the top of Lua stack and returns true */ static bool ReportErrors(lua_State * a_LuaState, int status); - /// Logs all items in the current stack trace to the server console + /** Logs all items in the current stack trace to the server console */ void LogStackTrace(void); - /// Logs all items in the current stack trace to the server console + /** Logs all items in the current stack trace to the server console */ static void LogStackTrace(lua_State * a_LuaState); - /// Returns the type of the item on the specified position in the stack + /** Returns the type of the item on the specified position in the stack */ AString GetTypeText(int a_StackPos); + /** Calls the function specified by its name, with arguments copied off the foreign state. + If successful, keeps the return values on the stack and returns their number. + If unsuccessful, returns a negative number and keeps the stack position unchanged. */ + int CallFunctionWithForeignParams( + const AString & a_FunctionName, + cLuaState & a_SrcLuaState, + int a_SrcParamStart, + int a_SrcParamEnd + ); + + /** Copies objects on the stack from the specified state. + Only numbers, bools, strings and userdatas are copied. + If successful, returns the number of objects copied. + If failed, returns a negative number and rewinds the stack position. */ + int CopyStackFrom(cLuaState & a_SrcLuaState, int a_SrcStart, int a_SrcEnd); + + /** Reads the value at the specified stack position as a string and sets it to a_String. */ + void ToString(int a_StackPos, AString & a_String); + + /** Logs all the elements' types on the API stack, with an optional header for the listing. */ + void LogStack(const char * a_Header); + + /** Logs all the elements' types on the API stack, with an optional header for the listing. */ + static void LogStack(lua_State * a_LuaState, const char * a_Header = NULL); + protected: lua_State * m_LuaState; - /// If true, the state is owned by this object and will be auto-Closed. False => attached state + /** If true, the state is owned by this object and will be auto-Closed. False => attached state */ bool m_IsOwned; /** The subsystem name is used for reporting errors to the console, it is either "plugin %s" or "LuaScript" @@ -847,10 +874,10 @@ protected: */ AString m_SubsystemName; - /// Name of the currently pushed function (for the Push / Call chain) + /** Name of the currently pushed function (for the Push / Call chain) */ AString m_CurrentFunctionName; - /// Number of arguments currently pushed (for the Push / Call chain) + /** Number of arguments currently pushed (for the Push / Call chain) */ int m_NumCurrentFunctionArgs; @@ -869,19 +896,19 @@ protected: */ bool PushFunction(const cTableRef & a_TableRef); - /// Pushes a usertype of the specified class type onto the stack + /** Pushes a usertype of the specified class type onto the stack */ void PushUserType(void * a_Object, const char * a_Type); - /// Retrieve value returned at a_StackPos, if it is a valid bool. If not, a_ReturnedVal is unchanged + /** Retrieve value returned at a_StackPos, if it is a valid bool. If not, a_ReturnedVal is unchanged */ void GetReturn(int a_StackPos, bool & a_ReturnedVal); - /// Retrieve value returned at a_StackPos, if it is a valid string. If not, a_ReturnedVal is unchanged + /** Retrieve value returned at a_StackPos, if it is a valid string. If not, a_ReturnedVal is unchanged */ void GetReturn(int a_StackPos, AString & a_ReturnedVal); - /// Retrieve value returned at a_StackPos, if it is a valid number. If not, a_ReturnedVal is unchanged + /** Retrieve value returned at a_StackPos, if it is a valid number. If not, a_ReturnedVal is unchanged */ void GetReturn(int a_StackPos, int & a_ReturnedVal); - /// Retrieve value returned at a_StackPos, if it is a valid number. If not, a_ReturnedVal is unchanged + /** Retrieve value returned at a_StackPos, if it is a valid number. If not, a_ReturnedVal is unchanged */ void GetReturn(int a_StackPos, double & a_ReturnedVal); /** diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp index 2206dd371..f1160f941 100644 --- a/src/Bindings/ManualBindings.cpp +++ b/src/Bindings/ManualBindings.cpp @@ -1540,6 +1540,85 @@ static int tolua_cPluginManager_BindConsoleCommand(lua_State * L) +static int tolua_cPluginManager_CallPlugin(lua_State * tolua_S) +{ + /* + Function signature: + cPluginManager:CallPlugin("PluginName", "FunctionName", args...) + */ + + // Check the parameters: + cLuaState L(tolua_S); + if ( + !L.CheckParamUserTable(1, "cPluginManager") || + !L.CheckParamString(2, 3)) + { + return 0; + } + + // Retrieve the plugin name and function name + AString PluginName, FunctionName; + L.ToString(2, PluginName); + L.ToString(3, FunctionName); + if (PluginName.empty() || FunctionName.empty()) + { + LOGWARNING("cPluginManager:CallPlugin(): Invalid plugin name or function name"); + L.LogStackTrace(); + return 0; + } + + // If requesting calling the current plugin, refuse: + cPluginLua * ThisPlugin = GetLuaPlugin(L); + if (ThisPlugin == NULL) + { + return 0; + } + if (ThisPlugin->GetName() == PluginName) + { + LOGWARNING("cPluginManager::CallPlugin(): Calling self is not implemented (why would it?)"); + L.LogStackTrace(); + return 0; + } + + // Call the destination plugin using a plugin callback: + class cCallback : + public cPluginManager::cPluginCallback + { + public: + int m_NumReturns; + + cCallback(const AString & a_FunctionName, cLuaState & a_SrcLuaState) : + m_FunctionName(a_FunctionName), + m_SrcLuaState(a_SrcLuaState), + m_NumReturns(0) + { + } + protected: + const AString & m_FunctionName; + cLuaState & m_SrcLuaState; + + virtual bool Item(cPlugin * a_Plugin) override + { + m_NumReturns = ((cPluginLua *)a_Plugin)->CallFunctionFromForeignState( + m_FunctionName, m_SrcLuaState, 4, lua_gettop(m_SrcLuaState) + ); + return true; + } + } Callback(FunctionName, L); + if (!cPluginManager::Get()->DoWithPlugin(PluginName, Callback)) + { + // TODO 2014_01_20 _X: This might be too much logging, plugins cannot know if other plugins are loaded (async) + LOGWARNING("cPluginManager::CallPlugin: No such plugin name (\"%s\")", PluginName.c_str()); + L.LogStackTrace(); + return 0; + } + return Callback.m_NumReturns; +} + + + + + static int tolua_cPlayer_GetGroups(lua_State* tolua_S) { cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); @@ -1734,112 +1813,28 @@ static int tolua_cPluginLua_AddTab(lua_State* tolua_S) -// Perhaps use this as well for copying tables https://github.com/keplerproject/rings/pull/1 -static int copy_lua_values(lua_State * a_Source, lua_State * a_Destination, int i, int top) + +static int tolua_cPlugin_Call(lua_State * tolua_S) { - for(; i <= top; ++i ) - { - int t = lua_type(a_Source, i); - switch (t) { - case LUA_TSTRING: /* strings */ - { - const char * s = lua_tostring(a_Source, i); - LOGD("%i push string: %s", i, s); - tolua_pushstring(a_Destination, s); - } - break; - case LUA_TBOOLEAN: /* booleans */ - { - int b = tolua_toboolean(a_Source, i, false); - LOGD("%i push bool: %i", i, b); - tolua_pushboolean(a_Destination, b ); - } - break; - case LUA_TNUMBER: /* numbers */ - { - lua_Number d = tolua_tonumber(a_Source, i, 0); - LOGD("%i push number: %0.2f", i, d); - tolua_pushnumber(a_Destination, d ); - } - break; - case LUA_TUSERDATA: - { - const char * type = 0; - if (lua_getmetatable(a_Source,i)) - { - lua_rawget(a_Source, LUA_REGISTRYINDEX); - type = lua_tostring(a_Source, -1); - lua_pop(a_Source, 1); // Pop.. something?! I don't knooow~~ T_T - } - - // don't need tolua_tousertype we already have the type - void * ud = tolua_touserdata(a_Source, i, 0); - LOGD("%i push usertype: %p of type '%s'", i, ud, type); - if( type == 0 ) - { - LOGERROR("Call(): Something went wrong when trying to get usertype name!"); - return 0; - } - tolua_pushusertype(a_Destination, ud, type); - } - break; - default: /* other values */ - LOGERROR("Call(): Unsupported value: '%s'. Can only use numbers and strings!", lua_typename(a_Source, t)); - return 0; - } - } - return 1; -} - - - - - -static int tolua_cPlugin_Call(lua_State* tolua_S) -{ - cPluginLua * self = (cPluginLua *) tolua_tousertype(tolua_S, 1, 0); - lua_State* targetState = self->GetLuaState(); - int targetTop = lua_gettop(targetState); - - int top = lua_gettop(tolua_S); - LOGD("total in stack: %i", top ); - - std::string funcName = tolua_tostring(tolua_S, 2, ""); - LOGD("Func name: %s", funcName.c_str() ); - - lua_getglobal(targetState, funcName.c_str()); - if(!lua_isfunction(targetState,-1)) - { - LOGWARN("Error could not find function '%s' in plugin '%s'", funcName.c_str(), self->GetName().c_str() ); - lua_pop(targetState,1); - return 0; - } - - if( copy_lua_values(tolua_S, targetState, 3, top) == 0 ) // Start at 3 because 1 and 2 are the plugin and function name respectively - { - // something went wrong, exit - return 0; - } + cLuaState L(tolua_S); - int s = lua_pcall(targetState, top - 2, LUA_MULTRET, 0); - if (cLuaState::ReportErrors(targetState, s)) + // Log the obsoletion warning: + LOGWARNING("cPlugin:Call() is obsolete and unsafe, use cPluginManager:CallPlugin() instead."); + L.LogStackTrace(); + + // Retrieve the params: plugin and the function name to call + cPluginLua * TargetPlugin = (cPluginLua *) tolua_tousertype(tolua_S, 1, 0); + AString FunctionName = tolua_tostring(tolua_S, 2, ""); + + // Call the function: + int NumReturns = TargetPlugin->CallFunctionFromForeignState(FunctionName, L, 3, lua_gettop(L)); + if (NumReturns < 0) { - LOGWARN("Error while calling function '%s' in plugin '%s'", funcName.c_str(), self->GetName().c_str() ); + LOGWARNING("cPlugin::Call() failed to call destination function"); + L.LogStackTrace(); return 0; } - - int nresults = lua_gettop(targetState) - targetTop; - LOGD("num results: %i", nresults); - int ttop = lua_gettop(targetState); - if( copy_lua_values(targetState, tolua_S, targetTop+1, ttop) == 0 ) // Start at targetTop+1 and I have no idea why xD - { - // something went wrong, exit - return 0; - } - - lua_pop(targetState, nresults); // I have no idea what I'm doing, but it works - - return nresults; + return NumReturns; } @@ -2305,6 +2300,7 @@ void ManualBindings::Bind(lua_State * tolua_S) tolua_function(tolua_S, "AddHook", tolua_cPluginManager_AddHook); tolua_function(tolua_S, "BindCommand", tolua_cPluginManager_BindCommand); tolua_function(tolua_S, "BindConsoleCommand", tolua_cPluginManager_BindConsoleCommand); + tolua_function(tolua_S, "CallPlugin", tolua_cPluginManager_CallPlugin); tolua_function(tolua_S, "ForEachCommand", tolua_cPluginManager_ForEachCommand); tolua_function(tolua_S, "ForEachConsoleCommand", tolua_cPluginManager_ForEachConsoleCommand); tolua_function(tolua_S, "GetAllPlugins", tolua_cPluginManager_GetAllPlugins); diff --git a/src/Bindings/PluginLua.cpp b/src/Bindings/PluginLua.cpp index 4c4664815..1d8c4c6ed 100644 --- a/src/Bindings/PluginLua.cpp +++ b/src/Bindings/PluginLua.cpp @@ -1469,6 +1469,40 @@ bool cPluginLua::AddHookRef(int a_HookType, int a_FnRefIdx) +int cPluginLua::CallFunctionFromForeignState( + const AString & a_FunctionName, + cLuaState & a_ForeignState, + int a_ParamStart, + int a_ParamEnd +) +{ + cCSLock Lock(m_CriticalSection); + + // Call the function: + int NumReturns = m_LuaState.CallFunctionWithForeignParams(a_FunctionName, a_ForeignState, a_ParamStart, a_ParamEnd); + if (NumReturns < 0) + { + // The call has failed, an error has already been output to the log, so just silently bail out with the same error + return NumReturns; + } + + // Copy all the return values: + int Top = lua_gettop(m_LuaState); + int res = a_ForeignState.CopyStackFrom(m_LuaState, Top - NumReturns + 1, Top); + + // Remove the return values off this stack: + if (NumReturns > 0) + { + lua_pop(m_LuaState, NumReturns); + } + + return res; +} + + + + + AString cPluginLua::HandleWebRequest(const HTTPRequest * a_Request ) { cCSLock Lock(m_CriticalSection); diff --git a/src/Bindings/PluginLua.h b/src/Bindings/PluginLua.h index c01f5ca89..c13f31424 100644 --- a/src/Bindings/PluginLua.h +++ b/src/Bindings/PluginLua.h @@ -105,7 +105,7 @@ public: virtual void ClearConsoleCommands(void) override; - /// Returns true if the plugin contains the function for the specified hook type, using the old-style registration (#121) + /** Returns true if the plugin contains the function for the specified hook type, using the old-style registration (#121) */ bool CanAddOldStyleHook(int a_HookType); // cWebPlugin override @@ -115,26 +115,26 @@ public: virtual AString HandleWebRequest(const HTTPRequest * a_Request ) override; bool AddWebTab(const AString & a_Title, lua_State * a_LuaState, int a_FunctionReference); // >> EXPORTED IN MANUALBINDINGS << - /// Binds the command to call the function specified by a Lua function reference. Simply adds to CommandMap. + /** Binds the command to call the function specified by a Lua function reference. Simply adds to CommandMap. */ void BindCommand(const AString & a_Command, int a_FnRef); - /// Binds the console command to call the function specified by a Lua function reference. Simply adds to CommandMap. + /** Binds the console command to call the function specified by a Lua function reference. Simply adds to CommandMap. */ void BindConsoleCommand(const AString & a_Command, int a_FnRef); cLuaState & GetLuaState(void) { return m_LuaState; } cCriticalSection & GetCriticalSection(void) { return m_CriticalSection; } - /// Removes a previously referenced object (luaL_unref()) + /** Removes a previously referenced object (luaL_unref()) */ void Unreference(int a_LuaRef); - /// Calls the plugin-specified "cLuaWindow closing" callback. Returns true only if the callback returned true + /** Calls the plugin-specified "cLuaWindow closing" callback. Returns true only if the callback returned true */ bool CallbackWindowClosing(int a_FnRef, cWindow & a_Window, cPlayer & a_Player, bool a_CanRefuse); - /// Calls the plugin-specified "cLuaWindow slot changed" callback. + /** Calls the plugin-specified "cLuaWindow slot changed" callback. */ void CallbackWindowSlotChanged(int a_FnRef, cWindow & a_Window, int a_SlotNum); - /// Returns the name of Lua function that should handle the specified hook type in the older (#121) API + /** Returns the name of Lua function that should handle the specified hook type in the older (#121) API */ static const char * GetHookFnName(int a_HookType); /** Adds a Lua function to be called for the specified hook. @@ -143,37 +143,47 @@ public: */ bool AddHookRef(int a_HookType, int a_FnRefIdx); + /** Calls a function in this plugin's LuaState with parameters copied over from a_ForeignState. + The values that the function returns are placed onto a_ForeignState. + Returns the number of values returned, if successful, or negative number on failure. */ + int CallFunctionFromForeignState( + const AString & a_FunctionName, + cLuaState & a_ForeignState, + int a_ParamStart, + int a_ParamEnd + ); + // The following templates allow calls to arbitrary Lua functions residing in the plugin: - /// Call a Lua function with 0 args + /** Call a Lua function with 0 args */ template bool Call(FnT a_Fn) { cCSLock Lock(m_CriticalSection); return m_LuaState.Call(a_Fn); } - /// Call a Lua function with 1 arg + /** Call a Lua function with 1 arg */ template bool Call(FnT a_Fn, ArgT0 a_Arg0) { cCSLock Lock(m_CriticalSection); return m_LuaState.Call(a_Fn, a_Arg0); } - /// Call a Lua function with 2 args + /** Call a Lua function with 2 args */ template bool Call(FnT a_Fn, ArgT0 a_Arg0, ArgT1 a_Arg1) { cCSLock Lock(m_CriticalSection); return m_LuaState.Call(a_Fn, a_Arg0, a_Arg1); } - /// Call a Lua function with 3 args + /** Call a Lua function with 3 args */ template bool Call(FnT a_Fn, ArgT0 a_Arg0, ArgT1 a_Arg1, ArgT2 a_Arg2) { cCSLock Lock(m_CriticalSection); return m_LuaState.Call(a_Fn, a_Arg0, a_Arg1, a_Arg2); } - /// Call a Lua function with 4 args + /** Call a Lua function with 4 args */ template bool Call(FnT a_Fn, ArgT0 a_Arg0, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3) { cCSLock Lock(m_CriticalSection); @@ -181,13 +191,13 @@ public: } protected: - /// Maps command name into Lua function reference + /** Maps command name into Lua function reference */ typedef std::map CommandMap; - /// Provides an array of Lua function references + /** Provides an array of Lua function references */ typedef std::vector cLuaRefs; - /// Maps hook types into arrays of Lua function references to call for each hook type + /** Maps hook types into arrays of Lua function references to call for each hook type */ typedef std::map cHookMap; cCriticalSection m_CriticalSection; @@ -198,7 +208,7 @@ protected: cHookMap m_HookMap; - /// Releases all Lua references and closes the LuaState + /** Releases all Lua references and closes the LuaState */ void Close(void); } ; // tolua_export diff --git a/src/Bindings/PluginManager.cpp b/src/Bindings/PluginManager.cpp index 24bb914d1..92c06487c 100644 --- a/src/Bindings/PluginManager.cpp +++ b/src/Bindings/PluginManager.cpp @@ -1736,6 +1736,21 @@ bool cPluginManager::IsValidHookType(int a_HookType) +bool cPluginManager::DoWithPlugin(const AString & a_PluginName, cPluginCallback & a_Callback) +{ + // TODO: Implement locking for plugins + PluginMap::iterator itr = m_Plugins.find(a_PluginName); + if (itr == m_Plugins.end()) + { + return false; + } + return a_Callback.Item(itr->second); +} + + + + + bool cPluginManager::AddPlugin(cPlugin * a_Plugin) { m_Plugins[a_Plugin->GetDirectory()] = a_Plugin; diff --git a/src/Bindings/PluginManager.h b/src/Bindings/PluginManager.h index 9936f5a35..d8b838d80 100644 --- a/src/Bindings/PluginManager.h +++ b/src/Bindings/PluginManager.h @@ -122,7 +122,7 @@ public: // tolua_export } ; // tolua_end - /// Used as a callback for enumerating bound commands + /** Used as a callback for enumerating bound commands */ class cCommandEnumCallback { public: @@ -132,7 +132,11 @@ public: // tolua_export virtual bool Command(const AString & a_Command, const cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString) = 0; } ; - /// Returns the instance of the Plugin Manager (there is only ever one) + /** The interface used for enumerating and extern-calling plugins */ + typedef cItemCallback cPluginCallback; + + + /** Returns the instance of the Plugin Manager (there is only ever one) */ static cPluginManager * Get(void); // tolua_export typedef std::map< AString, cPlugin * > PluginMap; @@ -143,7 +147,7 @@ public: // tolua_export void FindPlugins(); // tolua_export void ReloadPlugins(); // tolua_export - /// Adds the plugin to the list of plugins called for the specified hook type. Handles multiple adds as a single add + /** Adds the plugin to the list of plugins called for the specified hook type. Handles multiple adds as a single add */ void AddHook(cPlugin * a_Plugin, int a_HookType); unsigned int GetNumPlugins() const; // tolua_export @@ -206,46 +210,46 @@ public: // tolua_export bool DisablePlugin(const AString & a_PluginName); // tolua_export bool LoadPlugin (const AString & a_PluginName); // tolua_export - /// Removes all hooks the specified plugin has registered + /** Removes all hooks the specified plugin has registered */ void RemoveHooks(cPlugin * a_Plugin); - /// Removes the plugin from the internal structures and deletes its object. + /** Removes the plugin from the internal structures and deletes its object. */ void RemovePlugin(cPlugin * a_Plugin); - /// Removes all command bindings that the specified plugin has made + /** Removes all command bindings that the specified plugin has made */ void RemovePluginCommands(cPlugin * a_Plugin); - /// Binds a command to the specified plugin. Returns true if successful, false if command already bound. + /** Binds a command to the specified plugin. Returns true if successful, false if command already bound. */ bool BindCommand(const AString & a_Command, cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString); // Exported in ManualBindings.cpp, without the a_Plugin param - /// Calls a_Callback for each bound command, returns true if all commands were enumerated + /** Calls a_Callback for each bound command, returns true if all commands were enumerated */ bool ForEachCommand(cCommandEnumCallback & a_Callback); // Exported in ManualBindings.cpp - /// Returns true if the command is in the command map + /** Returns true if the command is in the command map */ bool IsCommandBound(const AString & a_Command); // tolua_export - /// Returns the permission needed for the specified command; empty string if command not found + /** Returns the permission needed for the specified command; empty string if command not found */ AString GetCommandPermission(const AString & a_Command); // tolua_export - /// Executes the command, as if it was requested by a_Player. Checks permissions first. Returns true if executed. + /** Executes the command, as if it was requested by a_Player. Checks permissions first. Returns true if executed. */ bool ExecuteCommand(cPlayer * a_Player, const AString & a_Command); // tolua_export - /// Executes the command, as if it was requested by a_Player. Permisssions are not checked. Returns true if executed (false if not found) + /** Executes the command, as if it was requested by a_Player. Permisssions are not checked. Returns true if executed (false if not found) */ bool ForceExecuteCommand(cPlayer * a_Player, const AString & a_Command); // tolua_export - /// Removes all console command bindings that the specified plugin has made + /** Removes all console command bindings that the specified plugin has made */ void RemovePluginConsoleCommands(cPlugin * a_Plugin); - /// Binds a console command to the specified plugin. Returns true if successful, false if command already bound. + /** Binds a console command to the specified plugin. Returns true if successful, false if command already bound. */ bool BindConsoleCommand(const AString & a_Command, cPlugin * a_Plugin, const AString & a_HelpString); // Exported in ManualBindings.cpp, without the a_Plugin param - /// Calls a_Callback for each bound console command, returns true if all commands were enumerated + /** Calls a_Callback for each bound console command, returns true if all commands were enumerated */ bool ForEachConsoleCommand(cCommandEnumCallback & a_Callback); // Exported in ManualBindings.cpp - /// Returns true if the console command is in the command map + /** Returns true if the console command is in the command map */ bool IsConsoleCommandBound(const AString & a_Command); // tolua_export - /// Executes the command split into a_Split, as if it was given on the console. Returns true if executed. Output is sent to the a_Output callback + /** Executes the command split into a_Split, as if it was given on the console. Returns true if executed. Output is sent to the a_Output callback */ bool ExecuteConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output); /** Appends all commands beginning with a_Text (case-insensitive) into a_Results. @@ -253,9 +257,13 @@ public: // tolua_export */ void TabCompleteCommand(const AString & a_Text, AStringVector & a_Results, cPlayer * a_Player); - /// Returns true if the specified hook type is within the allowed range + /** Returns true if the specified hook type is within the allowed range */ static bool IsValidHookType(int a_HookType); + /** Calls the specified callback with the plugin object of the specified plugin. + Returns false if plugin not found, and the value that the callback has returned otherwise. */ + bool DoWithPlugin(const AString & a_PluginName, cPluginCallback & a_Callback); + private: friend class cRoot; @@ -281,22 +289,22 @@ private: cPluginManager(); virtual ~cPluginManager(); - /// Reloads all plugins, defaulting to settings.ini for settings location + /** Reloads all plugins, defaulting to settings.ini for settings location */ void ReloadPluginsNow(void); - /// Reloads all plugins with a cIniFile object expected to be initialised to settings.ini + /** Reloads all plugins with a cIniFile object expected to be initialised to settings.ini */ void ReloadPluginsNow(cIniFile & a_SettingsIni); - /// Unloads all plugins + /** Unloads all plugins */ void UnloadPluginsNow(void); - /// Handles writing default plugins if 'Plugins' key not found using a cIniFile object expected to be intialised to settings.ini + /** Handles writing default plugins if 'Plugins' key not found using a cIniFile object expected to be intialised to settings.ini */ void InsertDefaultPlugins(cIniFile & a_SettingsIni); - /// Adds the plugin into the internal list of plugins and initializes it. If initialization fails, the plugin is removed again. + /** Adds the plugin into the internal list of plugins and initializes it. If initialization fails, the plugin is removed again. */ bool AddPlugin(cPlugin * a_Plugin); - /// Tries to match a_Command to the internal table of commands, if a match is found, the corresponding plugin is called. Returns true if the command is handled. + /** Tries to match a_Command to the internal table of commands, if a match is found, the corresponding plugin is called. Returns true if the command is handled. */ bool HandleCommand(cPlayer * a_Player, const AString & a_Command, bool a_ShouldCheckPermissions, bool & a_WasCommandForbidden); bool HandleCommand(cPlayer * a_Player, const AString & a_Command, bool a_ShouldCheckPermissions) { From f58d11fc1aa673bad1463718ff518ee518c78252 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Wed, 22 Jan 2014 10:18:58 +0100 Subject: [PATCH 09/12] InfoDump: Dump all referenced permissions. --- MCServer/Plugins/InfoDump.lua | 116 ++++++++++++++++++++++++++++++++-- 1 file changed, 112 insertions(+), 4 deletions(-) diff --git a/MCServer/Plugins/InfoDump.lua b/MCServer/Plugins/InfoDump.lua index df47d566b..c272f7818 100644 --- a/MCServer/Plugins/InfoDump.lua +++ b/MCServer/Plugins/InfoDump.lua @@ -17,22 +17,23 @@ if (_VERSION ~= "Lua 5.1") then return; end --- Try to load lfs, do not abort if not found +-- Try to load lfs, do not abort if not found ... local lfs, err = pcall( function() return require("lfs") end ); --- Rather, print a nice message with instructions: +-- ... rather, print a nice message with instructions: if not(lfs) then print([[ Cannot load LuaFileSystem Install it through luarocks by executing the following command: - sudo luarocks install luafilesystem + luarocks install luafilesystem (Windows) + sudo luarocks install luafilesystem (*nix) If you don't have luarocks installed, you need to install them using your OS's package manager, usually: - sudo apt-get install luarocks + sudo apt-get install luarocks (Ubuntu / Debian) On windows, a binary distribution can be downloaded from the LuaRocks homepage, http://luarocks.org/en/Download ]]); @@ -161,6 +162,21 @@ end +--- Returns a string specifying the command. +-- If a_Command is a simple string, returns a_Command colorized to blue +-- If a_Command is a table, expects members Name (full command name) and Params (command parameters), +-- colorizes command name blue and params green +local function GetCommandRefForum(a_Command) + if (type(a_Command) == "string") then + return "[color=blue]" .. a_Command .. "[/color]"; + end + return "[color=blue]" .. a_Command.Name .. "[/color] [color=green]" .. a_Command.Params .. "[/color]"; +end + + + + + --- Writes the specified command detailed help array to the output file, in the forum dump format local function WriteCommandParameterCombinationsForum(a_CmdString, a_ParameterCombinations, f) assert(type(a_CmdString) == "string"); @@ -270,6 +286,97 @@ end +--- Collects all permissions mentioned in the info, returns them as a sorted array +-- Each array item is {Name = "PermissionName", Info = { PermissionInfo }} +local function BuildPermissions(a_PluginInfo) + -- Collect all used permissions from Commands, reference the commands that use the permission: + local Permissions = a_PluginInfo.Permissions or {}; + local function CollectPermissions(a_CmdPrefix, a_Commands) + for cmd, info in pairs(a_Commands) do + CommandString = a_CmdPrefix .. cmd; + if ((info.Permission ~= nil) and (info.Permission ~= "")) then + -- Add the permission to the list of permissions: + local Permission = Permissions[info.Permission] or {}; + Permissions[info.Permission] = Permission; + -- Add the command to the list of commands using this permission: + Permission.CommandsAffected = Permission.CommandsAffected or {}; + table.insert(Permission.CommandsAffected, CommandString); + end + + -- Process the command param combinations for permissions: + local ParamCombinations = info.ParameterCombinations or {}; + for idx, comb in ipairs(ParamCombinations) do + if ((comb.Permission ~= nil) and (comb.Permission ~= "")) then + -- Add the permission to the list of permissions: + local Permission = Permissions[comb.Permission] or {}; + Permissions[info.Permission] = Permission; + -- Add the command to the list of commands using this permission: + Permission.CommandsAffected = Permission.CommandsAffected or {}; + table.insert(Permission.CommandsAffected, {Name = CommandString, Params = comb.Params}); + end + end + + -- Process subcommands: + if (info.Subcommands ~= nil) then + CollectPermissions(CommandString .. " ", info.Subcommands); + end + end + end + CollectPermissions("", a_PluginInfo.Commands); + + -- Copy the list of permissions to an array: + local PermArray = {}; + for name, perm in pairs(Permissions) do + table.insert(PermArray, {Name = name, Info = perm}); + end + + -- Sort the permissions array: + table.sort(PermArray, + function(p1, p2) + return (p1.Name < p2.Name); + end + ); + return PermArray; +end + + + + + +local function DumpPermissionsForum(a_PluginInfo, f) + -- Get the processed sorted array of permissions: + local Permissions = BuildPermissions(a_PluginInfo); + if ((Permissions == nil) or (#Permissions <= 0)) then + return; + end + + -- Dump the permissions: + f:write("\n[size=X-Large]Permissions[/size]\n[list]\n"); + for idx, perm in ipairs(Permissions) do + f:write(" - [color=red]", perm.Name, "[/color] - "); + f:write(perm.Info.Description or ""); + local CommandsAffected = perm.Info.CommandsAffected or {}; + if (#CommandsAffected > 0) then + f:write("\n[list] Commands affected:\n- "); + local Affects = {}; + for idx2, cmd in ipairs(CommandsAffected) do + table.insert(Affects, GetCommandRefForum(cmd)); + end + f:write(table.concat(Affects, "\n - ")); + f:write("\n[/list]"); + end + if (perm.Info.RecommendedGroups ~= nil) then + f:write("\n[list] Recommended groups: ", perm.Info.RecommendedGroups, "[/list]"); + end + f:write("\n"); + end + f:write("[/list]"); +end + + + + + local function DumpPluginInfoForum(a_PluginFolder, a_PluginInfo) -- Open the output file: local f, msg = io.open(a_PluginInfo.Name .. "_forum.txt", "w"); @@ -282,6 +389,7 @@ local function DumpPluginInfoForum(a_PluginFolder, a_PluginInfo) f:write(ForumizeString(a_PluginInfo.Description), "\n"); DumpAdditionalInfoForum(a_PluginInfo, f); DumpCommandsForum(a_PluginInfo, f); + DumpPermissionsForum(a_PluginInfo, f); f:close(); end From a6661e899a486badd8215bbc2fb04adc5542795c Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Wed, 22 Jan 2014 12:41:19 +0100 Subject: [PATCH 10/12] InfoDump: Can dump a single plugin without LFS. --- MCServer/Plugins/InfoDump.lua | 112 ++++++++++++++++++++-------------- 1 file changed, 65 insertions(+), 47 deletions(-) diff --git a/MCServer/Plugins/InfoDump.lua b/MCServer/Plugins/InfoDump.lua index c272f7818..6b1da7a77 100644 --- a/MCServer/Plugins/InfoDump.lua +++ b/MCServer/Plugins/InfoDump.lua @@ -2,10 +2,17 @@ -- InfoDump.lua --- Goes through all subfolders, loads Info.lua and dumps its g_PluginInfo into various text formats --- This is used for generating plugin documentation for the forum and for GitHub's INFO.md files +--[[ +Loads plugins' Info.lua and dumps its g_PluginInfo into various text formats +This is used for generating plugin documentation for the forum and for GitHub's INFO.md files --- This script requires LuaRocks with LFS installed, instructions are printed when this is not present. +This script can be used in two ways: +Executing "lua InfoDump.lua" will go through all subfolders and dump each Info.lua file it can find + Note that this mode of operation requires LuaRocks with LFS installed; instructions are printed + when the prerequisites are not met. +Executing "lua InfoDump.lua PluginName" will load the Info.lua file from PluginName's folder and dump +only that one plugin's documentation. This mode of operation doesn't require LuaRocks +--]] @@ -17,34 +24,6 @@ if (_VERSION ~= "Lua 5.1") then return; end --- Try to load lfs, do not abort if not found ... -local lfs, err = pcall( - function() - return require("lfs") - end -); - --- ... rather, print a nice message with instructions: -if not(lfs) then - print([[ -Cannot load LuaFileSystem -Install it through luarocks by executing the following command: - luarocks install luafilesystem (Windows) - sudo luarocks install luafilesystem (*nix) - -If you don't have luarocks installed, you need to install them using your OS's package manager, usually: - sudo apt-get install luarocks (Ubuntu / Debian) -On windows, a binary distribution can be downloaded from the LuaRocks homepage, http://luarocks.org/en/Download -]]); - - print("Original error text: ", err); - return; -end - --- We now know that LFS is present, get it normally: -lfs = require("lfs"); - - @@ -409,12 +388,6 @@ end --- Tries to load the g_PluginInfo from the plugin's Info.lua file -- Returns the g_PluginInfo table on success, or nil and error message on failure local function LoadPluginInfo(a_FolderName) - -- Check if the Info file is present at all: - local Attribs = lfs.attributes(a_FolderName .. "/Info.lua"); - if ((Attribs == nil) or (Attribs.mode ~= "file")) then - return nil; - end - -- Load and compile the Info file: local cfg, err = loadfile(a_FolderName .. "/Info.lua"); if (cfg == nil) then @@ -440,7 +413,7 @@ local function ProcessPluginFolder(a_FolderName) local PluginInfo, Msg = LoadPluginInfo(a_FolderName); if (PluginInfo == nil) then if (Msg ~= nil) then - print("\tCannot load Info.lua: " .. Msg); + print("\t" .. Msg); end return; end @@ -451,19 +424,64 @@ end -print("Processing plugin subfolders:"); -for fnam in lfs.dir(".") do - if ((fnam ~= ".") and (fnam ~= "..")) then - local Attributes = lfs.attributes(fnam); - if (Attributes ~= nil) then - if (Attributes.mode == "directory") then - print(fnam); - ProcessPluginFolder(fnam); - end +--- Tries to load LFS through LuaRocks, returns the LFS instance, or nil on error +local function LoadLFS() + -- Try to load lfs, do not abort if not found ... + local lfs, err = pcall( + function() + return require("lfs") end + ); + + -- ... rather, print a nice message with instructions: + if not(lfs) then + print([[ + Cannot load LuaFileSystem + Install it through luarocks by executing the following command: + luarocks install luafilesystem (Windows) + sudo luarocks install luafilesystem (*nix) + + If you don't have luarocks installed, you need to install them using your OS's package manager, usually: + sudo apt-get install luarocks (Ubuntu / Debian) + On windows, a binary distribution can be downloaded from the LuaRocks homepage, http://luarocks.org/en/Download + ]]); + + print("Original error text: ", err); + return nil; end + + -- We now know that LFS is present, get it normally: + return require("lfs"); end + +local Arg1 = ...; +if ((Arg1 ~= nil) and (Arg1 ~= "")) then + -- Called with a plugin folder name, export only that one + ProcessPluginFolder(Arg1) +else + -- Called without any arguments, process all subfolders: + local lfs = LoadLFS(); + if (lfs == nil) then + -- LFS not loaded, error has already been printed, just bail out + return; + end + print("Processing plugin subfolders:"); + for fnam in lfs.dir(".") do + if ((fnam ~= ".") and (fnam ~= "..")) then + local Attributes = lfs.attributes(fnam); + if (Attributes ~= nil) then + if (Attributes.mode == "directory") then + print(fnam); + ProcessPluginFolder(fnam); + end + end + end + end +end +print("Done."); + + From dd04f5a73ccc125be80a3ba3a3ab508ac300b99a Mon Sep 17 00:00:00 2001 From: andrew Date: Wed, 22 Jan 2014 15:49:21 +0200 Subject: [PATCH 11/12] cWorld now saves/loads the scoreboard --- src/Scoreboard.cpp | 3 +++ src/Scoreboard.h | 32 ++++++++++++++++------- src/World.cpp | 9 +++++++ src/WorldStorage/ScoreboardSerializer.cpp | 15 +++++++---- 4 files changed, 44 insertions(+), 15 deletions(-) diff --git a/src/Scoreboard.cpp b/src/Scoreboard.cpp index e6812d3d7..b2edd613b 100644 --- a/src/Scoreboard.cpp +++ b/src/Scoreboard.cpp @@ -238,6 +238,8 @@ bool cTeam::HasPlayer(const AString & a_Name) const void cTeam::Reset(void) { + // TODO 2014-01-22 xdot: Inform online players + m_Players.clear(); } @@ -505,3 +507,4 @@ unsigned int cScoreboard::GetNumTeams(void) const + diff --git a/src/Scoreboard.h b/src/Scoreboard.h index 11b456739..f64ba2bce 100644 --- a/src/Scoreboard.h +++ b/src/Scoreboard.h @@ -27,8 +27,6 @@ class cObjective { public: - // tolua_end - typedef int Score; enum eType @@ -52,6 +50,8 @@ public: E_TYPE_STAT_ENTITY_KILLED_BY }; + // tolua_end + static AString TypeToString(eType a_Type); static eType StringToType(const AString & a_Name); @@ -60,6 +60,8 @@ public: cObjective(const AString & a_Name, const AString & a_DisplayName, eType a_Type, cWorld * a_World); + // tolua_begin + eType GetType(void) const { return m_Type; } const AString & GetName(void) const { return m_Name; } @@ -85,6 +87,8 @@ public: void SetDisplayName(const AString & a_Name); + // tolua_end + /// Send this objective to the specified client void SendTo(cClientHandle & a_Client); @@ -135,6 +139,8 @@ public: /// Removes all registered players void Reset(void); + // tolua_begin + /// Returns the number of registered players unsigned int GetNumPlayers(void) const; @@ -147,13 +153,15 @@ public: const AString & GetPrefix(void) const { return m_Prefix; } const AString & GetSuffix(void) const { return m_Suffix; } - void SetFriendlyFire(bool a_Flag) { m_AllowsFriendlyFire = a_Flag; } + void SetFriendlyFire(bool a_Flag) { m_AllowsFriendlyFire = a_Flag; } void SetCanSeeFriendlyInvisible(bool a_Flag) { m_CanSeeFriendlyInvisible = a_Flag; } void SetDisplayName(const AString & a_Name); - void SetPrefix(const AString & a_Prefix); - void SetSuffix(const AString & a_Suffix); + void SetPrefix(const AString & a_Prefix) { m_Prefix = a_Prefix; } + void SetSuffix(const AString & a_Suffix) { m_Suffix = a_Suffix; } + + // tolua_end private: @@ -183,8 +191,6 @@ class cScoreboard { public: - // tolua_end - enum eDisplaySlot { E_DISPLAY_SLOT_LIST = 0, @@ -194,11 +200,15 @@ public: E_DISPLAY_SLOT_COUNT }; + // tolua_end + public: cScoreboard(cWorld * a_World); + // tolua_begin + /// Registers a new scoreboard objective, returns the cObjective instance, NULL on name collision cObjective * RegisterObjective(const AString & a_Name, const AString & a_DisplayName, cObjective::eType a_Type); @@ -228,13 +238,15 @@ public: /// Execute callback for each objective with the specified type void ForEachObjectiveWith(cObjective::eType a_Type, cObjectiveCallback& a_Callback); - /// Send this scoreboard to the specified client - void SendTo(cClientHandle & a_Client); - unsigned int GetNumObjectives(void) const; unsigned int GetNumTeams(void) const; + // tolua_end + + /// Send this scoreboard to the specified client + void SendTo(cClientHandle & a_Client); + private: diff --git a/src/World.cpp b/src/World.cpp index f83de0342..49d42f08e 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -12,6 +12,7 @@ #include "ChunkMap.h" #include "Generating/ChunkDesc.h" #include "OSSupport/Timer.h" +#include "WorldStorage/ScoreboardSerializer.h" // Entities (except mobs): #include "Entities/ExpOrb.h" @@ -248,6 +249,10 @@ cWorld::cWorld(const AString & a_WorldName) : LOGD("cWorld::cWorld(\"%s\")", a_WorldName.c_str()); cFile::CreateFolder(FILE_IO_PREFIX + m_WorldName); + + // Load the scoreboard + cScoreboardSerializer Serializer(m_WorldName, &m_Scoreboard); + Serializer.Load(); } @@ -267,6 +272,10 @@ cWorld::~cWorld() m_Storage.WaitForFinish(); + // Unload the scoreboard + cScoreboardSerializer Serializer(m_WorldName, &m_Scoreboard); + Serializer.Save(); + delete m_ChunkMap; } diff --git a/src/WorldStorage/ScoreboardSerializer.cpp b/src/WorldStorage/ScoreboardSerializer.cpp index bcac50bb6..dabc5e2e1 100644 --- a/src/WorldStorage/ScoreboardSerializer.cpp +++ b/src/WorldStorage/ScoreboardSerializer.cpp @@ -22,7 +22,12 @@ cScoreboardSerializer::cScoreboardSerializer(const AString & a_WorldName, cScoreboard* a_ScoreBoard) : m_ScoreBoard(a_ScoreBoard) { - Printf(m_Path, "%s/data/scoreboard.dat", a_WorldName.c_str()); + AString DataPath; + Printf(DataPath, "%s/data", a_WorldName.c_str()); + + m_Path = DataPath + "/scoreboard.dat"; + + cFile::CreateFolder(FILE_IO_PREFIX + DataPath); } @@ -33,7 +38,7 @@ bool cScoreboardSerializer::Load(void) { cFile File; - if (!File.Open(m_Path, cFile::fmReadWrite)) + if (!File.Open(FILE_IO_PREFIX + m_Path, cFile::fmReadWrite)) { return false; } @@ -60,7 +65,7 @@ bool cScoreboardSerializer::Load(void) { return false; } - + // Parse the NBT data: cParsedNBT NBT(Uncompressed, strm.total_out); if (!NBT.IsValid()) @@ -81,7 +86,7 @@ bool cScoreboardSerializer::Save(void) cFastNBTWriter Writer; Writer.BeginCompound(""); - + m_ScoreBoard->RegisterObjective("test","test",cObjective::E_TYPE_DUMMY)->AddScore("dot", 2); SaveScoreboardToNBT(Writer); Writer.EndCompound(); @@ -91,7 +96,7 @@ bool cScoreboardSerializer::Save(void) cParsedNBT TestParse(Writer.GetResult().data(), Writer.GetResult().size()); ASSERT(TestParse.IsValid()); #endif // _DEBUG - + gzFile gz = gzopen((FILE_IO_PREFIX + m_Path).c_str(), "wb"); if (gz != NULL) { From d59a0156ce88e1d70169ccf42e7c3c4de7181020 Mon Sep 17 00:00:00 2001 From: tonibm19 Date: Wed, 22 Jan 2014 16:58:25 +0100 Subject: [PATCH 12/12] Fixed compilation on VC2008 --- src/Entities/Player.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index c6b24a465..e5def0156 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -862,6 +862,7 @@ void cPlayer::KilledBy(cEntity * a_Killer) virtual bool Item(cObjective * a_Objective) override { a_Objective->AddScore(m_Name, 1); + return true; } } IncrementCounter (GetName());