summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Bindings/LuaState.cpp199
-rw-r--r--src/Bindings/LuaState.h143
-rw-r--r--src/Bindings/ManualBindings.cpp196
-rw-r--r--src/Bindings/PluginLua.cpp34
-rw-r--r--src/Bindings/PluginLua.h42
-rw-r--r--src/Bindings/PluginManager.cpp15
-rw-r--r--src/Bindings/PluginManager.h56
-rw-r--r--src/ClientHandle.cpp33
-rw-r--r--src/ClientHandle.h4
-rw-r--r--src/Entities/Player.cpp78
-rw-r--r--src/Entities/Player.h14
-rw-r--r--src/Protocol/Protocol.h4
-rw-r--r--src/Protocol/Protocol125.h7
-rw-r--r--src/Protocol/Protocol15x.cpp54
-rw-r--r--src/Protocol/Protocol15x.h3
-rw-r--r--src/Protocol/Protocol17x.cpp43
-rw-r--r--src/Protocol/Protocol17x.h3
-rw-r--r--src/Protocol/ProtocolRecognizer.cpp32
-rw-r--r--src/Protocol/ProtocolRecognizer.h3
-rw-r--r--src/Scoreboard.cpp510
-rw-r--r--src/Scoreboard.h276
-rw-r--r--src/Server.cpp2
-rw-r--r--src/World.cpp66
-rw-r--r--src/World.h15
-rw-r--r--src/WorldStorage/ScoreboardSerializer.cpp389
-rw-r--r--src/WorldStorage/ScoreboardSerializer.h52
-rw-r--r--src/WorldStorage/WSSAnvil.cpp2
27 files changed, 2047 insertions, 228 deletions
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 = "<callback>";
@@ -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()))
+ {
+ LOGWARNING("Function '%s' not found", a_FunctionName.c_str());
+ lua_pop(m_LuaState, 2);
+ return -1;
+ }
+
+ // Copy the function parameters to the target state
+ if (CopyStackFrom(a_SrcLuaState, a_SrcParamStart, a_SrcParamEnd) < 0)
{
- 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";
+ // 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;
}
- return Printf("Unknown (%d)", Type);
+
+ // 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 <typename FnT>
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)
-{
- 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)
+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))
- {
- LOGWARN("Error while calling function '%s' in plugin '%s'", funcName.c_str(), self->GetName().c_str() );
- 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
+ // 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)
{
- // something went wrong, exit
+ LOGWARNING("cPlugin::Call() failed to call destination function");
+ L.LogStackTrace();
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 <typename FnT> 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 <typename FnT, typename ArgT0> 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 <typename FnT, typename ArgT0, typename ArgT1> 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 <typename FnT, typename ArgT0, typename ArgT1, typename ArgT2> 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 <typename FnT, typename ArgT0, typename ArgT1, typename ArgT2, typename ArgT3> 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<AString, int> CommandMap;
- /// Provides an array of Lua function references
+ /** Provides an array of Lua function references */
typedef std::vector<cLuaState::cRef *> 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<int, cLuaRefs> 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<cPlugin> 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)
{
diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp
index 74d192129..0a2d3c1be 100644
--- a/src/ClientHandle.cpp
+++ b/src/ClientHandle.cpp
@@ -263,6 +263,12 @@ void cClientHandle::Authenticate(void)
m_Player->Initialize(World);
m_State = csAuthenticated;
+ // Query player team
+ m_Player->UpdateTeam();
+
+ // Send scoreboard data
+ World->GetScoreBoard().SendTo(*this);
+
cRoot::Get()->GetPluginManager()->CallHookPlayerSpawned(*m_Player);
}
@@ -2111,6 +2117,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 4add022a6..e1f326543 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 c1f2456eb..e5def0156 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->AllowsFriendlyFire())
+ {
+ // Friendly fire is disabled
+ return;
+ }
+ }
+ }
super::DoTakeDamage(a_TDI);
@@ -836,6 +851,25 @@ 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);
+ return true;
+ }
+ } IncrementCounter (GetName());
+
+ cScoreboard & Scoreboard = m_World->GetScoreBoard();
+
+ // Update scoreboard objectives
+ Scoreboard.ForEachObjectiveWith(cObjective::E_TYPE_DEATH_COUNT, IncrementCounter);
}
@@ -916,6 +950,50 @@ bool cPlayer::IsGameModeAdventure(void) const
+void cPlayer::SetTeam(cTeam * a_Team)
+{
+ if (m_Team == a_Team)
+ {
+ return;
+ }
+
+ if (m_Team)
+ {
+ m_Team->RemovePlayer(GetName());
+ }
+
+ m_Team = a_Team;
+
+ if (m_Team)
+ {
+ m_Team->AddPlayer(GetName());
+ }
+}
+
+
+
+
+
+cTeam * cPlayer::UpdateTeam(void)
+{
+ if (m_World == NULL)
+ {
+ SetTeam(NULL);
+ }
+ else
+ {
+ cScoreboard & Scoreboard = m_World->GetScoreBoard();
+
+ SetTeam(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 bf3ca08e8..52ba2065c 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,15 @@ 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);
+
+ /// Forces the player to query the scoreboard for his team
+ cTeam * UpdateTeam(void);
+
// tolua_end
void SetIP(const AString & a_IP);
@@ -456,6 +466,8 @@ protected:
int m_FloaterID;
+ cTeam* m_Team;
+
void ResolvePermissions(void);
@@ -463,7 +475,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/Protocol/Protocol.h b/src/Protocol/Protocol.h
index 1bc5d528e..791082537 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 310f9dd78..cd15ab518 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;
@@ -83,8 +86,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 e5a380f8a..9506332dc 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);
@@ -707,6 +706,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 3ae774c18..3f440f313 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 5524af136..de5dd3fb9 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);
@@ -807,7 +837,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 2dccace6e..daeed1845 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
new file mode 100644
index 000000000..b2edd613b
--- /dev/null
+++ b/src/Scoreboard.cpp
@@ -0,0 +1,510 @@
+
+// Scoreboard.cpp
+
+// Implementation of a scoreboard that keeps track of specified objectives
+
+#include "Globals.h"
+
+#include "Scoreboard.h"
+#include "World.h"
+#include "ClientHandle.h"
+
+
+
+
+
+AString cObjective::TypeToString(eType a_Type)
+{
+ 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_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)
+{
+}
+
+
+
+
+
+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();
+}
+
+
+
+
+
+cObjective::Score cObjective::GetScore(const AString & a_Name) const
+{
+ cScoreMap::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;
+
+ m_World->BroadcastScoreUpdate(m_Name, a_Name, a_Score, 0);
+}
+
+
+
+
+
+void cObjective::ResetScore(const AString & a_Name)
+{
+ m_Scores.erase(a_Name);
+
+ m_World->BroadcastScoreUpdate(m_Name, a_Name, 0, 1);
+}
+
+
+
+
+
+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;
+
+ SetScore(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;
+
+ SetScore(a_Name, NewScore);
+
+ return NewScore;
+}
+
+
+
+
+
+void cObjective::SetDisplayName(const AString & a_Name)
+{
+ m_DisplayName = a_Name;
+
+ m_World->BroadcastScoreboardObjective(m_Name, m_DisplayName, 2);
+}
+
+
+
+
+
+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)
+ , m_CanSeeFriendlyInvisible(false)
+ , m_Name(a_Name)
+ , m_DisplayName(a_DisplayName)
+ , m_Prefix(a_Prefix)
+ , m_Suffix(a_Suffix)
+{}
+
+
+
+
+
+bool cTeam::AddPlayer(const AString & a_Name)
+{
+ return m_Players.insert(a_Name).second;
+}
+
+
+
+
+
+bool cTeam::RemovePlayer(const AString & a_Name)
+{
+ return m_Players.erase(a_Name) > 0;
+}
+
+
+
+
+
+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)
+{
+ // TODO 2014-01-22 xdot: Inform online players
+
+ m_Players.clear();
+}
+
+
+
+
+unsigned int cTeam::GetNumPlayers(void) const
+{
+ return m_Players.size();
+}
+
+
+
+
+
+cScoreboard::cScoreboard(cWorld * a_World) : m_World(a_World)
+{
+ for (int i = 0; i < (int) E_DISPLAY_SLOT_COUNT; ++i)
+ {
+ m_Display[i] = NULL;
+ }
+}
+
+
+
+
+
+cObjective* cScoreboard::RegisterObjective(const AString & a_Name, const AString & a_DisplayName, cObjective::eType a_Type)
+{
+ cObjective Objective(a_Name, a_DisplayName, a_Type, m_World);
+
+ std::pair<cObjectiveMap::iterator, bool> Status = m_Objectives.insert(cNamedObjective(a_Name, Objective));
+
+ if (Status.second)
+ {
+ ASSERT(m_World != NULL);
+ m_World->BroadcastScoreboardObjective(a_Name, a_DisplayName, 0);
+
+ return &Status.first->second;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+
+
+
+
+bool cScoreboard::RemoveObjective(const AString & a_Name)
+{
+ cCSLock Lock(m_CSObjectives);
+
+ cObjectiveMap::iterator it = m_Objectives.find(a_Name);
+
+ if (it == m_Objectives.end())
+ {
+ return false;
+ }
+
+ m_Objectives.erase(it);
+
+ ASSERT(m_World != NULL);
+ m_World->BroadcastScoreboardObjective(it->second.GetName(), it->second.GetDisplayName(), 1);
+
+ return true;
+}
+
+
+
+
+
+cObjective * cScoreboard::GetObjective(const AString & a_Name)
+{
+ cCSLock Lock(m_CSObjectives);
+
+ cObjectiveMap::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(a_Name, a_DisplayName, a_Prefix, a_Suffix);
+
+ std::pair<cTeamMap::iterator, bool> Status = m_Teams.insert(cNamedTeam(a_Name, Team));
+
+ return Status.second ? &Status.first->second : NULL;
+}
+
+
+
+
+
+bool cScoreboard::RemoveTeam(const AString & a_Name)
+{
+ cCSLock Lock(m_CSTeams);
+
+ cTeamMap::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)
+{
+ cCSLock Lock(m_CSTeams);
+
+ cTeamMap::iterator it = m_Teams.find(a_Name);
+
+ if (it == m_Teams.end())
+ {
+ return NULL;
+ }
+ else
+ {
+ return &it->second;
+ }
+}
+
+
+
+
+
+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))
+ {
+ 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);
+
+ 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(a_Objective ? a_Objective->GetName() : "", a_Slot);
+}
+
+
+
+
+
+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)
+{
+ cCSLock Lock(m_CSObjectives);
+
+ for (cObjectiveMap::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;
+ }
+ }
+ }
+}
+
+
+
+
+
+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();
+}
+
+
+
+
+
+unsigned int cScoreboard::GetNumTeams(void) const
+{
+ return m_Teams.size();
+}
+
+
+
+
+
diff --git a/src/Scoreboard.h b/src/Scoreboard.h
new file mode 100644
index 000000000..f64ba2bce
--- /dev/null
+++ b/src/Scoreboard.h
@@ -0,0 +1,276 @@
+
+// Scoreboard.h
+
+// Implementation of a scoreboard that keeps track of specified objectives
+
+
+
+
+
+#pragma once
+
+
+
+
+
+class cObjective;
+class cWorld;
+
+typedef cItemCallback<cObjective> cObjectiveCallback;
+
+
+
+
+
+// tolua_begin
+class cObjective
+{
+public:
+
+ typedef int Score;
+
+ enum eType
+ {
+ E_TYPE_DUMMY,
+
+ 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
+ };
+
+ // tolua_end
+
+ static AString TypeToString(eType a_Type);
+
+ static eType StringToType(const AString & a_Name);
+
+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; }
+ const AString & GetDisplayName(void) const { return m_DisplayName; }
+
+ /// 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);
+
+ void SetDisplayName(const AString & a_Name);
+
+ // tolua_end
+
+ /// Send this objective to the specified client
+ void SendTo(cClientHandle & a_Client);
+
+private:
+
+ typedef std::pair<AString, Score> cTrackedPlayer;
+
+ typedef std::map<AString, Score> cScoreMap;
+
+ cScoreMap m_Scores;
+
+ AString m_DisplayName;
+ AString m_Name;
+
+ eType m_Type;
+
+ cWorld * m_World;
+
+ friend class cScoreboardSerializer;
+
+};
+
+
+
+
+
+// tolua_begin
+class cTeam
+{
+public:
+
+ // tolua_end
+
+ 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(const AString & a_Name);
+
+ /// 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);
+
+ // tolua_begin
+
+ /// Returns the number of registered players
+ unsigned int GetNumPlayers(void) const;
+
+ bool AllowsFriendlyFire(void) const { return m_AllowsFriendlyFire; }
+ bool CanSeeFriendlyInvisible(void) const { return m_CanSeeFriendlyInvisible; }
+
+ const AString & GetDisplayName(void) const { return m_DisplayName; }
+ 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) { 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) { m_Prefix = a_Prefix; }
+ void SetSuffix(const AString & a_Suffix) { m_Suffix = a_Suffix; }
+
+ // tolua_end
+
+private:
+
+ typedef std::set<AString> cPlayerNameSet;
+
+ bool m_AllowsFriendlyFire;
+ bool m_CanSeeFriendlyInvisible;
+
+ AString m_DisplayName;
+ AString m_Name;
+
+ AString m_Prefix;
+ AString m_Suffix;
+
+ cPlayerNameSet m_Players;
+
+ friend class cScoreboardSerializer;
+
+};
+
+
+
+
+
+// tolua_begin
+class cScoreboard
+{
+public:
+
+ enum eDisplaySlot
+ {
+ E_DISPLAY_SLOT_LIST = 0,
+ E_DISPLAY_SLOT_SIDEBAR,
+ E_DISPLAY_SLOT_NAME,
+
+ 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);
+
+ /// 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, NULL on name collision
+ 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 * QueryPlayerTeam(const AString & a_Name); // WARNING: O(n logn)
+
+ 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);
+
+ 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:
+
+ typedef std::pair<AString, cObjective> cNamedObjective;
+ typedef std::pair<AString, cTeam> cNamedTeam;
+
+ typedef std::map<AString, cObjective> cObjectiveMap;
+ typedef std::map<AString, cTeam> 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 4d551e164..34ee066cb 100644
--- a/src/Server.cpp
+++ b/src/Server.cpp
@@ -492,7 +492,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 5e9f4a38a..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"
@@ -242,11 +243,16 @@ 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());
cFile::CreateFolder(FILE_IO_PREFIX + m_WorldName);
+
+ // Load the scoreboard
+ cScoreboardSerializer Serializer(m_WorldName, &m_Scoreboard);
+ Serializer.Load();
}
@@ -266,6 +272,10 @@ cWorld::~cWorld()
m_Storage.WaitForFinish();
+ // Unload the scoreboard
+ cScoreboardSerializer Serializer(m_WorldName, &m_Scoreboard);
+ Serializer.Save();
+
delete m_ChunkMap;
}
@@ -1981,6 +1991,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 f0dd45c90..d941772aa 100644
--- a/src/World.h
+++ b/src/World.h
@@ -23,6 +23,7 @@
#include "Mobs/Monster.h"
#include "Entities/ProjectileEntity.h"
#include "ForEachChunkProvider.h"
+#include "Scoreboard.h"
@@ -170,6 +171,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);
@@ -229,10 +233,10 @@ public:
/** Calls the callback for each player in the list; returns true if all players processed, false if the callback aborted by returning true */
bool ForEachPlayer(cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS <<
- /// Calls the callback for the player of the given name; returns true if the player was found and the callback called, false if player not found. Callback return ignored
- bool DoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS <<
+ /** Calls the callback for the player of the given name; returns true if the player was found and the callback called, false if player not found. Callback return ignored */
+ bool DoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS <<
- /// Finds a player from a partial or complete player name and calls the callback - case-insensitive
+ /** Finds a player from a partial or complete player name and calls the callback - case-insensitive */
bool FindAndDoWithPlayer(const AString & a_PlayerNameHint, cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS <<
// TODO: This interface is dangerous - rewrite to DoWithClosestPlayer(pos, sight, action)
@@ -505,6 +509,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
@@ -773,6 +780,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;
diff --git a/src/WorldStorage/ScoreboardSerializer.cpp b/src/WorldStorage/ScoreboardSerializer.cpp
new file mode 100644
index 000000000..dabc5e2e1
--- /dev/null
+++ b/src/WorldStorage/ScoreboardSerializer.cpp
@@ -0,0 +1,389 @@
+
+// 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)
+{
+ AString DataPath;
+ Printf(DataPath, "%s/data", a_WorldName.c_str());
+
+ m_Path = DataPath + "/scoreboard.dat";
+
+ cFile::CreateFolder(FILE_IO_PREFIX + DataPath);
+}
+
+
+
+
+
+bool cScoreboardSerializer::Load(void)
+{
+ cFile File;
+
+ if (!File.Open(FILE_IO_PREFIX + 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("");
+ m_ScoreBoard->RegisterObjective("test","test",cObjective::E_TYPE_DUMMY)->AddScore("dot", 2);
+ 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);
+
+ 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.EndCompound();
+ }
+
+ 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();
+}
+
+
+
+
+
+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..048fa3ab4
--- /dev/null
+++ b/src/WorldStorage/ScoreboardSerializer.h
@@ -0,0 +1,52 @@
+
+// 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 8983f25ba..8be6372e2 100644
--- a/src/WorldStorage/WSSAnvil.cpp
+++ b/src/WorldStorage/WSSAnvil.cpp
@@ -1936,7 +1936,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;
}