From 24e89bbb2c656224d06aed084b952bbc885e3914 Mon Sep 17 00:00:00 2001 From: Samuel Barney Date: Mon, 18 Jul 2016 14:39:11 -0600 Subject: Feature: Channel Management with Lua API Allows lua plugins to register handles for channel messages. * Only one handle can be registered for one channel at a time. * Plugins can also add and remove clients from channels, sending the appropriate packet to the client. --- src/Bindings/AllToLua.pkg | 5 +- src/Bindings/CMakeLists.txt | 2 + src/Bindings/LuaState.cpp | 36 +++ src/Bindings/LuaState.h | 3 + src/Bindings/ManualBindings.cpp | 597 +++++++++++++++++++++++++++++++++++++++- src/Bindings/Plugin.h | 9 +- src/Bindings/PluginLua.cpp | 10 +- src/Bindings/PluginLua.h | 6 +- src/Bindings/PluginManager.cpp | 10 +- src/Bindings/PluginManager.h | 3 +- src/ByteBuffer.cpp | 35 +++ src/ByteBuffer.h | 101 +++---- src/CMakeLists.txt | 4 + src/ChannelCallback.cpp | 34 +++ src/ChannelCallback.h | 14 + src/ChannelManager.cpp | 222 +++++++++++++++ src/ChannelManager.h | 66 +++++ src/ClientHandle.cpp | 61 +--- src/ClientHandle.h | 25 +- src/Protocol/Protocol18x.cpp | 27 +- src/Protocol/Protocol19x.cpp | 29 +- src/Root.h | 5 - src/Server.cpp | 17 ++ src/Server.h | 12 +- 24 files changed, 1167 insertions(+), 166 deletions(-) create mode 100644 src/ChannelCallback.cpp create mode 100644 src/ChannelCallback.h create mode 100644 src/ChannelManager.cpp create mode 100644 src/ChannelManager.h (limited to 'src') diff --git a/src/Bindings/AllToLua.pkg b/src/Bindings/AllToLua.pkg index 6ca9c8658..1e8c797be 100644 --- a/src/Bindings/AllToLua.pkg +++ b/src/Bindings/AllToLua.pkg @@ -42,10 +42,12 @@ $cfile "LuaWindow.h" $cfile "../BlockID.h" $cfile "../BlockInfo.h" +$cfile "../ByteBuffer.h" $cfile "../StringUtils.h" $cfile "../Defines.h" $cfile "../ChatColor.h" $cfile "../ClientHandle.h" +$cfile "../ChannelManager.h" $cfile "../Server.h" $cfile "../World.h" $cfile "../Inventory.h" @@ -125,6 +127,3 @@ typedef unsigned char Byte; $renaming Vector3 @ Vector3d $renaming Vector3 @ Vector3f $renaming Vector3 @ Vector3i - - - diff --git a/src/Bindings/CMakeLists.txt b/src/Bindings/CMakeLists.txt index 640fd60fa..e9adde150 100644 --- a/src/Bindings/CMakeLists.txt +++ b/src/Bindings/CMakeLists.txt @@ -84,6 +84,8 @@ set(BINDING_DEPENDENCIES ../BlockID.h ../BlockInfo.h ../BoundingBox.h + ../ByteBuffer.h + ../ChannelManager.h ../ChatColor.h ../ChunkDef.h ../ClientHandle.h diff --git a/src/Bindings/LuaState.cpp b/src/Bindings/LuaState.cpp index 5e6c24365..75e667821 100644 --- a/src/Bindings/LuaState.cpp +++ b/src/Bindings/LuaState.cpp @@ -798,6 +798,18 @@ void cLuaState::Push(const Vector3i * a_Vector) +void cLuaState::Push(const cByteBuffer & a_Buffer) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, reinterpret_cast(const_cast(&a_Buffer)), "cByteBuffer"); + m_NumCurrentFunctionArgs += 1; +} + + + + + void cLuaState::Push(bool a_Value) { ASSERT(IsValid()); @@ -948,6 +960,18 @@ void cLuaState::Push(long a_Value) +void cLuaState::Push(const Int64 a_Value) +{ + ASSERT(IsValid()); + + tolua_pushnumber(m_LuaState, a_Value); + m_NumCurrentFunctionArgs += 1; +} + + + + + void cLuaState::Push(UInt32 a_Value) { ASSERT(IsValid()); @@ -960,6 +984,18 @@ void cLuaState::Push(UInt32 a_Value) +void cLuaState::Push(const UInt64 a_Value) +{ + ASSERT(IsValid()); + + tolua_pushnumber(m_LuaState, a_Value); + m_NumCurrentFunctionArgs += 1; +} + + + + + void cLuaState::Push(std::chrono::milliseconds a_Value) { ASSERT(IsValid()); diff --git a/src/Bindings/LuaState.h b/src/Bindings/LuaState.h index 106d8a783..f89076d95 100644 --- a/src/Bindings/LuaState.h +++ b/src/Bindings/LuaState.h @@ -358,6 +358,7 @@ public: void Push(const Vector3d * a_Vector); void Push(const Vector3i & a_Vector); void Push(const Vector3i * a_Vector); + void Push(const cByteBuffer & a_Buffer); // Push a simple value onto the stack (keep alpha-sorted): void Push(bool a_Value); @@ -368,7 +369,9 @@ public: void Push(double a_Value); void Push(int a_Value); void Push(long a_Value); + void Push(const Int64 a_Value); void Push(const UInt32 a_Value); + void Push(const UInt64 a_Value); void Push(std::chrono::milliseconds a_time); /** Pops the specified number of values off the top of the Lua stack. */ diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp index 8bcd5a4e6..d288d37f7 100644 --- a/src/Bindings/ManualBindings.cpp +++ b/src/Bindings/ManualBindings.cpp @@ -18,6 +18,7 @@ #include "../Entities/Player.h" #include "../WebAdmin.h" #include "../ClientHandle.h" +#include "../ChannelManager.h" #include "../BlockArea.h" #include "../BlockEntities/BeaconEntity.h" #include "../BlockEntities/BrewingstandEntity.h" @@ -39,6 +40,7 @@ #include "../BuildInfo.h" #include "../HTTP/UrlParser.h" #include "../BoundingBox.h" +#include "../ChannelCallback.h" @@ -3718,6 +3720,571 @@ static int tolua_cCompositeChat_UnderlineUrls(lua_State * tolua_S) +static int tolua_cChannelManager_RegisterChannel(lua_State * tolua_S) +{ + + // Retrieve the cPlugin from the LuaState: + cPluginLua * Plugin = cManualBindings::GetLuaPlugin(tolua_S); + if (Plugin == nullptr) + { + // An error message has been already printed in GetLuaPlugin() + return 0; + } + + // Check params: + cLuaState L(tolua_S); + if (!L.CheckParamUserType(1, "cChannelManager")) + { + return 0; + } + cChannelManager * self = reinterpret_cast(tolua_tousertype(tolua_S, 1, nullptr)); + if (self == nullptr) + { + return cManualBindings::lua_do_error(tolua_S, "invalid 'self' in function 'cChannelManager:RegisterChannel'"); + } + if (!L.CheckParamString(2) || !L.CheckParamFunction(3)) + { + return 0; + } + + AString Channel; + L.GetStackValue(2, Channel); + cLuaState::cCallbackPtr Callback; + L.GetStackValue(3, Callback); + auto ChannelCallback = std::make_shared(*Plugin, Callback); + + auto Result = self->RegisterChannel(Channel, ChannelCallback); + + // Cut away everything from the stack and push on the result of RegisterChannel + lua_settop(L, 2); + L.Push(Result); + return 1; +} + + + + + +static int tolua_cByteBuffer_ReadBEInt8(lua_State * tolua_S) +{ + cLuaState L(tolua_S); + if (!L.CheckParamUserType(1, "cByteBuffer")) + { + return 0; + } + + cByteBuffer * Buffer = reinterpret_cast(tolua_tousertype(tolua_S, 1, nullptr)); + if (Buffer == nullptr) + { + return cManualBindings::lua_do_error(tolua_S, "invalid 'self' in function 'cByteBuffer:ReadBEInt8'"); + } + + Int8 Value = 0; + auto Success = Buffer->ReadBEInt8(Value); + + lua_settop(tolua_S, 2); + L.Push(Success); + L.Push(Value); + return 2; +} + + + + + +static int tolua_cByteBuffer_ReadBEInt16(lua_State * tolua_S) +{ + cLuaState L(tolua_S); + if (!L.CheckParamUserType(1, "cByteBuffer")) + { + return 0; + } + + cByteBuffer * Buffer = reinterpret_cast(tolua_tousertype(tolua_S, 1, nullptr)); + if (Buffer == nullptr) + { + return cManualBindings::lua_do_error(tolua_S, "invalid 'self' in function 'cByteBuffer:ReadBEInt16'"); + } + + Int16 Value = 0; + auto Success = Buffer->ReadBEInt16(Value); + + lua_settop(tolua_S, 2); + L.Push(Success); + L.Push(Value); + return 2; +} + + + + + +static int tolua_cByteBuffer_ReadBEInt32(lua_State * tolua_S) +{ + cLuaState L(tolua_S); + if (!L.CheckParamUserType(1, "cByteBuffer")) + { + return 0; + } + + cByteBuffer * Buffer = reinterpret_cast(tolua_tousertype(tolua_S, 1, nullptr)); + if (Buffer == nullptr) + { + return cManualBindings::lua_do_error(tolua_S, "invalid 'self' in function 'cByteBuffer:ReadBEInt32'"); + } + + Int32 Value = 0; + auto Success = Buffer->ReadBEInt32(Value); + + lua_settop(tolua_S, 2); + L.Push(Success); + L.Push(Value); + return 2; +} + + + + + +static int tolua_cByteBuffer_ReadBEInt64(lua_State * tolua_S) +{ + cLuaState L(tolua_S); + if (!L.CheckParamUserType(1, "cByteBuffer")) + { + return 0; + } + + cByteBuffer * Buffer = reinterpret_cast(tolua_tousertype(tolua_S, 1, nullptr)); + if (Buffer == nullptr) + { + return cManualBindings::lua_do_error(tolua_S, "invalid 'self' in function 'cByteBuffer:ReadBEInt64'"); + } + + Int64 Value = 0; + auto Success = Buffer->ReadBEInt64(Value); + + lua_settop(tolua_S, 2); + L.Push(Success); + L.Push(Value); + return 2; +} + + + + + +static int tolua_cByteBuffer_ReadBEUInt8(lua_State * tolua_S) +{ + cLuaState L(tolua_S); + if (!L.CheckParamUserType(1, "cByteBuffer")) + { + return 0; + } + + cByteBuffer * Buffer = reinterpret_cast(tolua_tousertype(tolua_S, 1, nullptr)); + if (Buffer == nullptr) + { + return cManualBindings::lua_do_error(tolua_S, "invalid 'self' in function 'cByteBuffer:ReadBEUInt8'"); + } + + UInt8 Value = 0; + auto Success = Buffer->ReadBEUInt8(Value); + + lua_settop(tolua_S, 2); + L.Push(Success); + L.Push(Value); + return 2; +} + + + + + +static int tolua_cByteBuffer_ReadBEUInt16(lua_State * tolua_S) +{ + cLuaState L(tolua_S); + if (!L.CheckParamUserType(1, "cByteBuffer")) + { + return 0; + } + + cByteBuffer * Buffer = reinterpret_cast(tolua_tousertype(tolua_S, 1, nullptr)); + if (Buffer == nullptr) + { + return cManualBindings::lua_do_error(tolua_S, "invalid 'self' in function 'cByteBuffer:ReadBEUInt16'"); + } + + UInt16 Value = 0; + auto Success = Buffer->ReadBEUInt16(Value); + + lua_settop(tolua_S, 2); + L.Push(Success); + L.Push(Value); + return 2; +} + + + + + +static int tolua_cByteBuffer_ReadBEUInt32(lua_State * tolua_S) +{ + cLuaState L(tolua_S); + if (!L.CheckParamUserType(1, "cByteBuffer")) + { + return 0; + } + + cByteBuffer * Buffer = reinterpret_cast(tolua_tousertype(tolua_S, 1, nullptr)); + if (Buffer == nullptr) + { + return cManualBindings::lua_do_error(tolua_S, "invalid 'self' in function 'cByteBuffer:ReadBEUInt32'"); + } + + UInt32 Value = 0; + auto Success = Buffer->ReadBEUInt32(Value); + + lua_settop(tolua_S, 2); + L.Push(Success); + L.Push(Value); + return 2; +} + + + + + +static int tolua_cByteBuffer_ReadBEUInt64(lua_State * tolua_S) +{ + cLuaState L(tolua_S); + if (!L.CheckParamUserType(1, "cByteBuffer")) + { + return 0; + } + + cByteBuffer * Buffer = reinterpret_cast(tolua_tousertype(tolua_S, 1, nullptr)); + if (Buffer == nullptr) + { + return cManualBindings::lua_do_error(tolua_S, "invalid 'self' in function 'cByteBuffer:ReadBEUInt64'"); + } + + UInt64 Value = 0; + auto Success = Buffer->ReadBEUInt64(Value); + + lua_settop(tolua_S, 2); + L.Push(Success); + L.Push(Value); + return 2; +} + + + + + +static int tolua_cByteBuffer_ReadBEFloat(lua_State * tolua_S) +{ + cLuaState L(tolua_S); + if (!L.CheckParamUserType(1, "cByteBuffer")) + { + return 0; + } + + cByteBuffer * Buffer = reinterpret_cast(tolua_tousertype(tolua_S, 1, nullptr)); + if (Buffer == nullptr) + { + return cManualBindings::lua_do_error(tolua_S, "invalid 'self' in function 'cByteBuffer:ReadBEFloat'"); + } + + float Value = 0.0f; + auto Success = Buffer->ReadBEFloat(Value); + + lua_settop(tolua_S, 2); + L.Push(Success); + L.Push(Value); + return 2; +} + + + + + +static int tolua_cByteBuffer_ReadBEDouble(lua_State * tolua_S) +{ + cLuaState L(tolua_S); + if (!L.CheckParamUserType(1, "cByteBuffer")) + { + return 0; + } + + cByteBuffer * Buffer = reinterpret_cast(tolua_tousertype(tolua_S, 1, nullptr)); + if (Buffer == nullptr) + { + return cManualBindings::lua_do_error(tolua_S, "invalid 'self' in function 'cByteBuffer:ReadBEDouble'"); + } + + double Value = 0.0; + auto Success = Buffer->ReadBEDouble(Value); + + lua_settop(tolua_S, 2); + L.Push(Success); + L.Push(Value); + return 2; +} + + + + + +static int tolua_cByteBuffer_ReadBool(lua_State * tolua_S) +{ + cLuaState L(tolua_S); + if (!L.CheckParamUserType(1, "cByteBuffer")) + { + return 0; + } + + cByteBuffer * Buffer = reinterpret_cast(tolua_tousertype(tolua_S, 1, nullptr)); + if (Buffer == nullptr) + { + return cManualBindings::lua_do_error(tolua_S, "invalid 'self' in function 'cByteBuffer:ReadBool'"); + } + + bool Value = false; + auto Success = Buffer->ReadBool(Value); + + lua_settop(tolua_S, 2); + L.Push(Success); + L.Push(Value); + return 2; +} + + + + + +static int tolua_cByteBuffer_ReadVarInt32(lua_State * tolua_S) +{ + cLuaState L(tolua_S); + if (!L.CheckParamUserType(1, "cByteBuffer")) + { + return 0; + } + + cByteBuffer * Buffer = reinterpret_cast(tolua_tousertype(tolua_S, 1, nullptr)); + if (Buffer == nullptr) + { + return cManualBindings::lua_do_error(tolua_S, "invalid 'self' in function 'cByteBuffer:ReadVarInt32'"); + } + + UInt32 Value = 0; + auto Success = Buffer->ReadVarInt32(Value); + + lua_settop(tolua_S, 2); + L.Push(Success); + L.Push(Value); + return 2; +} + + + + + +static int tolua_cByteBuffer_ReadVarInt64(lua_State * tolua_S) +{ + cLuaState L(tolua_S); + if (!L.CheckParamUserType(1, "cByteBuffer")) + { + return 0; + } + + cByteBuffer * Buffer = reinterpret_cast(tolua_tousertype(tolua_S, 1, nullptr)); + if (Buffer == nullptr) + { + return cManualBindings::lua_do_error(tolua_S, "invalid 'self' in function 'cByteBuffer:ReadVarInt64'"); + } + + UInt64 Value = 0; + auto Success = Buffer->ReadVarInt64(Value); + + lua_settop(tolua_S, 2); + L.Push(Success); + L.Push(Value); + return 2; +} + + + + + +static int tolua_cByteBuffer_ReadVarUTF8String(lua_State * tolua_S) +{ + cLuaState L(tolua_S); + if (!L.CheckParamUserType(1, "cByteBuffer")) + { + return 0; + } + + cByteBuffer * Buffer = reinterpret_cast(tolua_tousertype(tolua_S, 1, nullptr)); + if (Buffer == nullptr) + { + return cManualBindings::lua_do_error(tolua_S, "invalid 'self' in function 'cByteBuffer:ReadVarUTF8String'"); + } + + AString Value; + auto Success = Buffer->ReadVarUTF8String(Value); + + lua_settop(tolua_S, 2); + L.Push(Success); + L.Push(Value); + return 2; +} + + + + + +static int tolua_cByteBuffer_ReadLEInt(lua_State * tolua_S) +{ + cLuaState L(tolua_S); + if (!L.CheckParamUserType(1, "cByteBuffer")) + { + return 0; + } + + cByteBuffer * Buffer = reinterpret_cast(tolua_tousertype(tolua_S, 1, nullptr)); + if (Buffer == nullptr) + { + return cManualBindings::lua_do_error(tolua_S, "invalid 'self' in function 'cByteBuffer:ReadLEInt'"); + } + + int Value = 0; + auto Success = Buffer->ReadLEInt(Value); + + lua_settop(tolua_S, 2); + L.Push(Success); + L.Push(Value); + return 2; +} + + + + + +static int tolua_cByteBuffer_ReadPosition64(lua_State * tolua_S) +{ + cLuaState L(tolua_S); + if (!L.CheckParamUserType(1, "cByteBuffer")) + { + return 0; + } + + cByteBuffer * Buffer = reinterpret_cast(tolua_tousertype(tolua_S, 1, nullptr)); + if (Buffer == nullptr) + { + return cManualBindings::lua_do_error(tolua_S, "invalid 'self' in function 'cByteBuffer:ReadPosition64'"); + } + + int X = 0; + int Y = 0; + int Z = 0; + auto Success = Buffer->ReadPosition64(X, Y, Z); + + lua_settop(tolua_S, 2); + L.Push(Success); + L.Push(X); + L.Push(Y); + L.Push(Z); + return 4; +} + + + + +static int tolua_cByteBuffer_ReadString(lua_State * tolua_S) +{ + cLuaState L(tolua_S); + if (!L.CheckParamUserType(1, "cByteBuffer")) + { + return 0; + } + + cByteBuffer * Buffer = reinterpret_cast(tolua_tousertype(tolua_S, 1, nullptr)); + if (Buffer == nullptr) + { + return cManualBindings::lua_do_error(tolua_S, "invalid 'self' in function 'cByteBuffer:ReadString'"); + } + if (!L.CheckParamNumber(2)) + { + return 0; + } + + AString Value; + size_t Count; + L.GetStackValue(2, Count); + auto Success = Buffer->ReadString(Value, Count); + + lua_settop(tolua_S, 2); + L.Push(Success); + L.Push(Value); + return 2; +} + + + + + +static int tolua_cByteBuffer_ReadAll(lua_State * tolua_S) +{ + cLuaState L(tolua_S); + if (!L.CheckParamUserType(1, "cByteBuffer")) + { + return 0; + } + + cByteBuffer * Buffer = reinterpret_cast(tolua_tousertype(tolua_S, 1, nullptr)); + if (Buffer == nullptr) + { + return cManualBindings::lua_do_error(tolua_S, "invalid 'self' in function 'cByteBuffer:ReadAll'"); + } + + AString Value; + Buffer->ReadAll(Value); + + lua_settop(tolua_S, 2); + L.Push(Value); + return 1; +} + + + + + +static int tolua_cByteBuffer_ReadAgain(lua_State * tolua_S) +{ + cLuaState L(tolua_S); + if (!L.CheckParamUserType(1, "cByteBuffer")) + { + return 0; + } + + cByteBuffer * Buffer = reinterpret_cast(tolua_tousertype(tolua_S, 1, nullptr)); + if (Buffer == nullptr) + { + return cManualBindings::lua_do_error(tolua_S, "invalid 'self' in function 'cByteBuffer:ReadAgain'"); + } + + AString Value; + Buffer->ReadAll(Value); + + lua_settop(tolua_S, 2); + L.Push(Value); + return 1; +} + + + + + void cManualBindings::Bind(lua_State * tolua_S) { tolua_beginmodule(tolua_S, nullptr); @@ -3929,13 +4496,35 @@ void cManualBindings::Bind(lua_State * tolua_S) tolua_variable(tolua_S, "PostParams", tolua_get_HTTPRequest_PostParams, nullptr); tolua_endmodule(tolua_S); + tolua_beginmodule(tolua_S, "cChannelManager"); + tolua_function(tolua_S, "RegisterChannel", tolua_cChannelManager_RegisterChannel); + tolua_endmodule(tolua_S); + + tolua_beginmodule(tolua_S, "cByteBuffer"); + tolua_function(tolua_S, "ReadBEInt8", tolua_cByteBuffer_ReadBEInt8); + tolua_function(tolua_S, "ReadBEInt16", tolua_cByteBuffer_ReadBEInt16); + tolua_function(tolua_S, "ReadBEInt32", tolua_cByteBuffer_ReadBEInt32); + tolua_function(tolua_S, "ReadBEInt64", tolua_cByteBuffer_ReadBEInt64); + tolua_function(tolua_S, "ReadBEUInt8", tolua_cByteBuffer_ReadBEUInt8); + tolua_function(tolua_S, "ReadBEUInt16", tolua_cByteBuffer_ReadBEUInt16); + tolua_function(tolua_S, "ReadBEUInt32", tolua_cByteBuffer_ReadBEUInt32); + tolua_function(tolua_S, "ReadBEUInt64", tolua_cByteBuffer_ReadBEUInt64); + tolua_function(tolua_S, "ReadBEFloat", tolua_cByteBuffer_ReadBEFloat); + tolua_function(tolua_S, "ReadBEDouble", tolua_cByteBuffer_ReadBEDouble); + tolua_function(tolua_S, "ReadBool", tolua_cByteBuffer_ReadBool); + tolua_function(tolua_S, "ReadVarInt32", tolua_cByteBuffer_ReadVarInt32); + tolua_function(tolua_S, "ReadVarInt64", tolua_cByteBuffer_ReadVarInt64); + tolua_function(tolua_S, "ReadVarUTF8String", tolua_cByteBuffer_ReadVarUTF8String); + tolua_function(tolua_S, "ReadLEInt", tolua_cByteBuffer_ReadLEInt); + tolua_function(tolua_S, "ReadPosition64", tolua_cByteBuffer_ReadPosition64); + tolua_function(tolua_S, "ReadString", tolua_cByteBuffer_ReadString); + tolua_function(tolua_S, "ReadAll", tolua_cByteBuffer_ReadAll); + tolua_function(tolua_S, "ReadAgain", tolua_cByteBuffer_ReadAgain); + tolua_endmodule(tolua_S); + BindNetwork(tolua_S); BindRankManager(tolua_S); BindWorld(tolua_S); tolua_endmodule(tolua_S); } - - - - diff --git a/src/Bindings/Plugin.h b/src/Bindings/Plugin.h index 3276fde67..c477a97aa 100644 --- a/src/Bindings/Plugin.h +++ b/src/Bindings/Plugin.h @@ -15,6 +15,13 @@ + +class cByteBuffer; + + + + + // tolua_begin class cPlugin { @@ -91,7 +98,7 @@ public: virtual bool OnPlayerUsedItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) = 0; virtual bool OnPlayerUsingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0; virtual bool OnPlayerUsingItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) = 0; - virtual bool OnPluginMessage (cClientHandle & a_Client, const AString & a_Channel, const AString & a_Message) = 0; + virtual bool OnPluginMessage (cClientHandle & a_Client, const AString & a_Channel, const cByteBuffer & a_Message) = 0; virtual bool OnPluginsLoaded (void) = 0; virtual bool OnPostCrafting (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe) = 0; virtual bool OnPreCrafting (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe) = 0; diff --git a/src/Bindings/PluginLua.cpp b/src/Bindings/PluginLua.cpp index d1fc2ae4f..26c221200 100644 --- a/src/Bindings/PluginLua.cpp +++ b/src/Bindings/PluginLua.cpp @@ -12,11 +12,14 @@ #endif #include "PluginLua.h" +#include "../ByteBuffer.h" #include "../CommandOutput.h" #include "PluginManager.h" #include "../Item.h" #include "../Root.h" #include "../WebAdmin.h" +#include "../ChannelManager.h" +#include "../Server.h" extern "C" { @@ -185,6 +188,7 @@ void cPluginLua::Unload(void) { ClearWebTabs(); super::Unload(); + cRoot::Get()->GetServer()->GetChannelManager()->HandlePluginUnloading(this); Close(); } @@ -762,7 +766,7 @@ bool cPluginLua::OnPlayerUsingItem(cPlayer & a_Player, int a_BlockX, int a_Block -bool cPluginLua::OnPluginMessage(cClientHandle & a_Client, const AString & a_Channel, const AString & a_Message) +bool cPluginLua::OnPluginMessage(cClientHandle & a_Client, const AString & a_Channel, const cByteBuffer & a_Message) { return CallSimpleHooks(cPluginManager::HOOK_PLUGIN_MESSAGE, &a_Client, a_Channel, a_Message); } @@ -1148,7 +1152,3 @@ void cPluginLua::ClearWebTabs(void) webAdmin->RemoveAllPluginWebTabs(m_Name); } } - - - - diff --git a/src/Bindings/PluginLua.h b/src/Bindings/PluginLua.h index dc3c91880..de9343b08 100644 --- a/src/Bindings/PluginLua.h +++ b/src/Bindings/PluginLua.h @@ -119,7 +119,7 @@ public: virtual bool OnPlayerUsedItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; virtual bool OnPlayerUsingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; virtual bool OnPlayerUsingItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; - virtual bool OnPluginMessage (cClientHandle & a_Client, const AString & a_Channel, const AString & a_Message) override; + virtual bool OnPluginMessage (cClientHandle & a_Client, const AString & a_Channel, const cByteBuffer & a_Message) override; virtual bool OnPluginsLoaded (void) override; virtual bool OnPostCrafting (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe) override; virtual bool OnPreCrafting (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe) override; @@ -207,7 +207,3 @@ protected: return false; } } ; // tolua_export - - - - diff --git a/src/Bindings/PluginManager.cpp b/src/Bindings/PluginManager.cpp index 19d2e8b4d..fc37c2e2d 100644 --- a/src/Bindings/PluginManager.cpp +++ b/src/Bindings/PluginManager.cpp @@ -1188,14 +1188,20 @@ bool cPluginManager::CallHookPlayerUsingItem(cPlayer & a_Player, int a_BlockX, i -bool cPluginManager::CallHookPluginMessage(cClientHandle & a_Client, const AString & a_Channel, const AString & a_Message) +bool cPluginManager::CallHookPluginMessage(cClientHandle & a_Client, const AString & a_Channel, cByteBuffer & a_Buffer) { FIND_HOOK(HOOK_PLUGIN_MESSAGE); VERIFY_HOOK; + a_Buffer.CommitRead(); + AString Data; + a_Buffer.ReadAll(Data); + cByteBuffer Temp(Data.size()); + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) { - if ((*itr)->OnPluginMessage(a_Client, a_Channel, a_Message)) + Temp.WriteBuf(Data.c_str(), Data.size()); + if ((*itr)->OnPluginMessage(a_Client, a_Channel, Temp)) { return true; } diff --git a/src/Bindings/PluginManager.h b/src/Bindings/PluginManager.h index 0423d6af1..9ce88e583 100644 --- a/src/Bindings/PluginManager.h +++ b/src/Bindings/PluginManager.h @@ -11,6 +11,7 @@ // fwd: class cBlockEntityWithItems; class cBrewingstandEntity; +class cByteBuffer; class cChunkDesc; class cClientHandle; class cCommandOutputCallback; @@ -267,7 +268,7 @@ public: bool CallHookPlayerUsedItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ); bool CallHookPlayerUsingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); bool CallHookPlayerUsingItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ); - bool CallHookPluginMessage (cClientHandle & a_Client, const AString & a_Channel, const AString & a_Message); + bool CallHookPluginMessage (cClientHandle & a_Client, const AString & a_Channel, cByteBuffer & a_Buffer); bool CallHookPluginsLoaded (void); bool CallHookPostCrafting (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe); bool CallHookPreCrafting (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe); diff --git a/src/ByteBuffer.cpp b/src/ByteBuffer.cpp index 77e3f61fd..4a1e6f13a 100644 --- a/src/ByteBuffer.cpp +++ b/src/ByteBuffer.cpp @@ -117,6 +117,20 @@ cByteBuffer::cByteBuffer(size_t a_BufferSize) : +cByteBuffer::cByteBuffer(const cByteBuffer & a_ByteBuffer) +{ + m_Buffer = new char[a_ByteBuffer.m_BufferSize]; + m_BufferSize = a_ByteBuffer.m_BufferSize; + m_DataStart = a_ByteBuffer.m_DataStart; + m_WritePos = a_ByteBuffer.m_WritePos; + m_ReadPos = a_ByteBuffer.m_ReadPos; + memcpy(m_Buffer, a_ByteBuffer.m_Buffer, m_BufferSize); +} + + + + + cByteBuffer::~cByteBuffer() { CheckValid(); @@ -986,3 +1000,24 @@ size_t cByteBuffer::GetVarIntSize(UInt32 a_Value) +cByteBuffer * cByteBuffer::Create(size_t a_BufferSize) +{ + return new cByteBuffer(a_BufferSize); +} + + + + + +void cByteBuffer::Destroy(cByteBuffer * a_ByteBuffer) +{ + if (a_ByteBuffer != nullptr) + { + delete a_ByteBuffer; + } +} + + + + + diff --git a/src/ByteBuffer.h b/src/ByteBuffer.h index 128a907b2..9800de83d 100644 --- a/src/ByteBuffer.h +++ b/src/ByteBuffer.h @@ -24,50 +24,54 @@ To re-start reading from the beginning, call ResetRead(). This class doesn't implement thread safety, the clients of this class need to provide their own synchronization. */ +// tolua_begin class cByteBuffer { +// tolua_end public: cByteBuffer(size_t a_BufferSize); + cByteBuffer(const cByteBuffer & a_ByteBuffer); ~cByteBuffer(); /** Writes the bytes specified to the ringbuffer. Returns true if successful, false if not */ bool Write(const void * a_Bytes, size_t a_Count); /** Returns the number of bytes that can be successfully written to the ringbuffer */ - size_t GetFreeSpace(void) const; + size_t GetFreeSpace(void) const; // tolua_export /** Returns the number of bytes that are currently in the ringbuffer. Note GetReadableBytes() */ - size_t GetUsedSpace(void) const; + size_t GetUsedSpace(void) const; // tolua_export /** Returns the number of bytes that are currently available for reading (may be less than UsedSpace due to some data having been read already) */ - size_t GetReadableSpace(void) const; + size_t GetReadableSpace(void) const; // tolua_export /** Returns the current data start index. For debugging purposes. */ size_t GetDataStart(void) const { return m_DataStart; } /** Returns true if the specified amount of bytes are available for reading */ - bool CanReadBytes(size_t a_Count) const; + bool CanReadBytes(size_t a_Count) const; // tolua_export /** Returns true if the specified amount of bytes are available for writing */ - bool CanWriteBytes(size_t a_Count) const; + bool CanWriteBytes(size_t a_Count) const; // tolua_export // Read the specified datatype and advance the read pointer; return true if successfully read: - bool ReadBEInt8 (Int8 & a_Value); - bool ReadBEInt16 (Int16 & a_Value); - bool ReadBEInt32 (Int32 & a_Value); - bool ReadBEInt64 (Int64 & a_Value); - bool ReadBEUInt8 (UInt8 & a_Value); - bool ReadBEUInt16 (UInt16 & a_Value); - bool ReadBEUInt32 (UInt32 & a_Value); - bool ReadBEUInt64 (UInt64 & a_Value); - bool ReadBEFloat (float & a_Value); - bool ReadBEDouble (double & a_Value); - bool ReadBool (bool & a_Value); - bool ReadVarInt32 (UInt32 & a_Value); - bool ReadVarInt64 (UInt64 & a_Value); - bool ReadVarUTF8String (AString & a_Value); // string length as VarInt, then string as UTF-8 - bool ReadLEInt (int & a_Value); - bool ReadPosition64 (int & a_BlockX, int & a_BlockY, int & a_BlockZ); + bool ReadBEInt8 (Int8 & a_Value); // exported in manual bindings + bool ReadBEInt16 (Int16 & a_Value); // exported in manual bindings + bool ReadBEInt32 (Int32 & a_Value); // exported in manual bindings + bool ReadBEInt64 (Int64 & a_Value); // exported in manual bindings + bool ReadBEUInt8 (UInt8 & a_Value); // exported in manual bindings + bool ReadBEUInt16 (UInt16 & a_Value); // exported in manual bindings + bool ReadBEUInt32 (UInt32 & a_Value); // exported in manual bindings + bool ReadBEUInt64 (UInt64 & a_Value); // exported in manual bindings + bool ReadBEFloat (float & a_Value); // exported in manual bindings + bool ReadBEDouble (double & a_Value); // exported in manual bindings + bool ReadBool (bool & a_Value); // exported in manual bindings + bool ReadVarInt32 (UInt32 & a_Value); // exported in manual bindings + bool ReadVarInt64 (UInt64 & a_Value); // exported in manual bindings + // string length as VarInt, then string as UTF-8. + bool ReadVarUTF8String (AString & a_Value); // exported in manual bindings + bool ReadLEInt (int & a_Value); // exported in manual bindings + bool ReadPosition64 (int & a_BlockX, int & a_BlockY, int & a_BlockZ); // exported in manual bindings /** Reads VarInt, assigns it to anything that can be assigned from an UInt64 (unsigned short, char, Byte, double, ...) */ template bool ReadVarInt(T & a_Value) @@ -82,22 +86,23 @@ public: } // Write the specified datatype; return true if successfully written - bool WriteBEInt8 (Int8 a_Value); - bool WriteBEInt16 (Int16 a_Value); - bool WriteBEInt32 (Int32 a_Value); - bool WriteBEInt64 (Int64 a_Value); - bool WriteBEUInt8 (UInt8 a_Value); - bool WriteBEUInt16 (UInt16 a_Value); - bool WriteBEUInt32 (UInt32 a_Value); - bool WriteBEUInt64 (UInt64 a_Value); - bool WriteBEFloat (float a_Value); - bool WriteBEDouble (double a_Value); - bool WriteBool (bool a_Value); - bool WriteVarInt32 (UInt32 a_Value); - bool WriteVarInt64 (UInt64 a_Value); - bool WriteVarUTF8String (const AString & a_Value); // string length as VarInt, then string as UTF-8 - bool WriteLEInt32 (Int32 a_Value); - bool WritePosition64 (Int32 a_BlockX, Int32 a_BlockY, Int32 a_BlockZ); + bool WriteBEInt8 (Int8 a_Value); // tolua_export + bool WriteBEInt16 (Int16 a_Value); // tolua_export + bool WriteBEInt32 (Int32 a_Value); // tolua_export + bool WriteBEInt64 (Int64 a_Value); // tolua_export + bool WriteBEUInt8 (UInt8 a_Value); // tolua_export + bool WriteBEUInt16 (UInt16 a_Value); // tolua_export + bool WriteBEUInt32 (UInt32 a_Value); // tolua_export + bool WriteBEUInt64 (UInt64 a_Value); // tolua_export + bool WriteBEFloat (float a_Value); // tolua_export + bool WriteBEDouble (double a_Value); // tolua_export + bool WriteBool (bool a_Value); // tolua_export + bool WriteVarInt32 (UInt32 a_Value); // tolua_export + bool WriteVarInt64 (UInt64 a_Value); // tolua_export + // string length as VarInt, then string as UTF-8 + bool WriteVarUTF8String (const AString & a_Value); // tolua_export + bool WriteLEInt32 (Int32 a_Value); // tolua_export + bool WritePosition64 (Int32 a_BlockX, Int32 a_BlockY, Int32 a_BlockZ); // tolua_export /** Reads a_Count bytes into a_Buffer; returns true if successful */ bool ReadBuf(void * a_Buffer, size_t a_Count); @@ -106,31 +111,35 @@ public: bool WriteBuf(const void * a_Buffer, size_t a_Count); /** Reads a_Count bytes into a_String; returns true if successful */ - bool ReadString(AString & a_String, size_t a_Count); + bool ReadString(AString & a_String, size_t a_Count); // exported im manual bindings /** Skips reading by a_Count bytes; returns false if not enough bytes in the ringbuffer */ - bool SkipRead(size_t a_Count); + bool SkipRead(size_t a_Count); // tolua_export /** Reads all available data into a_Data */ - void ReadAll(AString & a_Data); + void ReadAll(AString & a_Data); // exported in manual bindings /** Reads the specified number of bytes and writes it into the destinatio bytebuffer. Returns true on success. */ bool ReadToByteBuffer(cByteBuffer & a_Dst, size_t a_NumBytes); /** Removes the bytes that have been read from the ringbuffer */ - void CommitRead(void); + void CommitRead(void); // tolua_export /** Restarts next reading operation at the start of the ringbuffer */ - void ResetRead(void); + void ResetRead(void); // tolua_export /** Re-reads the data that has been read since the last commit to the current readpos. Used by ProtoProxy to duplicate communication */ - void ReadAgain(AString & a_Out); + void ReadAgain(AString & a_Out); // exported in manual bindings /** Checks if the internal state is valid (read and write positions in the correct bounds) using ASSERTs */ - void CheckValid(void) const; + void CheckValid(void) const; // tolua_export /** Gets the number of bytes that are needed to represent the given VarInt */ - static size_t GetVarIntSize(UInt32 a_Value); + static size_t GetVarIntSize(UInt32 a_Value); // tolua_export + + static cByteBuffer * Create(size_t a_BufferSize); // tolua_export + + static void Destroy(cByteBuffer * a_ByteBuffer); // tolua_export protected: char * m_Buffer; @@ -148,7 +157,7 @@ protected: /** Advances the m_ReadPos by a_Count bytes */ void AdvanceReadPos(size_t a_Count); -} ; +}; // tolua_export diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 11e2f8376..f3fd7a824 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -22,6 +22,8 @@ SET (SRCS BoundingBox.cpp ByteBuffer.cpp ChatColor.cpp + ChannelCallback.cpp + ChannelManager.cpp Chunk.cpp ChunkData.cpp ChunkMap.cpp @@ -90,6 +92,8 @@ SET (HDRS BuildInfo.h.cmake ByteBuffer.h ChatColor.h + ChannelCallback.h + ChannelManager.h Chunk.h ChunkData.h ChunkDataCallback.h diff --git a/src/ChannelCallback.cpp b/src/ChannelCallback.cpp new file mode 100644 index 000000000..efa11d69a --- /dev/null +++ b/src/ChannelCallback.cpp @@ -0,0 +1,34 @@ + +// ChannelCallback.cpp + +// Implements the cChannelCallback class representing the callbacks used for plugin channel management + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "ChannelCallback.h" +#include "ByteBuffer.h" +#include "ClientHandle.h" + +cChannelCallback::cChannelCallback(cPluginLua & a_Plugin, cLuaState::cCallbackPtr & a_Callback) +{ + m_Plugin = &a_Plugin; + m_Callback = std::move(a_Callback); +} + +void cChannelCallback::Call(cClientHandle & a_Handle, const cByteBuffer & a_Data) +{ + cCSLock Lock(m_CSPlugin); + if (m_Callback->IsValid()) + { + m_Callback->Call(a_Handle.GetPlayer(), a_Data); + } +} + + + + + +bool cChannelCallback::BelongsTo(const cPluginLua * a_Plugin) +{ + return (a_Plugin == m_Plugin); +} diff --git a/src/ChannelCallback.h b/src/ChannelCallback.h new file mode 100644 index 000000000..e9fc145f5 --- /dev/null +++ b/src/ChannelCallback.h @@ -0,0 +1,14 @@ +#include "Bindings/PluginLua.h" + +class cByteBuffer; + +class cChannelCallback +{ + cLuaState::cCallbackPtr m_Callback; + cPluginLua * m_Plugin; + cCriticalSection m_CSPlugin; +public: + cChannelCallback(cPluginLua & a_Plugin, cLuaState::cCallbackPtr & a_Callback); + void Call(cClientHandle & a_Handle, const cByteBuffer & a_Data); + bool BelongsTo(const cPluginLua * a_Plugin); +}; diff --git a/src/ChannelManager.cpp b/src/ChannelManager.cpp new file mode 100644 index 000000000..3d0f326b7 --- /dev/null +++ b/src/ChannelManager.cpp @@ -0,0 +1,222 @@ +// ChannelManager.cpp + +// Implements the cChannelManager class which stores and calls channel callbacks + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include + +#include "ByteBuffer.h" +#include "ChannelManager.h" +#include "ChannelCallback.h" +#include "ClientHandle.h" +#include "Entities/Player.h" +#include "OSSupport/CriticalSection.h" +#include "World.h" + + + + + +class cChannelBroadcastCallback : public cPlayerListCallback +{ +public: + cChannelBroadcastCallback(const AString & a_Channel, const AString & a_Data) + : m_Channel(a_Channel), m_Data(a_Data){} + + virtual bool Item(cPlayer * a_Type) + { + cClientHandle * Handle = a_Type->GetClientHandle(); + if (Handle->HasPluginChannel(m_Channel)) + { + Handle->SendPluginMessage(m_Channel, m_Data); + } + return false; + } +private: + AString m_Channel; + AString m_Data; +}; + + + + + +bool cChannelManager::RegisterChannel(const AString & a_Channel, cChannelCallbackPtr a_Callback) +{ + cCSLock Lock(m_CSCallbacks); + auto Result = m_ChannelCallbacks.emplace(a_Channel, a_Callback); + + return Result.second; +} + + + + + +bool cChannelManager::RemoveChannel(const AString & a_Channel) +{ + cCSLock Lock(m_CSCallbacks); + auto callback = m_ChannelCallbacks.find(a_Channel); + if (callback != m_ChannelCallbacks.end()) + { + m_ChannelCallbacks.erase(callback); + return true; + } + return false; +} + + + + + +void cChannelManager::AddClientToChannel(cClientHandle & a_Client, const AString & a_Channel) +{ + a_Client.RegisterChannel(a_Channel); + a_Client.SendPluginMessage("REGISTER", a_Channel); +} + + + + + +void cChannelManager::AddClientToChannels(cClientHandle & a_Client, const AString & a_Channels) +{ + auto channels = BreakApartChannels(a_Channels); + a_Client.RegisterChannels(channels); +} + + + + + +void cChannelManager::RemoveClientFromChannel(cClientHandle & a_Client, const AString & a_Channel) +{ + a_Client.UnregisterChannel(a_Channel); + a_Client.SendPluginMessage("UNREGISTER", a_Channel); +} + + + + + +void cChannelManager::RemoveClientFromChannels(cClientHandle & a_Client, const AString & a_Channels) +{ + auto channels = BreakApartChannels(a_Channels); + a_Client.UnregisterChannels(channels); +} + + + + + +bool cChannelManager::ClientHasChannel(cClientHandle & a_Client, const AString & a_Channel) +{ + return a_Client.HasPluginChannel(a_Channel); +} + + + + + +void cChannelManager::HandleChannelMessage(cClientHandle & a_Client, const AString & a_Channel, cByteBuffer & a_Data) +{ + bool HasChannel = ClientHasChannel(a_Client, a_Channel); + + // If the client has not registered to send and recieve this channel, log it and send an unregister message. + // Otherwise try to use it. + if (HasChannel) + { + cCSLock Lock(m_CSCallbacks); + + auto Callback = m_ChannelCallbacks.find(a_Channel); + + // If a callback has been registered for this channel, call it. + // Otherwise, call the hook + if (HasChannel && (Callback != m_ChannelCallbacks.end())) + { + Callback->second->Call(a_Client, a_Data); + } + else + { + cPluginManager::Get()->CallHookPluginMessage(a_Client, a_Channel, a_Data); + } + } + else + { + // Ignore if client sent something but didn't register the channel first + LOGD("Player %s sent a plugin message on channel \"%s\", but didn't REGISTER it first", a_Client.GetUsername().c_str(), a_Channel.c_str()); + a_Client.SendPluginMessage("UNREGISTER", a_Channel); + } + +} + + + + + +void cChannelManager::HandlePluginUnloading(const cPluginLua * a_Plugin) +{ + cCSLock Lock(m_CSCallbacks); + + for (auto it = m_ChannelCallbacks.begin(); it != m_ChannelCallbacks.end();) + { + if (!it->second->BelongsTo(a_Plugin)) + { + ++it; + } + else + { + it = m_ChannelCallbacks.erase(it); + } + } +} + + + + + +void cChannelManager::BroadcastChannelMessage(const AString & a_Channel, cByteBuffer & a_Data, cWorld * a_World) +{ + AString Temp; + a_Data.ReadAll(Temp); + if (a_World != nullptr) + { + cChannelBroadcastCallback Callback(a_Channel, Temp); + a_World->ForEachPlayer(Callback); + } + else + { + // TODO: Implement server-wide broadcast + } +} + + + + + +AStringVector cChannelManager::BreakApartChannels(const AString & a_Channels) +{ + // Break the string on each NUL character. + // Note that StringSplit() doesn't work on this because NUL is a special char - string terminator + size_t len = a_Channels.size(); + size_t first = 0; + AStringVector res; + for (size_t i = 0; i < len; i++) + { + if (a_Channels[i] != 0) + { + continue; + } + if (i > first) + { + res.push_back(a_Channels.substr(first, i - first)); + } + first = i + 1; + } // for i - a_PluginChannels[] + if (first < len) + { + res.push_back(a_Channels.substr(first, len - first)); + } + return res; +} diff --git a/src/ChannelManager.h b/src/ChannelManager.h new file mode 100644 index 000000000..874588d29 --- /dev/null +++ b/src/ChannelManager.h @@ -0,0 +1,66 @@ +#pragma once + +#include +#include + +#include "StringUtils.h" + + +class cClientHandle; +class cCriticalSection; +class cPluginLua; +class cWorld; +class cChannelCallback; +class cByteBuffer; + +// tolua_begin +class cChannelManager +{ +// tolua_end +public: + typedef SharedPtr cChannelCallbackPtr; + typedef std::unordered_map CallbackMap; + + /** Adds a client to a specific channel. */ + void AddClientToChannel (cClientHandle & a_Client, const AString & a_Channel); // tolua_export + + /** Adds a client to multiple channels at once. */ + void AddClientToChannels (cClientHandle & a_Client, const AString & a_Channels); + + /** Removes a client from a specific channel. */ + void RemoveClientFromChannel (cClientHandle & a_Client, const AString & a_Channel); // tolua_export + + /** Removes a client from multiple channels at once. */ + void RemoveClientFromChannels(cClientHandle & a_Client, const AString & a_Channels); + + /** Returns true if the client is registered for the specified channel. */ + bool ClientHasChannel (cClientHandle & a_Client, const AString & a_Channel); + + /** Broadcasts a channel message to all clients. + If a_World is provided, broadcast is limited to that world. */ + void BroadcastChannelMessage (const AString & a_Channel, cByteBuffer & a_Data, cWorld * a_World = nullptr); // tolua_export + + /** Registers a handler for a specific channel. + Returns true if registration was successful. */ + bool RegisterChannel (const AString & a_Channel, cChannelCallbackPtr a_Callback); // Exported in manual bindings + + /** Removes the handler for the specified channel. + Returns true if the handler was removed. */ + bool RemoveChannel (const AString & a_Channel); // tolua_export + + + void HandleChannelMessage (cClientHandle & a_Client, const AString & a_Channel, cByteBuffer & a_Data); + + + void HandlePluginUnloading (const cPluginLua * a_Plugin); + +private: + + AStringVector BreakApartChannels(const AString & a_Channels); + + /** This protects m_ChannelCallbacks */ + cCriticalSection m_CSCallbacks; + + CallbackMap m_ChannelCallbacks; + +}; // tolua_export diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index 997510684..b44f23abe 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -803,68 +803,25 @@ void cClientHandle::HandlePlayerPos(double a_PosX, double a_PosY, double a_PosZ, -void cClientHandle::HandlePluginMessage(const AString & a_Channel, const AString & a_Message) +void cClientHandle::RegisterChannel(const AString & a_Channel) { - if (a_Channel == "REGISTER") - { - if (HasPluginChannel(a_Channel)) - { - SendPluginMessage("UNREGISTER", a_Channel); - return; // Can't register again if already taken - kinda defeats the point of plugin messaging! - } - - RegisterPluginChannels(BreakApartPluginChannels(a_Message)); - } - else if (a_Channel == "UNREGISTER") - { - UnregisterPluginChannels(BreakApartPluginChannels(a_Message)); - } - else if (!HasPluginChannel(a_Channel)) - { - // Ignore if client sent something but didn't register the channel first - LOGD("Player %s sent a plugin message on channel \"%s\", but didn't REGISTER it first", GetUsername().c_str(), a_Channel.c_str()); - SendPluginMessage("UNREGISTER", a_Channel); - return; - } - - cPluginManager::Get()->CallHookPluginMessage(*this, a_Channel, a_Message); + m_PluginChannels.insert(a_Channel); } -AStringVector cClientHandle::BreakApartPluginChannels(const AString & a_PluginChannels) +void cClientHandle::UnregisterChannel(const AString & a_Channel) { - // Break the string on each NUL character. - // Note that StringSplit() doesn't work on this because NUL is a special char - string terminator - size_t len = a_PluginChannels.size(); - size_t first = 0; - AStringVector res; - for (size_t i = 0; i < len; i++) - { - if (a_PluginChannels[i] != 0) - { - continue; - } - if (i > first) - { - res.push_back(a_PluginChannels.substr(first, i - first)); - } - first = i + 1; - } // for i - a_PluginChannels[] - if (first < len) - { - res.push_back(a_PluginChannels.substr(first, len - first)); - } - return res; + m_PluginChannels.erase(a_Channel); } -void cClientHandle::RegisterPluginChannels(const AStringVector & a_ChannelList) +void cClientHandle::RegisterChannels(const AStringVector & a_ChannelList) { for (AStringVector::const_iterator itr = a_ChannelList.begin(), end = a_ChannelList.end(); itr != end; ++itr) { @@ -876,7 +833,7 @@ void cClientHandle::RegisterPluginChannels(const AStringVector & a_ChannelList) -void cClientHandle::UnregisterPluginChannels(const AStringVector & a_ChannelList) +void cClientHandle::UnregisterChannels(const AStringVector & a_ChannelList) { for (AStringVector::const_iterator itr = a_ChannelList.begin(), end = a_ChannelList.end(); itr != end; ++itr) { @@ -3026,7 +2983,7 @@ void cClientHandle::SetViewDistance(int a_ViewDistance) bool cClientHandle::HasPluginChannel(const AString & a_PluginChannel) { - return (m_PluginChannels.find(a_PluginChannel) != m_PluginChannels.end()); + return (m_PluginChannels.find(a_PluginChannel) != m_PluginChannels.end()) || (a_PluginChannel == "REGISTER") || (a_PluginChannel == "UNREGISTER"); } @@ -3185,7 +3142,3 @@ void cClientHandle::OnError(int a_ErrorCode, const AString & a_ErrorMsg) } SocketClosed(); } - - - - diff --git a/src/ClientHandle.h b/src/ClientHandle.h index c49de647f..73e2105d4 100644 --- a/src/ClientHandle.h +++ b/src/ClientHandle.h @@ -331,7 +331,6 @@ public: // tolua_export void HandlePlayerPos(double a_PosX, double a_PosY, double a_PosZ, double a_Stance, bool a_IsOnGround); - void HandlePluginMessage (const AString & a_Channel, const AString & a_Message); void HandleRespawn (void); void HandleRightClick (int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, const cItem & a_HeldItem); void HandleSlotSelected (Int16 a_SlotNum); @@ -368,6 +367,16 @@ public: // tolua_export bool IsPlayerChunkSent(); + void RegisterChannel(const AString & a_Channel); + + void UnregisterChannel(const AString & a_Channel); + + /** Adds all of the channels to the list of current plugin channels. Handles duplicates gracefully. */ + void RegisterChannels(const AStringVector & a_ChannelList); + + /** Removes all of the channels from the list of current plugin channels. Ignores channels that are not found. */ + void UnregisterChannels(const AStringVector & a_ChannelList); + private: friend class cServer; // Needs access to SetSelf() @@ -528,15 +537,6 @@ private: /** The clients will receive a finished dig animation */ void FinishDigAnimation(); - /** Converts the protocol-formatted channel list (NUL-separated) into a proper string vector. */ - AStringVector BreakApartPluginChannels(const AString & a_PluginChannels); - - /** Adds all of the channels to the list of current plugin channels. Handles duplicates gracefully. */ - void RegisterPluginChannels(const AStringVector & a_ChannelList); - - /** Removes all of the channels from the list of current plugin channels. Ignores channels that are not found. */ - void UnregisterPluginChannels(const AStringVector & a_ChannelList); - /** Called when the network socket has been closed. */ void SocketClosed(void); @@ -549,8 +549,3 @@ private: virtual void OnRemoteClosed(void) override; virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override; }; // tolua_export - - - - - diff --git a/src/Protocol/Protocol18x.cpp b/src/Protocol/Protocol18x.cpp index ae9571f03..efd9cd5a9 100644 --- a/src/Protocol/Protocol18x.cpp +++ b/src/Protocol/Protocol18x.cpp @@ -16,6 +16,7 @@ Implements the 1.8.x protocol classes: #include "Packetizer.h" #include "../ClientHandle.h" +#include "../ChannelManager.h" #include "../Root.h" #include "../Server.h" #include "../World.h" @@ -2483,10 +2484,24 @@ void cProtocol180::HandlePacketPluginMessage(cByteBuffer & a_ByteBuffer) return; } - // Read the plugin message and relay to clienthandle: + // Read the rest of the message + cByteBuffer Buffer = a_ByteBuffer; AString Data; VERIFY(a_ByteBuffer.ReadString(Data, a_ByteBuffer.GetReadableSpace() - 1)); // Always succeeds - m_Client->HandlePluginMessage(Channel, Data); + + auto manager = cRoot::Get()->GetServer()->GetChannelManager(); + // Register the client on the requested channels + if (Channel == "REGISTER") + { + manager->AddClientToChannels(*m_Client, Data); + } + else if (Channel == "UNREGISTER") + { + manager->RemoveClientFromChannels(*m_Client, Data); + } + + // Call the channel message handler + manager->HandleChannelMessage(*m_Client, Channel, Buffer); } @@ -2738,10 +2753,7 @@ void cProtocol180::HandleVanillaPluginMessage(cByteBuffer & a_ByteBuffer, const } LOG("Unhandled vanilla plugin channel: \"%s\".", a_Channel.c_str()); - // Read the payload and send it through to the clienthandle: - AString Message; - VERIFY(a_ByteBuffer.ReadString(Message, a_ByteBuffer.GetReadableSpace() - 1)); - m_Client->HandlePluginMessage(a_Channel, Message); + cRoot::Get()->GetServer()->GetChannelManager()->HandleChannelMessage(*m_Client, a_Channel, a_ByteBuffer); } @@ -3592,6 +3604,3 @@ void cProtocol180::WriteEntityProperties(cPacketizer & a_Pkt, const cEntity & a_ a_Pkt.WriteBEInt32(0); // NumProperties } - - - diff --git a/src/Protocol/Protocol19x.cpp b/src/Protocol/Protocol19x.cpp index c11e05565..a8c916e5b 100644 --- a/src/Protocol/Protocol19x.cpp +++ b/src/Protocol/Protocol19x.cpp @@ -22,6 +22,7 @@ Implements the 1.9.x protocol classes: #include "Packetizer.h" #include "../ClientHandle.h" +#include "../ChannelManager.h" #include "../Root.h" #include "../Server.h" #include "../World.h" @@ -2552,10 +2553,24 @@ void cProtocol190::HandlePacketPluginMessage(cByteBuffer & a_ByteBuffer) return; } - // Read the plugin message and relay to clienthandle: + // Read the rest of the message + cByteBuffer Buffer = a_ByteBuffer; AString Data; VERIFY(a_ByteBuffer.ReadString(Data, a_ByteBuffer.GetReadableSpace() - 1)); // Always succeeds - m_Client->HandlePluginMessage(Channel, Data); + + auto manager = cRoot::Get()->GetServer()->GetChannelManager(); + // Register the client on the requested channels + if (Channel == "REGISTER") + { + manager->AddClientToChannels(*m_Client, Data); + } + else if (Channel == "UNREGISTER") + { + manager->RemoveClientFromChannels(*m_Client, Data); + } + + // Call the channel message handler + manager->HandleChannelMessage(*m_Client, Channel, Buffer); } @@ -2843,10 +2858,7 @@ void cProtocol190::HandleVanillaPluginMessage(cByteBuffer & a_ByteBuffer, const } LOG("Unhandled vanilla plugin channel: \"%s\".", a_Channel.c_str()); - // Read the payload and send it through to the clienthandle: - AString Message; - VERIFY(a_ByteBuffer.ReadString(Message, a_ByteBuffer.GetReadableSpace() - 1)); - m_Client->HandlePluginMessage(a_Channel, Message); + cRoot::Get()->GetServer()->GetChannelManager()->HandleChannelMessage(*m_Client, a_Channel, a_ByteBuffer); } @@ -4381,8 +4393,3 @@ void cProtocol194::SendUpdateSign(int a_BlockX, int a_BlockY, int a_BlockZ, cons Writer.Finish(); Pkt.WriteBuf(Writer.GetResult().data(), Writer.GetResult().size()); } - - - - - diff --git a/src/Root.h b/src/Root.h index 722554332..864253cc7 100644 --- a/src/Root.h +++ b/src/Root.h @@ -241,8 +241,3 @@ private: static void InputThread(cRoot & a_Params); }; // tolua_export - - - - - diff --git a/src/Server.cpp b/src/Server.cpp index ba469bd3e..7eb99dfde 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -17,6 +17,7 @@ #include "WebAdmin.h" #include "Protocol/ProtocolRecognizer.h" #include "CommandOutput.h" +#include "ChannelManager.h" #include "IniFile.h" #include "Vector3.h" @@ -89,6 +90,15 @@ public: +cServer::~cServer() +{ + m_ChannelManager = nullptr; +} + + + + + //////////////////////////////////////////////////////////////////////////////// // cServer::cTickThread: @@ -147,6 +157,9 @@ cServer::cServer(void) : { // Initialize the LuaStateTracker singleton before the app goes multithreaded: cLuaStateTracker::GetStats(); + + // Create a new channel manager + m_ChannelManager = UniquePtr(new cChannelManager()); } @@ -705,3 +718,7 @@ void cServer::AuthenticateUser(int a_ClientID, const AString & a_Name, const ASt +cChannelManager * cServer::GetChannelManager() +{ + return m_ChannelManager.get(); +} diff --git a/src/Server.h b/src/Server.h index 600e7ca97..6dbbafd11 100644 --- a/src/Server.h +++ b/src/Server.h @@ -40,6 +40,7 @@ typedef std::list cClientHandlePtrs; typedef std::list cClientHandles; class cCommandOutputCallback; class cSettingsRepositoryInterface; +class cChannelManager; namespace Json @@ -57,7 +58,7 @@ class cServer public: // tolua_end - virtual ~cServer() {} + virtual ~cServer(); bool InitServer(cSettingsRepositoryInterface & a_Settings, bool a_ShouldAuth); // tolua_begin @@ -143,6 +144,9 @@ public: from the settings. */ bool ShouldAllowMultiWorldTabCompletion(void) const { return m_ShouldAllowMultiWorldTabCompletion; } + /** Returns the channel manager. */ + cChannelManager * GetChannelManager(); // tolua_export + private: friend class cRoot; // so cRoot can create and destroy cServer @@ -241,6 +245,8 @@ private: Initialized in InitServer(), used in Start(). */ AStringVector m_Ports; + UniquePtr m_ChannelManager; + cServer(void); @@ -256,7 +262,3 @@ private: /** Ticks the clients in m_Clients, manages the list in respect to removing clients */ void TickClients(float a_Dt); }; // tolua_export - - - - -- cgit v1.2.3