From 360c632e36acfe8b271c3212feef9b6f93623ba1 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Wed, 28 Jan 2015 15:14:05 +0100 Subject: [PATCH 01/95] cNetwork: Exported the Connect() method and cTCPLink class to Lua. --- src/Bindings/CMakeLists.txt | 3 + src/Bindings/LuaState.cpp | 12 + src/Bindings/LuaState.h | 2 + src/Bindings/LuaTCPLink.cpp | 229 +++++++++++++++++++ src/Bindings/LuaTCPLink.h | 77 +++++++ src/Bindings/ManualBindings.cpp | 3 +- src/Bindings/ManualBindings.h | 10 + src/Bindings/ManualBindings_Network.cpp | 279 ++++++++++++++++++++++++ 8 files changed, 614 insertions(+), 1 deletion(-) create mode 100644 src/Bindings/LuaTCPLink.cpp create mode 100644 src/Bindings/LuaTCPLink.h create mode 100644 src/Bindings/ManualBindings_Network.cpp diff --git a/src/Bindings/CMakeLists.txt b/src/Bindings/CMakeLists.txt index d47579cd6..a72611f76 100644 --- a/src/Bindings/CMakeLists.txt +++ b/src/Bindings/CMakeLists.txt @@ -9,8 +9,10 @@ SET (SRCS DeprecatedBindings.cpp LuaChunkStay.cpp LuaState.cpp + LuaTCPLink.cpp LuaWindow.cpp ManualBindings.cpp + ManualBindings_Network.cpp ManualBindings_RankManager.cpp Plugin.cpp PluginLua.cpp @@ -24,6 +26,7 @@ SET (HDRS LuaChunkStay.h LuaFunctions.h LuaState.h + LuaTCPLink.h LuaWindow.h ManualBindings.h Plugin.h diff --git a/src/Bindings/LuaState.cpp b/src/Bindings/LuaState.cpp index 01d3ac687..4de34c88d 100644 --- a/src/Bindings/LuaState.cpp +++ b/src/Bindings/LuaState.cpp @@ -656,6 +656,18 @@ void cLuaState::Push(cItems * a_Items) +void cLuaState::Push(cLuaTCPLink * a_TCPLink) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, a_TCPLink, "cTCPLink"); + m_NumCurrentFunctionArgs += 1; +} + + + + + void cLuaState::Push(cMonster * a_Monster) { ASSERT(IsValid()); diff --git a/src/Bindings/LuaState.h b/src/Bindings/LuaState.h index 97e6b47e1..0dcd248fe 100644 --- a/src/Bindings/LuaState.h +++ b/src/Bindings/LuaState.h @@ -59,6 +59,7 @@ class cTNTEntity; class cHopperEntity; class cBlockEntity; class cBoundingBox; +class cLuaTCPLink; typedef cBoundingBox * pBoundingBox; typedef cWorld * pWorld; @@ -202,6 +203,7 @@ public: void Push(cHopperEntity * a_Hopper); void Push(cItem * a_Item); void Push(cItems * a_Items); + void Push(cLuaTCPLink * a_TCPLink); void Push(cMonster * a_Monster); void Push(cPickup * a_Pickup); void Push(cPlayer * a_Player); diff --git a/src/Bindings/LuaTCPLink.cpp b/src/Bindings/LuaTCPLink.cpp new file mode 100644 index 000000000..f88aeff84 --- /dev/null +++ b/src/Bindings/LuaTCPLink.cpp @@ -0,0 +1,229 @@ + +// LuaTCPLink.cpp + +// Implements the cLuaTCPLink class representing a Lua wrapper for the cTCPLink class and the callbacks it needs + +#include "Globals.h" +#include "LuaTCPLink.h" + + + + + +cLuaTCPLink::cLuaTCPLink(cPluginLua & a_Plugin, int a_CallbacksTableStackPos): + m_Plugin(a_Plugin), + m_Callbacks(a_Plugin.GetLuaState(), a_CallbacksTableStackPos) +{ +} + + + + + +bool cLuaTCPLink::Send(const AString & a_Data) +{ + // Safely grab a copy of the link: + cTCPLinkPtr Link = m_Link; + if (Link == nullptr) + { + return false; + } + + // Send the data: + return Link->Send(a_Data); +} + + + + + +AString cLuaTCPLink::GetLocalIP(void) const +{ + // Safely grab a copy of the link: + cTCPLinkPtr Link = m_Link; + if (Link == nullptr) + { + return ""; + } + + // Get the IP address: + return Link->GetLocalIP(); +} + + + + + +UInt16 cLuaTCPLink::GetLocalPort(void) const +{ + // Safely grab a copy of the link: + cTCPLinkPtr Link = m_Link; + if (Link == nullptr) + { + return 0; + } + + // Get the port: + return Link->GetLocalPort(); +} + + + + + +AString cLuaTCPLink::GetRemoteIP(void) const +{ + // Safely grab a copy of the link: + cTCPLinkPtr Link = m_Link; + if (Link == nullptr) + { + return ""; + } + + // Get the IP address: + return Link->GetRemoteIP(); +} + + + + + +UInt16 cLuaTCPLink::GetRemotePort(void) const +{ + // Safely grab a copy of the link: + cTCPLinkPtr Link = m_Link; + if (Link == nullptr) + { + return 0; + } + + // Get the port: + return Link->GetRemotePort(); +} + + + + + +void cLuaTCPLink::Shutdown(void) +{ + // Safely grab a copy of the link: + cTCPLinkPtr Link = m_Link; + if (Link == nullptr) + { + return; + } + + // Shutdown: + Link->Shutdown(); +} + + + + + +void cLuaTCPLink::Close(void) +{ + // Safely grab a copy of the link: + cTCPLinkPtr Link = m_Link; + if (Link == nullptr) + { + return; + } + + // Close the link: + Link->Close(); +} + +void cLuaTCPLink::OnConnected(cTCPLink & a_Link) +{ + // Check if we're still valid: + if (!m_Callbacks.IsValid()) + { + return; + } + + // Call the callback: + cPluginLua::cOperation Op(m_Plugin); + if (!Op().Call(cLuaState::cTableRef(m_Callbacks, "OnConnected"), this)) + { + LOGINFO("cTCPLink OnConnected() callback failed in plugin %s.", m_Plugin.GetName().c_str()); + } +} + + + + + +void cLuaTCPLink::OnError(int a_ErrorCode, const AString & a_ErrorMsg) +{ + // Check if we're still valid: + if (!m_Callbacks.IsValid()) + { + return; + } + + // Call the callback: + cPluginLua::cOperation Op(m_Plugin); + if (!Op().Call(cLuaState::cTableRef(m_Callbacks, "OnError"), this, a_ErrorCode, a_ErrorMsg)) + { + LOGINFO("cTCPLink OnError() callback failed in plugin %s; the link error is %d (%s).", + m_Plugin.GetName().c_str(), a_ErrorCode, a_ErrorMsg.c_str() + ); + } +} + + + + + +void cLuaTCPLink::OnLinkCreated(cTCPLinkPtr a_Link) +{ + // Store the cTCPLink for later use: + m_Link = a_Link; +} + + + + + +void cLuaTCPLink::OnReceivedData(const char * a_Data, size_t a_Length) +{ + // Check if we're still valid: + if (!m_Callbacks.IsValid()) + { + return; + } + + // Call the callback: + cPluginLua::cOperation Op(m_Plugin); + if (!Op().Call(cLuaState::cTableRef(m_Callbacks, "OnReceivedData"), this, AString(a_Data, a_Length))) + { + LOGINFO("cTCPLink OnReceivedData callback failed in plugin %s.", m_Plugin.GetName().c_str()); + } +} + + + + + +void cLuaTCPLink::OnRemoteClosed(void) +{ + // Check if we're still valid: + if (!m_Callbacks.IsValid()) + { + return; + } + + // Call the callback: + cPluginLua::cOperation Op(m_Plugin); + if (!Op().Call(cLuaState::cTableRef(m_Callbacks, "OnRemoteClosed"), this)) + { + LOGINFO("cTCPLink OnRemoteClosed() callback failed in plugin %s.", m_Plugin.GetName().c_str()); + } + m_Link.reset(); +} + + + + diff --git a/src/Bindings/LuaTCPLink.h b/src/Bindings/LuaTCPLink.h new file mode 100644 index 000000000..54c1d2dbf --- /dev/null +++ b/src/Bindings/LuaTCPLink.h @@ -0,0 +1,77 @@ + +// LuaTCPLink.h + +// Declares the cLuaTCPLink class representing a Lua wrapper for the cTCPLink class and the callbacks it needs + + + + + +#pragma once + +#include "../OSSupport/Network.h" +#include "LuaState.h" +#include "PluginLua.h" + + + + + +class cLuaTCPLink: + public cNetwork::cConnectCallbacks, + public cTCPLink::cCallbacks +{ +public: + /** Creates a new instance of the link, attached to the specified lua state and wrapping the callbacks that are in a table at the specified stack pos. */ + cLuaTCPLink(cPluginLua & a_Plugin, int a_CallbacksTableStackPos); + + /** Sends the data contained in the string to the remote peer. + Returns true if successful, false on immediate failure (queueing the data failed or link not available). */ + bool Send(const AString & a_Data); + + /** Returns the IP address of the local endpoint of the connection. */ + AString GetLocalIP(void) const; + + /** Returns the port used by the local endpoint of the connection. */ + UInt16 GetLocalPort(void) const; + + /** Returns the IP address of the remote endpoint of the connection. */ + AString GetRemoteIP(void) const; + + /** Returns the port used by the remote endpoint of the connection. */ + UInt16 GetRemotePort(void) const; + + /** Closes the link gracefully. + The link will send any queued outgoing data, then it will send the FIN packet. + The link will still receive incoming data from remote until the remote closes the connection. */ + void Shutdown(void); + + /** Drops the connection without any more processing. + Sends the RST packet, queued outgoing and incoming data is lost. */ + void Close(void); + +protected: + /** The plugin for which the link is created. */ + cPluginLua & m_Plugin; + + /** The Lua table that holds the callbacks to be invoked. */ + cLuaState::cRef m_Callbacks; + + /** The underlying link representing the connection. + May be nullptr. */ + cTCPLinkPtr m_Link; + + // cNetwork::cConnectCallbacks overrides: + virtual void OnConnected(cTCPLink & a_Link) override; + virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override; + + // cTCPLink::cCallbacks overrides: + virtual void OnLinkCreated(cTCPLinkPtr a_Link) override; + virtual void OnReceivedData(const char * a_Data, size_t a_Length) override; + virtual void OnRemoteClosed(void) override; + // The OnError() callback is shared with cNetwork::cConnectCallbacks +}; + + + + diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp index 56f2e73bc..24e3f0052 100644 --- a/src/Bindings/ManualBindings.cpp +++ b/src/Bindings/ManualBindings.cpp @@ -256,7 +256,7 @@ static int tolua_Base64Decode(lua_State * tolua_S) -static cPluginLua * GetLuaPlugin(lua_State * L) +cPluginLua * GetLuaPlugin(lua_State * L) { // Get the plugin identification out of LuaState: lua_getglobal(L, LUA_PLUGIN_INSTANCE_VAR_NAME); @@ -3556,6 +3556,7 @@ void ManualBindings::Bind(lua_State * tolua_S) tolua_function(tolua_S, "md5", tolua_md5); BindRankManager(tolua_S); + BindNetwork(tolua_S); tolua_endmodule(tolua_S); } diff --git a/src/Bindings/ManualBindings.h b/src/Bindings/ManualBindings.h index 1b6e65654..74d24d5f5 100644 --- a/src/Bindings/ManualBindings.h +++ b/src/Bindings/ManualBindings.h @@ -1,6 +1,7 @@ #pragma once struct lua_State; +class cPluginLua; @@ -17,8 +18,17 @@ protected: /** Binds the manually implemented cRankManager glue code to tolua_S. Implemented in ManualBindings_RankManager.cpp. */ static void BindRankManager(lua_State * tolua_S); + + /** Binds the manually implemented cNetwork-related API to tolua_S. + Implemented in ManualBindings_Network.cpp. */ + static void BindNetwork(lua_State * tolua_S); }; +extern cPluginLua * GetLuaPlugin(lua_State * L); + + + + diff --git a/src/Bindings/ManualBindings_Network.cpp b/src/Bindings/ManualBindings_Network.cpp new file mode 100644 index 000000000..882af4e9e --- /dev/null +++ b/src/Bindings/ManualBindings_Network.cpp @@ -0,0 +1,279 @@ + +// ManualBindings_Network.cpp + +// Implements the cNetwork-related API bindings for Lua + +#include "Globals.h" +#include "LuaTCPLink.h" +#include "ManualBindings.h" +#include "tolua++/include/tolua++.h" +#include "LuaState.h" +#include "LuaTCPLink.h" + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cNetwork API functions: + +/** Binds cNetwork::Connect */ +static int tolua_cNetwork_Connect(lua_State * L) +{ + // Function signature: + // cNetwork:Connect(Host, Port, Callbacks) -> bool + + cLuaState S(L); + if ( + !S.CheckParamUserTable(1, "cNetwork") || + !S.CheckParamString(2) || + !S.CheckParamNumber(3) || + !S.CheckParamTable(4) || + !S.CheckParamEnd(5) + ) + { + return 0; + } + + // Get the plugin instance: + cPluginLua * Plugin = GetLuaPlugin(L); + if (Plugin == nullptr) + { + // An error message has been already printed in GetLuaPlugin() + S.Push(false); + return 1; + } + + // Read the params: + AString Host; + int Port; + S.GetStackValues(2, Host, Port); + + // Check validity: + if ((Port < 0) || (Port > 65535)) + { + LOGWARNING("cNetwork:Connect() called with invalid port (%d), failing the request.", Port); + S.Push(false); + return 1; + } + + // Create the LuaTCPLink glue class: + auto Link = std::make_shared(*Plugin, 4); + + // Try to connect: + bool res = cNetwork::Connect(Host, static_cast(Port), Link, Link); + S.Push(res); + + return 1; +} + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cTCPLink bindings (routed through cLuaTCPLink): + +static int tolua_cTCPLink_Send(lua_State * L) +{ + // Function signature: + // LinkInstance:Send(DataString) -> bool + + cLuaState S(L); + if ( + !S.CheckParamUserType(1, "cTCPLink") || + !S.CheckParamString(2) || + !S.CheckParamEnd(3) + ) + { + return 0; + } + + // Get the link: + cLuaTCPLink * Link; + if (lua_isnil(L, 1)) + { + LOGWARNING("cTCPLink:Send(): invalid link object. Stack trace:"); + S.LogStackTrace(); + return 0; + } + Link = *static_cast(lua_touserdata(L, 1)); + + // Get the data to send: + AString Data; + S.GetStackValues(2, Data); + + // Send the data: + Link->Send(Data); + return 0; +} + + + + + +static int tolua_cTCPLink_GetLocalIP(lua_State * L) +{ + // Function signature: + // LinkInstance:GetLocalIP() -> string + + cLuaState S(L); + if ( + !S.CheckParamUserType(1, "cTCPLink") || + !S.CheckParamEnd(2) + ) + { + return 0; + } + + // Get the link: + cLuaTCPLink * Link; + if (lua_isnil(L, 1)) + { + LOGWARNING("cTCPLink:GetLocalIP(): invalid link object. Stack trace:"); + S.LogStackTrace(); + return 0; + } + Link = *static_cast(lua_touserdata(L, 1)); + + // Get the IP: + S.Push(Link->GetLocalIP()); + return 1; +} + + + + + +static int tolua_cTCPLink_GetLocalPort(lua_State * L) +{ + // Function signature: + // LinkInstance:GetLocalPort() -> number + + cLuaState S(L); + if ( + !S.CheckParamUserType(1, "cTCPLink") || + !S.CheckParamEnd(2) + ) + { + return 0; + } + + // Get the link: + cLuaTCPLink * Link; + if (lua_isnil(L, 1)) + { + LOGWARNING("cTCPLink:GetLocalPort(): invalid link object. Stack trace:"); + S.LogStackTrace(); + return 0; + } + Link = *static_cast(lua_touserdata(L, 1)); + + // Get the Port: + S.Push(Link->GetLocalPort()); + return 1; +} + + + + + +static int tolua_cTCPLink_GetRemoteIP(lua_State * L) +{ + // Function signature: + // LinkInstance:GetRemoteIP() -> string + + cLuaState S(L); + if ( + !S.CheckParamUserType(1, "cTCPLink") || + !S.CheckParamEnd(2) + ) + { + return 0; + } + + // Get the link: + cLuaTCPLink * Link; + if (lua_isnil(L, 1)) + { + LOGWARNING("cTCPLink:GetRemoteIP(): invalid link object. Stack trace:"); + S.LogStackTrace(); + return 0; + } + Link = *static_cast(lua_touserdata(L, 1)); + + // Get the IP: + S.Push(Link->GetRemoteIP()); + return 1; +} + + + + + +static int tolua_cTCPLink_GetRemotePort(lua_State * L) +{ + // Function signature: + // LinkInstance:GetRemotePort() -> number + + cLuaState S(L); + if ( + !S.CheckParamUserType(1, "cTCPLink") || + !S.CheckParamEnd(2) + ) + { + return 0; + } + + // Get the link: + cLuaTCPLink * Link; + if (lua_isnil(L, 1)) + { + LOGWARNING("cTCPLink:GetRemotePort(): invalid link object. Stack trace:"); + S.LogStackTrace(); + return 0; + } + Link = *static_cast(lua_touserdata(L, 1)); + + // Get the Port: + S.Push(Link->GetRemotePort()); + return 1; +} + + + + + +//////////////////////////////////////////////////////////////////////////////// +// Register the bindings: + +void ManualBindings::BindNetwork(lua_State * tolua_S) +{ + // Create the cNetwork API classes: + tolua_usertype(tolua_S, "cNetwork"); + tolua_cclass(tolua_S, "cNetwork", "cNetwork", "", nullptr); + tolua_usertype(tolua_S, "cTCPLink"); + tolua_cclass(tolua_S, "cTCPLink", "cTCPLink", "", nullptr); + + // Fill in the functions (alpha-sorted): + tolua_beginmodule(tolua_S, "cNetwork"); + tolua_function(tolua_S, "Connect", tolua_cNetwork_Connect); + /* + tolua_function(tolua_S, "HostnameToIP", tolua_cNetwork_HostnameToIP); + tolua_function(tolua_S, "IPToHostname", tolua_cNetwork_IPToHostname); + tolua_function(tolua_S, "Listen", tolua_cNetwork_Listen); + */ + tolua_endmodule(tolua_S); + + tolua_beginmodule(tolua_S, "cTCPLink"); + tolua_function(tolua_S, "Send", tolua_cTCPLink_Send); + tolua_function(tolua_S, "GetLocalIP", tolua_cTCPLink_GetLocalIP); + tolua_function(tolua_S, "GetLocalPort", tolua_cTCPLink_GetLocalPort); + tolua_function(tolua_S, "GetRemoteIP", tolua_cTCPLink_GetRemoteIP); + tolua_function(tolua_S, "GetRemotePort", tolua_cTCPLink_GetRemotePort); + tolua_endmodule(tolua_S); +} + + + + From e098728fa8986aea8997a183839c8fc91c4775f8 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Wed, 28 Jan 2015 15:14:35 +0100 Subject: [PATCH 02/95] cTCPLink: Fixed missing addresses on link connection. --- src/OSSupport/TCPLinkImpl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/OSSupport/TCPLinkImpl.cpp b/src/OSSupport/TCPLinkImpl.cpp index f97db7582..88fb57838 100644 --- a/src/OSSupport/TCPLinkImpl.cpp +++ b/src/OSSupport/TCPLinkImpl.cpp @@ -221,6 +221,8 @@ void cTCPLinkImpl::EventCallback(bufferevent * a_BufferEvent, short a_What, void // Pending connection succeeded, call the connection callback: if (a_What & BEV_EVENT_CONNECTED) { + Self->UpdateLocalAddress(); + Self->UpdateRemoteAddress(); if (Self->m_ConnectCallbacks != nullptr) { Self->m_ConnectCallbacks->OnConnected(*Self); @@ -228,8 +230,6 @@ void cTCPLinkImpl::EventCallback(bufferevent * a_BufferEvent, short a_What, void Self->m_ConnectCallbacks.reset(); return; } - Self->UpdateLocalAddress(); - Self->UpdateRemoteAddress(); } // If the connection has been closed, call the link callback and remove the connection: From 04347084d658baedd6dc615fa34b62d3c19f1d2e Mon Sep 17 00:00:00 2001 From: Mattes D Date: Wed, 28 Jan 2015 15:15:54 +0100 Subject: [PATCH 03/95] NetworkTest plugin: Added cNetwork:Connect test code. --- MCServer/Plugins/NetworkTest/Info.lua | 46 ++++++++++++++ MCServer/Plugins/NetworkTest/NetworkTest.lua | 63 ++++++++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 MCServer/Plugins/NetworkTest/Info.lua create mode 100644 MCServer/Plugins/NetworkTest/NetworkTest.lua diff --git a/MCServer/Plugins/NetworkTest/Info.lua b/MCServer/Plugins/NetworkTest/Info.lua new file mode 100644 index 000000000..6bb639860 --- /dev/null +++ b/MCServer/Plugins/NetworkTest/Info.lua @@ -0,0 +1,46 @@ + +-- Info.lua + +-- Implements the g_PluginInfo standard plugin description + +g_PluginInfo = +{ + Name = "NetworkTest", + Version = "1", + Date = "2015-01-28", + Description = [[Implements test code (and examples) for the cNetwork API]], + + Commands = + { + }, + + ConsoleCommands = + { + net = + { + Subcommands = + { + client = + { + HelpString = "Connects, as a client, to a specified webpage (google.com by default) and downloads its front page using HTTP", + Handler = HandleConsoleNetClient, + ParameterCombinations = + { + { + Params = "", + Help = "Connects, as a client, to google.com and downloads its front page using HTTP", + }, + { + Params = "host [port]", + Help = "Connects, as a client, to the specified host and downloads its front page using HTTP", + }, + }, -- ParameterCombinations + }, -- client + }, -- Subcommands + }, -- net + }, +} + + + + diff --git a/MCServer/Plugins/NetworkTest/NetworkTest.lua b/MCServer/Plugins/NetworkTest/NetworkTest.lua new file mode 100644 index 000000000..1a24d4865 --- /dev/null +++ b/MCServer/Plugins/NetworkTest/NetworkTest.lua @@ -0,0 +1,63 @@ + +-- NetworkTest.lua + +-- Implements a few tests for the cNetwork API + + + + + +function Initialize() + -- Use the InfoReg shared library to process the Info.lua file: + dofile(cPluginManager:GetPluginsPath() .. "/InfoReg.lua") + RegisterPluginInfoCommands() + RegisterPluginInfoConsoleCommands() + + return true +end + + + + + +function HandleConsoleNetClient(a_Split) + -- Get the address to connect to: + local Host = a_Split[3] or "google.com" + local Port = a_Split[4] or 80 + + -- Create the callbacks "personalised" for the address: + local Callbacks = + { + OnConnected = function (a_Link) + LOG("Connected to " .. Host .. ":" .. Port .. ".") + LOG("Connection stats: Remote address: " .. a_Link:GetRemoteIP() .. ":" .. a_Link:GetRemotePort() .. ", Local address: " .. a_Link:GetLocalIP() .. ":" .. a_Link:GetLocalPort()) + LOG("Sending HTTP request for front page.") + a_Link:Send("GET / HTTP/1.0\r\nHost: " .. Host .. "\r\n\r\n") + end, + + OnError = function (a_Link, a_ErrorCode, a_ErrorMsg) + LOG("Connection to " .. Host .. ":" .. Port .. " failed: " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")") + end, + + OnReceivedData = function (a_Link, a_Data) + LOG("Received data from " .. Host .. ":" .. Port .. ":\r\n" .. a_Data) + end, + + OnRemoteClosed = function (a_Link) + LOG("Connection to " .. Host .. ":" .. Port .. " was closed by the remote peer.") + end + } + + -- Queue a connect request: + local res = cNetwork:Connect(Host, Port, Callbacks) + if not(res) then + LOGWARNING("cNetwork:Connect call failed immediately") + return true + end + + return true, "Client connection request queued." +end + + + + From 17498a97a289119debdb651ab898ddea99e86ff9 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Thu, 29 Jan 2015 11:09:56 +0100 Subject: [PATCH 04/95] cNetwork: Exported lookup functions to Lua API. Also added an example in the NetworkTest plugin. --- MCServer/Plugins/NetworkTest/Info.lua | 21 +++++ MCServer/Plugins/NetworkTest/NetworkTest.lua | 42 ++++++++++ src/Bindings/CMakeLists.txt | 2 + src/Bindings/LuaNameLookup.cpp | 88 ++++++++++++++++++++ src/Bindings/LuaNameLookup.h | 46 ++++++++++ src/Bindings/LuaTCPLink.h | 3 +- src/Bindings/ManualBindings_Network.cpp | 83 +++++++++++++++++- 7 files changed, 282 insertions(+), 3 deletions(-) create mode 100644 src/Bindings/LuaNameLookup.cpp create mode 100644 src/Bindings/LuaNameLookup.h diff --git a/MCServer/Plugins/NetworkTest/Info.lua b/MCServer/Plugins/NetworkTest/Info.lua index 6bb639860..8c2604e31 100644 --- a/MCServer/Plugins/NetworkTest/Info.lua +++ b/MCServer/Plugins/NetworkTest/Info.lua @@ -36,6 +36,27 @@ g_PluginInfo = }, }, -- ParameterCombinations }, -- client + + lookup = + { + HelpString = "Looks up the IP addresses corresponding to the given hostname (google.com by default)", + Handler = HandleConsoleNetLookup, + ParameterCombinations = + { + { + Params = "", + Help = "Looks up the IP addresses of google.com.", + }, + { + Params = "Hostname", + Help = "Looks up the IP addresses of the specified hostname.", + }, + { + Params = "IP", + Help = "Looks up the canonical name of the specified IP.", + }, + }, + }, -- lookup }, -- Subcommands }, -- net }, diff --git a/MCServer/Plugins/NetworkTest/NetworkTest.lua b/MCServer/Plugins/NetworkTest/NetworkTest.lua index 1a24d4865..30f34c879 100644 --- a/MCServer/Plugins/NetworkTest/NetworkTest.lua +++ b/MCServer/Plugins/NetworkTest/NetworkTest.lua @@ -61,3 +61,45 @@ end + +function HandleConsoleNetLookup(a_Split) + -- Get the name to look up: + local Addr = a_Split[3] or "google.com" + + -- Create the callbacks "personalised" for the host: + local Callbacks = + { + OnNameResolved = function (a_Hostname, a_IP) + LOG(a_Hostname .. " resolves to " .. a_IP) + end, + + OnError = function (a_Query, a_ErrorCode, a_ErrorMsg) + LOG("Failed to retrieve information for " .. a_Query .. ": " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")") + assert(a_Query == Addr) + end, + + OnFinished = function (a_Query) + LOG("Resolving " .. a_Query .. " has finished.") + assert(a_Query == Addr) + end, + } + + -- Queue both name and IP DNS queries; + -- we don't distinguish between an IP and a hostname in this command so we don't know which one to use: + local res = cNetwork:HostnameToIP(Addr, Callbacks) + if not(res) then + LOGWARNING("cNetwork:HostnameToIP call failed immediately") + return true + end + res = cNetwork:IPToHostname(Addr, Callbacks) + if not(res) then + LOGWARNING("cNetwork:IPToHostname call failed immediately") + return true + end + + return true, "DNS query has been queued." +end + + + + diff --git a/src/Bindings/CMakeLists.txt b/src/Bindings/CMakeLists.txt index a72611f76..13428b5c6 100644 --- a/src/Bindings/CMakeLists.txt +++ b/src/Bindings/CMakeLists.txt @@ -8,6 +8,7 @@ SET (SRCS Bindings.cpp DeprecatedBindings.cpp LuaChunkStay.cpp + LuaNameLookup.cpp LuaState.cpp LuaTCPLink.cpp LuaWindow.cpp @@ -25,6 +26,7 @@ SET (HDRS DeprecatedBindings.h LuaChunkStay.h LuaFunctions.h + LuaNameLookup.h LuaState.h LuaTCPLink.h LuaWindow.h diff --git a/src/Bindings/LuaNameLookup.cpp b/src/Bindings/LuaNameLookup.cpp new file mode 100644 index 000000000..e52d8dbdc --- /dev/null +++ b/src/Bindings/LuaNameLookup.cpp @@ -0,0 +1,88 @@ + +// LuaNameLookup.cpp + +// Implements the cLuaNameLookup class used as the cNetwork API callbacks for name and IP lookups from Lua + +#include "Globals.h" +#include "LuaNameLookup.h" + + + + + +cLuaNameLookup::cLuaNameLookup(const AString & a_Query, cPluginLua & a_Plugin, int a_CallbacksTableStackPos): + m_Plugin(a_Plugin), + m_Callbacks(a_Plugin.GetLuaState(), a_CallbacksTableStackPos), + m_Query(a_Query) +{ +} + + + + + +void cLuaNameLookup::OnNameResolved(const AString & a_Name, const AString & a_IP) +{ + // Check if we're still valid: + if (!m_Callbacks.IsValid()) + { + return; + } + + // Call the callback: + cPluginLua::cOperation Op(m_Plugin); + if (!Op().Call(cLuaState::cTableRef(m_Callbacks, "OnNameResolved"), a_Name, a_IP)) + { + LOGINFO("cNetwork name lookup OnNameResolved callback failed in plugin %s looking up %s. %s resolves to %s.", + m_Plugin.GetName().c_str(), m_Query.c_str(), a_Name.c_str(), a_IP.c_str() + ); + } +} + + + + + +void cLuaNameLookup::OnError(int a_ErrorCode, const AString & a_ErrorMsg) +{ + // Check if we're still valid: + if (!m_Callbacks.IsValid()) + { + return; + } + + // Call the callback: + cPluginLua::cOperation Op(m_Plugin); + if (!Op().Call(cLuaState::cTableRef(m_Callbacks, "OnError"), m_Query, a_ErrorCode, a_ErrorMsg)) + { + LOGINFO("cNetwork name lookup OnError callback failed in plugin %s looking up %s. The error is %d (%s)", + m_Plugin.GetName().c_str(), m_Query.c_str(), a_ErrorCode, a_ErrorMsg.c_str() + ); + } +} + + + + + +void cLuaNameLookup::OnFinished(void) +{ + // Check if we're still valid: + if (!m_Callbacks.IsValid()) + { + return; + } + + // Call the callback: + cPluginLua::cOperation Op(m_Plugin); + if (!Op().Call(cLuaState::cTableRef(m_Callbacks, "OnFinished"), m_Query)) + { + LOGINFO("cNetwork name lookup OnFinished callback failed in plugin %s, looking up %s.", + m_Plugin.GetName().c_str(), m_Query.c_str() + ); + } +} + + + + diff --git a/src/Bindings/LuaNameLookup.h b/src/Bindings/LuaNameLookup.h new file mode 100644 index 000000000..8b456ad90 --- /dev/null +++ b/src/Bindings/LuaNameLookup.h @@ -0,0 +1,46 @@ + +// LuaNameLookup.h + +// Declares the cLuaNameLookup class used as the cNetwork API callbacks for name and IP lookups from Lua + + + + + +#pragma once + +#include "../OSSupport/Network.h" +#include "PluginLua.h" + + + + + +class cLuaNameLookup: + public cNetwork::cResolveNameCallbacks +{ +public: + /** Creates a new instance of the lookup callbacks for the specified query, + attached to the specified lua plugin and wrapping the callbacks that are in a table at the specified stack pos. */ + cLuaNameLookup(const AString & a_Query, cPluginLua & a_Plugin, int a_CallbacksTableStackPos); + +protected: + /** The plugin for which the link is created. */ + cPluginLua & m_Plugin; + + /** The Lua table that holds the callbacks to be invoked. */ + cLuaState::cRef m_Callbacks; + + /** The query used to start the lookup (either hostname or IP). */ + AString m_Query; + + + // cNetwork::cResolveNameCallbacks overrides: + virtual void OnNameResolved(const AString & a_Name, const AString & a_IP) override; + virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override; + virtual void OnFinished(void) override; +}; + + + + diff --git a/src/Bindings/LuaTCPLink.h b/src/Bindings/LuaTCPLink.h index 54c1d2dbf..125cc1b31 100644 --- a/src/Bindings/LuaTCPLink.h +++ b/src/Bindings/LuaTCPLink.h @@ -10,7 +10,6 @@ #pragma once #include "../OSSupport/Network.h" -#include "LuaState.h" #include "PluginLua.h" @@ -22,7 +21,7 @@ class cLuaTCPLink: public cTCPLink::cCallbacks { public: - /** Creates a new instance of the link, attached to the specified lua state and wrapping the callbacks that are in a table at the specified stack pos. */ + /** Creates a new instance of the link, attached to the specified plugin and wrapping the callbacks that are in a table at the specified stack pos. */ cLuaTCPLink(cPluginLua & a_Plugin, int a_CallbacksTableStackPos); /** Sends the data contained in the string to the remote peer. diff --git a/src/Bindings/ManualBindings_Network.cpp b/src/Bindings/ManualBindings_Network.cpp index 882af4e9e..3123ef885 100644 --- a/src/Bindings/ManualBindings_Network.cpp +++ b/src/Bindings/ManualBindings_Network.cpp @@ -9,6 +9,7 @@ #include "tolua++/include/tolua++.h" #include "LuaState.h" #include "LuaTCPLink.h" +#include "LuaNameLookup.h" @@ -71,6 +72,86 @@ static int tolua_cNetwork_Connect(lua_State * L) +static int tolua_cNetwork_HostnameToIP(lua_State * L) +{ + // Function signature: + // cNetwork:HostnameToIP(Host, Callbacks) -> bool + + cLuaState S(L); + if ( + !S.CheckParamUserTable(1, "cNetwork") || + !S.CheckParamString(2) || + !S.CheckParamTable(3) || + !S.CheckParamEnd(4) + ) + { + return 0; + } + + // Get the plugin instance: + cPluginLua * Plugin = GetLuaPlugin(L); + if (Plugin == nullptr) + { + // An error message has been already printed in GetLuaPlugin() + S.Push(false); + return 1; + } + + // Read the params: + AString Host; + S.GetStackValue(2, Host); + + // Try to look up: + bool res = cNetwork::HostnameToIP(Host, std::make_shared(Host, *Plugin, 3)); + S.Push(res); + + return 1; +} + + + + + +static int tolua_cNetwork_IPToHostname(lua_State * L) +{ + // Function signature: + // cNetwork:IPToHostname(IP, Callbacks) -> bool + + cLuaState S(L); + if ( + !S.CheckParamUserTable(1, "cNetwork") || + !S.CheckParamString(2) || + !S.CheckParamTable(3) || + !S.CheckParamEnd(4) + ) + { + return 0; + } + + // Get the plugin instance: + cPluginLua * Plugin = GetLuaPlugin(L); + if (Plugin == nullptr) + { + // An error message has been already printed in GetLuaPlugin() + S.Push(false); + return 1; + } + + // Read the params: + AString Host; + S.GetStackValue(2, Host); + + // Try to look up: + bool res = cNetwork::IPToHostName(Host, std::make_shared(Host, *Plugin, 3)); + S.Push(res); + + return 1; +} + + + + + //////////////////////////////////////////////////////////////////////////////// // cTCPLink bindings (routed through cLuaTCPLink): @@ -258,9 +339,9 @@ void ManualBindings::BindNetwork(lua_State * tolua_S) // Fill in the functions (alpha-sorted): tolua_beginmodule(tolua_S, "cNetwork"); tolua_function(tolua_S, "Connect", tolua_cNetwork_Connect); - /* tolua_function(tolua_S, "HostnameToIP", tolua_cNetwork_HostnameToIP); tolua_function(tolua_S, "IPToHostname", tolua_cNetwork_IPToHostname); + /* tolua_function(tolua_S, "Listen", tolua_cNetwork_Listen); */ tolua_endmodule(tolua_S); From 014b96adb33fa902072d9f35661bc4f5e7c323e8 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Fri, 30 Jan 2015 21:24:02 +0100 Subject: [PATCH 05/95] Exported cServerHandle and cNetwork:Listen to Lua. Also added an example to the NetworkTest plugin. --- MCServer/Plugins/NetworkTest/Info.lua | 27 ++ MCServer/Plugins/NetworkTest/NetworkTest.lua | 148 +++++++- MCServer/Plugins/NetworkTest/splashes.txt | 357 +++++++++++++++++++ src/Bindings/CMakeLists.txt | 2 + src/Bindings/LuaNameLookup.h | 2 +- src/Bindings/LuaServerHandle.cpp | 213 +++++++++++ src/Bindings/LuaServerHandle.h | 89 +++++ src/Bindings/LuaState.cpp | 33 ++ src/Bindings/LuaState.h | 9 + src/Bindings/LuaTCPLink.cpp | 97 ++++- src/Bindings/LuaTCPLink.h | 21 ++ src/Bindings/ManualBindings_Network.cpp | 157 +++++++- src/Globals.h | 1 + src/OSSupport/Network.h | 3 + tests/Network/EchoServer.cpp | 1 + 15 files changed, 1145 insertions(+), 15 deletions(-) create mode 100644 MCServer/Plugins/NetworkTest/splashes.txt create mode 100644 src/Bindings/LuaServerHandle.cpp create mode 100644 src/Bindings/LuaServerHandle.h diff --git a/MCServer/Plugins/NetworkTest/Info.lua b/MCServer/Plugins/NetworkTest/Info.lua index 8c2604e31..f366fd1be 100644 --- a/MCServer/Plugins/NetworkTest/Info.lua +++ b/MCServer/Plugins/NetworkTest/Info.lua @@ -37,6 +37,32 @@ g_PluginInfo = }, -- ParameterCombinations }, -- client + close = + { + HelpString = "Close a listening socket", + Handler = HandleConsoleNetClose, + ParameterCombinations = + { + { + Params = "[Port]", + Help = "Closes a socket listening on the specified port [1024]", + }, + }, -- ParameterCombinations + }, -- close + + listen = + { + HelpString = "Creates a new listening socket on the specified port with the specified service attached to it", + Handler = HandleConsoleNetListen, + ParameterCombinations = + { + { + Params = "[Port] [Service]", + Help = "Starts listening on the specified port [1024] providing the specified service [echo]", + }, + }, -- ParameterCombinations + }, -- listen + lookup = { HelpString = "Looks up the IP addresses corresponding to the given hostname (google.com by default)", @@ -57,6 +83,7 @@ g_PluginInfo = }, }, }, -- lookup + }, -- Subcommands }, -- net }, diff --git a/MCServer/Plugins/NetworkTest/NetworkTest.lua b/MCServer/Plugins/NetworkTest/NetworkTest.lua index 30f34c879..9b69ee5b3 100644 --- a/MCServer/Plugins/NetworkTest/NetworkTest.lua +++ b/MCServer/Plugins/NetworkTest/NetworkTest.lua @@ -7,11 +7,109 @@ -function Initialize() +--- Map of all servers currently open +-- g_Servers[PortNum] = cServerHandle +local g_Servers = {} + +--- List of fortune messages for the fortune server +-- A random message is chosen for each incoming connection +-- The contents are loaded from the splashes.txt file on plugin startup +local g_Fortunes = +{ + "Empty splashes.txt", +} + +--- Map of all services that can be run as servers +-- g_Services[ServiceName] = function() -> callbacks +local g_Services = +{ + -- Echo service: each connection echoes back what has been sent to it + echo = function (a_Port) + return + { + -- A new connection has come, give it new link callbacks: + OnIncomingConnection = function (a_RemoteIP, a_RemotePort) + return + { + OnError = function (a_Link, a_ErrorCode, a_ErrorMsg) + LOG("EchoServer(" .. a_Port .. ": Connection to " .. a_Link:GetRemoteIP() .. ":" .. a_Link:GetRemotePort() .. " failed: " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")") + end, + + OnReceivedData = function (a_Link, a_Data) + -- Echo the received data back to the link: + a_Link:Send(a_Data) + end, + + OnRemoteClosed = function (a_Link) + end + } -- Link callbacks + end, -- OnIncomingConnection() + + -- Send a welcome message to newly accepted connections: + OnAccepted = function (a_Link) + a_Link:Send("Hello, " .. a_Link:GetRemoteIP() .. ", welcome to the echo server @ MCServer-Lua\r\n") + end, -- OnAccepted() + + -- There was an error listening on the port: + OnError = function (a_ErrorCode, a_ErrorMsg) + LOGINFO("EchoServer(" .. a_Port .. ": Cannot listen: " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")") + end, -- OnError() + } -- Listen callbacks + end, -- echo + + fortune = function (a_Port) + return + { + -- A new connection has come, give it new link callbacks: + OnIncomingConnection = function (a_RemoteIP, a_RemotePort) + return + { + OnError = function (a_Link, a_ErrorCode, a_ErrorMsg) + LOG("FortuneServer(" .. a_Port .. ": Connection to " .. a_Link:GetRemoteIP() .. ":" .. a_Link:GetRemotePort() .. " failed: " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")") + end, + + OnReceivedData = function (a_Link, a_Data) + -- Ignore any received data + end, + + OnRemoteClosed = function (a_Link) + end + } -- Link callbacks + end, -- OnIncomingConnection() + + -- Send a welcome message to newly accepted connections: + OnAccepted = function (a_Link) + a_Link:Send("Hello, " .. a_Link:GetRemoteIP() .. ", welcome to the fortune server @ MCServer-Lua\r\n\r\nYour fortune:\r\n") + a_Link:Send(g_Fortunes[math.random(#g_Fortunes)] .. "\r\n") + end, -- OnAccepted() + + -- There was an error listening on the port: + OnError = function (a_ErrorCode, a_ErrorMsg) + LOGINFO("FortuneServer(" .. a_Port .. ": Cannot listen: " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")") + end, -- OnError() + } -- Listen callbacks + end, -- fortune + + -- TODO: Other services (fortune, daytime, ...) +} + + + + + +function Initialize(a_Plugin) + -- Load the splashes.txt file into g_Fortunes: + for line in io.lines(a_Plugin:GetLocalFolder() .. "/splashes.txt") do + table.insert(g_Fortunes, line) + end + -- Use the InfoReg shared library to process the Info.lua file: dofile(cPluginManager:GetPluginsPath() .. "/InfoReg.lua") RegisterPluginInfoCommands() RegisterPluginInfoConsoleCommands() + + -- Seed the random generator: + math.randomseed(os.time()) return true end @@ -62,6 +160,26 @@ end +function HandleConsoleNetClose(a_Split) + -- Get the port to close: + local Port = tonumber(a_Split[3] or 1024) + if not(Port) then + return true, "Bad port number: \"" .. Port .. "\"." + end + + -- Close the server, if there is one: + if not(g_Servers[Port]) then + return true, "There is no server currently listening on port " .. Port .. "." + end + g_Servers[Port]:Close() + g_Servers[Port] = nil + return true, "Port " .. Port .. " closed." +end + + + + + function HandleConsoleNetLookup(a_Split) -- Get the name to look up: local Addr = a_Split[3] or "google.com" @@ -103,3 +221,31 @@ end + +function HandleConsoleNetListen(a_Split) + -- Get the params: + local Port = tonumber(a_Split[3] or 1024) + if not(Port) then + return true, "Invalid port: \"" .. Port .. "\"." + end + local Service = string.lower(a_Split[4] or "echo") + + -- Create the callbacks specific for the service: + if (g_Services[Service] == nil) then + return true, "No such service: " .. Service + end + local Callbacks = g_Services[Service](Port) + + -- Start the server: + local srv = cNetwork:Listen(Port, Callbacks) + if not(srv:IsListening()) then + -- The error message has already been printed in the Callbacks.OnError() + return true + end + g_Servers[Port] = srv + return true, Service .. " server started on port " .. Port +end + + + + diff --git a/MCServer/Plugins/NetworkTest/splashes.txt b/MCServer/Plugins/NetworkTest/splashes.txt new file mode 100644 index 000000000..50a87bd91 --- /dev/null +++ b/MCServer/Plugins/NetworkTest/splashes.txt @@ -0,0 +1,357 @@ +As seen on TV! +Awesome! +100% pure! +May contain nuts! +Better than Prey! +More polygons! +Sexy! +Limited edition! +Flashing letters! +Made by Notch! +It's here! +Best in class! +It's finished! +Kind of dragon free! +Excitement! +More than 500 sold! +One of a kind! +Heaps of hits on YouTube! +Indev! +Spiders everywhere! +Check it out! +Holy cow, man! +It's a game! +Made in Sweden! +Uses LWJGL! +Reticulating splines! +Minecraft! +Yaaay! +Singleplayer! +Keyboard compatible! +Undocumented! +Ingots! +Exploding creepers! +That's no moon! +l33t! +Create! +Survive! +Dungeon! +Exclusive! +The bee's knees! +Down with O.P.P.! +Closed source! +Classy! +Wow! +Not on steam! +Oh man! +Awesome community! +Pixels! +Teetsuuuuoooo! +Kaaneeeedaaaa! +Now with difficulty! +Enhanced! +90% bug free! +Pretty! +12 herbs and spices! +Fat free! +Absolutely no memes! +Free dental! +Ask your doctor! +Minors welcome! +Cloud computing! +Legal in Finland! +Hard to label! +Technically good! +Bringing home the bacon! +Indie! +GOTY! +Ceci n'est pas une title screen! +Euclidian! +Now in 3D! +Inspirational! +Herregud! +Complex cellular automata! +Yes, sir! +Played by cowboys! +OpenGL 2.1 (if supported)! +Thousands of colors! +Try it! +Age of Wonders is better! +Try the mushroom stew! +Sensational! +Hot tamale, hot hot tamale! +Play him off, keyboard cat! +Guaranteed! +Macroscopic! +Bring it on! +Random splash! +Call your mother! +Monster infighting! +Loved by millions! +Ultimate edition! +Freaky! +You've got a brand new key! +Water proof! +Uninflammable! +Whoa, dude! +All inclusive! +Tell your friends! +NP is not in P! +Notch <3 ez! +Music by C418! +Livestreamed! +Haunted! +Polynomial! +Terrestrial! +All is full of love! +Full of stars! +Scientific! +Cooler than Spock! +Collaborate and listen! +Never dig down! +Take frequent breaks! +Not linear! +Han shot first! +Nice to meet you! +Buckets of lava! +Ride the pig! +Larger than Earth! +sqrt(-1) love you! +Phobos anomaly! +Punching wood! +Falling off cliffs! +0% sugar! +150% hyperbole! +Synecdoche! +Let's danec! +Seecret Friday update! +Reference implementation! +Lewd with two dudes with food! +Kiss the sky! +20 GOTO 10! +Verlet intregration! +Peter Griffin! +Do not distribute! +Cogito ergo sum! +4815162342 lines of code! +A skeleton popped out! +The Work of Notch! +The sum of its parts! +BTAF used to be good! +I miss ADOM! +umop-apisdn! +OICU812! +Bring me Ray Cokes! +Finger-licking! +Thematic! +Pneumatic! +Sublime! +Octagonal! +Une baguette! +Gargamel plays it! +Rita is the new top dog! +SWM forever! +Representing Edsbyn! +Matt Damon! +Supercalifragilisticexpialidocious! +Consummate V's! +Cow Tools! +Double buffered! +Fan fiction! +Flaxkikare! +Jason! Jason! Jason! +Hotter than the sun! +Internet enabled! +Autonomous! +Engage! +Fantasy! +DRR! DRR! DRR! +Kick it root down! +Regional resources! +Woo, facepunch! +Woo, somethingawful! +Woo, /v/! +Woo, tigsource! +Woo, minecraftforum! +Woo, worldofminecraft! +Woo, reddit! +Woo, 2pp! +Google anlyticsed! +Now supports åäö! +Give us Gordon! +Tip your waiter! +Very fun! +12345 is a bad password! +Vote for net neutrality! +Lives in a pineapple under the sea! +MAP11 has two names! +Omnipotent! +Gasp! +...! +Bees, bees, bees, bees! +Jag känner en bot! +This text is hard to read if you play the game at the default resolution, but at 1080p it's fine! +Haha, LOL! +Hampsterdance! +Switches and ores! +Menger sponge! +idspispopd! +Eple (original edit)! +So fresh, so clean! +Slow acting portals! +Try the Nether! +Don't look directly at the bugs! +Oh, ok, Pigmen! +Finally with ladders! +Scary! +Play Minecraft, Watch Topgear, Get Pig! +Twittered about! +Jump up, jump up, and get down! +Joel is neat! +A riddle, wrapped in a mystery! +Huge tracts of land! +Welcome to your Doom! +Stay a while, stay forever! +Stay a while and listen! +Treatment for your rash! +"Autological" is! +Information wants to be free! +"Almost never" is an interesting concept! +Lots of truthiness! +The creeper is a spy! +Turing complete! +It's groundbreaking! +Let our battle's begin! +The sky is the limit! +Jeb has amazing hair! +Ryan also has amazing hair! +Casual gaming! +Undefeated! +Kinda like Lemmings! +Follow the train, CJ! +Leveraging synergy! +This message will never appear on the splash screen, isn't that weird? +DungeonQuest is unfair! +110813! +90210! +Check out the far lands! +Tyrion would love it! +Also try VVVVVV! +Also try Super Meat Boy! +Also try Terraria! +Also try Mount And Blade! +Also try Project Zomboid! +Also try World of Goo! +Also try Limbo! +Also try Pixeljunk Shooter! +Also try Braid! +That's super! +Bread is pain! +Read more books! +Khaaaaaaaaan! +Less addictive than TV Tropes! +More addictive than lemonade! +Bigger than a bread box! +Millions of peaches! +Fnord! +This is my true form! +Totally forgot about Dre! +Don't bother with the clones! +Pumpkinhead! +Hobo humping slobo babe! +Made by Jeb! +Has an ending! +Finally complete! +Feature packed! +Boots with the fur! +Stop, hammertime! +Testificates! +Conventional! +Homeomorphic to a 3-sphere! +Doesn't avoid double negatives! +Place ALL the blocks! +Does barrel rolls! +Meeting expectations! +PC gaming since 1873! +Ghoughpteighbteau tchoghs! +Déjà vu! +Déjà vu! +Got your nose! +Haley loves Elan! +Afraid of the big, black bat! +Doesn't use the U-word! +Child's play! +See you next Friday or so! +From the streets of Södermalm! +150 bpm for 400000 minutes! +Technologic! +Funk soul brother! +Pumpa kungen! +日本ハロー! +한국 안녕하세요! +Helo Cymru! +Cześć Polsko! +你好中国! +Привет Россия! +Γεια σου Ελλάδα! +My life for Aiur! +Lennart lennart = new Lennart(); +I see your vocabulary has improved! +Who put it there? +You can't explain that! +if not ok then return end +§1C§2o§3l§4o§5r§6m§7a§8t§9i§ac +§kFUNKY LOL +SOPA means LOSER in Swedish! +Big Pointy Teeth! +Bekarton guards the gate! +Mmmph, mmph! +Don't feed avocados to parrots! +Swords for everyone! +Plz reply to my tweet! +.party()! +Take her pillow! +Put that cookie down! +Pretty scary! +I have a suggestion. +Now with extra hugs! +Now Java 6! +Woah. +HURNERJSGER? +What's up, Doc? +Now contains 32 random daily cats! +That's Numberwang! +pls rt +Do you want to join my server? +Put a little fence around it! +Throw a blanket over it! +One day, somewhere in the future, my work will be quoted! +Now with additional stuff! +Extra things! +Yay, puppies for everyone! +So sweet, like a nice bon bon! +Popping tags! +Very influential in its circle! +Now With Multiplayer! +Rise from your grave! +Warning! A huge battleship "STEVE" is approaching fast! +Blue warrior shot the food! +Run, coward! I hunger! +Flavor with no seasoning! +Strange, but not a stranger! +Tougher than diamonds, rich like cream! +Getting ready to show! +Getting ready to know! +Getting ready to drop! +Getting ready to shock! +Getting ready to freak! +Getting ready to speak! +It swings, it jives! +Cruising streets for gold! +Take an eggbeater and beat it against a skillet! +Make me a table, a funky table! +Take the elevator to the mezzanine! +Stop being reasonable, this is the Internet! +/give @a hugs 64 +This is good for Realms. +Any computer is a laptop if you're brave enough! \ No newline at end of file diff --git a/src/Bindings/CMakeLists.txt b/src/Bindings/CMakeLists.txt index 13428b5c6..40b8d4bd9 100644 --- a/src/Bindings/CMakeLists.txt +++ b/src/Bindings/CMakeLists.txt @@ -9,6 +9,7 @@ SET (SRCS DeprecatedBindings.cpp LuaChunkStay.cpp LuaNameLookup.cpp + LuaServerHandle.cpp LuaState.cpp LuaTCPLink.cpp LuaWindow.cpp @@ -27,6 +28,7 @@ SET (HDRS LuaChunkStay.h LuaFunctions.h LuaNameLookup.h + LuaServerHandle.h LuaState.h LuaTCPLink.h LuaWindow.h diff --git a/src/Bindings/LuaNameLookup.h b/src/Bindings/LuaNameLookup.h index 8b456ad90..e4cdb9f53 100644 --- a/src/Bindings/LuaNameLookup.h +++ b/src/Bindings/LuaNameLookup.h @@ -25,7 +25,7 @@ public: cLuaNameLookup(const AString & a_Query, cPluginLua & a_Plugin, int a_CallbacksTableStackPos); protected: - /** The plugin for which the link is created. */ + /** The plugin for which the query is created. */ cPluginLua & m_Plugin; /** The Lua table that holds the callbacks to be invoked. */ diff --git a/src/Bindings/LuaServerHandle.cpp b/src/Bindings/LuaServerHandle.cpp new file mode 100644 index 000000000..7b82003bb --- /dev/null +++ b/src/Bindings/LuaServerHandle.cpp @@ -0,0 +1,213 @@ + +// LuaServerHandle.cpp + +// Implements the cLuaServerHandle class wrapping the cServerHandle functionality for Lua API + +#include "Globals.h" +#include "LuaServerHandle.h" +#include "LuaTCPLink.h" +#include "tolua++/include/tolua++.h" + + + + + +cLuaServerHandle::cLuaServerHandle(UInt16 a_Port, cPluginLua & a_Plugin, int a_CallbacksTableStackPos): + m_Plugin(a_Plugin), + m_Callbacks(a_Plugin.GetLuaState(), a_CallbacksTableStackPos), + m_Port(a_Port) +{ + LOGD("Creating LuaServerHandle at %p.", this); +} + + + + + + +cLuaServerHandle::~cLuaServerHandle() +{ + // If the server handle is still open, close it explicitly: + LOGD("Deleting LuaServerHandle at %p.", this); + Close(); +} + + + + + +void cLuaServerHandle::SetServerHandle(cServerHandlePtr a_ServerHandle, cLuaServerHandlePtr a_Self) +{ + ASSERT(m_ServerHandle == nullptr); // The handle can be set only once + + m_ServerHandle = a_ServerHandle; + m_Self = a_Self; +} + + + + + +void cLuaServerHandle::Close(void) +{ + LOGD("Closing LuaServerHandle at %p.", this); + + // Safely grab a copy of the server handle: + cServerHandlePtr ServerHandle = m_ServerHandle; + if (ServerHandle == nullptr) + { + return; + } + + // Close the underlying server handle: + ServerHandle->Close(); + + // Close all connections at this server: + cLuaTCPLinkPtrs Connections; + { + cCSLock Lock(m_CSConnections); + std::swap(Connections, m_Connections); + } + for (auto & conn: Connections) + { + conn->Close(); + } + Connections.clear(); + + // Allow the internal server handle to be deallocated: + m_ServerHandle.reset(); +} + + + + + +bool cLuaServerHandle::IsListening(void) +{ + // Safely grab a copy of the server handle: + cServerHandlePtr ServerHandle = m_ServerHandle; + if (ServerHandle == nullptr) + { + return false; + } + + // Query the underlying server handle: + return ServerHandle->IsListening(); +} + + + + + +void cLuaServerHandle::RemoveLink(cLuaTCPLink * a_Link) +{ + cCSLock Lock(m_CSConnections); + for (auto itr = m_Connections.begin(), end = m_Connections.end(); itr != end; ++itr) + { + if (itr->get() == a_Link) + { + m_Connections.erase(itr); + break; + } + } // for itr - m_Connections[] +} + + + + + +void cLuaServerHandle::Release(void) +{ + // Close the server, if it isn't closed yet: + Close(); + + // Allow self to deallocate: + m_Self.reset(); +} + + + + + +cTCPLink::cCallbacksPtr cLuaServerHandle::OnIncomingConnection(const AString & a_RemoteIPAddress, UInt16 a_RemotePort) +{ + // If not valid anymore, drop the connection: + if (!m_Callbacks.IsValid()) + { + return nullptr; + } + + // Ask the plugin for link callbacks: + cPluginLua::cOperation Op(m_Plugin); + cLuaState::cRef LinkCallbacks; + if ( + !Op().Call(cLuaState::cTableRef(m_Callbacks, "OnIncomingConnection"), a_RemoteIPAddress, a_RemotePort, m_Port, cLuaState::Return, LinkCallbacks) || + !LinkCallbacks.IsValid() + ) + { + LOGINFO("cNetwork server (port %d) OnIncomingConnection callback failed in plugin %s. Dropping connection.", + m_Port, m_Plugin.GetName().c_str() + ); + return nullptr; + } + + // Create the link wrapper to use with the callbacks: + auto res = std::make_shared(m_Plugin, std::move(LinkCallbacks), m_Self); + + // Add the link to the list of our connections: + cCSLock Lock(m_CSConnections); + m_Connections.push_back(res); + + return res; +} + + + + + +void cLuaServerHandle::OnAccepted(cTCPLink & a_Link) +{ + // Check if we're still valid: + if (!m_Callbacks.IsValid()) + { + return; + } + + // Notify the plugin: + cPluginLua::cOperation Op(m_Plugin); + if (!Op().Call(cLuaState::cTableRef(m_Callbacks, "OnAccepted"), static_cast(a_Link.GetCallbacks().get()))) + { + LOGINFO("cNetwork server (port %d) OnAccepted callback failed in plugin %s, connection to %s:%d.", + m_Port, m_Plugin.GetName().c_str(), a_Link.GetRemoteIP().c_str(), a_Link.GetRemotePort() + ); + return; + } +} + + + + + +void cLuaServerHandle::OnError(int a_ErrorCode, const AString & a_ErrorMsg) +{ + // Check if we're still valid: + if (!m_Callbacks.IsValid()) + { + return; + } + + // Notify the plugin: + cPluginLua::cOperation Op(m_Plugin); + if (!Op().Call(cLuaState::cTableRef(m_Callbacks, "OnError"), a_ErrorCode, a_ErrorMsg)) + { + LOGINFO("cNetwork server (port %d) OnError callback failed in plugin %s. The error is %d (%s).", + m_Port, m_Plugin.GetName().c_str(), a_ErrorCode, a_ErrorMsg.c_str() + ); + return; + } +} + + + + + diff --git a/src/Bindings/LuaServerHandle.h b/src/Bindings/LuaServerHandle.h new file mode 100644 index 000000000..9325bca3e --- /dev/null +++ b/src/Bindings/LuaServerHandle.h @@ -0,0 +1,89 @@ + +// LuaServerHandle.h + +// Declares the cLuaServerHandle class wrapping the cServerHandle functionality for Lua API + + + + + +#pragma once + +#include "../OSSupport/Network.h" +#include "PluginLua.h" + + + + + +// fwd: +class cLuaTCPLink; +typedef SharedPtr cLuaTCPLinkPtr; +typedef std::vector cLuaTCPLinkPtrs; +class cLuaServerHandle; +typedef SharedPtr cLuaServerHandlePtr; + + + + +class cLuaServerHandle: + public cNetwork::cListenCallbacks +{ +public: + /** Creates a new instance of the server handle, + attached to the specified lua plugin and wrapping the (listen-) callbacks that are in a table at the specified stack pos. */ + cLuaServerHandle(UInt16 a_Port, cPluginLua & a_Plugin, int a_CallbacksTableStackPos); + + ~cLuaServerHandle(); + + /** Called by cNetwork::Listen()'s binding. + Sets the server handle around which this instance is wrapped, and a self SharedPtr for link management. */ + void SetServerHandle(cServerHandlePtr a_ServerHandle, cLuaServerHandlePtr a_Self); + + /** Terminates all connections and closes the listening socket. */ + void Close(void); + + /** Returns true if the server is currently listening. */ + bool IsListening(void); + + /** Removes the link from the list of links that the server is currently tracking. */ + void RemoveLink(cLuaTCPLink * a_Link); + + /** Called when Lua garbage-collects the object. + Releases the internal SharedPtr to self, so that the instance may be deallocated. */ + void Release(void); + +protected: + /** The plugin for which the server is created. */ + cPluginLua & m_Plugin; + + /** The Lua table that holds the callbacks to be invoked. */ + cLuaState::cRef m_Callbacks; + + /** The port on which the server is listening. + Used mainly for better error reporting. */ + UInt16 m_Port; + + /** The cServerHandle around which this instance is wrapped. */ + cServerHandlePtr m_ServerHandle; + + /** Protects m_Connections against multithreaded access. */ + cCriticalSection m_CSConnections; + + /** All connections that are currently active in this server. + Protected by m_CSConnections. */ + cLuaTCPLinkPtrs m_Connections; + + /** SharedPtr to self, given out to newly created links. */ + cLuaServerHandlePtr m_Self; + + + // cNetwork::cListenCallbacks overrides: + virtual cTCPLink::cCallbacksPtr OnIncomingConnection(const AString & a_RemoteIPAddress, UInt16 a_RemotePort) override; + virtual void OnAccepted(cTCPLink & a_Link) override; + virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override; +}; + + + + diff --git a/src/Bindings/LuaState.cpp b/src/Bindings/LuaState.cpp index 4de34c88d..73b114599 100644 --- a/src/Bindings/LuaState.cpp +++ b/src/Bindings/LuaState.cpp @@ -656,6 +656,18 @@ void cLuaState::Push(cItems * a_Items) +void cLuaState::Push(cLuaServerHandle * a_ServerHandle) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, a_ServerHandle, "cServerHandle"); + m_NumCurrentFunctionArgs += 1; +} + + + + + void cLuaState::Push(cLuaTCPLink * a_TCPLink) { ASSERT(IsValid()); @@ -970,6 +982,15 @@ void cLuaState::GetStackValue(int a_StackPos, pWorld & a_ReturnedVal) +void cLuaState::GetStackValue(int a_StackPos, cRef & a_Ref) +{ + a_Ref.RefStack(*this, a_StackPos); +} + + + + + bool cLuaState::CallFunction(int a_NumResults) { ASSERT (m_NumCurrentFunctionArgs >= 0); // A function must be pushed to stack first @@ -1539,6 +1560,18 @@ cLuaState::cRef::cRef(cLuaState & a_LuaState, int a_StackPos) : +cLuaState::cRef::cRef(cRef && a_FromRef): + m_LuaState(a_FromRef.m_LuaState), + m_Ref(a_FromRef.m_Ref) +{ + a_FromRef.m_LuaState = nullptr; + a_FromRef.m_Ref = LUA_REFNIL; +} + + + + + cLuaState::cRef::~cRef() { if (m_LuaState != nullptr) diff --git a/src/Bindings/LuaState.h b/src/Bindings/LuaState.h index 0dcd248fe..f68b022ea 100644 --- a/src/Bindings/LuaState.h +++ b/src/Bindings/LuaState.h @@ -60,6 +60,7 @@ class cHopperEntity; class cBlockEntity; class cBoundingBox; class cLuaTCPLink; +class cLuaServerHandle; typedef cBoundingBox * pBoundingBox; typedef cWorld * pWorld; @@ -84,6 +85,10 @@ public: /** Creates a reference in the specified LuaState for object at the specified StackPos */ cRef(cLuaState & a_LuaState, int a_StackPos); + + /** Moves the reference from the specified instance into a newly created instance. + The old instance is then "!IsValid()". */ + cRef(cRef && a_FromRef); ~cRef(); @@ -203,6 +208,7 @@ public: void Push(cHopperEntity * a_Hopper); void Push(cItem * a_Item); void Push(cItems * a_Items); + void Push(cLuaServerHandle * a_ServerHandle); void Push(cLuaTCPLink * a_TCPLink); void Push(cMonster * a_Monster); void Push(cPickup * a_Pickup); @@ -242,6 +248,9 @@ public: /** Retrieve value at a_StackPos, if it is a valid cWorld class. If not, a_Value is unchanged */ void GetStackValue(int a_StackPos, pWorld & a_Value); + + /** Store the value at a_StackPos as a reference. */ + void GetStackValue(int a_StackPos, cRef & a_Ref); /** Call the specified Lua function. Returns true if call succeeded, false if there was an error. diff --git a/src/Bindings/LuaTCPLink.cpp b/src/Bindings/LuaTCPLink.cpp index f88aeff84..6b8395806 100644 --- a/src/Bindings/LuaTCPLink.cpp +++ b/src/Bindings/LuaTCPLink.cpp @@ -5,6 +5,7 @@ #include "Globals.h" #include "LuaTCPLink.h" +#include "LuaServerHandle.h" @@ -14,6 +15,47 @@ cLuaTCPLink::cLuaTCPLink(cPluginLua & a_Plugin, int a_CallbacksTableStackPos): m_Plugin(a_Plugin), m_Callbacks(a_Plugin.GetLuaState(), a_CallbacksTableStackPos) { + // Warn if the callbacks aren't valid: + if (!m_Callbacks.IsValid()) + { + LOGWARNING("cTCPLink in plugin %s: callbacks could not be retrieved", m_Plugin.GetName().c_str()); + cPluginLua::cOperation Op(m_Plugin); + Op().LogStackTrace(); + } +} + + + + + +cLuaTCPLink::cLuaTCPLink(cPluginLua & a_Plugin, cLuaState::cRef && a_CallbacksTableRef, cLuaServerHandleWPtr a_ServerHandle): + m_Plugin(a_Plugin), + m_Callbacks(std::move(a_CallbacksTableRef)), + m_Server(std::move(a_ServerHandle)) +{ + // Warn if the callbacks aren't valid: + if (!m_Callbacks.IsValid()) + { + LOGWARNING("cTCPLink in plugin %s: callbacks could not be retrieved", m_Plugin.GetName().c_str()); + cPluginLua::cOperation Op(m_Plugin); + Op().LogStackTrace(); + } +} + + + + + +cLuaTCPLink::~cLuaTCPLink() +{ + // If the link is still open, close it: + cTCPLinkPtr Link = m_Link; + if (Link != nullptr) + { + Link->Close(); + } + + Terminated(); } @@ -107,15 +149,14 @@ UInt16 cLuaTCPLink::GetRemotePort(void) const void cLuaTCPLink::Shutdown(void) { - // Safely grab a copy of the link: + // Safely grab a copy of the link and shut it down: cTCPLinkPtr Link = m_Link; - if (Link == nullptr) + if (Link != nullptr) { - return; + Link->Shutdown(); } - // Shutdown: - Link->Shutdown(); + Terminated(); } @@ -124,17 +165,48 @@ void cLuaTCPLink::Shutdown(void) void cLuaTCPLink::Close(void) { - // Safely grab a copy of the link: + // If the link is still open, close it: cTCPLinkPtr Link = m_Link; - if (Link == nullptr) + if (Link != nullptr) { - return; + Link->Close(); } - // Close the link: - Link->Close(); + Terminated(); } + + + + +void cLuaTCPLink::Terminated(void) +{ + // Disable the callbacks: + if (m_Callbacks.IsValid()) + { + m_Callbacks.UnRef(); + } + + // If the managing server is still alive, let it know we're terminating: + auto Server = m_Server.lock(); + if (Server != nullptr) + { + Server->RemoveLink(this); + } + + // If the link is still open, close it: + cTCPLinkPtr Link = m_Link; + if (Link != nullptr) + { + Link->Close(); + m_Link.reset(); + } +} + + + + + void cLuaTCPLink::OnConnected(cTCPLink & a_Link) { // Check if we're still valid: @@ -171,6 +243,8 @@ void cLuaTCPLink::OnError(int a_ErrorCode, const AString & a_ErrorMsg) m_Plugin.GetName().c_str(), a_ErrorCode, a_ErrorMsg.c_str() ); } + + Terminated(); } @@ -221,7 +295,8 @@ void cLuaTCPLink::OnRemoteClosed(void) { LOGINFO("cTCPLink OnRemoteClosed() callback failed in plugin %s.", m_Plugin.GetName().c_str()); } - m_Link.reset(); + + Terminated(); } diff --git a/src/Bindings/LuaTCPLink.h b/src/Bindings/LuaTCPLink.h index 125cc1b31..f2af911ec 100644 --- a/src/Bindings/LuaTCPLink.h +++ b/src/Bindings/LuaTCPLink.h @@ -16,6 +16,14 @@ +// fwd: +class cLuaServerHandle; +typedef WeakPtr cLuaServerHandleWPtr; + + + + + class cLuaTCPLink: public cNetwork::cConnectCallbacks, public cTCPLink::cCallbacks @@ -24,6 +32,11 @@ public: /** Creates a new instance of the link, attached to the specified plugin and wrapping the callbacks that are in a table at the specified stack pos. */ cLuaTCPLink(cPluginLua & a_Plugin, int a_CallbacksTableStackPos); + /** Creates a new instance of the link, attached to the specified plugin and wrapping the callbacks that are in the specified referenced table. */ + cLuaTCPLink(cPluginLua & a_Plugin, cLuaState::cRef && a_CallbacksTableRef, cLuaServerHandleWPtr a_Server); + + ~cLuaTCPLink(); + /** Sends the data contained in the string to the remote peer. Returns true if successful, false on immediate failure (queueing the data failed or link not available). */ bool Send(const AString & a_Data); @@ -60,6 +73,14 @@ protected: May be nullptr. */ cTCPLinkPtr m_Link; + /** The server that is responsible for this link, if any. */ + cLuaServerHandleWPtr m_Server; + + + /** Common code called when the link is considered as terminated. + Releases m_Link, m_Callbacks and this from m_Server, each when applicable. */ + void Terminated(void); + // cNetwork::cConnectCallbacks overrides: virtual void OnConnected(cTCPLink & a_Link) override; virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override; diff --git a/src/Bindings/ManualBindings_Network.cpp b/src/Bindings/ManualBindings_Network.cpp index 3123ef885..24c8c73b8 100644 --- a/src/Bindings/ManualBindings_Network.cpp +++ b/src/Bindings/ManualBindings_Network.cpp @@ -10,6 +10,7 @@ #include "LuaState.h" #include "LuaTCPLink.h" #include "LuaNameLookup.h" +#include "LuaServerHandle.h" @@ -72,6 +73,7 @@ static int tolua_cNetwork_Connect(lua_State * L) +/** Binds cNetwork::HostnameToIP */ static int tolua_cNetwork_HostnameToIP(lua_State * L) { // Function signature: @@ -112,6 +114,7 @@ static int tolua_cNetwork_HostnameToIP(lua_State * L) +/** Binds cNetwork::IPToHostname */ static int tolua_cNetwork_IPToHostname(lua_State * L) { // Function signature: @@ -152,9 +155,66 @@ static int tolua_cNetwork_IPToHostname(lua_State * L) +/** Binds cNetwork::Listen */ +static int tolua_cNetwork_Listen(lua_State * L) +{ + // Function signature: + // cNetwork:Listen(Port, Callbacks) -> bool + + cLuaState S(L); + if ( + !S.CheckParamUserTable(1, "cNetwork") || + !S.CheckParamNumber(2) || + !S.CheckParamTable(3) || + !S.CheckParamEnd(4) + ) + { + return 0; + } + + // Get the plugin instance: + cPluginLua * Plugin = GetLuaPlugin(L); + if (Plugin == nullptr) + { + // An error message has been already printed in GetLuaPlugin() + S.Push(false); + return 1; + } + + // Read the params: + int Port; + S.GetStackValues(2, Port); + if ((Port < 0) || (Port > 65535)) + { + LOGWARNING("cNetwork:Listen() called with invalid port (%d), failing the request.", Port); + S.Push(false); + return 1; + } + UInt16 Port16 = static_cast(Port); + + // Create the LuaTCPLink glue class: + auto Srv = std::make_shared(Port16, *Plugin, 3); + + // Listen: + Srv->SetServerHandle(cNetwork::Listen(Port16, Srv), Srv); + + // Register the server to be garbage-collected by Lua: + tolua_pushusertype(L, Srv.get(), "cServerHandle"); + tolua_register_gc(L, lua_gettop(L)); + + // Return the server handle wrapper: + S.Push(Srv.get()); + return 1; +} + + + + + //////////////////////////////////////////////////////////////////////////////// // cTCPLink bindings (routed through cLuaTCPLink): +/** Binds cLuaTCPLink::Send */ static int tolua_cTCPLink_Send(lua_State * L) { // Function signature: @@ -193,6 +253,7 @@ static int tolua_cTCPLink_Send(lua_State * L) +/** Binds cLuaTCPLink::GetLocalIP */ static int tolua_cTCPLink_GetLocalIP(lua_State * L) { // Function signature: @@ -226,6 +287,7 @@ static int tolua_cTCPLink_GetLocalIP(lua_State * L) +/** Binds cLuaTCPLink::GetLocalPort */ static int tolua_cTCPLink_GetLocalPort(lua_State * L) { // Function signature: @@ -259,6 +321,7 @@ static int tolua_cTCPLink_GetLocalPort(lua_State * L) +/** Binds cLuaTCPLink::GetRemoteIP */ static int tolua_cTCPLink_GetRemoteIP(lua_State * L) { // Function signature: @@ -292,6 +355,7 @@ static int tolua_cTCPLink_GetRemoteIP(lua_State * L) +/** Binds cLuaTCPLink::GetRemotePort */ static int tolua_cTCPLink_GetRemotePort(lua_State * L) { // Function signature: @@ -325,6 +389,90 @@ static int tolua_cTCPLink_GetRemotePort(lua_State * L) +//////////////////////////////////////////////////////////////////////////////// +// cServerHandle bindings (routed through cLuaServerHandle): + +/** Called when Lua destroys the object instance. +Close the server and let it deallocate on its own (it's in a SharedPtr). */ +static int tolua_collect_cServerHandle(lua_State * L) +{ + cLuaServerHandle * Srv = static_cast(tolua_tousertype(L, 1, nullptr)); + Srv->Release(); + return 0; +} + + + + + +/** Binds cLuaServerHandle::Close */ +static int tolua_cServerHandle_Close(lua_State * L) +{ + // Function signature: + // ServerInstance:Close() + + cLuaState S(L); + if ( + !S.CheckParamUserType(1, "cServerHandle") || + !S.CheckParamEnd(2) + ) + { + return 0; + } + + // Get the server handle: + cLuaServerHandle * Srv; + if (lua_isnil(L, 1)) + { + LOGWARNING("cServerHandle:Close(): invalid server handle object. Stack trace:"); + S.LogStackTrace(); + return 0; + } + Srv = *static_cast(lua_touserdata(L, 1)); + + // Close it: + Srv->Close(); + return 0; +} + + + + + +/** Binds cLuaServerHandle::IsListening */ +static int tolua_cServerHandle_IsListening(lua_State * L) +{ + // Function signature: + // ServerInstance:IsListening() -> bool + + cLuaState S(L); + if ( + !S.CheckParamUserType(1, "cServerHandle") || + !S.CheckParamEnd(2) + ) + { + return 0; + } + + // Get the server handle: + cLuaServerHandle * Srv; + if (lua_isnil(L, 1)) + { + LOGWARNING("cServerHandle:IsListening(): invalid server handle object. Stack trace:"); + S.LogStackTrace(); + return 0; + } + Srv = *static_cast(lua_touserdata(L, 1)); + + // Close it: + S.Push(Srv->IsListening()); + return 1; +} + + + + + //////////////////////////////////////////////////////////////////////////////// // Register the bindings: @@ -335,15 +483,15 @@ void ManualBindings::BindNetwork(lua_State * tolua_S) tolua_cclass(tolua_S, "cNetwork", "cNetwork", "", nullptr); tolua_usertype(tolua_S, "cTCPLink"); tolua_cclass(tolua_S, "cTCPLink", "cTCPLink", "", nullptr); + tolua_usertype(tolua_S, "cServerHandle"); + tolua_cclass(tolua_S, "cServerHandle", "cServerHandle", "", tolua_collect_cServerHandle); // Fill in the functions (alpha-sorted): tolua_beginmodule(tolua_S, "cNetwork"); tolua_function(tolua_S, "Connect", tolua_cNetwork_Connect); tolua_function(tolua_S, "HostnameToIP", tolua_cNetwork_HostnameToIP); tolua_function(tolua_S, "IPToHostname", tolua_cNetwork_IPToHostname); - /* tolua_function(tolua_S, "Listen", tolua_cNetwork_Listen); - */ tolua_endmodule(tolua_S); tolua_beginmodule(tolua_S, "cTCPLink"); @@ -353,6 +501,11 @@ void ManualBindings::BindNetwork(lua_State * tolua_S) tolua_function(tolua_S, "GetRemoteIP", tolua_cTCPLink_GetRemoteIP); tolua_function(tolua_S, "GetRemotePort", tolua_cTCPLink_GetRemotePort); tolua_endmodule(tolua_S); + + tolua_beginmodule(tolua_S, "cServerHandle"); + tolua_function(tolua_S, "Close", tolua_cServerHandle_Close); + tolua_function(tolua_S, "IsListening", tolua_cServerHandle_IsListening); + tolua_endmodule(tolua_S); } diff --git a/src/Globals.h b/src/Globals.h index 654ede95f..29eaac871 100644 --- a/src/Globals.h +++ b/src/Globals.h @@ -381,6 +381,7 @@ void inline LOG(const char * a_Format, ...) // Unified shared ptr from before C++11. Also no silly undercores. #define SharedPtr std::shared_ptr +#define WeakPtr std::weak_ptr diff --git a/src/OSSupport/Network.h b/src/OSSupport/Network.h index cdf6ba0e9..e883dfb29 100644 --- a/src/OSSupport/Network.h +++ b/src/OSSupport/Network.h @@ -90,6 +90,9 @@ public: Sends the RST packet, queued outgoing and incoming data is lost. */ virtual void Close(void) = 0; + /** Returns the callbacks that are used. */ + cCallbacksPtr GetCallbacks(void) const { return m_Callbacks; } + protected: /** Callbacks to be used for the various situations. */ cCallbacksPtr m_Callbacks; diff --git a/tests/Network/EchoServer.cpp b/tests/Network/EchoServer.cpp index 5f4b7651d..49fb89122 100644 --- a/tests/Network/EchoServer.cpp +++ b/tests/Network/EchoServer.cpp @@ -119,6 +119,7 @@ void DoTest(void) LOG("Server terminating."); Server->Close(); ASSERT(!Server->IsListening()); + Server.reset(); LOGD("Server has been closed."); } From 0e769f12ac39e1ef587c1eb7af549c553cd8a330 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Fri, 30 Jan 2015 23:08:09 +0100 Subject: [PATCH 06/95] LuaServerHandle: Removed debugging output. --- src/Bindings/LuaServerHandle.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Bindings/LuaServerHandle.cpp b/src/Bindings/LuaServerHandle.cpp index 7b82003bb..a84f894b5 100644 --- a/src/Bindings/LuaServerHandle.cpp +++ b/src/Bindings/LuaServerHandle.cpp @@ -17,7 +17,6 @@ cLuaServerHandle::cLuaServerHandle(UInt16 a_Port, cPluginLua & a_Plugin, int a_C m_Callbacks(a_Plugin.GetLuaState(), a_CallbacksTableStackPos), m_Port(a_Port) { - LOGD("Creating LuaServerHandle at %p.", this); } @@ -28,7 +27,6 @@ cLuaServerHandle::cLuaServerHandle(UInt16 a_Port, cPluginLua & a_Plugin, int a_C cLuaServerHandle::~cLuaServerHandle() { // If the server handle is still open, close it explicitly: - LOGD("Deleting LuaServerHandle at %p.", this); Close(); } @@ -50,8 +48,6 @@ void cLuaServerHandle::SetServerHandle(cServerHandlePtr a_ServerHandle, cLuaServ void cLuaServerHandle::Close(void) { - LOGD("Closing LuaServerHandle at %p.", this); - // Safely grab a copy of the server handle: cServerHandlePtr ServerHandle = m_ServerHandle; if (ServerHandle == nullptr) From d1c9ce2a03772a8c00c29450b0ba7a30d2aacadd Mon Sep 17 00:00:00 2001 From: Mattes D Date: Wed, 4 Feb 2015 10:39:48 +0100 Subject: [PATCH 07/95] NetworkTest plugin: updated comments and splash loading. --- MCServer/Plugins/NetworkTest/NetworkTest.lua | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/MCServer/Plugins/NetworkTest/NetworkTest.lua b/MCServer/Plugins/NetworkTest/NetworkTest.lua index 9b69ee5b3..7932f4b88 100644 --- a/MCServer/Plugins/NetworkTest/NetworkTest.lua +++ b/MCServer/Plugins/NetworkTest/NetworkTest.lua @@ -20,14 +20,14 @@ local g_Fortunes = } --- Map of all services that can be run as servers --- g_Services[ServiceName] = function() -> callbacks +-- g_Services[ServiceName] = function() -> accept-callbacks local g_Services = { -- Echo service: each connection echoes back what has been sent to it echo = function (a_Port) return { - -- A new connection has come, give it new link callbacks: + -- A new connection has come, give it new link-callbacks: OnIncomingConnection = function (a_RemoteIP, a_RemotePort) return { @@ -57,10 +57,11 @@ local g_Services = } -- Listen callbacks end, -- echo + -- Fortune service: each incoming connection gets a welcome message plus a random fortune text; all communication is ignored afterwards fortune = function (a_Port) return { - -- A new connection has come, give it new link callbacks: + -- A new connection has come, give it new link-callbacks: OnIncomingConnection = function (a_RemoteIP, a_RemotePort) return { @@ -77,7 +78,7 @@ local g_Services = } -- Link callbacks end, -- OnIncomingConnection() - -- Send a welcome message to newly accepted connections: + -- Send a welcome message and the fortune to newly accepted connections: OnAccepted = function (a_Link) a_Link:Send("Hello, " .. a_Link:GetRemoteIP() .. ", welcome to the fortune server @ MCServer-Lua\r\n\r\nYour fortune:\r\n") a_Link:Send(g_Fortunes[math.random(#g_Fortunes)] .. "\r\n") @@ -90,7 +91,7 @@ local g_Services = } -- Listen callbacks end, -- fortune - -- TODO: Other services (fortune, daytime, ...) + -- TODO: Other services (daytime, ...) } @@ -98,9 +99,11 @@ local g_Services = function Initialize(a_Plugin) - -- Load the splashes.txt file into g_Fortunes: + -- Load the splashes.txt file into g_Fortunes, overwriting current content: + local idx = 1 for line in io.lines(a_Plugin:GetLocalFolder() .. "/splashes.txt") do - table.insert(g_Fortunes, line) + g_Fortunes[idx] = line + idx = idx + 1 end -- Use the InfoReg shared library to process the Info.lua file: From adf0020cd41f6a947ef883c582dde74d67255b1f Mon Sep 17 00:00:00 2001 From: Mattes D Date: Fri, 6 Feb 2015 18:44:05 +0100 Subject: [PATCH 08/95] APIDump: Added cNetwork documentation. --- MCServer/Plugins/APIDump/Classes/Network.lua | 183 +++++++++++++++++++ src/Bindings/ManualBindings_Network.cpp | 2 +- 2 files changed, 184 insertions(+), 1 deletion(-) create mode 100644 MCServer/Plugins/APIDump/Classes/Network.lua diff --git a/MCServer/Plugins/APIDump/Classes/Network.lua b/MCServer/Plugins/APIDump/Classes/Network.lua new file mode 100644 index 000000000..2f1880a52 --- /dev/null +++ b/MCServer/Plugins/APIDump/Classes/Network.lua @@ -0,0 +1,183 @@ + +-- Network.lua + +-- Defines the documentation for the cNetwork-related classes + + + + + +return +{ + cNetwork = + { + Desc = + [[ + This is the namespace for high-level network-related operations. Allows plugins to make TCP + connections to the outside world using a callback-based API.

+

+ All functions in this namespace are static, they should be called on the cNetwork class itself: +

+local Server = cNetwork:Listen(1024, ListenCallbacks);
+

+ ]], + AdditionalInfo = + { + { + Header = "Using callbacks", + Contents = + [[ + The entire Networking API is callback-based. Whenever an event happens on the network object, a + specific plugin-provided function is called. The callbacks are stored in tables which are passed + to the API functions, each table contains multiple callbacks for the various situations.

+

+ There are three different callback variants used: LinkCallbacks, LookupCallbacks and + ListenCallbacks. Each is used in the situation appropriate by its name - LinkCallbacks are used + for handling the traffic on a single network link (plus additionally creation of such link when + connecting as a client), LookupCallbacks are used when doing DNS and reverse-DNS lookups, and + ListenCallbacks are used for handling incoming connections as a server.

+

+ LinkCallbacks have the following structure:
+

+local LinkCallbacks =
+{
+	OnConnected = function ({{cTCPLink|a_TCPLink}})
+		-- The specified {{cTCPLink|link}} has succeeded in connecting to the remote server.
+		-- Only called if the link is being connected as a client (using cNetwork:Connect() )
+		-- Not used for incoming server links
+		-- All returned values are ignored
+	end,
+	
+	OnError = function ({{cTCPLink|a_TCPLink}}, a_ErrorCode, a_ErrorMsg)
+		-- The specified error has occured on the {{cTCPLink|link}}
+		-- No other callback will be called for this link from now on
+		-- For a client link being connected, this reports a connection error (destination unreachable etc.)
+		-- It is an Undefined Behavior to send data to a_TCPLink in or after this callback
+		-- All returned values are ignored
+	end,
+	
+	OnReceivedData = function ({{cTCPLink|a_TCPLink}}, a_Data)
+		-- Data has been received on the {{cTCPLink|link}}
+		-- Will get called whenever there's new data on the {{cTCPLink|link}}
+		-- a_Data contains the raw received data, as a string
+		-- All returned values are ignored
+	end,
+	
+	OnRemoteClosed = function ({{cTCPLink|a_TCPLink}})
+		-- The remote peer has closed the {{cTCPLink|link}}
+		-- The link is already closed, any data sent to it now will be lost
+		-- No other callback will be called for this link from now on
+		-- All returned values are ignored
+	end,
+}
+

+

+ LookupCallbacks have the following structure:
+

+local LookupCallbacks =
+{
+	OnError = function (a_Query, a_ErrorCode, a_ErrorMsg)
+		-- The specified error has occured while doing the lookup
+		-- a_Query is the hostname or IP being looked up
+		-- No other callback will be called for this lookup from now on
+		-- All returned values are ignored
+	end,
+
+	OnFinished = function (a_Query)
+		-- There are no more DNS records for this query
+		-- a_Query is the hostname or IP being looked up
+		-- No other callback will be called for this lookup from now on
+		-- All returned values are ignored
+	end,
+
+	OnNameResolved = function (a_Hostname, a_IP)
+		-- A DNS record has been found, the specified hostname resolves to the IP
+		-- Called for both Hostname -> IP and IP -> Hostname lookups
+		-- May be called multiple times if a hostname resolves to multiple IPs
+		-- All returned values are ignored
+	end,
+}
+

+

+ ListenCallbacks have the following structure:
+

+local ListenCallbacks =
+{
+	OnAccepted = function ({{cTCPLink|a_TCPLink}})
+		-- A new connection has been accepted and a {{cTCPLink|Link}} for it created
+		-- It is safe to send data to the link now
+		-- All returned values are ignored
+	end,
+	
+	OnError = function (a_ErrorCode, a_ErrorMsg)
+		-- The specified error has occured while trying to listen
+		-- No other callback will be called for this lookup from now on
+		-- This callback is called before the cNetwork:Listen() call returns
+		-- All returned values are ignored
+	end,
+
+	OnIncomingConnection = function (a_RemoteIP, a_RemotePort, a_LocalPort)
+		-- A new connection is being accepted, from the specified remote peer
+		-- This function needs to return either nil to drop the connection,
+		-- or valid LinkCallbacks to use for the new connection's {{cTCPLink|TCPLink}} object
+		return SomeLinkCallbacks
+	end,
+}
+

+ ]], + }, + }, -- AdditionalInfo + + Functions = + { + Connect = { Params = "Host, Port, LinkCallbacks", Return = "bool", Notes = "(STATIC) Begins establishing a (client) TCP connection to the specified host. Uses the LinkCallbacks table to report progress, success, errors and incoming data. Returns false if it fails immediately (bad port value, bad hostname format), true otherwise. Host can be either an IP address or a hostname." }, + HostnameToIP = { Params = "Host, LookupCallbacks", Return = "bool", Notes = "(STATIC) Begins a DNS lookup to find the IP address(es) for the specified host. Uses the LookupCallbacks table to report progress, success or errors. Returns false if it fails immediately (bad hostname format), true if the lookup started successfully. Host can be either a hostname or an IP address." }, + IPToHostname = { Params = "Address, LookupCallbacks", Return = "bool", Notes = "(STATIC) Begins a reverse-DNS lookup to find out the hostname for the specified IP address. Uses the LookupCallbacks table to report progress, success or errors. Returns false if it fails immediately (bad address format), true if the lookup started successfully." }, + Listen = { Params = "Port, ListenCallbacks", Return = "{{cServerHandle|ServerHandle}}", Notes = "(STATIC) Starts listening on the specified port. Uses the ListenCallbacks to report incoming connections or errors. Returns a {{cServerHandle}} object representing the server. If the listen operation failed, the OnError callback is called with the error details and the returned server handle will report IsListening() == false. The plugin needs to store the server handle object for as long as it needs the server running, if the server handle is garbage-collected by Lua, the listening socket will be closed and all current connections dropped." }, + }, + }, -- cNetwork + + cTCPLink = + { + Desc = + [[ + This class wraps a single TCP connection, that has been established. Plugins can create these by + calling {{cNetwork}}:Connect() to connect to a remote server, or by listening using + {{cNetwork}}:Listen() and accepting incoming connections. The links are callback-based - when an event + such as incoming data or remote disconnect happens on the link, a specific callback is called. See the + additional information in {{cNetwork}} documentation for details. + ]], + + Functions = + { + GetLocalIP = { Params = "", Return = "string", Notes = "Returns the IP address of the local endpoint of the TCP connection." }, + GetLocalPort = { Params = "", Return = "number", Notes = "Returns the port of the local endpoint of the TCP connection." }, + GetRemoteIP = { Params = "", Return = "string", Notes = "Returns the IP address of the remote endpoint of the TCP connection." }, + GetRemotePort = { Params = "", Return = "number", Notes = "Returns the port of the remote endpoint of the TCP connection." }, + Send = { Params = "Data", Return = "", Notes = "Sends the data (raw string) to the remote peer. The data is sent asynchronously and there is no report on the success of the send operation, other than the connection being closed or reset by the underlying OS." }, + }, + }, -- cTCPLink + + cServerHandle = + { + Desc = + [[ + This class provides an interface for TCP sockets listening for a connection. In order to listen, the + plugin needs to use the {{cNetwork}}:Listen() function to create the listening socket.

+

+ Note that when Lua garbage-collects this class, the listening socket is closed. Therefore the plugin + should keep it referenced in a global variable for as long as it wants the server running. + ]], + + Functions = + { + Close = { Params = "", Return = "", Notes = "Closes the listening socket. No more connections will be accepted, and all current connections will be closed." }, + IsListening = { Params = "", Return = "bool", Notes = "Returns true if the socket is listening." }, + }, + + }, -- cServerHandle +} + + + + diff --git a/src/Bindings/ManualBindings_Network.cpp b/src/Bindings/ManualBindings_Network.cpp index 24c8c73b8..902f687c8 100644 --- a/src/Bindings/ManualBindings_Network.cpp +++ b/src/Bindings/ManualBindings_Network.cpp @@ -218,7 +218,7 @@ static int tolua_cNetwork_Listen(lua_State * L) static int tolua_cTCPLink_Send(lua_State * L) { // Function signature: - // LinkInstance:Send(DataString) -> bool + // LinkInstance:Send(DataString) cLuaState S(L); if ( From 9b9ce6fa3b13cb00e380664a2b51e51ffb209b7d Mon Sep 17 00:00:00 2001 From: Howaner Date: Fri, 6 Feb 2015 21:40:20 +0100 Subject: [PATCH 09/95] Added IsOnGround() to cEntity --- src/Entities/Entity.h | 3 +++ src/Protocol/Protocol18x.cpp | 12 ++++++------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/Entities/Entity.h b/src/Entities/Entity.h index de9b88dfb..4a819fa4a 100644 --- a/src/Entities/Entity.h +++ b/src/Entities/Entity.h @@ -443,6 +443,9 @@ public: /** Set the invulnerable ticks from the entity */ void SetInvulnerableTicks(int a_InvulnerableTicks) { m_InvulnerableTicks = a_InvulnerableTicks; } + + /** Returns whether the player is on ground or not */ + bool IsOnGround(void) const { return m_bOnGround; } // tolua_end diff --git a/src/Protocol/Protocol18x.cpp b/src/Protocol/Protocol18x.cpp index 7d954a297..9b0f1c37c 100644 --- a/src/Protocol/Protocol18x.cpp +++ b/src/Protocol/Protocol18x.cpp @@ -386,7 +386,7 @@ void cProtocol180::SendEntityLook(const cEntity & a_Entity) Pkt.WriteVarInt(a_Entity.GetUniqueID()); Pkt.WriteByteAngle(a_Entity.GetYaw()); Pkt.WriteByteAngle(a_Entity.GetPitch()); - Pkt.WriteBool(true); // TODO: IsOnGround() on entities + Pkt.WriteBool(a_Entity.IsOnGround()); } @@ -429,7 +429,7 @@ void cProtocol180::SendEntityRelMove(const cEntity & a_Entity, char a_RelX, char Pkt.WriteByte(a_RelX); Pkt.WriteByte(a_RelY); Pkt.WriteByte(a_RelZ); - Pkt.WriteBool(true); // TODO: IsOnGround() on entities + Pkt.WriteBool(a_Entity.IsOnGround()); } @@ -447,7 +447,7 @@ void cProtocol180::SendEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, Pkt.WriteByte(a_RelZ); Pkt.WriteByteAngle(a_Entity.GetYaw()); Pkt.WriteByteAngle(a_Entity.GetPitch()); - Pkt.WriteBool(true); // TODO: IsOnGround() on entities + Pkt.WriteBool(a_Entity.IsOnGround()); } @@ -947,7 +947,7 @@ void cProtocol180::SendPlayerMoveLook(void) Pkt.WriteDouble(Player->GetPosX()); // The "+ 0.001" is there because otherwise the player falls through the block they were standing on. - Pkt.WriteDouble(Player->GetStance() + 0.001); + Pkt.WriteDouble(Player->GetPosY() + 0.001); Pkt.WriteDouble(Player->GetPosZ()); Pkt.WriteFloat((float)Player->GetYaw()); @@ -976,7 +976,7 @@ void cProtocol180::SendPlayerSpawn(const cPlayer & a_Player) Pkt.WriteVarInt(a_Player.GetUniqueID()); Pkt.WriteUUID(cMojangAPI::MakeUUIDShort(a_Player.GetUUID())); Pkt.WriteFPInt(a_Player.GetPosX()); - Pkt.WriteFPInt(a_Player.GetPosY()); + Pkt.WriteFPInt(a_Player.GetPosY() + 0.001); // The "+ 0.001" is there because otherwise the player falls through the block they were standing on. Pkt.WriteFPInt(a_Player.GetPosZ()); Pkt.WriteByteAngle(a_Player.GetYaw()); Pkt.WriteByteAngle(a_Player.GetPitch()); @@ -1305,7 +1305,7 @@ void cProtocol180::SendTeleportEntity(const cEntity & a_Entity) Pkt.WriteFPInt(a_Entity.GetPosZ()); Pkt.WriteByteAngle(a_Entity.GetYaw()); Pkt.WriteByteAngle(a_Entity.GetPitch()); - Pkt.WriteBool(true); // TODO: IsOnGrond() on entities + Pkt.WriteBool(a_Entity.IsOnGround()); } From 7813cd20222451f3f1f02b3264bb2d689006f95a Mon Sep 17 00:00:00 2001 From: Howaner Date: Fri, 6 Feb 2015 21:42:32 +0100 Subject: [PATCH 10/95] cPlayer should override IsOnGround() --- src/Entities/Entity.h | 4 ++-- src/Entities/Player.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Entities/Entity.h b/src/Entities/Entity.h index 4a819fa4a..809e974d2 100644 --- a/src/Entities/Entity.h +++ b/src/Entities/Entity.h @@ -444,8 +444,8 @@ public: /** Set the invulnerable ticks from the entity */ void SetInvulnerableTicks(int a_InvulnerableTicks) { m_InvulnerableTicks = a_InvulnerableTicks; } - /** Returns whether the player is on ground or not */ - bool IsOnGround(void) const { return m_bOnGround; } + /** Returns whether the entity is on ground or not */ + virtual bool IsOnGround(void) const { return m_bOnGround; } // tolua_end diff --git a/src/Entities/Player.h b/src/Entities/Player.h index fa9ac7cad..47bff64f2 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -121,7 +121,7 @@ public: inline void SetStance( const double a_Stance) { m_Stance = a_Stance; } double GetEyeHeight(void) const; // tolua_export Vector3d GetEyePosition(void) const; // tolua_export - inline bool IsOnGround(void) const {return m_bTouchGround; } // tolua_export + virtual bool IsOnGround(void) const override {return m_bTouchGround; } inline double GetStance(void) const { return GetPosY() + 1.62; } // tolua_export // TODO: Proper stance when crouching etc. inline cInventory & GetInventory(void) { return m_Inventory; } // tolua_export inline const cInventory & GetInventory(void) const { return m_Inventory; } From d36e8ffd778c58e1d77d6aa57eb16f0de5cd9fb4 Mon Sep 17 00:00:00 2001 From: Howaner Date: Fri, 6 Feb 2015 21:44:53 +0100 Subject: [PATCH 11/95] Updated IsOnGround() documentation --- MCServer/Plugins/APIDump/APIDesc.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MCServer/Plugins/APIDump/APIDesc.lua b/MCServer/Plugins/APIDump/APIDesc.lua index b2c7108e9..4c8dbd1e9 100644 --- a/MCServer/Plugins/APIDump/APIDesc.lua +++ b/MCServer/Plugins/APIDump/APIDesc.lua @@ -844,6 +844,7 @@ end IsMinecart = { Params = "", Return = "bool", Notes = "Returns true if the entity represents a {{cMinecart|minecart}}" }, IsMob = { Params = "", Return = "bool", Notes = "Returns true if the entity represents any {{cMonster|mob}}." }, IsOnFire = { Params = "", Return = "bool", Notes = "Returns true if the entity is on fire" }, + IsOnGround = { Params = "", Return = "bool", Notes = "Returns true if the entity is on ground (not falling, not jumping, not flying)" }, IsPainting = { Params = "", Return = "bool", Notes = "Returns if this entity is a painting." }, IsPawn = { Params = "", Return = "bool", Notes = "Returns true if the entity is a {{cPawn}} descendant." }, IsPickup = { Params = "", Return = "bool", Notes = "Returns true if the entity represents a {{cPickup|pickup}}." }, @@ -1832,7 +1833,6 @@ a_Player:OpenWindow(Window); IsGameModeSpectator = { Params = "", Return = "bool", Notes = "Returns true if the player is in the gmSpectator gamemode, or has their gamemode unset and the world is a gmSpectator world." }, IsGameModeSurvival = { Params = "", Return = "bool", Notes = "Returns true if the player is in the gmSurvival gamemode, or has their gamemode unset and the world is a gmSurvival world." }, IsInBed = { Params = "", Return = "bool", Notes = "Returns true if the player is currently lying in a bed." }, - IsOnGround = { Params = "", Return = "bool", Notes = "Returns true if the player is on ground (not falling, not jumping, not flying)" }, IsSatiated = { Params = "", Return = "bool", Notes = "Returns true if the player is satiated (cannot eat)." }, IsVisible = { Params = "", Return = "bool", Notes = "Returns true if the player is visible to other players" }, LoadRank = { Params = "", Return = "", Notes = "Reloads the player's rank, message visuals and permissions from the {{cRankManager}}, based on the player's current rank." }, From ca591c15a043f66d52d835b316473bdf8ef658d8 Mon Sep 17 00:00:00 2001 From: Howaner Date: Fri, 6 Feb 2015 21:45:29 +0100 Subject: [PATCH 12/95] Spacing --- src/Entities/Player.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Entities/Player.h b/src/Entities/Player.h index 47bff64f2..7abb1b98a 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -121,7 +121,7 @@ public: inline void SetStance( const double a_Stance) { m_Stance = a_Stance; } double GetEyeHeight(void) const; // tolua_export Vector3d GetEyePosition(void) const; // tolua_export - virtual bool IsOnGround(void) const override {return m_bTouchGround; } + virtual bool IsOnGround(void) const override { return m_bTouchGround; } inline double GetStance(void) const { return GetPosY() + 1.62; } // tolua_export // TODO: Proper stance when crouching etc. inline cInventory & GetInventory(void) { return m_Inventory; } // tolua_export inline const cInventory & GetInventory(void) const { return m_Inventory; } From d32831d7e8549227176655da494f32aa6b77ac15 Mon Sep 17 00:00:00 2001 From: Howaner Date: Sat, 7 Feb 2015 11:03:38 +0100 Subject: [PATCH 13/95] Set reuse flag to sockets Should fix #1726 --- src/OSSupport/ServerHandleImpl.cpp | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/OSSupport/ServerHandleImpl.cpp b/src/OSSupport/ServerHandleImpl.cpp index 5fc5662e1..a3a08e84f 100644 --- a/src/OSSupport/ServerHandleImpl.cpp +++ b/src/OSSupport/ServerHandleImpl.cpp @@ -125,6 +125,17 @@ bool cServerHandleImpl::Listen(UInt16 a_Port) bool NeedsTwoSockets = false; int err; evutil_socket_t MainSock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); + + // Set reuse flag + { + #if defined(_WIN32) || defined(ANDROID_NDK) + char yes = 1; + #else + int yes = 1; + #endif + setsockopt(MainSock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)); + } + if (!IsValidSocket(MainSock)) { // Failed to create IPv6 socket, create an IPv4 one instead: @@ -193,6 +204,7 @@ bool cServerHandleImpl::Listen(UInt16 a_Port) } m_ConnListener = evconnlistener_new(cNetworkSingleton::Get().GetEventBase(), Callback, this, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 0, MainSock); m_IsListening = true; + if (!NeedsTwoSockets) { return true; @@ -201,6 +213,17 @@ bool cServerHandleImpl::Listen(UInt16 a_Port) // If a secondary socket is required (WinXP dual-stack), create it here: LOGD("Creating a second socket for IPv4"); evutil_socket_t SecondSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + + // Set reuse flag + { + #if defined(_WIN32) || defined(ANDROID_NDK) + char yes = 1; + #else + int yes = 1; + #endif + setsockopt(SecondSock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)); + } + if (!IsValidSocket(SecondSock)) { err = EVUTIL_SOCKET_ERROR(); @@ -233,7 +256,7 @@ bool cServerHandleImpl::Listen(UInt16 a_Port) if (listen(SecondSock, 0) != 0) { err = EVUTIL_SOCKET_ERROR(); - LOGD("Cannot listen on on secondary socket on port %d: %d (%s)", a_Port, err, evutil_socket_error_to_string(err)); + LOGD("Cannot listen on secondary socket on port %d: %d (%s)", a_Port, err, evutil_socket_error_to_string(err)); evutil_closesocket(SecondSock); return true; // Report as success, the primary socket is working } From 43b68975f7a2491ee9c0702f1d2fe01d78bb8a8e Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sat, 7 Feb 2015 13:30:45 +0100 Subject: [PATCH 14/95] APIDump: Added client and server examples. --- MCServer/Plugins/APIDump/Classes/Network.lua | 137 +++++++++++++++++++ 1 file changed, 137 insertions(+) diff --git a/MCServer/Plugins/APIDump/Classes/Network.lua b/MCServer/Plugins/APIDump/Classes/Network.lua index 2f1880a52..065a743d8 100644 --- a/MCServer/Plugins/APIDump/Classes/Network.lua +++ b/MCServer/Plugins/APIDump/Classes/Network.lua @@ -126,6 +126,143 @@ local ListenCallbacks =

]], }, + + { + Header = "Example client connection", + Contents = + [[ + The following example, adapted from the NetworkTest plugin, shows a simple example of a client + connection using the cNetwork API. It connects to www.google.com on port 80 (http) and sends a http + request for the front page. It dumps the received data to the console.

+

+ First, the callbacks are defined in a table. The OnConnected() callback takes care of sending the http + request once the socket is connected. The OnReceivedData() callback sends all data to the console. The + OnError() callback logs any errors. Then, the connection is initiated using the cNetwork::Connect() API + function.

+

+

+-- Define the callbacks to use for the client connection:
+local ConnectCallbacks =
+{
+	OnConnected = function (a_Link)
+		-- Connection succeeded, send the http request:
+		a_Link:Send("GET / HTTP/1.0\r\nHost: www.google.com\r\n\r\n")
+	end,
+	
+	OnError = function (a_Link, a_ErrorCode, a_ErrorMsg)
+		-- Log the error to console:
+		LOG("An error has occurred while talking to google.com: " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")")
+	end,
+	
+	OnReceivedData = function (a_Link, a_Data)
+		-- Log the received data to console:
+		LOG("Incoming http data:\r\n" .. a_Data)
+	end,
+	
+	OnRemoteClosed = function (a_Link)
+		-- Log the event into the console:
+		LOG("Connection to www.google.com closed")
+	end,
+}
+
+-- Connect:
+if not(cNetwork:Connect("www.google.com", 80, ConnectCallbacks)) then
+	-- Highly unlikely, but better check for errors
+	LOG("Cannot queue connection to www.google.com")
+end
+-- Note that the connection is being made on the background,
+-- there's no guarantee that it's already connected at this point in code
+
+ ]], + }, + + { + Header = "Example server implementation", + Contents = + [[ + The following example, adapted from the NetworkTest plugin, shows a simple example of creating a + server listening on a TCP port using the cNetwork API. The server listens on port 9876 and for + each incoming connection it sends a welcome message and then echoes back whatever the client has + sent ("echo server").

+

+ First, the callbacks for the listening server are defined. The most important of those is the + OnIncomingConnection() callback that must return the LinkCallbacks that will be used for the new + connection. In our simple scenario each connection uses the same callbacks, so a predefined + callbacks table is returned; it is, however, possible to define different callbacks for each + connection. This allows the callbacks to be "personalised", for example by the remote IP or the + time of connection. The OnAccepted() and OnError() callbacks only log that they occurred, there's + no processing needed for them.

+

+ Finally, the cNetwork:Listen() API function is used to create the listening server. The status of + the server is checked and if it is successfully listening, it is stored in a global variable, so + that Lua doesn't garbage-collect it until we actually want the server closed.

+

+

+-- Define the callbacks used for the incoming connections:
+local EchoLinkCallbacks =
+{
+	OnConnected = function (a_Link)
+		-- This will not be called for a server connection, ever
+		assert(false, "Unexpected Connect callback call")
+	end,
+	
+	OnError = function (a_Link, a_ErrorCode, a_ErrorMsg)
+		-- Log the error to console:
+		local RemoteName = "'" .. a_Link:GetRemoteIP() .. ":" .. a_Link:GetRemotePort() .. "'"
+		LOG("An error has occurred while talking to " .. RemoteName .. ": " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")")
+	end,
+	
+	OnReceivedData = function (a_Link, a_Data)
+		-- Send the received data back to the remote peer
+		a_Link:Send(Data)
+	end,
+	
+	OnRemoteClosed = function (a_Link)
+		-- Log the event into the console:
+		local RemoteName = "'" .. a_Link:GetRemoteIP() .. ":" .. a_Link:GetRemotePort() .. "'"
+		LOG("Connection to '" .. RemoteName .. "' closed")
+	end,
+}
+
+-- Define the callbacks used by the server:
+local ListenCallbacks =
+{
+	OnAccepted = function (a_Link)
+		-- No processing needed, just log that this happened:
+		LOG("OnAccepted callback called")
+	end,
+	
+	OnError = function (a_ErrorCode, a_ErrorMsg)
+		-- An error has occured while listening for incoming connections, log it:
+		LOG("Cannot listen, error " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")"
+	end,
+
+	OnIncomingConnection = function (a_RemoteIP, a_RemotePort, a_LocalPort)
+		-- A new connection is being accepted, give it the EchoCallbacks
+		return EchoLinkCallbacks
+	end,
+}
+
+-- Start the server:
+local Server = cNetwork:Listen(9876, ListenCallbacks)
+if not(Server:IsListening()) then
+	-- The error has been already printed in the OnError() callbacks
+	-- Just bail out
+	return;
+end
+
+-- Store the server globally, so that it stays open:
+g_Server = Server
+
+...
+
+-- Elsewhere in the code, when terminating:
+-- Close the server and let it be garbage-collected:
+g_Server:Close()
+g_Server = nil
+
+ ]], + }, }, -- AdditionalInfo Functions = From be528a9f527e631181b590346d77eaaf64c914cd Mon Sep 17 00:00:00 2001 From: Howaner Date: Sat, 7 Feb 2015 18:39:24 +0100 Subject: [PATCH 15/95] Use evutil_make_listen_socket_reuseable --- src/OSSupport/ServerHandleImpl.cpp | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/src/OSSupport/ServerHandleImpl.cpp b/src/OSSupport/ServerHandleImpl.cpp index a3a08e84f..6f4343b1f 100644 --- a/src/OSSupport/ServerHandleImpl.cpp +++ b/src/OSSupport/ServerHandleImpl.cpp @@ -127,14 +127,9 @@ bool cServerHandleImpl::Listen(UInt16 a_Port) evutil_socket_t MainSock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); // Set reuse flag - { - #if defined(_WIN32) || defined(ANDROID_NDK) - char yes = 1; - #else - int yes = 1; - #endif - setsockopt(MainSock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)); - } + #if !defined(_WIN32) + evutil_make_listen_socket_reuseable(MainSock); + #endif if (!IsValidSocket(MainSock)) { @@ -215,14 +210,9 @@ bool cServerHandleImpl::Listen(UInt16 a_Port) evutil_socket_t SecondSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // Set reuse flag - { - #if defined(_WIN32) || defined(ANDROID_NDK) - char yes = 1; - #else - int yes = 1; - #endif - setsockopt(SecondSock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)); - } + #if !defined(_WIN32) + evutil_make_listen_socket_reuseable(SecondSock); + #endif if (!IsValidSocket(SecondSock)) { From a939e2ded97730b7847593dab2b9abb849bd6555 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sun, 8 Feb 2015 12:24:15 +0100 Subject: [PATCH 16/95] WSSAnvil: Fixed chunk data padding. When the chunk data fit perfectly into the old space, an extra 4 KiB of padding zeroes were written, overwriting the next chunk. Fixes #1730. --- src/WorldStorage/WSSAnvil.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp index a76e9461a..ae82db346 100755 --- a/src/WorldStorage/WSSAnvil.cpp +++ b/src/WorldStorage/WSSAnvil.cpp @@ -3136,8 +3136,11 @@ bool cWSSAnvil::cMCAFile::SetChunkData(const cChunkCoords & a_Chunk, const AStri // Add padding to 4K boundary: size_t BytesWritten = a_Data.size() + MCA_CHUNK_HEADER_LENGTH; - static const char Padding[4095] = {0}; - m_File.Write(Padding, 4096 - (BytesWritten % 4096)); + if (BytesWritten % 4096 != 0) + { + static const char Padding[4095] = {0}; + m_File.Write(Padding, 4096 - (BytesWritten % 4096)); + } // Store the header: ChunkSize = ((u_long)a_Data.size() + MCA_CHUNK_HEADER_LENGTH + 4095) / 4096; // Round data size *up* to nearest 4KB sector, make it a sector number From 81d7329ad31657e9e587dd7231be503ae1b157a9 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sun, 8 Feb 2015 14:41:24 +0100 Subject: [PATCH 17/95] ServerHandle: Fixed socket reuse. Fixes CID 104670, CID 104670 and CID 103724. --- src/OSSupport/ServerHandleImpl.cpp | 40 ++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/src/OSSupport/ServerHandleImpl.cpp b/src/OSSupport/ServerHandleImpl.cpp index 6f4343b1f..72092df10 100644 --- a/src/OSSupport/ServerHandleImpl.cpp +++ b/src/OSSupport/ServerHandleImpl.cpp @@ -126,11 +126,6 @@ bool cServerHandleImpl::Listen(UInt16 a_Port) int err; evutil_socket_t MainSock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); - // Set reuse flag - #if !defined(_WIN32) - evutil_make_listen_socket_reuseable(MainSock); - #endif - if (!IsValidSocket(MainSock)) { // Failed to create IPv6 socket, create an IPv4 one instead: @@ -144,6 +139,16 @@ bool cServerHandleImpl::Listen(UInt16 a_Port) return false; } + // Allow the port to be reused right after the socket closes: + if (evutil_make_listen_socket_reuseable(MainSock) != 0) + { + m_ErrorCode = EVUTIL_SOCKET_ERROR(); + Printf(m_ErrorMsg, "Port %d cannot be made reusable: %d (%s). Restarting the server might not work.", + a_Port, m_ErrorCode, evutil_socket_error_to_string(m_ErrorCode) + ); + LOG("%s", m_ErrorMsg.c_str()); + } + // Bind to all interfaces: sockaddr_in name; memset(&name, 0, sizeof(name)); @@ -170,6 +175,16 @@ bool cServerHandleImpl::Listen(UInt16 a_Port) setsockopt(MainSock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast(&Zero), sizeof(Zero)); #endif + // Allow the port to be reused right after the socket closes: + if (evutil_make_listen_socket_reuseable(MainSock) != 0) + { + m_ErrorCode = EVUTIL_SOCKET_ERROR(); + Printf(m_ErrorMsg, "Port %d cannot be made reusable: %d (%s). Restarting the server might not work.", + a_Port, m_ErrorCode, evutil_socket_error_to_string(m_ErrorCode) + ); + LOG("%s", m_ErrorMsg.c_str()); + } + // Bind to all interfaces: sockaddr_in6 name; memset(&name, 0, sizeof(name)); @@ -209,11 +224,6 @@ bool cServerHandleImpl::Listen(UInt16 a_Port) LOGD("Creating a second socket for IPv4"); evutil_socket_t SecondSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - // Set reuse flag - #if !defined(_WIN32) - evutil_make_listen_socket_reuseable(SecondSock); - #endif - if (!IsValidSocket(SecondSock)) { err = EVUTIL_SOCKET_ERROR(); @@ -221,6 +231,16 @@ bool cServerHandleImpl::Listen(UInt16 a_Port) return true; // Report as success, the primary socket is working } + // Allow the port to be reused right after the socket closes: + if (evutil_make_listen_socket_reuseable(MainSock) != 0) + { + m_ErrorCode = EVUTIL_SOCKET_ERROR(); + Printf(m_ErrorMsg, "Port %d cannot be made reusable (second socket): %d (%s). Restarting the server might not work.", + a_Port, m_ErrorCode, evutil_socket_error_to_string(m_ErrorCode) + ); + LOG("%s", m_ErrorMsg.c_str()); + } + // Make the secondary socket nonblocking: if (evutil_make_socket_nonblocking(SecondSock) != 0) { From 1ce91646949467f72226472116088a5aaaa59664 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sun, 8 Feb 2015 14:49:46 +0100 Subject: [PATCH 18/95] Protocol 1.7: Fixed Coverity issues. Fixes CID 66411, CID 103166 and CID 103167. --- src/Protocol/Protocol17x.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp index 169367949..f78c2e54b 100644 --- a/src/Protocol/Protocol17x.cpp +++ b/src/Protocol/Protocol17x.cpp @@ -679,8 +679,8 @@ void cProtocol172::SendMapDecorators(int a_ID, const cMapDecoratorList & a_Decor for (cMapDecoratorList::const_iterator it = a_Decorators.begin(); it != a_Decorators.end(); ++it) { - ASSERT((it->GetPixelX() >= 0) && (it->GetPixelX() < 256)); - ASSERT((it->GetPixelZ() >= 0) && (it->GetPixelZ() < 256)); + ASSERT(it->GetPixelX() < 256); + ASSERT(it->GetPixelZ() < 256); Pkt.WriteByte(static_cast((it->GetType() << 4) | static_cast(it->GetRot() & 0xf))); Pkt.WriteByte(static_cast(it->GetPixelX())); Pkt.WriteByte(static_cast(it->GetPixelZ())); @@ -694,7 +694,7 @@ void cProtocol172::SendMapDecorators(int a_ID, const cMapDecoratorList & a_Decor void cProtocol172::SendMapInfo(int a_ID, unsigned int a_Scale) { ASSERT(m_State == 3); // In game mode? - ASSERT((a_Scale >= 0) && (a_Scale < 256)); + ASSERT(a_Scale < 256); cPacketizer Pkt(*this, 0x34); Pkt.WriteVarInt(static_cast(a_ID)); @@ -1757,7 +1757,10 @@ void cProtocol172::HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer) void cProtocol172::HandlePacketLoginEncryptionResponse(cByteBuffer & a_ByteBuffer) { short EncKeyLength, EncNonceLength; - a_ByteBuffer.ReadBEShort(EncKeyLength); + if (!a_ByteBuffer.ReadBEShort(EncKeyLength)) + { + return; + } if ((EncKeyLength < 0) || (EncKeyLength > MAX_ENC_LEN)) { LOGD("Invalid Encryption Key length: %d. Kicking client.", EncKeyLength); From 086e112161f1f52b4978183ce5b3faaf371bb76f Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sun, 8 Feb 2015 18:18:11 +0100 Subject: [PATCH 19/95] Update COMPILING.md Fixed VS2013 download link --- COMPILING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/COMPILING.md b/COMPILING.md index 34b1e976f..f8b292ebc 100644 --- a/COMPILING.md +++ b/COMPILING.md @@ -4,8 +4,8 @@ To compile MCServer from source, you need CMake and make, as well as a C compile ## Windows ## -We use Microsoft Visual Studio for Windows compilation. It is possible to use other toolchains, but it isn't tested nor supported. Visual Studio versions 2013 Express is being actively used for development. -You can find download links for VS2013 Express here: http://www.microsoft.com/en-us/download/details.aspx?id=40787 +We use Microsoft Visual Studio for Windows compilation. It is possible to use other toolchains, but it isn't tested nor supported. Visual Studio versions 2013 Express for Desktop is being actively used for development. +You can find download links for VS2013 Express here: http://go.microsoft.com/?linkid=9832280 Next, you need to download and install CMake. Download from here: http://cmake.org/cmake/resources/software.html . You should download a full installation package, so that the installer will set everything up for you (especially the paths). From 16636ff6e2bff3658e0843eee9dfad440771b62f Mon Sep 17 00:00:00 2001 From: Mattes D Date: Thu, 12 Feb 2015 20:05:55 +0100 Subject: [PATCH 20/95] LuaAPI: Added client TLS support for TCP links. --- MCServer/Plugins/APIDump/Classes/Network.lua | 11 +- MCServer/Plugins/NetworkTest/Info.lua | 6 + MCServer/Plugins/NetworkTest/NetworkTest.lua | 44 ++++ src/Bindings/LuaState.cpp | 12 ++ src/Bindings/LuaState.h | 2 + src/Bindings/LuaTCPLink.cpp | 201 +++++++++++++++++++ src/Bindings/LuaTCPLink.h | 55 +++++ src/Bindings/ManualBindings_Network.cpp | 56 +++++- src/Globals.h | 3 +- src/HTTPServer/SslHTTPConnection.cpp | 9 + src/HTTPServer/SslHTTPConnection.h | 2 + src/PolarSSL++/CryptoKey.cpp | 2 +- 12 files changed, 395 insertions(+), 8 deletions(-) diff --git a/MCServer/Plugins/APIDump/Classes/Network.lua b/MCServer/Plugins/APIDump/Classes/Network.lua index 065a743d8..ace6c2449 100644 --- a/MCServer/Plugins/APIDump/Classes/Network.lua +++ b/MCServer/Plugins/APIDump/Classes/Network.lua @@ -282,7 +282,15 @@ g_Server = nil calling {{cNetwork}}:Connect() to connect to a remote server, or by listening using {{cNetwork}}:Listen() and accepting incoming connections. The links are callback-based - when an event such as incoming data or remote disconnect happens on the link, a specific callback is called. See the - additional information in {{cNetwork}} documentation for details. + additional information in {{cNetwork}} documentation for details.

+

+ The link can also optionally perform TLS encryption. Plugins can use the StartTLSClient() function to + start the TLS handshake as the client side. Since that call, the OnReceivedData() callback is + overridden internally so that the data is first routed through the TLS decryptor, and the plugin's + callback is only called for the decrypted data, once it starts arriving. The Send function changes its + behavior so that the data written by the plugin is first encrypted and only then sent over the + network. Note that calling Send() before the TLS handshake finishes is supported, but the data is + queued internally and only sent once the TLS handshake is completed. ]], Functions = @@ -292,6 +300,7 @@ g_Server = nil GetRemoteIP = { Params = "", Return = "string", Notes = "Returns the IP address of the remote endpoint of the TCP connection." }, GetRemotePort = { Params = "", Return = "number", Notes = "Returns the port of the remote endpoint of the TCP connection." }, Send = { Params = "Data", Return = "", Notes = "Sends the data (raw string) to the remote peer. The data is sent asynchronously and there is no report on the success of the send operation, other than the connection being closed or reset by the underlying OS." }, + StartTLSClient = { Params = "OwnCert, OwnPrivateKey, OwnPrivateKeyPassword", Return = "", Notes = "Starts a TLS handshake on the link, as a client side of the TLS. The Own___ parameters specify the client certificate and its corresponding private key and password; all three parameters are optional and no client certificate is presented to the remote peer if they are not used or all empty. Once the TLS handshake is started by this call, all incoming data is first decrypted before being sent to the OnReceivedData callback, and all outgoing data is queued until the TLS handshake completes, and then sent encrypted over the link." }, }, }, -- cTCPLink diff --git a/MCServer/Plugins/NetworkTest/Info.lua b/MCServer/Plugins/NetworkTest/Info.lua index f366fd1be..c3c2ea8fc 100644 --- a/MCServer/Plugins/NetworkTest/Info.lua +++ b/MCServer/Plugins/NetworkTest/Info.lua @@ -84,6 +84,12 @@ g_PluginInfo = }, }, -- lookup + wasc = + { + HelpString = "Requests the webadmin homepage using https", + Handler = HandleConsoleNetWasc, + }, -- wasc + }, -- Subcommands }, -- net }, diff --git a/MCServer/Plugins/NetworkTest/NetworkTest.lua b/MCServer/Plugins/NetworkTest/NetworkTest.lua index 7932f4b88..21f89c7f9 100644 --- a/MCServer/Plugins/NetworkTest/NetworkTest.lua +++ b/MCServer/Plugins/NetworkTest/NetworkTest.lua @@ -252,3 +252,47 @@ end + +function HandleConsoleNetWasc(a_Split) + local Callbacks = + { + OnConnected = function (a_Link) + LOG("Connected to webadmin, starting TLS...") + local res, msg = a_Link:StartTLSClient("", "", "") + if not(res) then + LOG("Failed to start TLS client: " .. msg) + return + end + -- We need to send a keep-alive due to #1737 + a_Link:Send("GET / HTTP/1.0\r\nHost: localhost\r\nConnection: keep-alive\r\n\r\n") + end, + + OnError = function (a_Link, a_ErrorCode, a_ErrorMsg) + LOG("Connection to webadmin failed: " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")") + end, + + OnReceivedData = function (a_Link, a_Data) + LOG("Received data from webadmin:\r\n" .. a_Data) + + -- Close the link once all the data is received: + if (a_Data == "0\r\n\r\n") then -- Poor man's end-of-data detection; works on localhost + -- TODO: The Close() method is not yet exported to Lua + -- a_Link:Close() + end + end, + + OnRemoteClosed = function (a_Link) + LOG("Connection to webadmin was closed") + end, + } + + if not(cNetwork:Connect("localhost", "8080", Callbacks)) then + LOG("Canot connect to webadmin") + end + + return true +end + + + + diff --git a/src/Bindings/LuaState.cpp b/src/Bindings/LuaState.cpp index 73b114599..81770058c 100644 --- a/src/Bindings/LuaState.cpp +++ b/src/Bindings/LuaState.cpp @@ -343,6 +343,18 @@ bool cLuaState::PushFunction(const cTableRef & a_TableRef) +void cLuaState::PushNil(void) +{ + ASSERT(IsValid()); + + lua_pushnil(m_LuaState); + m_NumCurrentFunctionArgs += 1; +} + + + + + void cLuaState::Push(const AString & a_String) { ASSERT(IsValid()); diff --git a/src/Bindings/LuaState.h b/src/Bindings/LuaState.h index f68b022ea..7fc3197eb 100644 --- a/src/Bindings/LuaState.h +++ b/src/Bindings/LuaState.h @@ -184,6 +184,8 @@ public: /** Returns true if a_FunctionName is a valid Lua function that can be called */ bool HasFunction(const char * a_FunctionName); + void PushNil(void); + // Push a const value onto the stack (keep alpha-sorted): void Push(const AString & a_String); void Push(const AStringVector & a_Vector); diff --git a/src/Bindings/LuaTCPLink.cpp b/src/Bindings/LuaTCPLink.cpp index 6b8395806..7e2c10e13 100644 --- a/src/Bindings/LuaTCPLink.cpp +++ b/src/Bindings/LuaTCPLink.cpp @@ -64,6 +64,13 @@ cLuaTCPLink::~cLuaTCPLink() bool cLuaTCPLink::Send(const AString & a_Data) { + // If running in SSL mode, push the data into the SSL context instead: + if (m_SslContext != nullptr) + { + m_SslContext->Send(a_Data); + return true; + } + // Safely grab a copy of the link: cTCPLinkPtr Link = m_Link; if (Link == nullptr) @@ -179,6 +186,58 @@ void cLuaTCPLink::Close(void) +AString cLuaTCPLink::StartTLSClient( + const AString & a_OwnCertData, + const AString & a_OwnPrivKeyData, + const AString & a_OwnPrivKeyPassword +) +{ + // Check preconditions: + if (m_SslContext != nullptr) + { + return "TLS is already active on this link"; + } + if ( + (a_OwnCertData.empty() && !a_OwnPrivKeyData.empty()) || + (!a_OwnCertData.empty() && a_OwnPrivKeyData.empty()) + ) + { + return "Either provide both the certificate and private key, or neither"; + } + + // Create the SSL context: + m_SslContext = std::make_unique(*this); + m_SslContext->Initialize(true); + + // Create the peer cert, if required: + if (!a_OwnCertData.empty() && !a_OwnPrivKeyData.empty()) + { + auto OwnCert = std::make_shared(); + int res = OwnCert->Parse(a_OwnCertData.data(), a_OwnCertData.size()); + if (res != 0) + { + m_SslContext.reset(); + return Printf("Cannot parse peer certificate: -0x%x", res); + } + auto OwnPrivKey = std::make_shared(); + res = OwnPrivKey->ParsePrivate(a_OwnPrivKeyData.data(), a_OwnPrivKeyData.size(), a_OwnPrivKeyPassword); + if (res != 0) + { + m_SslContext.reset(); + return Printf("Cannot parse peer private key: -0x%x", res); + } + m_SslContext->SetOwnCert(OwnCert, OwnPrivKey); + } + + // Start the handshake: + m_SslContext->Handshake(); + return ""; +} + + + + + void cLuaTCPLink::Terminated(void) { // Disable the callbacks: @@ -207,6 +266,26 @@ void cLuaTCPLink::Terminated(void) +void cLuaTCPLink::ReceivedCleartextData(const char * a_Data, size_t a_NumBytes) +{ + // Check if we're still valid: + if (!m_Callbacks.IsValid()) + { + return; + } + + // Call the callback: + cPluginLua::cOperation Op(m_Plugin); + if (!Op().Call(cLuaState::cTableRef(m_Callbacks, "OnReceivedData"), this, AString(a_Data, a_NumBytes))) + { + LOGINFO("cTCPLink OnReceivedData callback failed in plugin %s.", m_Plugin.GetName().c_str()); + } +} + + + + + void cLuaTCPLink::OnConnected(cTCPLink & a_Link) { // Check if we're still valid: @@ -269,6 +348,13 @@ void cLuaTCPLink::OnReceivedData(const char * a_Data, size_t a_Length) return; } + // If we're running in SSL mode, put the data into the SSL decryptor: + if (m_SslContext != nullptr) + { + m_SslContext->StoreReceivedData(a_Data, a_Length); + return; + } + // Call the callback: cPluginLua::cOperation Op(m_Plugin); if (!Op().Call(cLuaState::cTableRef(m_Callbacks, "OnReceivedData"), this, AString(a_Data, a_Length))) @@ -302,3 +388,118 @@ void cLuaTCPLink::OnRemoteClosed(void) + +//////////////////////////////////////////////////////////////////////////////// +// cLuaTCPLink::cLinkSslContext: + +cLuaTCPLink::cLinkSslContext::cLinkSslContext(cLuaTCPLink & a_Link): + m_Link(a_Link) +{ +} + + + + + +void cLuaTCPLink::cLinkSslContext::StoreReceivedData(const char * a_Data, size_t a_NumBytes) +{ + m_EncryptedData.append(a_Data, a_NumBytes); + + // Try to finish a pending handshake: + TryFinishHandshaking(); + + // Flush any cleartext data that can be "received": + FlushBuffers(); +} + + + + + +void cLuaTCPLink::cLinkSslContext::FlushBuffers(void) +{ + // If the handshake didn't complete yet, bail out: + if (!HasHandshaken()) + { + return; + } + + char Buffer[1024]; + int NumBytes; + while ((NumBytes = ReadPlain(Buffer, sizeof(Buffer))) > 0) + { + m_Link.ReceivedCleartextData(Buffer, static_cast(NumBytes)); + } +} + + + + + +void cLuaTCPLink::cLinkSslContext::TryFinishHandshaking(void) +{ + // If the handshake hasn't finished yet, retry: + if (!HasHandshaken()) + { + Handshake(); + } + + // If the handshake succeeded, write all the queued plaintext data: + if (HasHandshaken()) + { + WritePlain(m_CleartextData.data(), m_CleartextData.size()); + m_CleartextData.clear(); + } +} + + + + + +void cLuaTCPLink::cLinkSslContext::Send(const AString & a_Data) +{ + // If the handshake hasn't completed yet, queue the data: + if (!HasHandshaken()) + { + m_CleartextData.append(a_Data); + TryFinishHandshaking(); + return; + } + + // The connection is all set up, write the cleartext data into the SSL context: + WritePlain(a_Data.data(), a_Data.size()); + FlushBuffers(); +} + + + + + +int cLuaTCPLink::cLinkSslContext::ReceiveEncrypted(unsigned char * a_Buffer, size_t a_NumBytes) +{ + // If there's nothing queued in the buffer, report empty buffer: + if (m_EncryptedData.empty()) + { + return POLARSSL_ERR_NET_WANT_READ; + } + + // Copy as much data as possible to the provided buffer: + size_t BytesToCopy = std::min(a_NumBytes, m_EncryptedData.size()); + memcpy(a_Buffer, m_EncryptedData.data(), BytesToCopy); + m_EncryptedData.erase(0, BytesToCopy); + return static_cast(BytesToCopy); +} + + + + + +int cLuaTCPLink::cLinkSslContext::SendEncrypted(const unsigned char * a_Buffer, size_t a_NumBytes) +{ + m_Link.m_Link->Send(a_Buffer, a_NumBytes); + return static_cast(a_NumBytes); +} + + + + diff --git a/src/Bindings/LuaTCPLink.h b/src/Bindings/LuaTCPLink.h index f2af911ec..9536c052b 100644 --- a/src/Bindings/LuaTCPLink.h +++ b/src/Bindings/LuaTCPLink.h @@ -11,6 +11,7 @@ #include "../OSSupport/Network.h" #include "PluginLua.h" +#include "../PolarSSL++/SslContext.h" @@ -62,7 +63,53 @@ public: Sends the RST packet, queued outgoing and incoming data is lost. */ void Close(void); + /** Starts a TLS handshake as a client connection. + If a client certificate should be used for the connection, set the certificate into a_OwnCertData and + its corresponding private key to a_OwnPrivKeyData. If both are empty, no client cert is presented. + a_OwnPrivKeyPassword is the password to be used for decoding PrivKey, empty if not passworded. + Returns empty string on success, non-empty error description on failure. */ + AString StartTLSClient( + const AString & a_OwnCertData, + const AString & a_OwnPrivKeyData, + const AString & a_OwnPrivKeyPassword + ); + protected: + /** Wrapper around cSslContext that is used when this link is being encrypted by SSL. */ + class cLinkSslContext : + public cSslContext + { + cLuaTCPLink & m_Link; + + /** Buffer for storing the incoming encrypted data until it is requested by the SSL decryptor. */ + AString m_EncryptedData; + + /** Buffer for storing the outgoing cleartext data until the link has finished handshaking. */ + AString m_CleartextData; + + public: + cLinkSslContext(cLuaTCPLink & a_Link); + + /** Stores the specified block of data into the buffer of the data to be decrypted (incoming from remote). + Also flushes the SSL buffers by attempting to read any data through the SSL context. */ + void StoreReceivedData(const char * a_Data, size_t a_NumBytes); + + /** Tries to read any cleartext data available through the SSL, reports it in the link. */ + void FlushBuffers(void); + + /** Tries to finish handshaking the SSL. */ + void TryFinishHandshaking(void); + + /** Sends the specified cleartext data over the SSL to the remote peer. + If the handshake hasn't been completed yet, queues the data for sending when it completes. */ + void Send(const AString & a_Data); + + // cSslContext overrides: + virtual int ReceiveEncrypted(unsigned char * a_Buffer, size_t a_NumBytes) override; + virtual int SendEncrypted(const unsigned char * a_Buffer, size_t a_NumBytes) override; + }; + + /** The plugin for which the link is created. */ cPluginLua & m_Plugin; @@ -76,11 +123,19 @@ protected: /** The server that is responsible for this link, if any. */ cLuaServerHandleWPtr m_Server; + /** The SSL context used for encryption, if this link uses SSL. + If valid, the link uses encryption through this context. */ + UniquePtr m_SslContext; + /** Common code called when the link is considered as terminated. Releases m_Link, m_Callbacks and this from m_Server, each when applicable. */ void Terminated(void); + /** Called by the SSL context when there's incoming data available in the cleartext. + Reports the data via the Lua callback function. */ + void ReceivedCleartextData(const char * a_Data, size_t a_NumBytes); + // cNetwork::cConnectCallbacks overrides: virtual void OnConnected(cTCPLink & a_Link) override; virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override; diff --git a/src/Bindings/ManualBindings_Network.cpp b/src/Bindings/ManualBindings_Network.cpp index 902f687c8..ff0f3568c 100644 --- a/src/Bindings/ManualBindings_Network.cpp +++ b/src/Bindings/ManualBindings_Network.cpp @@ -389,6 +389,51 @@ static int tolua_cTCPLink_GetRemotePort(lua_State * L) +/** Binds cLuaTCPLink::StartTLSClient */ +static int tolua_cTCPLink_StartTLSClient(lua_State * L) +{ + // Function signature: + // LinkInstance:StartTLSClient(OwnCert, OwnPrivKey, OwnPrivKeyPassword) -> [true] or [nil, ErrMsg] + + cLuaState S(L); + if ( + !S.CheckParamUserType(1, "cTCPLink") || + !S.CheckParamString(2, 4) || + !S.CheckParamEnd(5) + ) + { + return 0; + } + + // Get the link: + cLuaTCPLink * Link; + if (lua_isnil(L, 1)) + { + LOGWARNING("cTCPLink:StartTLSClient(): invalid link object. Stack trace:"); + S.LogStackTrace(); + return 0; + } + Link = *static_cast(lua_touserdata(L, 1)); + + // Read the params: + AString OwnCert, OwnPrivKey, OwnPrivKeyPassword; + S.GetStackValues(2, OwnCert, OwnPrivKey, OwnPrivKeyPassword); + + // Start the TLS handshake: + AString res = Link->StartTLSClient(OwnCert, OwnPrivKey, OwnPrivKeyPassword); + if (!res.empty()) + { + S.PushNil(); + S.Push(Printf("Cannot start TLS on link to %s:%d: %s", Link->GetRemoteIP().c_str(), Link->GetRemotePort(), res.c_str())); + return 2; + } + return 1; +} + + + + + //////////////////////////////////////////////////////////////////////////////// // cServerHandle bindings (routed through cLuaServerHandle): @@ -495,11 +540,12 @@ void ManualBindings::BindNetwork(lua_State * tolua_S) tolua_endmodule(tolua_S); tolua_beginmodule(tolua_S, "cTCPLink"); - tolua_function(tolua_S, "Send", tolua_cTCPLink_Send); - tolua_function(tolua_S, "GetLocalIP", tolua_cTCPLink_GetLocalIP); - tolua_function(tolua_S, "GetLocalPort", tolua_cTCPLink_GetLocalPort); - tolua_function(tolua_S, "GetRemoteIP", tolua_cTCPLink_GetRemoteIP); - tolua_function(tolua_S, "GetRemotePort", tolua_cTCPLink_GetRemotePort); + tolua_function(tolua_S, "Send", tolua_cTCPLink_Send); + tolua_function(tolua_S, "GetLocalIP", tolua_cTCPLink_GetLocalIP); + tolua_function(tolua_S, "GetLocalPort", tolua_cTCPLink_GetLocalPort); + tolua_function(tolua_S, "GetRemoteIP", tolua_cTCPLink_GetRemoteIP); + tolua_function(tolua_S, "GetRemotePort", tolua_cTCPLink_GetRemotePort); + tolua_function(tolua_S, "StartTLSClient", tolua_cTCPLink_StartTLSClient); tolua_endmodule(tolua_S); tolua_beginmodule(tolua_S, "cServerHandle"); diff --git a/src/Globals.h b/src/Globals.h index 29eaac871..7c2ab38d8 100644 --- a/src/Globals.h +++ b/src/Globals.h @@ -379,9 +379,10 @@ void inline LOG(const char * a_Format, ...) #define assert_test(x) ( !!(x) || (assert(!#x), exit(1), 0)) #endif -// Unified shared ptr from before C++11. Also no silly undercores. +// Unified ptr types from before C++11. Also no silly undercores. #define SharedPtr std::shared_ptr #define WeakPtr std::weak_ptr +#define UniquePtr std::unique_ptr diff --git a/src/HTTPServer/SslHTTPConnection.cpp b/src/HTTPServer/SslHTTPConnection.cpp index f09daac8f..f8dea0731 100644 --- a/src/HTTPServer/SslHTTPConnection.cpp +++ b/src/HTTPServer/SslHTTPConnection.cpp @@ -25,6 +25,15 @@ cSslHTTPConnection::cSslHTTPConnection(cHTTPServer & a_HTTPServer, const cX509Ce +cSslHTTPConnection::~cSslHTTPConnection() +{ + m_Ssl.NotifyClose(); +} + + + + + void cSslHTTPConnection::OnReceivedData(const char * a_Data, size_t a_Size) { // Process the received data: diff --git a/src/HTTPServer/SslHTTPConnection.h b/src/HTTPServer/SslHTTPConnection.h index dc54b1eff..c461a3a24 100644 --- a/src/HTTPServer/SslHTTPConnection.h +++ b/src/HTTPServer/SslHTTPConnection.h @@ -25,6 +25,8 @@ public: /** Creates a new connection on the specified server. Sends the specified cert as the server certificate, uses the private key for decryption. */ cSslHTTPConnection(cHTTPServer & a_HTTPServer, const cX509CertPtr & a_Cert, const cCryptoKeyPtr & a_PrivateKey); + + ~cSslHTTPConnection(); protected: cBufferedSslContext m_Ssl; diff --git a/src/PolarSSL++/CryptoKey.cpp b/src/PolarSSL++/CryptoKey.cpp index 7c4f021b3..9354ddf50 100644 --- a/src/PolarSSL++/CryptoKey.cpp +++ b/src/PolarSSL++/CryptoKey.cpp @@ -45,7 +45,7 @@ cCryptoKey::cCryptoKey(const AString & a_PrivateKeyData, const AString & a_Passw if (res != 0) { LOGWARNING("Failed to parse private key: -0x%x", res); - ASSERT(!"Cannot parse PubKey"); + ASSERT(!"Cannot parse PrivKey"); return; } } From 505dce1fc38def5d28f8f71fa060a2bc76dd40b4 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Thu, 12 Feb 2015 20:22:39 +0100 Subject: [PATCH 21/95] Fixed Linux compilation. std::make_unique is not available in C++11. --- src/Bindings/LuaTCPLink.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Bindings/LuaTCPLink.cpp b/src/Bindings/LuaTCPLink.cpp index 7e2c10e13..c533456ad 100644 --- a/src/Bindings/LuaTCPLink.cpp +++ b/src/Bindings/LuaTCPLink.cpp @@ -206,7 +206,7 @@ AString cLuaTCPLink::StartTLSClient( } // Create the SSL context: - m_SslContext = std::make_unique(*this); + m_SslContext.reset(new cLinkSslContext(*this)); m_SslContext->Initialize(true); // Create the peer cert, if required: From bae8b2e1faa918ad483f9f2c88621e7b7498ca3a Mon Sep 17 00:00:00 2001 From: Mattes D Date: Thu, 12 Feb 2015 20:23:04 +0100 Subject: [PATCH 22/95] PolarSSL++: Fixed debugging output. --- src/PolarSSL++/SslContext.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/PolarSSL++/SslContext.cpp b/src/PolarSSL++/SslContext.cpp index 66dfefc65..8ab207df6 100644 --- a/src/PolarSSL++/SslContext.cpp +++ b/src/PolarSSL++/SslContext.cpp @@ -7,6 +7,7 @@ #include "SslContext.h" #include "EntropyContext.h" #include "CtrDrbgContext.h" +#include "polarssl/debug.h" @@ -69,6 +70,8 @@ int cSslContext::Initialize(bool a_IsClient, const SharedPtr & // These functions allow us to debug SSL and certificate problems, but produce way too much output, // so they're disabled until someone needs them ssl_set_dbg(&m_Ssl, &SSLDebugMessage, this); + debug_set_threshold(4); + ssl_set_verify(&m_Ssl, &SSLVerifyCert, this); //*/ From b8bf795dd1701a32075950a6ea98a16eacb9edc9 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Fri, 13 Feb 2015 18:31:54 +0100 Subject: [PATCH 23/95] Exported cTCPLink:Close and :Shutdown() to Lua API. --- MCServer/Plugins/APIDump/Classes/Network.lua | 2 + src/Bindings/ManualBindings_Network.cpp | 96 +++++++++++++++++--- 2 files changed, 85 insertions(+), 13 deletions(-) diff --git a/MCServer/Plugins/APIDump/Classes/Network.lua b/MCServer/Plugins/APIDump/Classes/Network.lua index ace6c2449..1dc0f3ae7 100644 --- a/MCServer/Plugins/APIDump/Classes/Network.lua +++ b/MCServer/Plugins/APIDump/Classes/Network.lua @@ -295,11 +295,13 @@ g_Server = nil Functions = { + Close = { Params = "", Return = "", Notes = "Closes the link forcefully (TCP RST). There's no guarantee that the last sent data is even being delivered. See also the Shutdown() method." }, GetLocalIP = { Params = "", Return = "string", Notes = "Returns the IP address of the local endpoint of the TCP connection." }, GetLocalPort = { Params = "", Return = "number", Notes = "Returns the port of the local endpoint of the TCP connection." }, GetRemoteIP = { Params = "", Return = "string", Notes = "Returns the IP address of the remote endpoint of the TCP connection." }, GetRemotePort = { Params = "", Return = "number", Notes = "Returns the port of the remote endpoint of the TCP connection." }, Send = { Params = "Data", Return = "", Notes = "Sends the data (raw string) to the remote peer. The data is sent asynchronously and there is no report on the success of the send operation, other than the connection being closed or reset by the underlying OS." }, + Shutdown = { Params = "", Return = "", Notes = "Shuts the socket down for sending data. Notifies the remote peer that there will be no more data coming from us (TCP FIN). The data that is in flight will still be delivered. The underlying socket will be closed when the remote end shuts down as well, or after a timeout." }, StartTLSClient = { Params = "OwnCert, OwnPrivateKey, OwnPrivateKeyPassword", Return = "", Notes = "Starts a TLS handshake on the link, as a client side of the TLS. The Own___ parameters specify the client certificate and its corresponding private key and password; all three parameters are optional and no client certificate is presented to the remote peer if they are not used or all empty. Once the TLS handshake is started by this call, all incoming data is first decrypted before being sent to the OnReceivedData callback, and all outgoing data is queued until the TLS handshake completes, and then sent encrypted over the link." }, }, }, -- cTCPLink diff --git a/src/Bindings/ManualBindings_Network.cpp b/src/Bindings/ManualBindings_Network.cpp index ff0f3568c..4a6b7bc0e 100644 --- a/src/Bindings/ManualBindings_Network.cpp +++ b/src/Bindings/ManualBindings_Network.cpp @@ -214,17 +214,16 @@ static int tolua_cNetwork_Listen(lua_State * L) //////////////////////////////////////////////////////////////////////////////// // cTCPLink bindings (routed through cLuaTCPLink): -/** Binds cLuaTCPLink::Send */ -static int tolua_cTCPLink_Send(lua_State * L) +/** Binds cLuaTCPLink::Close */ +static int tolua_cTCPLink_Close(lua_State * L) { // Function signature: - // LinkInstance:Send(DataString) + // LinkInstance:Close() cLuaState S(L); if ( !S.CheckParamUserType(1, "cTCPLink") || - !S.CheckParamString(2) || - !S.CheckParamEnd(3) + !S.CheckParamEnd(2) ) { return 0; @@ -234,18 +233,14 @@ static int tolua_cTCPLink_Send(lua_State * L) cLuaTCPLink * Link; if (lua_isnil(L, 1)) { - LOGWARNING("cTCPLink:Send(): invalid link object. Stack trace:"); + LOGWARNING("cTCPLink:Close(): invalid link object. Stack trace:"); S.LogStackTrace(); return 0; } Link = *static_cast(lua_touserdata(L, 1)); - // Get the data to send: - AString Data; - S.GetStackValues(2, Data); - - // Send the data: - Link->Send(Data); + // CLose the link: + Link->Close(); return 0; } @@ -389,6 +384,79 @@ static int tolua_cTCPLink_GetRemotePort(lua_State * L) +/** Binds cLuaTCPLink::Send */ +static int tolua_cTCPLink_Send(lua_State * L) +{ + // Function signature: + // LinkInstance:Send(DataString) + + cLuaState S(L); + if ( + !S.CheckParamUserType(1, "cTCPLink") || + !S.CheckParamString(2) || + !S.CheckParamEnd(3) + ) + { + return 0; + } + + // Get the link: + cLuaTCPLink * Link; + if (lua_isnil(L, 1)) + { + LOGWARNING("cTCPLink:Send(): invalid link object. Stack trace:"); + S.LogStackTrace(); + return 0; + } + Link = *static_cast(lua_touserdata(L, 1)); + + // Get the data to send: + AString Data; + S.GetStackValues(2, Data); + + // Send the data: + Link->Send(Data); + return 0; +} + + + + + +/** Binds cLuaTCPLink::Shutdown */ +static int tolua_cTCPLink_Shutdown(lua_State * L) +{ + // Function signature: + // LinkInstance:Shutdown() + + cLuaState S(L); + if ( + !S.CheckParamUserType(1, "cTCPLink") || + !S.CheckParamEnd(2) + ) + { + return 0; + } + + // Get the link: + cLuaTCPLink * Link; + if (lua_isnil(L, 1)) + { + LOGWARNING("cTCPLink:Shutdown(): invalid link object. Stack trace:"); + S.LogStackTrace(); + return 0; + } + Link = *static_cast(lua_touserdata(L, 1)); + + // Shutdown the link: + Link->Shutdown(); + return 0; +} + + + + + /** Binds cLuaTCPLink::StartTLSClient */ static int tolua_cTCPLink_StartTLSClient(lua_State * L) { @@ -540,11 +608,13 @@ void ManualBindings::BindNetwork(lua_State * tolua_S) tolua_endmodule(tolua_S); tolua_beginmodule(tolua_S, "cTCPLink"); - tolua_function(tolua_S, "Send", tolua_cTCPLink_Send); + tolua_function(tolua_S, "Close", tolua_cTCPLink_Close); tolua_function(tolua_S, "GetLocalIP", tolua_cTCPLink_GetLocalIP); tolua_function(tolua_S, "GetLocalPort", tolua_cTCPLink_GetLocalPort); tolua_function(tolua_S, "GetRemoteIP", tolua_cTCPLink_GetRemoteIP); tolua_function(tolua_S, "GetRemotePort", tolua_cTCPLink_GetRemotePort); + tolua_function(tolua_S, "Send", tolua_cTCPLink_Send); + tolua_function(tolua_S, "Shutdown", tolua_cTCPLink_Shutdown); tolua_function(tolua_S, "StartTLSClient", tolua_cTCPLink_StartTLSClient); tolua_endmodule(tolua_S); From 557adf3be944b8a91c768ee85241b7c8bc57c0a6 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Fri, 13 Feb 2015 23:18:22 +0100 Subject: [PATCH 24/95] Exported TLS server start on cTCPLink to Lua API. --- MCServer/Plugins/APIDump/Classes/Network.lua | 1 + MCServer/Plugins/NetworkTest/NetworkTest.lua | 106 ++++++++++++++++- src/Bindings/LuaTCPLink.cpp | 117 ++++++++++++++++++- src/Bindings/LuaTCPLink.h | 31 ++++- src/Bindings/ManualBindings_Network.cpp | 47 ++++++++ 5 files changed, 292 insertions(+), 10 deletions(-) diff --git a/MCServer/Plugins/APIDump/Classes/Network.lua b/MCServer/Plugins/APIDump/Classes/Network.lua index 1dc0f3ae7..274c8d035 100644 --- a/MCServer/Plugins/APIDump/Classes/Network.lua +++ b/MCServer/Plugins/APIDump/Classes/Network.lua @@ -303,6 +303,7 @@ g_Server = nil Send = { Params = "Data", Return = "", Notes = "Sends the data (raw string) to the remote peer. The data is sent asynchronously and there is no report on the success of the send operation, other than the connection being closed or reset by the underlying OS." }, Shutdown = { Params = "", Return = "", Notes = "Shuts the socket down for sending data. Notifies the remote peer that there will be no more data coming from us (TCP FIN). The data that is in flight will still be delivered. The underlying socket will be closed when the remote end shuts down as well, or after a timeout." }, StartTLSClient = { Params = "OwnCert, OwnPrivateKey, OwnPrivateKeyPassword", Return = "", Notes = "Starts a TLS handshake on the link, as a client side of the TLS. The Own___ parameters specify the client certificate and its corresponding private key and password; all three parameters are optional and no client certificate is presented to the remote peer if they are not used or all empty. Once the TLS handshake is started by this call, all incoming data is first decrypted before being sent to the OnReceivedData callback, and all outgoing data is queued until the TLS handshake completes, and then sent encrypted over the link." }, + StartTLSServer = { Params = "Certificate, PrivateKey, PrivateKeyPassword, StartTLSData", Return = "", Notes = "Starts a TLS handshake on the link, as a server side of the TLS. The plugin needs to specify the server certificate and its corresponding private key and password. The StartTLSData can contain data that the link has already reported as received but it should be used as part of the TLS handshake. Once the TLS handshake is started by this call, all incoming data is first decrypted before being sent to the OnReceivedData callback, and all outgoing data is queued until the TLS handshake completes, and then sent encrypted over the link." }, }, }, -- cTCPLink diff --git a/MCServer/Plugins/NetworkTest/NetworkTest.lua b/MCServer/Plugins/NetworkTest/NetworkTest.lua index 21f89c7f9..251e29884 100644 --- a/MCServer/Plugins/NetworkTest/NetworkTest.lua +++ b/MCServer/Plugins/NetworkTest/NetworkTest.lua @@ -19,6 +19,62 @@ local g_Fortunes = "Empty splashes.txt", } +-- HTTPS certificate to be used for the SSL server: +local g_HTTPSCert = [[ +-----BEGIN CERTIFICATE----- +MIIDfzCCAmegAwIBAgIJAOBHN+qOWodcMA0GCSqGSIb3DQEBBQUAMFYxCzAJBgNV +BAYTAmN6MQswCQYDVQQIDAJjejEMMAoGA1UEBwwDbG9jMQswCQYDVQQKDAJfWDEL +MAkGA1UECwwCT1UxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0xNTAxMjQwODQ2MzFa +Fw0yNTAxMjEwODQ2MzFaMFYxCzAJBgNVBAYTAmN6MQswCQYDVQQIDAJjejEMMAoG +A1UEBwwDbG9jMQswCQYDVQQKDAJfWDELMAkGA1UECwwCT1UxEjAQBgNVBAMMCWxv +Y2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJkFYSElu/jw +nxqjimmj246DejKJK8uy/l9QQibb/Z4kO/3s0gVPOYo0mKv32xUFP7wYIE3XWT61 +zyfvK+1jpnlQTCtM8T5xw/7CULKgLmuIzlQx5Dhy7d+tW46kOjFKwQajS9YzwqWu +KBOPnFamQWz6vIzuM05+7aIMXbzamInvW/1x3klIrpGQgALwSB1N+oUzTInTBRKK +21pecUE9t3qrU40Cs5bN0fQBnBjLwbgmnTh6LEplfQZHG5wLvj0IeERVU9vH7luM +e9/IxuEZluCiu5ViF3jqLPpjYOrkX7JDSKme64CCmNIf0KkrwtFjF104Qylike60 +YD3+kw8Q+DECAwEAAaNQME4wHQYDVR0OBBYEFHHIDTc7mrLDXftjQ5ejU9Udfdyo +MB8GA1UdIwQYMBaAFHHIDTc7mrLDXftjQ5ejU9UdfdyoMAwGA1UdEwQFMAMBAf8w +DQYJKoZIhvcNAQEFBQADggEBAHxCJxZPmH9tvx8GKiDV3rgGY++sMItzrW5Uhf0/ +bl3DPbVz51CYF8nXiWvSJJzxhH61hKpZiqvRlpyMuovV415dYQ+Xc2d2IrTX6e+d +Z4Pmwfb4yaX+kYqIygjXMoyNxOJyhTnCbJzycV3v5tvncBWN9Wqez6ZonWDdFdAm +J+Moty+atc4afT02sUg1xz+CDr1uMbt62tHwKYCdxXCwT//bOs6W21+mQJ5bEAyA +YrHQPgX76uo8ed8rPf6y8Qj//lzq/+33EIWqf9pnbklQgIPXJU07h+5L+Y63RF4A +ComLkzas+qnQLcEN28Dg8QElXop6hfiD0xq3K0ac2bDnjoU= +-----END CERTIFICATE----- +]] + +local g_HTTPSPrivKey = [[ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCZBWEhJbv48J8a +o4ppo9uOg3oyiSvLsv5fUEIm2/2eJDv97NIFTzmKNJir99sVBT+8GCBN11k+tc8n +7yvtY6Z5UEwrTPE+ccP+wlCyoC5riM5UMeQ4cu3frVuOpDoxSsEGo0vWM8KlrigT +j5xWpkFs+ryM7jNOfu2iDF282piJ71v9cd5JSK6RkIAC8EgdTfqFM0yJ0wUSitta +XnFBPbd6q1ONArOWzdH0AZwYy8G4Jp04eixKZX0GRxucC749CHhEVVPbx+5bjHvf +yMbhGZbgoruVYhd46iz6Y2Dq5F+yQ0ipnuuAgpjSH9CpK8LRYxddOEMpYpHutGA9 +/pMPEPgxAgMBAAECggEAWxQ4m+I54BJYoSJ2YCqHpGvdb/b1emkvvsumlDqc2mP2 +0U0ENOTS+tATj0gXvotBRFOX5r0nAYx1oO9a1hFaJRsGOz+w19ofLqO6JJfzCU6E +gNixXmgJ7fjhZiWZ/XzhJ3JK0VQ9px/h+sKf63NJvfQABmJBZ5dlGe8CXEZARNin +03TnE3RUIEK+jEgwShN2OrGjwK9fjcnXMHwEnKZtCBiYEfD2N+pQmS20gIm13L1t ++ZmObIC24NqllXxl4I821qzBdhmcT7+rGmKR0OT5YKbt6wFA5FPKD9dqlzXzlKck +r2VAh+JlCtFKxcScmWtQOnVDtf5+mcKFbP4ck724AQKBgQDLk+RDhvE5ykin5R+B +dehUQZgHb2pPL7N1DAZShfzwSmyZSOPQDFr7c0CMijn6G0Pw9VX6Vrln0crfTQYz +Hli+zxlmcMAD/WC6VImM1LCUzouNRy37rSCnuPtngZyHdsyzfheGnjORH7HlPjtY +JCTLaekg0ckQvt//HpRV3DCdaQKBgQDAbLmIOTyGfne74HLswWnY/kCOfFt6eO+E +lZ724MWmVPWkxq+9rltC2CDx2i8jjdkm90dsgR5OG2EaLnUWldUpkE0zH0ATrZSV +ezJWD9SsxTm8ksbThD+pJKAVPxDAboejF7kPvpaO2wY+bf0AbO3M24rJ2tccpMv8 +AcfXBICDiQKBgQCSxp81/I3hf7HgszaC7ZLDZMOK4M6CJz847aGFUCtsyAwCfGYb +8zyJvK/WZDam14+lpA0IQAzPCJg/ZVZJ9uA/OivzCum2NrHNxfOiQRrLPxuokaBa +q5k2tA02tGE53fJ6mze1DEzbnkFxqeu5gd2xdzvpOLfBxgzT8KU8PlQiuQKBgGn5 +NvCj/QZhDhYFVaW4G1ArLmiKamL3yYluUV7LiW7CaYp29gBzzsTwfKxVqhJdo5NH +KinCrmr7vy2JGmj22a+LTkjyU/rCZQsyDxXAoDMKZ3LILwH8WocPqa4pzlL8TGzw +urXGE+rXCwhE0Mp0Mz7YRgZHJKMcy06duG5dh11pAoGBALHbsBIDihgHPyp2eKMP +K1f42MdKrTBiIXV80hv2OnvWVRCYvnhrqpeRMzCR1pmVbh+QhnwIMAdWq9PAVTTn +ypusoEsG8Y5fx8xhgjs0D2yMcrmi0L0kCgHIFNoym+4pI+sv6GgxpemfrmaPNcMx +DXi9JpaquFRJLGJ7jMCDgotL +-----END PRIVATE KEY----- +]] + --- Map of all services that can be run as servers -- g_Services[ServiceName] = function() -> accept-callbacks local g_Services = @@ -66,7 +122,7 @@ local g_Services = return { OnError = function (a_Link, a_ErrorCode, a_ErrorMsg) - LOG("FortuneServer(" .. a_Port .. ": Connection to " .. a_Link:GetRemoteIP() .. ":" .. a_Link:GetRemotePort() .. " failed: " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")") + LOG("FortuneServer(" .. a_Port .. "): Connection to " .. a_Link:GetRemoteIP() .. ":" .. a_Link:GetRemotePort() .. " failed: " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")") end, OnReceivedData = function (a_Link, a_Data) @@ -86,11 +142,55 @@ local g_Services = -- There was an error listening on the port: OnError = function (a_ErrorCode, a_ErrorMsg) - LOGINFO("FortuneServer(" .. a_Port .. ": Cannot listen: " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")") + LOGINFO("FortuneServer(" .. a_Port .. "): Cannot listen: " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")") end, -- OnError() } -- Listen callbacks end, -- fortune + -- HTTPS time - serves current time for each https request received + httpstime = function (a_Port) + return + { + -- A new connection has come, give it new link-callbacks: + OnIncomingConnection = function (a_RemoteIP, a_RemotePort) + local IncomingData = "" -- accumulator for the incoming data, until processed by the http + return + { + OnError = function (a_Link, a_ErrorCode, a_ErrorMsg) + LOG("https-time server(" .. a_Port .. "): Connection to " .. a_Link:GetRemoteIP() .. ":" .. a_Link:GetRemotePort() .. " failed: " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")") + end, + + OnReceivedData = function (a_Link, a_Data) + IncomingData = IncomingData .. a_Data + if (IncomingData:find("\r\n\r\n")) then + local Content = os.date() + a_Link:Send("HTTP/1.0 200 OK\r\nContent-type: text/plain\r\nContent-length: " .. #Content .. "\r\n\r\n" .. Content) + -- TODO: shutdown is not yet properly implemented in cTCPLink + -- a_Link:Shutdown() + end + end, + + OnRemoteClosed = function (a_Link) + end + } -- Link callbacks + end, -- OnIncomingConnection() + + -- Start TLS on the new link: + OnAccepted = function (a_Link) + local res, msg = a_Link:StartTLSServer(g_HTTPSCert, g_HTTPSPrivKey, "") + if not(res) then + LOG("https-time server(" .. a_Port .. "): Cannot start TLS server: " .. msg) + a_Link:Close() + end + end, -- OnAccepted() + + -- There was an error listening on the port: + OnError = function (a_ErrorCode, a_ErrorMsg) + LOGINFO("https-time server(" .. a_Port .. "): Cannot listen: " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")") + end, -- OnError() + } -- Listen callbacks + end, -- httpstime + -- TODO: Other services (daytime, ...) } @@ -229,7 +329,7 @@ function HandleConsoleNetListen(a_Split) -- Get the params: local Port = tonumber(a_Split[3] or 1024) if not(Port) then - return true, "Invalid port: \"" .. Port .. "\"." + return true, "Invalid port: \"" .. a_Split[3] .. "\"." end local Service = string.lower(a_Split[4] or "echo") diff --git a/src/Bindings/LuaTCPLink.cpp b/src/Bindings/LuaTCPLink.cpp index c533456ad..40371d6da 100644 --- a/src/Bindings/LuaTCPLink.cpp +++ b/src/Bindings/LuaTCPLink.cpp @@ -160,10 +160,14 @@ void cLuaTCPLink::Shutdown(void) cTCPLinkPtr Link = m_Link; if (Link != nullptr) { + if (m_SslContext != nullptr) + { + m_SslContext->NotifyClose(); + m_SslContext->ResetSelf(); + m_SslContext.reset(); + } Link->Shutdown(); } - - Terminated(); } @@ -176,6 +180,12 @@ void cLuaTCPLink::Close(void) cTCPLinkPtr Link = m_Link; if (Link != nullptr) { + if (m_SslContext != nullptr) + { + m_SslContext->NotifyClose(); + m_SslContext->ResetSelf(); + m_SslContext.reset(); + } Link->Close(); } @@ -228,6 +238,58 @@ AString cLuaTCPLink::StartTLSClient( } m_SslContext->SetOwnCert(OwnCert, OwnPrivKey); } + m_SslContext->SetSelf(cLinkSslContextWPtr(m_SslContext)); + + // Start the handshake: + m_SslContext->Handshake(); + return ""; +} + + + + + +AString cLuaTCPLink::StartTLSServer( + const AString & a_OwnCertData, + const AString & a_OwnPrivKeyData, + const AString & a_OwnPrivKeyPassword, + const AString & a_StartTLSData +) +{ + // Check preconditions: + if (m_SslContext != nullptr) + { + return "TLS is already active on this link"; + } + if (a_OwnCertData.empty() || a_OwnPrivKeyData.empty()) + { + return "Provide the server certificate and private key"; + } + + // Create the SSL context: + m_SslContext.reset(new cLinkSslContext(*this)); + m_SslContext->Initialize(false); + + // Create the peer cert: + auto OwnCert = std::make_shared(); + int res = OwnCert->Parse(a_OwnCertData.data(), a_OwnCertData.size()); + if (res != 0) + { + m_SslContext.reset(); + return Printf("Cannot parse server certificate: -0x%x", res); + } + auto OwnPrivKey = std::make_shared(); + res = OwnPrivKey->ParsePrivate(a_OwnPrivKeyData.data(), a_OwnPrivKeyData.size(), a_OwnPrivKeyPassword); + if (res != 0) + { + m_SslContext.reset(); + return Printf("Cannot parse server private key: -0x%x", res); + } + m_SslContext->SetOwnCert(OwnCert, OwnPrivKey); + m_SslContext->SetSelf(cLinkSslContextWPtr(m_SslContext)); + + // Push the initial data: + m_SslContext->StoreReceivedData(a_StartTLSData.data(), a_StartTLSData.size()); // Start the handshake: m_SslContext->Handshake(); @@ -254,12 +316,17 @@ void cLuaTCPLink::Terminated(void) } // If the link is still open, close it: - cTCPLinkPtr Link = m_Link; - if (Link != nullptr) { - Link->Close(); - m_Link.reset(); + cTCPLinkPtr Link = m_Link; + if (Link != nullptr) + { + Link->Close(); + m_Link.reset(); + } } + + // If the SSL context still exists, free it: + m_SslContext.reset(); } @@ -401,8 +468,29 @@ cLuaTCPLink::cLinkSslContext::cLinkSslContext(cLuaTCPLink & a_Link): +void cLuaTCPLink::cLinkSslContext::SetSelf(cLinkSslContextWPtr & a_Self) +{ + m_Self = a_Self; +} + + + + + +void cLuaTCPLink::cLinkSslContext::ResetSelf(void) +{ + m_Self.reset(); +} + + + + + void cLuaTCPLink::cLinkSslContext::StoreReceivedData(const char * a_Data, size_t a_NumBytes) { + // Hold self alive for the duration of this function + cLinkSslContextPtr Self(m_Self); + m_EncryptedData.append(a_Data, a_NumBytes); // Try to finish a pending handshake: @@ -418,6 +506,9 @@ void cLuaTCPLink::cLinkSslContext::StoreReceivedData(const char * a_Data, size_t void cLuaTCPLink::cLinkSslContext::FlushBuffers(void) { + // Hold self alive for the duration of this function + cLinkSslContextPtr Self(m_Self); + // If the handshake didn't complete yet, bail out: if (!HasHandshaken()) { @@ -429,6 +520,11 @@ void cLuaTCPLink::cLinkSslContext::FlushBuffers(void) while ((NumBytes = ReadPlain(Buffer, sizeof(Buffer))) > 0) { m_Link.ReceivedCleartextData(Buffer, static_cast(NumBytes)); + if (m_Self.expired()) + { + // The callback closed the SSL context, bail out + return; + } } } @@ -438,6 +534,9 @@ void cLuaTCPLink::cLinkSslContext::FlushBuffers(void) void cLuaTCPLink::cLinkSslContext::TryFinishHandshaking(void) { + // Hold self alive for the duration of this function + cLinkSslContextPtr Self(m_Self); + // If the handshake hasn't finished yet, retry: if (!HasHandshaken()) { @@ -458,6 +557,9 @@ void cLuaTCPLink::cLinkSslContext::TryFinishHandshaking(void) void cLuaTCPLink::cLinkSslContext::Send(const AString & a_Data) { + // Hold self alive for the duration of this function + cLinkSslContextPtr Self(m_Self); + // If the handshake hasn't completed yet, queue the data: if (!HasHandshaken()) { @@ -477,6 +579,9 @@ void cLuaTCPLink::cLinkSslContext::Send(const AString & a_Data) int cLuaTCPLink::cLinkSslContext::ReceiveEncrypted(unsigned char * a_Buffer, size_t a_NumBytes) { + // Hold self alive for the duration of this function + cLinkSslContextPtr Self(m_Self); + // If there's nothing queued in the buffer, report empty buffer: if (m_EncryptedData.empty()) { diff --git a/src/Bindings/LuaTCPLink.h b/src/Bindings/LuaTCPLink.h index 9536c052b..4e0d7dcec 100644 --- a/src/Bindings/LuaTCPLink.h +++ b/src/Bindings/LuaTCPLink.h @@ -74,7 +74,27 @@ public: const AString & a_OwnPrivKeyPassword ); + /** Starts a TLS handshake as a server connection. + Set the server certificate into a_CertData and its corresponding private key to a_OwnPrivKeyData. + a_OwnPrivKeyPassword is the password to be used for decoding PrivKey, empty if not passworded. + a_StartTLSData is any data that should be pushed into the TLS before reading more data from the remote. + This is used mainly for protocols starting TLS in the middle of communication, when the TLS start command + can be received together with the TLS Client Hello message in one OnReceivedData() call, to re-queue the + Client Hello message into the TLS handshake buffer. + Returns empty string on success, non-empty error description on failure. */ + AString StartTLSServer( + const AString & a_OwnCertData, + const AString & a_OwnPrivKeyData, + const AString & a_OwnPrivKeyPassword, + const AString & a_StartTLSData + ); + protected: + // fwd: + class cLinkSslContext; + typedef SharedPtr cLinkSslContextPtr; + typedef WeakPtr cLinkSslContextWPtr; + /** Wrapper around cSslContext that is used when this link is being encrypted by SSL. */ class cLinkSslContext : public cSslContext @@ -87,9 +107,18 @@ protected: /** Buffer for storing the outgoing cleartext data until the link has finished handshaking. */ AString m_CleartextData; + /** Shared ownership of self, so that this object can keep itself alive for as long as it needs. */ + cLinkSslContextWPtr m_Self; + public: cLinkSslContext(cLuaTCPLink & a_Link); + /** Shares ownership of self, so that this object can keep itself alive for as long as it needs. */ + void SetSelf(cLinkSslContextWPtr & a_Self); + + /** Removes the self ownership so that we can detect the SSL closure. */ + void ResetSelf(void); + /** Stores the specified block of data into the buffer of the data to be decrypted (incoming from remote). Also flushes the SSL buffers by attempting to read any data through the SSL context. */ void StoreReceivedData(const char * a_Data, size_t a_NumBytes); @@ -125,7 +154,7 @@ protected: /** The SSL context used for encryption, if this link uses SSL. If valid, the link uses encryption through this context. */ - UniquePtr m_SslContext; + cLinkSslContextPtr m_SslContext; /** Common code called when the link is considered as terminated. diff --git a/src/Bindings/ManualBindings_Network.cpp b/src/Bindings/ManualBindings_Network.cpp index 4a6b7bc0e..30a34815c 100644 --- a/src/Bindings/ManualBindings_Network.cpp +++ b/src/Bindings/ManualBindings_Network.cpp @@ -502,6 +502,52 @@ static int tolua_cTCPLink_StartTLSClient(lua_State * L) +/** Binds cLuaTCPLink::StartTLSServer */ +static int tolua_cTCPLink_StartTLSServer(lua_State * L) +{ + // Function signature: + // LinkInstance:StartTLSServer(OwnCert, OwnPrivKey, OwnPrivKeyPassword, StartTLSData) -> [true] or [nil, ErrMsg] + + cLuaState S(L); + if ( + !S.CheckParamUserType(1, "cTCPLink") || + !S.CheckParamString(2, 4) || + // Param 5 is optional, don't check + !S.CheckParamEnd(6) + ) + { + return 0; + } + + // Get the link: + cLuaTCPLink * Link; + if (lua_isnil(L, 1)) + { + LOGWARNING("cTCPLink:StartTLSServer(): invalid link object. Stack trace:"); + S.LogStackTrace(); + return 0; + } + Link = *static_cast(lua_touserdata(L, 1)); + + // Read the params: + AString OwnCert, OwnPrivKey, OwnPrivKeyPassword, StartTLSData; + S.GetStackValues(2, OwnCert, OwnPrivKey, OwnPrivKeyPassword, StartTLSData); + + // Start the TLS handshake: + AString res = Link->StartTLSServer(OwnCert, OwnPrivKey, OwnPrivKeyPassword, StartTLSData); + if (!res.empty()) + { + S.PushNil(); + S.Push(Printf("Cannot start TLS on link to %s:%d: %s", Link->GetRemoteIP().c_str(), Link->GetRemotePort(), res.c_str())); + return 2; + } + return 1; +} + + + + + //////////////////////////////////////////////////////////////////////////////// // cServerHandle bindings (routed through cLuaServerHandle): @@ -616,6 +662,7 @@ void ManualBindings::BindNetwork(lua_State * tolua_S) tolua_function(tolua_S, "Send", tolua_cTCPLink_Send); tolua_function(tolua_S, "Shutdown", tolua_cTCPLink_Shutdown); tolua_function(tolua_S, "StartTLSClient", tolua_cTCPLink_StartTLSClient); + tolua_function(tolua_S, "StartTLSServer", tolua_cTCPLink_StartTLSServer); tolua_endmodule(tolua_S); tolua_beginmodule(tolua_S, "cServerHandle"); From 1f3d11de32e36dfc983393266f105b969d8dfef8 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sat, 14 Feb 2015 09:17:26 +0100 Subject: [PATCH 25/95] Fixed Linux compilation. --- src/Bindings/LuaTCPLink.cpp | 2 +- src/Bindings/LuaTCPLink.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Bindings/LuaTCPLink.cpp b/src/Bindings/LuaTCPLink.cpp index 40371d6da..d88c41120 100644 --- a/src/Bindings/LuaTCPLink.cpp +++ b/src/Bindings/LuaTCPLink.cpp @@ -468,7 +468,7 @@ cLuaTCPLink::cLinkSslContext::cLinkSslContext(cLuaTCPLink & a_Link): -void cLuaTCPLink::cLinkSslContext::SetSelf(cLinkSslContextWPtr & a_Self) +void cLuaTCPLink::cLinkSslContext::SetSelf(cLinkSslContextWPtr a_Self) { m_Self = a_Self; } diff --git a/src/Bindings/LuaTCPLink.h b/src/Bindings/LuaTCPLink.h index 4e0d7dcec..c8ae776fe 100644 --- a/src/Bindings/LuaTCPLink.h +++ b/src/Bindings/LuaTCPLink.h @@ -114,7 +114,7 @@ protected: cLinkSslContext(cLuaTCPLink & a_Link); /** Shares ownership of self, so that this object can keep itself alive for as long as it needs. */ - void SetSelf(cLinkSslContextWPtr & a_Self); + void SetSelf(cLinkSslContextWPtr a_Self); /** Removes the self ownership so that we can detect the SSL closure. */ void ResetSelf(void); From 1ca0a4915ece26d5700808320496227650819a9b Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sat, 14 Feb 2015 13:31:31 +0100 Subject: [PATCH 26/95] SslContext: Turned debug messages off. --- src/PolarSSL++/SslContext.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PolarSSL++/SslContext.cpp b/src/PolarSSL++/SslContext.cpp index 8ab207df6..5ac4bc227 100644 --- a/src/PolarSSL++/SslContext.cpp +++ b/src/PolarSSL++/SslContext.cpp @@ -70,7 +70,7 @@ int cSslContext::Initialize(bool a_IsClient, const SharedPtr & // These functions allow us to debug SSL and certificate problems, but produce way too much output, // so they're disabled until someone needs them ssl_set_dbg(&m_Ssl, &SSLDebugMessage, this); - debug_set_threshold(4); + debug_set_threshold(2); ssl_set_verify(&m_Ssl, &SSLVerifyCert, this); //*/ From d336a3ea9e581372e225ee64113fe7fd7e080d45 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sat, 14 Feb 2015 13:55:54 +0100 Subject: [PATCH 27/95] Fixed TCP link shutdown. The shutdown is postponed until there's no more outgoing data in the LibEvent buffers. --- MCServer/Plugins/NetworkTest/NetworkTest.lua | 5 +- src/OSSupport/TCPLinkImpl.cpp | 65 +++++++++++++++++--- src/OSSupport/TCPLinkImpl.h | 12 ++++ 3 files changed, 71 insertions(+), 11 deletions(-) diff --git a/MCServer/Plugins/NetworkTest/NetworkTest.lua b/MCServer/Plugins/NetworkTest/NetworkTest.lua index 251e29884..39774f403 100644 --- a/MCServer/Plugins/NetworkTest/NetworkTest.lua +++ b/MCServer/Plugins/NetworkTest/NetworkTest.lua @@ -163,14 +163,15 @@ local g_Services = OnReceivedData = function (a_Link, a_Data) IncomingData = IncomingData .. a_Data if (IncomingData:find("\r\n\r\n")) then + -- We have received the entire request headers, just send the response and shutdown the link: local Content = os.date() a_Link:Send("HTTP/1.0 200 OK\r\nContent-type: text/plain\r\nContent-length: " .. #Content .. "\r\n\r\n" .. Content) - -- TODO: shutdown is not yet properly implemented in cTCPLink - -- a_Link:Shutdown() + a_Link:Shutdown() end end, OnRemoteClosed = function (a_Link) + LOG("httpstime: link closed by remote") end } -- Link callbacks end, -- OnIncomingConnection() diff --git a/src/OSSupport/TCPLinkImpl.cpp b/src/OSSupport/TCPLinkImpl.cpp index 88fb57838..c6f1978ad 100644 --- a/src/OSSupport/TCPLinkImpl.cpp +++ b/src/OSSupport/TCPLinkImpl.cpp @@ -7,6 +7,7 @@ #include "TCPLinkImpl.h" #include "NetworkSingleton.h" #include "ServerHandleImpl.h" +#include "event2/buffer.h" @@ -17,7 +18,10 @@ cTCPLinkImpl::cTCPLinkImpl(cTCPLink::cCallbacksPtr a_LinkCallbacks): super(a_LinkCallbacks), - m_BufferEvent(bufferevent_socket_new(cNetworkSingleton::Get().GetEventBase(), -1, BEV_OPT_CLOSE_ON_FREE | BEV_OPT_THREADSAFE)) + m_BufferEvent(bufferevent_socket_new(cNetworkSingleton::Get().GetEventBase(), -1, BEV_OPT_CLOSE_ON_FREE | BEV_OPT_THREADSAFE)), + m_LocalPort(0), + m_RemotePort(0), + m_ShouldShutdown(false) { LOGD("Created new cTCPLinkImpl at %p with BufferEvent at %p", this, m_BufferEvent); } @@ -29,7 +33,10 @@ cTCPLinkImpl::cTCPLinkImpl(cTCPLink::cCallbacksPtr a_LinkCallbacks): cTCPLinkImpl::cTCPLinkImpl(evutil_socket_t a_Socket, cTCPLink::cCallbacksPtr a_LinkCallbacks, cServerHandleImplPtr a_Server, const sockaddr * a_Address, socklen_t a_AddrLen): super(a_LinkCallbacks), m_BufferEvent(bufferevent_socket_new(cNetworkSingleton::Get().GetEventBase(), a_Socket, BEV_OPT_CLOSE_ON_FREE | BEV_OPT_THREADSAFE)), - m_Server(a_Server) + m_Server(a_Server), + m_LocalPort(0), + m_RemotePort(0), + m_ShouldShutdown(false) { LOGD("Created new cTCPLinkImpl at %p with BufferEvent at %p", this, m_BufferEvent); @@ -111,7 +118,7 @@ void cTCPLinkImpl::Enable(cTCPLinkImplPtr a_Self) m_Self = a_Self; // Set the LibEvent callbacks and enable processing: - bufferevent_setcb(m_BufferEvent, ReadCallback, nullptr, EventCallback, this); + bufferevent_setcb(m_BufferEvent, ReadCallback, WriteCallback, EventCallback, this); bufferevent_enable(m_BufferEvent, EV_READ | EV_WRITE); } @@ -121,6 +128,11 @@ void cTCPLinkImpl::Enable(cTCPLinkImplPtr a_Self) bool cTCPLinkImpl::Send(const void * a_Data, size_t a_Length) { + if (m_ShouldShutdown) + { + LOGD("%s: Cannot send data, the link is already shut down.", __FUNCTION__); + return false; + } return (bufferevent_write(m_BufferEvent, a_Data, a_Length) == 0); } @@ -130,12 +142,15 @@ bool cTCPLinkImpl::Send(const void * a_Data, size_t a_Length) void cTCPLinkImpl::Shutdown(void) { - #ifdef _WIN32 - shutdown(bufferevent_getfd(m_BufferEvent), SD_SEND); - #else - shutdown(bufferevent_getfd(m_BufferEvent), SHUT_WR); - #endif - bufferevent_disable(m_BufferEvent, EV_WRITE); + // If there's no outgoing data, shutdown the socket directly: + if (evbuffer_get_length(bufferevent_get_output(m_BufferEvent)) == 0) + { + DoActualShutdown(); + return; + } + + // There's still outgoing data in the LibEvent buffer, schedule a shutdown when it's written to OS's TCP stack: + m_ShouldShutdown = true; } @@ -181,6 +196,24 @@ void cTCPLinkImpl::ReadCallback(bufferevent * a_BufferEvent, void * a_Self) +void cTCPLinkImpl::WriteCallback(bufferevent * a_BufferEvent, void * a_Self) +{ + ASSERT(a_Self != nullptr); + auto Self = static_cast(a_Self); + ASSERT(Self->m_Callbacks != nullptr); + + // If there's no more data to write and the link has been scheduled for shutdown, do the shutdown: + auto OutLen = evbuffer_get_length(bufferevent_get_output(Self->m_BufferEvent)); + if ((OutLen == 0) && (Self->m_ShouldShutdown)) + { + Self->DoActualShutdown(); + } +} + + + + + void cTCPLinkImpl::EventCallback(bufferevent * a_BufferEvent, short a_What, void * a_Self) { LOGD("cTCPLink event callback for link %p, BEV %p; what = 0x%02x", a_Self, a_BufferEvent, a_What); @@ -316,6 +349,20 @@ void cTCPLinkImpl::UpdateRemoteAddress(void) +void cTCPLinkImpl::DoActualShutdown(void) +{ + #ifdef _WIN32 + shutdown(bufferevent_getfd(m_BufferEvent), SD_SEND); + #else + shutdown(bufferevent_getfd(m_BufferEvent), SHUT_WR); + #endif + bufferevent_disable(m_BufferEvent, EV_WRITE); +} + + + + + //////////////////////////////////////////////////////////////////////////////// // cNetwork API: diff --git a/src/OSSupport/TCPLinkImpl.h b/src/OSSupport/TCPLinkImpl.h index 735e8ed9d..bea21aeff 100644 --- a/src/OSSupport/TCPLinkImpl.h +++ b/src/OSSupport/TCPLinkImpl.h @@ -94,6 +94,11 @@ protected: Initialized in Enable(), cleared in Close() and EventCallback(RemoteClosed). */ cTCPLinkImplPtr m_Self; + /** If true, Shutdown() has been called and is in queue. + No more data is allowed to be sent via Send() and after all the currently buffered + data is sent to the OS TCP stack, the socket gets shut down. */ + bool m_ShouldShutdown; + /** Creates a new link to be queued to connect to a specified host:port. Used for outgoing connections created using cNetwork::Connect(). @@ -104,6 +109,9 @@ protected: /** Callback that LibEvent calls when there's data available from the remote peer. */ static void ReadCallback(bufferevent * a_BufferEvent, void * a_Self); + /** Callback that LibEvent calls when the remote peer can receive more data. */ + static void WriteCallback(bufferevent * a_BufferEvent, void * a_Self); + /** Callback that LibEvent calls when there's a non-data-related event on the socket. */ static void EventCallback(bufferevent * a_BufferEvent, short a_What, void * a_Self); @@ -115,6 +123,10 @@ protected: /** Updates m_RemoteIP and m_RemotePort based on the metadata read from the socket. */ void UpdateRemoteAddress(void); + + /** Calls shutdown on the link and disables LibEvent writing. + Called after all data from LibEvent buffers is sent to the OS TCP stack and shutdown() has been called before. */ + void DoActualShutdown(void); }; From d2fdaf1d5bf431bf5745f0b18cc8cb18ee2c855d Mon Sep 17 00:00:00 2001 From: Creaprog Date: Sun, 15 Feb 2015 13:08:44 +0100 Subject: [PATCH 28/95] Fixed login_template.html Remove "/". --- MCServer/webadmin/login_template.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MCServer/webadmin/login_template.html b/MCServer/webadmin/login_template.html index 7a8601065..26b260bd3 100644 --- a/MCServer/webadmin/login_template.html +++ b/MCServer/webadmin/login_template.html @@ -17,7 +17,7 @@

-
+ From 5797f99defa2f8afb0641d5f6ec0cc7557e069ee Mon Sep 17 00:00:00 2001 From: Creaprog Date: Sun, 15 Feb 2015 14:29:11 +0100 Subject: [PATCH 29/95] Fixed Server.cpp Performance improvement. --- Tools/ProtoProxy/Server.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Tools/ProtoProxy/Server.cpp b/Tools/ProtoProxy/Server.cpp index 9545af852..77d1af827 100644 --- a/Tools/ProtoProxy/Server.cpp +++ b/Tools/ProtoProxy/Server.cpp @@ -33,7 +33,7 @@ int cServer::Init(short a_ListenPort, short a_ConnectPort) } #endif // _WIN32 - printf("Generating protocol encryption keypair...\n"); + puts("Generating protocol encryption keypair..."); m_PrivateKey.Generate(); m_PublicKeyDER = m_PrivateKey.GetPubKeyDER(); @@ -85,7 +85,7 @@ int cServer::Init(short a_ListenPort, short a_ConnectPort) void cServer::Run(void) { - printf("Server running.\n"); + puts("Server running."); while (true) { sockaddr_in Addr; @@ -97,10 +97,10 @@ void cServer::Run(void) printf("accept returned an error: %d; bailing out.\n", SocketError); return; } - printf("Client connected, proxying...\n"); + puts("Client connected, proxying..."); cConnection Connection(client, *this); Connection.Run(); - printf("Client disconnected. Ready for another connection.\n"); + puts("Client disconnected. Ready for another connection."); } } From a063da939868abfb9aa4169ca714bfadab708c48 Mon Sep 17 00:00:00 2001 From: Creaprog Date: Sun, 15 Feb 2015 17:28:19 +0100 Subject: [PATCH 30/95] Fixed Server.cpp --- Tools/ProtoProxy/Server.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Tools/ProtoProxy/Server.cpp b/Tools/ProtoProxy/Server.cpp index 77d1af827..7d890124d 100644 --- a/Tools/ProtoProxy/Server.cpp +++ b/Tools/ProtoProxy/Server.cpp @@ -33,7 +33,7 @@ int cServer::Init(short a_ListenPort, short a_ConnectPort) } #endif // _WIN32 - puts("Generating protocol encryption keypair..."); + LOG("Generating protocol encryption keypair..."); m_PrivateKey.Generate(); m_PublicKeyDER = m_PrivateKey.GetPubKeyDER(); @@ -85,7 +85,7 @@ int cServer::Init(short a_ListenPort, short a_ConnectPort) void cServer::Run(void) { - puts("Server running."); + LOG("Server running."); while (true) { sockaddr_in Addr; @@ -97,10 +97,10 @@ void cServer::Run(void) printf("accept returned an error: %d; bailing out.\n", SocketError); return; } - puts("Client connected, proxying..."); + LOG("Client connected, proxying..."); cConnection Connection(client, *this); Connection.Run(); - puts("Client disconnected. Ready for another connection."); + LOG("Client disconnected. Ready for another connection."); } } From cbd2a0913511ad4130c482055ffb123085f505be Mon Sep 17 00:00:00 2001 From: Creaprog Date: Sun, 15 Feb 2015 18:28:33 +0100 Subject: [PATCH 31/95] Fixed Server.cpp --- Tools/ProtoProxy/Server.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Tools/ProtoProxy/Server.cpp b/Tools/ProtoProxy/Server.cpp index 7d890124d..2932b37e3 100644 --- a/Tools/ProtoProxy/Server.cpp +++ b/Tools/ProtoProxy/Server.cpp @@ -6,6 +6,7 @@ #include "Globals.h" #include "Server.h" #include "Connection.h" +#include "Logger.h" From 6f9c62172bb1f6679f733f05e10e712ca7bc3c47 Mon Sep 17 00:00:00 2001 From: Creaprog Date: Sun, 15 Feb 2015 20:39:53 +0100 Subject: [PATCH 32/95] Fixed Server.cpp --- Tools/ProtoProxy/Server.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Tools/ProtoProxy/Server.cpp b/Tools/ProtoProxy/Server.cpp index 2932b37e3..e27c86e10 100644 --- a/Tools/ProtoProxy/Server.cpp +++ b/Tools/ProtoProxy/Server.cpp @@ -6,7 +6,6 @@ #include "Globals.h" #include "Server.h" #include "Connection.h" -#include "Logger.h" @@ -34,7 +33,7 @@ int cServer::Init(short a_ListenPort, short a_ConnectPort) } #endif // _WIN32 - LOG("Generating protocol encryption keypair..."); + LOGINFO("Generating protocol encryption keypair..."); m_PrivateKey.Generate(); m_PublicKeyDER = m_PrivateKey.GetPubKeyDER(); @@ -98,10 +97,10 @@ void cServer::Run(void) printf("accept returned an error: %d; bailing out.\n", SocketError); return; } - LOG("Client connected, proxying..."); + LOGINFO("Client connected, proxying..."); cConnection Connection(client, *this); Connection.Run(); - LOG("Client disconnected. Ready for another connection."); + LOGINFO("Client disconnected. Ready for another connection."); } } From 9fab1d85bc5508a941a1cb80e61e7a23b29c51ea Mon Sep 17 00:00:00 2001 From: Creaprog Date: Mon, 16 Feb 2015 09:27:52 +0100 Subject: [PATCH 33/95] Fixed Server.cpp --- Tools/ProtoProxy/Server.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tools/ProtoProxy/Server.cpp b/Tools/ProtoProxy/Server.cpp index e27c86e10..5bba98057 100644 --- a/Tools/ProtoProxy/Server.cpp +++ b/Tools/ProtoProxy/Server.cpp @@ -85,7 +85,7 @@ int cServer::Init(short a_ListenPort, short a_ConnectPort) void cServer::Run(void) { - LOG("Server running."); + LOGINFO("Server running."); while (true) { sockaddr_in Addr; From 41e2eaa91549d5e73203f373ecc35ff936417dfc Mon Sep 17 00:00:00 2001 From: Alexander Harkness Date: Mon, 16 Feb 2015 09:54:02 +0000 Subject: [PATCH 34/95] Fixed guest page. --- MCServer/webadmin/files/guest.html | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/MCServer/webadmin/files/guest.html b/MCServer/webadmin/files/guest.html index 7ae78a3f0..dd8e8d301 100644 --- a/MCServer/webadmin/files/guest.html +++ b/MCServer/webadmin/files/guest.html @@ -1,2 +1,16 @@ -Hello! Welcome to the MCServer WebAdmin.
-This is a default message, edit files/guest.html to add your own custom message. + + + + Guest Information + + + +

+ Hello! Welcome to the MCServer WebAdmin. +

+

+ This is a default message, edit files/guest.html to add your own custom message. +

+ + + From 3aebe686d7a5de2532fa5bbbca1d169cbf72c4c4 Mon Sep 17 00:00:00 2001 From: Creaprog Date: Mon, 16 Feb 2015 11:37:12 +0100 Subject: [PATCH 35/95] Fixed guest.html Fixed syntax. --- MCServer/webadmin/files/guest.html | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/MCServer/webadmin/files/guest.html b/MCServer/webadmin/files/guest.html index dd8e8d301..b35015a0a 100644 --- a/MCServer/webadmin/files/guest.html +++ b/MCServer/webadmin/files/guest.html @@ -1,6 +1,8 @@ - - + + + + Guest Information From 421d370955a73fe202cc1b0b5625c87f69c80c4a Mon Sep 17 00:00:00 2001 From: Creaprog Date: Mon, 16 Feb 2015 11:40:11 +0100 Subject: [PATCH 36/95] Fixed guest.html --- MCServer/webadmin/files/guest.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MCServer/webadmin/files/guest.html b/MCServer/webadmin/files/guest.html index b35015a0a..4f965b75c 100644 --- a/MCServer/webadmin/files/guest.html +++ b/MCServer/webadmin/files/guest.html @@ -1,5 +1,5 @@ - + From 64dae7a42212026226eedd928e9f38294539f953 Mon Sep 17 00:00:00 2001 From: Tommy Santerre Date: Wed, 18 Feb 2015 00:11:06 -0500 Subject: [PATCH 37/95] Ignore files generated on build --- .gitignore | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.gitignore b/.gitignore index c7f032b00..8dc90910c 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,12 @@ nbproject/ ipch/ Win32/ MCServer/MCServer +MCServer/buildinfo +MCServer/CONTRIBUTORS +MCServer/LICENSE +MCServer/Licenses MCServer/itemblacklist +Testing/ ChunkWorxSave.ini doxy/ Profiling @@ -14,6 +19,7 @@ cloc.xsl *.ncb *.user *.suo +*.sqlite /EveryNight.cmd /UploadLuaAPI.cmd AllFiles.lst From 612637ab2e3c6e63124053cce770bf406d06c742 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Wed, 18 Feb 2015 09:34:33 +0100 Subject: [PATCH 38/95] Network: Fixed two-socket servers. --- src/OSSupport/ServerHandleImpl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OSSupport/ServerHandleImpl.cpp b/src/OSSupport/ServerHandleImpl.cpp index 72092df10..44ace448b 100644 --- a/src/OSSupport/ServerHandleImpl.cpp +++ b/src/OSSupport/ServerHandleImpl.cpp @@ -232,7 +232,7 @@ bool cServerHandleImpl::Listen(UInt16 a_Port) } // Allow the port to be reused right after the socket closes: - if (evutil_make_listen_socket_reuseable(MainSock) != 0) + if (evutil_make_listen_socket_reuseable(SecondSock) != 0) { m_ErrorCode = EVUTIL_SOCKET_ERROR(); Printf(m_ErrorMsg, "Port %d cannot be made reusable (second socket): %d (%s). Restarting the server might not work.", From af1a5b36db5e051957508cfd3e23d1ded494bcef Mon Sep 17 00:00:00 2001 From: Mattes D Date: Wed, 18 Feb 2015 20:59:50 +0100 Subject: [PATCH 39/95] InfoReg: Fixed MultiCommand return values. --- MCServer/Plugins/InfoReg.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/MCServer/Plugins/InfoReg.lua b/MCServer/Plugins/InfoReg.lua index 92c4a2e59..e34b79564 100644 --- a/MCServer/Plugins/InfoReg.lua +++ b/MCServer/Plugins/InfoReg.lua @@ -87,8 +87,7 @@ local function MultiCommandHandler(a_Split, a_Player, a_CmdString, a_CmdInfo, a_ LOG("Cannot find handler for command " .. a_CmdString .. " " .. Verb); return false; end - MultiCommandHandler(a_Split, a_Player, a_CmdString .. " " .. Verb, Subcommand, a_Level + 1); - return true; + return MultiCommandHandler(a_Split, a_Player, a_CmdString .. " " .. Verb, Subcommand, a_Level + 1); end -- Execute: From 70d54054e332c05d94e69b1eeca45a8773115c14 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Wed, 18 Feb 2015 22:41:22 +0100 Subject: [PATCH 40/95] NetworkSingleton: LibEvent thread is joined properly on server exit. --- src/OSSupport/NetworkSingleton.cpp | 6 ++---- src/OSSupport/NetworkSingleton.h | 6 +++--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/OSSupport/NetworkSingleton.cpp b/src/OSSupport/NetworkSingleton.cpp index 000b17641..358e24438 100644 --- a/src/OSSupport/NetworkSingleton.cpp +++ b/src/OSSupport/NetworkSingleton.cpp @@ -63,8 +63,7 @@ cNetworkSingleton::cNetworkSingleton(void): } // Create the event loop thread: - std::thread EventLoopThread(RunEventLoop, this); - EventLoopThread.detach(); + m_EventLoopThread = std::thread(RunEventLoop, this); } @@ -98,7 +97,7 @@ void cNetworkSingleton::Terminate(void) // Wait for the LibEvent event loop to terminate: event_base_loopbreak(m_EventBase); - m_EventLoopTerminated.Wait(); + m_EventLoopThread.join(); // Remove all objects: { @@ -143,7 +142,6 @@ void cNetworkSingleton::LogCallback(int a_Severity, const char * a_Msg) void cNetworkSingleton::RunEventLoop(cNetworkSingleton * a_Self) { event_base_loop(a_Self->m_EventBase, EVLOOP_NO_EXIT_ON_EMPTY); - a_Self->m_EventLoopTerminated.Set(); } diff --git a/src/OSSupport/NetworkSingleton.h b/src/OSSupport/NetworkSingleton.h index e27e19012..0536a1c82 100644 --- a/src/OSSupport/NetworkSingleton.h +++ b/src/OSSupport/NetworkSingleton.h @@ -116,12 +116,12 @@ protected: /** Mutex protecting all containers against multithreaded access. */ cCriticalSection m_CS; - /** Event that gets signalled when the event loop terminates. */ - cEvent m_EventLoopTerminated; - /** Set to true if Terminate has been called. */ volatile bool m_HasTerminated; + /** The thread in which the main LibEvent loop runs. */ + std::thread m_EventLoopThread; + /** Initializes the LibEvent internals. */ cNetworkSingleton(void); From 1a60785ca248bb081bf9fc699273292540a1de5f Mon Sep 17 00:00:00 2001 From: Howaner Date: Wed, 18 Feb 2015 23:33:27 +0100 Subject: [PATCH 41/95] Flower pots: In 1.8 items are saved with the name and not the id. --- src/WorldStorage/WSSAnvil.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp index ae82db346..cc8b8d3f5 100755 --- a/src/WorldStorage/WSSAnvil.cpp +++ b/src/WorldStorage/WSSAnvil.cpp @@ -1007,21 +1007,28 @@ cBlockEntity * cWSSAnvil::LoadFlowerPotFromNBT(const cParsedNBT & a_NBT, int a_T } std::unique_ptr FlowerPot(new cFlowerPotEntity(a_BlockX, a_BlockY, a_BlockZ, m_World)); - short ItemType = 0, ItemData = 0; + cItem Item; int currentLine = a_NBT.FindChildByName(a_TagIdx, "Item"); if (currentLine >= 0) { - ItemType = (short) a_NBT.GetInt(currentLine); + if (a_NBT.GetType(currentLine) == TAG_String) + { + StringToItem(a_NBT.GetString(currentLine), Item); + } + else if (a_NBT.GetType(currentLine) == TAG_Int) + { + Item.m_ItemType = (short) a_NBT.GetInt(currentLine); + } } currentLine = a_NBT.FindChildByName(a_TagIdx, "Data"); - if (currentLine >= 0) + if ((currentLine >= 0) && (a_NBT.GetType(currentLine) == TAG_Int)) { - ItemData = (short) a_NBT.GetInt(currentLine); + Item.m_ItemDamage = (short) a_NBT.GetInt(currentLine); } - FlowerPot->SetItem(cItem(ItemType, 1, ItemData)); + FlowerPot->SetItem(Item); return FlowerPot.release(); } From 5d4dd103a12a44c8482d524c38841221da1d8fb2 Mon Sep 17 00:00:00 2001 From: Matyas Dolak Date: Fri, 20 Feb 2015 09:51:18 +0100 Subject: [PATCH 42/95] Fixed crash when logging nil values. Ref.: http://forum.mc-server.org/showthread.php?tid=1798 --- src/Bindings/ManualBindings.cpp | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp index 24e3f0052..30bce6525 100644 --- a/src/Bindings/ManualBindings.cpp +++ b/src/Bindings/ManualBindings.cpp @@ -165,6 +165,14 @@ static AString GetLogMessage(lua_State * tolua_S) static int tolua_LOG(lua_State * tolua_S) { + // If there's no param, spit out an error message instead of crashing: + if (lua_isnil(tolua_S, 1)) + { + LOGWARNING("Attempting to LOG a nil value!"); + cLuaState::LogStackTrace(tolua_S); + return 0; + } + // If the param is a cCompositeChat, read the log level from it: cLogger::eLogLevel LogLevel = cLogger::llRegular; tolua_Error err; @@ -184,6 +192,14 @@ static int tolua_LOG(lua_State * tolua_S) static int tolua_LOGINFO(lua_State * tolua_S) { + // If there's no param, spit out an error message instead of crashing: + if (lua_isnil(tolua_S, 1)) + { + LOGWARNING("Attempting to LOGINFO a nil value!"); + cLuaState::LogStackTrace(tolua_S); + return 0; + } + cLogger::GetInstance().LogSimple(GetLogMessage(tolua_S).c_str(), cLogger::llInfo); return 0; } @@ -194,6 +210,14 @@ static int tolua_LOGINFO(lua_State * tolua_S) static int tolua_LOGWARN(lua_State * tolua_S) { + // If there's no param, spit out an error message instead of crashing: + if (lua_isnil(tolua_S, 1)) + { + LOGWARNING("Attempting to LOGWARN a nil value!"); + cLuaState::LogStackTrace(tolua_S); + return 0; + } + cLogger::GetInstance().LogSimple(GetLogMessage(tolua_S).c_str(), cLogger::llWarning); return 0; } @@ -204,6 +228,14 @@ static int tolua_LOGWARN(lua_State * tolua_S) static int tolua_LOGERROR(lua_State * tolua_S) { + // If there's no param, spit out an error message instead of crashing: + if (lua_isnil(tolua_S, 1)) + { + LOGWARNING("Attempting to LOGERROR a nil value!"); + cLuaState::LogStackTrace(tolua_S); + return 0; + } + cLogger::GetInstance().LogSimple(GetLogMessage(tolua_S).c_str(), cLogger::llError); return 0; } From 9c5162041e6e0699283862b87e2e424bf8e3b8b8 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Fri, 20 Feb 2015 14:28:05 +0100 Subject: [PATCH 43/95] cNetwork: Added UDP API. --- MCServer/Plugins/APIDump/Classes/Network.lua | 69 ++- MCServer/Plugins/NetworkTest/Info.lua | 42 ++ MCServer/Plugins/NetworkTest/NetworkTest.lua | 93 ++- src/Bindings/CMakeLists.txt | 2 + src/Bindings/LuaState.cpp | 12 + src/Bindings/LuaState.h | 2 + src/Bindings/LuaUDPEndpoint.cpp | 221 +++++++ src/Bindings/LuaUDPEndpoint.h | 86 +++ src/Bindings/ManualBindings_Network.cpp | 328 +++++++++- src/OSSupport/CMakeLists.txt | 2 + src/OSSupport/HostnameLookup.cpp | 12 + src/OSSupport/Network.h | 78 ++- src/OSSupport/UDPEndpointImpl.cpp | 608 +++++++++++++++++++ src/OSSupport/UDPEndpointImpl.h | 81 +++ 14 files changed, 1588 insertions(+), 48 deletions(-) create mode 100644 src/Bindings/LuaUDPEndpoint.cpp create mode 100644 src/Bindings/LuaUDPEndpoint.h create mode 100644 src/OSSupport/UDPEndpointImpl.cpp create mode 100644 src/OSSupport/UDPEndpointImpl.h diff --git a/MCServer/Plugins/APIDump/Classes/Network.lua b/MCServer/Plugins/APIDump/Classes/Network.lua index 274c8d035..82a8d52dc 100644 --- a/MCServer/Plugins/APIDump/Classes/Network.lua +++ b/MCServer/Plugins/APIDump/Classes/Network.lua @@ -31,11 +31,12 @@ local Server = cNetwork:Listen(1024, ListenCallbacks); specific plugin-provided function is called. The callbacks are stored in tables which are passed to the API functions, each table contains multiple callbacks for the various situations.

- There are three different callback variants used: LinkCallbacks, LookupCallbacks and - ListenCallbacks. Each is used in the situation appropriate by its name - LinkCallbacks are used + There are four different callback variants used: LinkCallbacks, LookupCallbacks, ListenCallbacks + and UDPCallbacks. Each is used in the situation appropriate by its name - LinkCallbacks are used for handling the traffic on a single network link (plus additionally creation of such link when - connecting as a client), LookupCallbacks are used when doing DNS and reverse-DNS lookups, and - ListenCallbacks are used for handling incoming connections as a server.

+ connecting as a client), LookupCallbacks are used when doing DNS and reverse-DNS lookups, + ListenCallbacks are used for handling incoming connections as a server and UDPCallbacks are used + for incoming UDP datagrams.

LinkCallbacks have the following structure:

@@ -111,7 +112,7 @@ local ListenCallbacks =
 	
 	OnError = function (a_ErrorCode, a_ErrorMsg)
 		-- The specified error has occured while trying to listen
-		-- No other callback will be called for this lookup from now on
+		-- No other callback will be called for this server handle from now on
 		-- This callback is called before the cNetwork:Listen() call returns
 		-- All returned values are ignored
 	end,
@@ -124,6 +125,25 @@ local ListenCallbacks =
 	end,
 }
 

+

+ UDPCallbacks have the following structure:
+

+local UDPCallbacks =
+{
+	OnError = function (a_ErrorCode, a_ErrorMsg)
+		-- The specified error has occured when trying to listen for incoming UDP datagrams
+		-- No other callback will be called for this endpoint from now on
+		-- This callback is called before the cNetwork:CreateUDPEndpoint() call returns
+		-- All returned values are ignored
+	end,
+
+	OnReceivedData = function ({{cUDPEndpoint|a_UDPEndpoint}}, a_Data, a_RemotePeer, a_RemotePort)
+		-- A datagram has been received on the {{cUDPEndpoint|endpoint}} from the specified remote peer
+		-- a_Data contains the raw received data, as a string
+		-- All returned values are ignored
+	end,
+}
+
]], }, @@ -268,12 +288,31 @@ g_Server = nil Functions = { Connect = { Params = "Host, Port, LinkCallbacks", Return = "bool", Notes = "(STATIC) Begins establishing a (client) TCP connection to the specified host. Uses the LinkCallbacks table to report progress, success, errors and incoming data. Returns false if it fails immediately (bad port value, bad hostname format), true otherwise. Host can be either an IP address or a hostname." }, + CreateUDPEndpoint = { Params = "Port, UDPCallbacks", Return = "{{cUDPEndpoint|UDPEndpoint}}", Notes = "(STATIC) Creates a UDP endpoint that listens for incoming datagrams on the specified port, and can be used to send or broadcast datagrams. Uses the UDPCallbacks to report incoming datagrams or errors. If the endpoint cannot be created, the OnError callback is called with the error details and the returned endpoint will report IsOpen() == false. The plugin needs to store the returned endpoint object for as long as it needs the UDP port open; if the endpoint is garbage-collected by Lua, the socket will be closed and no more incoming data will be reported." }, HostnameToIP = { Params = "Host, LookupCallbacks", Return = "bool", Notes = "(STATIC) Begins a DNS lookup to find the IP address(es) for the specified host. Uses the LookupCallbacks table to report progress, success or errors. Returns false if it fails immediately (bad hostname format), true if the lookup started successfully. Host can be either a hostname or an IP address." }, IPToHostname = { Params = "Address, LookupCallbacks", Return = "bool", Notes = "(STATIC) Begins a reverse-DNS lookup to find out the hostname for the specified IP address. Uses the LookupCallbacks table to report progress, success or errors. Returns false if it fails immediately (bad address format), true if the lookup started successfully." }, Listen = { Params = "Port, ListenCallbacks", Return = "{{cServerHandle|ServerHandle}}", Notes = "(STATIC) Starts listening on the specified port. Uses the ListenCallbacks to report incoming connections or errors. Returns a {{cServerHandle}} object representing the server. If the listen operation failed, the OnError callback is called with the error details and the returned server handle will report IsListening() == false. The plugin needs to store the server handle object for as long as it needs the server running, if the server handle is garbage-collected by Lua, the listening socket will be closed and all current connections dropped." }, }, }, -- cNetwork - + + cServerHandle = + { + Desc = + [[ + This class provides an interface for TCP sockets listening for a connection. In order to listen, the + plugin needs to use the {{cNetwork}}:Listen() function to create the listening socket.

+

+ Note that when Lua garbage-collects this class, the listening socket is closed. Therefore the plugin + should keep it referenced in a global variable for as long as it wants the server running. + ]], + + Functions = + { + Close = { Params = "", Return = "", Notes = "Closes the listening socket. No more connections will be accepted, and all current connections will be closed." }, + IsListening = { Params = "", Return = "bool", Notes = "Returns true if the socket is listening." }, + }, + }, -- cServerHandle + cTCPLink = { Desc = @@ -307,24 +346,24 @@ g_Server = nil }, }, -- cTCPLink - cServerHandle = + cUDPEndpoint = { Desc = [[ - This class provides an interface for TCP sockets listening for a connection. In order to listen, the - plugin needs to use the {{cNetwork}}:Listen() function to create the listening socket.

+ Represents a UDP socket that is listening for incoming datagrams on a UDP port and can send or broadcast datagrams to other peers on the network. Plugins can create an instance of the endpoint by calling {{cNetwork}}:CreateUDPEndpoint(). The endpoints are callback-based - when a datagram is read from the network, the OnRececeivedData() callback is called with details about the datagram. See the additional information in {{cNetwork}} documentation for details.

- Note that when Lua garbage-collects this class, the listening socket is closed. Therefore the plugin - should keep it referenced in a global variable for as long as it wants the server running. + Note that when Lua garbage-collects this class, the listening socket is closed. Therefore the plugin should keep this object referenced in a global variable for as long as it wants the endpoint open. ]], Functions = { - Close = { Params = "", Return = "", Notes = "Closes the listening socket. No more connections will be accepted, and all current connections will be closed." }, - IsListening = { Params = "", Return = "bool", Notes = "Returns true if the socket is listening." }, + Close = { Params = "", Return = "", Notes = "Closes the UDP endpoint. No more datagrams will be reported through the callbacks, the UDP port will be closed." }, + EnableBroadcasts = { Params = "", Return = "", Notes = "Some OSes need this call before they allow UDP broadcasts on an endpoint." }, + GetPort = { Params = "", Return = "number", Notes = "Returns the local port number of the UDP endpoint listening for incoming datagrams. Especially useful if the UDP endpoint was created with auto-assign port (0)." }, + IsOpen = { Params = "", Return = "bool", Notes = "Returns true if the UDP endpoint is listening for incoming datagrams." }, + Send = { Params = "RawData, RemoteHost, RemotePort", Return = "bool", Notes = "Sends the specified raw data (string) to the specified remote host. The RemoteHost can be either a hostname or an IP address; if it is a hostname, the endpoint will queue a DNS lookup first, if it is an IP address, the send operation is executed immediately. Returns true if there was no immediate error, false on any failure. Note that the return value needn't represent whether the packet was actually sent, only if it was successfully queued." }, }, - - }, -- cServerHandle + }, -- cUDPEndpoint } diff --git a/MCServer/Plugins/NetworkTest/Info.lua b/MCServer/Plugins/NetworkTest/Info.lua index c3c2ea8fc..52422d427 100644 --- a/MCServer/Plugins/NetworkTest/Info.lua +++ b/MCServer/Plugins/NetworkTest/Info.lua @@ -84,6 +84,48 @@ g_PluginInfo = }, }, -- lookup + udp = + { + Subcommands = + { + close = + { + Handler = HandleConsoleNetUdpClose, + ParameterCombinations = + { + { + Params = "[Port]", + Help = "Closes the UDP endpoint on the specified port [1024].", + } + }, + }, -- close + + listen = + { + Handler = HandleConsoleNetUdpListen, + ParameterCombinations = + { + { + Params = "[Port]", + Help = "Listens on the specified UDP port [1024], dumping the incoming datagrams into log", + }, + }, + }, -- listen + + send = + { + Handler = HandleConsoleNetUdpSend, + ParameterCombinations = + { + { + Params = "[Host] [Port] [Message]", + Help = "Sends the message [\"hello\"] through UDP to the specified host [localhost] and port [1024]", + }, + }, + } -- send + }, -- Subcommands ("net udp") + }, -- udp + wasc = { HelpString = "Requests the webadmin homepage using https", diff --git a/MCServer/Plugins/NetworkTest/NetworkTest.lua b/MCServer/Plugins/NetworkTest/NetworkTest.lua index 39774f403..daab0a4bf 100644 --- a/MCServer/Plugins/NetworkTest/NetworkTest.lua +++ b/MCServer/Plugins/NetworkTest/NetworkTest.lua @@ -11,6 +11,10 @@ -- g_Servers[PortNum] = cServerHandle local g_Servers = {} +--- Map of all UDP endpoints currently open +-- g_UDPEndpoints[PortNum] = cUDPEndpoint +local g_UDPEndpoints = {} + --- List of fortune messages for the fortune server -- A random message is chosen for each incoming connection -- The contents are loaded from the splashes.txt file on plugin startup @@ -268,7 +272,7 @@ function HandleConsoleNetClose(a_Split) -- Get the port to close: local Port = tonumber(a_Split[3] or 1024) if not(Port) then - return true, "Bad port number: \"" .. Port .. "\"." + return true, "Bad port number: \"" .. a_Split[3] .. "\"." end -- Close the server, if there is one: @@ -354,6 +358,93 @@ end +function HandleConsoleNetUdpClose(a_Split) + -- Get the port to close: + local Port = tonumber(a_Split[4] or 1024) + if not(Port) then + return true, "Bad port number: \"" .. a_Split[4] .. "\"." + end + + -- Close the server, if there is one: + if not(g_UDPEndpoints[Port]) then + return true, "There is no UDP endpoint currently listening on port " .. Port .. "." + end + g_UDPEndpoints[Port]:Close() + g_UDPEndpoints[Port] = nil + return true, "UDP Port " .. Port .. " closed." +end + + + + + +function HandleConsoleNetUdpListen(a_Split) + -- Get the params: + local Port = tonumber(a_Split[4] or 1024) + if not(Port) then + return true, "Invalid port: \"" .. a_Split[4] .. "\"." + end + + local Callbacks = + { + OnReceivedData = function (a_Endpoint, a_Data, a_RemotePeer, a_RemotePort) + LOG("Incoming UDP datagram from " .. a_RemotePeer .. " port " .. a_RemotePort .. ":\r\n" .. a_Data) + end, + + OnError = function (a_Endpoint, a_ErrorCode, a_ErrorMsg) + LOG("Error in UDP endpoint: " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")") + end, + } + + g_UDPEndpoints[Port] = cNetwork:CreateUDPEndpoint(Port, Callbacks) + return true, "UDP listener on port " .. Port .. " started." +end + + + + + +function HandleConsoleNetUdpSend(a_Split) + -- Get the params: + local Host = a_Split[4] or "localhost" + local Port = tonumber(a_Split[5] or 1024) + if not(Port) then + return true, "Invalid port: \"" .. a_Split[5] .. "\"." + end + local Message + if (a_Split[6]) then + Message = table.concat(a_Split, " ", 6) + else + Message = "hello" + end + + -- Define minimum callbacks for the UDP endpoint: + local Callbacks = + { + OnError = function (a_Endpoint, a_ErrorCode, a_ErrorMsg) + LOG("Error in UDP datagram sending: " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")") + end, + + OnReceivedData = function () + -- ignore + end, + } + + -- Send the data: + local Endpoint = cNetwork:CreateUDPEndpoint(0, Callbacks) + Endpoint:EnableBroadcasts() + if not(Endpoint:Send(Message, Host, Port)) then + Endpoint:Close() + return true, "Sending UDP datagram failed" + end + Endpoint:Close() + return true, "UDP datagram sent" +end + + + + + function HandleConsoleNetWasc(a_Split) local Callbacks = { diff --git a/src/Bindings/CMakeLists.txt b/src/Bindings/CMakeLists.txt index 40b8d4bd9..4cc73b350 100644 --- a/src/Bindings/CMakeLists.txt +++ b/src/Bindings/CMakeLists.txt @@ -12,6 +12,7 @@ SET (SRCS LuaServerHandle.cpp LuaState.cpp LuaTCPLink.cpp + LuaUDPEndpoint.cpp LuaWindow.cpp ManualBindings.cpp ManualBindings_Network.cpp @@ -31,6 +32,7 @@ SET (HDRS LuaServerHandle.h LuaState.h LuaTCPLink.h + LuaUDPEndpoint.h LuaWindow.h ManualBindings.h Plugin.h diff --git a/src/Bindings/LuaState.cpp b/src/Bindings/LuaState.cpp index 81770058c..25c77a652 100644 --- a/src/Bindings/LuaState.cpp +++ b/src/Bindings/LuaState.cpp @@ -692,6 +692,18 @@ void cLuaState::Push(cLuaTCPLink * a_TCPLink) +void cLuaState::Push(cLuaUDPEndpoint * a_UDPEndpoint) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, a_UDPEndpoint, "cUDPEndpoint"); + m_NumCurrentFunctionArgs += 1; +} + + + + + void cLuaState::Push(cMonster * a_Monster) { ASSERT(IsValid()); diff --git a/src/Bindings/LuaState.h b/src/Bindings/LuaState.h index 7fc3197eb..159483634 100644 --- a/src/Bindings/LuaState.h +++ b/src/Bindings/LuaState.h @@ -61,6 +61,7 @@ class cBlockEntity; class cBoundingBox; class cLuaTCPLink; class cLuaServerHandle; +class cLuaUDPEndpoint; typedef cBoundingBox * pBoundingBox; typedef cWorld * pWorld; @@ -212,6 +213,7 @@ public: void Push(cItems * a_Items); void Push(cLuaServerHandle * a_ServerHandle); void Push(cLuaTCPLink * a_TCPLink); + void Push(cLuaUDPEndpoint * a_UDPEndpoint); void Push(cMonster * a_Monster); void Push(cPickup * a_Pickup); void Push(cPlayer * a_Player); diff --git a/src/Bindings/LuaUDPEndpoint.cpp b/src/Bindings/LuaUDPEndpoint.cpp new file mode 100644 index 000000000..8637eeb4e --- /dev/null +++ b/src/Bindings/LuaUDPEndpoint.cpp @@ -0,0 +1,221 @@ + +// LuaUDPEndpoint.cpp + +// Implements the cLuaUDPEndpoint class representing a Lua wrapper for the cUDPEndpoint class and the callbacks it needs + +#include "Globals.h" +#include "LuaUDPEndpoint.h" + + + + + +cLuaUDPEndpoint::cLuaUDPEndpoint(cPluginLua & a_Plugin, int a_CallbacksTableStackPos): + m_Plugin(a_Plugin), + m_Callbacks(a_Plugin.GetLuaState(), a_CallbacksTableStackPos) +{ + // Warn if the callbacks aren't valid: + if (!m_Callbacks.IsValid()) + { + LOGWARNING("cLuaUDPEndpoint in plugin %s: callbacks could not be retrieved", m_Plugin.GetName().c_str()); + cPluginLua::cOperation Op(m_Plugin); + Op().LogStackTrace(); + } +} + + + + + +cLuaUDPEndpoint::~cLuaUDPEndpoint() +{ + // If the endpoint is still open, close it: + cUDPEndpointPtr Endpoint = m_Endpoint; + if (Endpoint != nullptr) + { + Endpoint->Close(); + } + + Terminated(); +} + + + + + +bool cLuaUDPEndpoint::Open(UInt16 a_Port, cLuaUDPEndpointPtr a_Self) +{ + ASSERT(m_Self == nullptr); // Must not be opened yet + ASSERT(m_Endpoint == nullptr); + + m_Self = a_Self; + m_Endpoint = cNetwork::CreateUDPEndpoint(a_Port, *this); + return m_Endpoint->IsOpen(); +} + + + + + +bool cLuaUDPEndpoint::Send(const AString & a_Data, const AString & a_RemotePeer, UInt16 a_RemotePort) +{ + // Safely grab a copy of the endpoint: + cUDPEndpointPtr Endpoint = m_Endpoint; + if (Endpoint == nullptr) + { + return false; + } + + // Send the data: + return Endpoint->Send(a_Data, a_RemotePeer, a_RemotePort); +} + + + + + +UInt16 cLuaUDPEndpoint::GetPort(void) const +{ + // Safely grab a copy of the endpoint: + cUDPEndpointPtr Endpoint = m_Endpoint; + if (Endpoint == nullptr) + { + return 0; + } + + // Get the port: + return Endpoint->GetPort(); +} + + + + + +bool cLuaUDPEndpoint::IsOpen(void) const +{ + // Safely grab a copy of the endpoint: + cUDPEndpointPtr Endpoint = m_Endpoint; + if (Endpoint == nullptr) + { + // No endpoint means that we're not open + return false; + } + + // Get the state: + return Endpoint->IsOpen(); +} + + + + + +void cLuaUDPEndpoint::Close(void) +{ + // If the endpoint is still open, close it: + cUDPEndpointPtr Endpoint = m_Endpoint; + if (Endpoint != nullptr) + { + Endpoint->Close(); + m_Endpoint.reset(); + } + + Terminated(); +} + + + + + +void cLuaUDPEndpoint::EnableBroadcasts(void) +{ + // Safely grab a copy of the endpoint: + cUDPEndpointPtr Endpoint = m_Endpoint; + if (Endpoint != nullptr) + { + Endpoint->EnableBroadcasts(); + } +} + + + + + +void cLuaUDPEndpoint::Release(void) +{ + // Close the endpoint, if not already closed: + Close(); + + // Allow self to deallocate: + m_Self.reset(); +} + + + + + +void cLuaUDPEndpoint::Terminated(void) +{ + // Disable the callbacks: + if (m_Callbacks.IsValid()) + { + m_Callbacks.UnRef(); + } + + // If the endpoint is still open, close it: + { + cUDPEndpointPtr Endpoint = m_Endpoint; + if (Endpoint != nullptr) + { + Endpoint->Close(); + m_Endpoint.reset(); + } + } +} + + + + + +void cLuaUDPEndpoint::OnReceivedData(const char * a_Data, size_t a_NumBytes, const AString & a_RemotePeer, UInt16 a_RemotePort) +{ + // Check if we're still valid: + if (!m_Callbacks.IsValid()) + { + return; + } + + // Call the callback: + cPluginLua::cOperation Op(m_Plugin); + if (!Op().Call(cLuaState::cTableRef(m_Callbacks, "OnReceivedData"), this, AString(a_Data, a_NumBytes), a_RemotePeer, a_RemotePort)) + { + LOGINFO("cUDPEndpoint OnReceivedData callback failed in plugin %s.", m_Plugin.GetName().c_str()); + } +} + + + + + +void cLuaUDPEndpoint::OnError(int a_ErrorCode, const AString & a_ErrorMsg) +{ + // Check if we're still valid: + if (!m_Callbacks.IsValid()) + { + return; + } + + // Call the callback: + cPluginLua::cOperation Op(m_Plugin); + if (!Op().Call(cLuaState::cTableRef(m_Callbacks, "OnError"), a_ErrorCode, a_ErrorMsg)) + { + LOGINFO("cUDPEndpoint OnError() callback failed in plugin %s; the endpoint error is %d (%s).", + m_Plugin.GetName().c_str(), a_ErrorCode, a_ErrorMsg.c_str() + ); + } + + Terminated(); +} + + + + diff --git a/src/Bindings/LuaUDPEndpoint.h b/src/Bindings/LuaUDPEndpoint.h new file mode 100644 index 000000000..0587491ab --- /dev/null +++ b/src/Bindings/LuaUDPEndpoint.h @@ -0,0 +1,86 @@ + +// LuaUDPEndpoint.h + +// Declares the cLuaUDPEndpoint class representing a Lua wrapper for the cUDPEndpoint class and the callbacks it needs + + + + + +#pragma once + +#include "../OSSupport/Network.h" +#include "PluginLua.h" + + + + + +// fwd: +class cLuaUDPEndpoint; +typedef SharedPtr cLuaUDPEndpointPtr; + + + + + +class cLuaUDPEndpoint: + public cUDPEndpoint::cCallbacks +{ +public: + /** Creates a new instance of the endpoint, attached to the specified plugin and wrapping the callbacks that are in a table at the specified stack pos. */ + cLuaUDPEndpoint(cPluginLua & a_Plugin, int a_CallbacksTableStackPos); + + ~cLuaUDPEndpoint(); + + /** Opens the endpoint so that it starts listening for incoming data on the specified port. + a_Self is the shared pointer to self that the object keeps to keep itself alive for as long as it needs (for Lua). */ + bool Open(UInt16 a_Port, cLuaUDPEndpointPtr a_Self); + + /** Sends the data contained in the string to the specified remote peer. + Returns true if successful, false on immediate failure (queueing the data failed etc.) */ + bool Send(const AString & a_Data, const AString & a_RemotePeer, UInt16 a_RemotePort); + + /** Returns the port on which endpoint is listening for incoming data. */ + UInt16 GetPort(void) const; + + /** Returns true if the endpoint is open for incoming data. */ + bool IsOpen(void) const; + + /** Closes the UDP listener. */ + void Close(void); + + /** Enables outgoing broadcasts to be made using this endpoint. */ + void EnableBroadcasts(void); + + /** Called when Lua garbage-collects the object. + Releases the internal SharedPtr to self, so that the instance may be deallocated. */ + void Release(void); + +protected: + /** The plugin for which the link is created. */ + cPluginLua & m_Plugin; + + /** The Lua table that holds the callbacks to be invoked. */ + cLuaState::cRef m_Callbacks; + + /** SharedPtr to self, so that the object can keep itself alive for as long as it needs (for Lua). */ + cLuaUDPEndpointPtr m_Self; + + /** The underlying network endpoint. + May be nullptr. */ + cUDPEndpointPtr m_Endpoint; + + + /** Common code called when the endpoint is considered as terminated. + Releases m_Endpoint and m_Callbacks, each when applicable. */ + void Terminated(void); + + // cUDPEndpoint::cCallbacks overrides: + virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override; + virtual void OnReceivedData(const char * a_Data, size_t a_Size, const AString & a_RemotePeer, UInt16 a_RemotePort) override; +}; + + + + diff --git a/src/Bindings/ManualBindings_Network.cpp b/src/Bindings/ManualBindings_Network.cpp index 30a34815c..a628eb9ca 100644 --- a/src/Bindings/ManualBindings_Network.cpp +++ b/src/Bindings/ManualBindings_Network.cpp @@ -11,6 +11,7 @@ #include "LuaTCPLink.h" #include "LuaNameLookup.h" #include "LuaServerHandle.h" +#include "LuaUDPEndpoint.h" @@ -73,6 +74,61 @@ static int tolua_cNetwork_Connect(lua_State * L) +/** Binds cNetwork::CreateUDPEndpoint */ +static int tolua_cNetwork_CreateUDPEndpoint(lua_State * L) +{ + // Function signature: + // cNetwork:CreateUDPEndpoint(Port, Callbacks) -> cUDPEndpoint + + cLuaState S(L); + if ( + !S.CheckParamUserTable(1, "cNetwork") || + !S.CheckParamNumber(2) || + !S.CheckParamTable(3) || + !S.CheckParamEnd(4) + ) + { + return 0; + } + + // Get the plugin instance: + cPluginLua * Plugin = GetLuaPlugin(L); + if (Plugin == nullptr) + { + // An error message has been already printed in GetLuaPlugin() + S.Push(false); + return 1; + } + + // Read the params: + int Port; + S.GetStackValues(2, Port); + + // Check validity: + if ((Port < 0) || (Port > 65535)) + { + LOGWARNING("cNetwork:CreateUDPEndpoint() called with invalid port (%d), failing the request.", Port); + S.Push(false); + return 1; + } + + // Create the LuaUDPEndpoint glue class: + auto Endpoint = std::make_shared(*Plugin, 3); + Endpoint->Open(Port, Endpoint); + + // Register the endpoint to be garbage-collected by Lua: + tolua_pushusertype(L, Endpoint.get(), "cUDPEndpoint"); + tolua_register_gc(L, lua_gettop(L)); + + // Return the endpoint object: + S.Push(Endpoint.get()); + return 1; +} + + + + + /** Binds cNetwork::HostnameToIP */ static int tolua_cNetwork_HostnameToIP(lua_State * L) { @@ -159,7 +215,7 @@ static int tolua_cNetwork_IPToHostname(lua_State * L) static int tolua_cNetwork_Listen(lua_State * L) { // Function signature: - // cNetwork:Listen(Port, Callbacks) -> bool + // cNetwork:Listen(Port, Callbacks) -> cServerHandle cLuaState S(L); if ( @@ -211,6 +267,90 @@ static int tolua_cNetwork_Listen(lua_State * L) +//////////////////////////////////////////////////////////////////////////////// +// cServerHandle bindings (routed through cLuaServerHandle): + +/** Called when Lua destroys the object instance. +Close the server and let it deallocate on its own (it's in a SharedPtr). */ +static int tolua_collect_cServerHandle(lua_State * L) +{ + cLuaServerHandle * Srv = static_cast(tolua_tousertype(L, 1, nullptr)); + Srv->Release(); + return 0; +} + + + + + +/** Binds cLuaServerHandle::Close */ +static int tolua_cServerHandle_Close(lua_State * L) +{ + // Function signature: + // ServerInstance:Close() + + cLuaState S(L); + if ( + !S.CheckParamUserType(1, "cServerHandle") || + !S.CheckParamEnd(2) + ) + { + return 0; + } + + // Get the server handle: + cLuaServerHandle * Srv; + if (lua_isnil(L, 1)) + { + LOGWARNING("cServerHandle:Close(): invalid server handle object. Stack trace:"); + S.LogStackTrace(); + return 0; + } + Srv = *static_cast(lua_touserdata(L, 1)); + + // Close it: + Srv->Close(); + return 0; +} + + + + + +/** Binds cLuaServerHandle::IsListening */ +static int tolua_cServerHandle_IsListening(lua_State * L) +{ + // Function signature: + // ServerInstance:IsListening() -> bool + + cLuaState S(L); + if ( + !S.CheckParamUserType(1, "cServerHandle") || + !S.CheckParamEnd(2) + ) + { + return 0; + } + + // Get the server handle: + cLuaServerHandle * Srv; + if (lua_isnil(L, 1)) + { + LOGWARNING("cServerHandle:IsListening(): invalid server handle object. Stack trace:"); + S.LogStackTrace(); + return 0; + } + Srv = *static_cast(lua_touserdata(L, 1)); + + // Close it: + S.Push(Srv->IsListening()); + return 1; +} + + + + + //////////////////////////////////////////////////////////////////////////////// // cTCPLink bindings (routed through cLuaTCPLink): @@ -549,14 +689,15 @@ static int tolua_cTCPLink_StartTLSServer(lua_State * L) //////////////////////////////////////////////////////////////////////////////// -// cServerHandle bindings (routed through cLuaServerHandle): +// cUDPEndpoint bindings (routed through cLuaUDPEndpoint): /** Called when Lua destroys the object instance. -Close the server and let it deallocate on its own (it's in a SharedPtr). */ -static int tolua_collect_cServerHandle(lua_State * L) +Close the endpoint and let it deallocate on its own (it's in a SharedPtr). */ +static int tolua_collect_cUDPEndpoint(lua_State * L) { - cLuaServerHandle * Srv = static_cast(tolua_tousertype(L, 1, nullptr)); - Srv->Release(); + LOGD("Lua: Collecting cUDPEndpoint"); + cLuaUDPEndpoint * Endpoint = static_cast(tolua_tousertype(L, 1, nullptr)); + Endpoint->Release(); return 0; } @@ -564,33 +705,32 @@ static int tolua_collect_cServerHandle(lua_State * L) -/** Binds cLuaServerHandle::Close */ -static int tolua_cServerHandle_Close(lua_State * L) +/** Binds cLuaUDPEndpoint::Close */ +static int tolua_cUDPEndpoint_Close(lua_State * L) { // Function signature: - // ServerInstance:Close() + // EndpointInstance:Close() cLuaState S(L); if ( - !S.CheckParamUserType(1, "cServerHandle") || + !S.CheckParamUserType(1, "cUDPEndpoint") || !S.CheckParamEnd(2) ) { return 0; } - // Get the server handle: - cLuaServerHandle * Srv; + // Get the endpoint: if (lua_isnil(L, 1)) { - LOGWARNING("cServerHandle:Close(): invalid server handle object. Stack trace:"); + LOGWARNING("cUDPEndpoint:Close(): invalid endpoint object. Stack trace:"); S.LogStackTrace(); return 0; } - Srv = *static_cast(lua_touserdata(L, 1)); + auto Endpoint = *static_cast(lua_touserdata(L, 1)); // Close it: - Srv->Close(); + Endpoint->Close(); return 0; } @@ -598,33 +738,147 @@ static int tolua_cServerHandle_Close(lua_State * L) -/** Binds cLuaServerHandle::IsListening */ -static int tolua_cServerHandle_IsListening(lua_State * L) +/** Binds cLuaUDPEndpoint::EnableBroadcasts */ +static int tolua_cUDPEndpoint_EnableBroadcasts(lua_State * L) { // Function signature: - // ServerInstance:IsListening() -> bool + // EndpointInstance:EnableBroadcasts() cLuaState S(L); if ( - !S.CheckParamUserType(1, "cServerHandle") || + !S.CheckParamUserType(1, "cUDPEndpoint") || !S.CheckParamEnd(2) ) { return 0; } - // Get the server handle: - cLuaServerHandle * Srv; + // Get the endpoint: if (lua_isnil(L, 1)) { - LOGWARNING("cServerHandle:IsListening(): invalid server handle object. Stack trace:"); + LOGWARNING("cUDPEndpoint:EnableBroadcasts(): invalid endpoint object. Stack trace:"); S.LogStackTrace(); return 0; } - Srv = *static_cast(lua_touserdata(L, 1)); + auto Endpoint = *static_cast(lua_touserdata(L, 1)); + + // Enable the broadcasts: + Endpoint->EnableBroadcasts(); + return 0; +} + + + + + +/** Binds cLuaUDPEndpoint::GetPort */ +static int tolua_cUDPEndpoint_GetPort(lua_State * L) +{ + // Function signature: + // Endpoint:GetPort() -> number + + cLuaState S(L); + if ( + !S.CheckParamUserType(1, "cUDPEndpoint") || + !S.CheckParamEnd(2) + ) + { + return 0; + } + + // Get the endpoint: + if (lua_isnil(L, 1)) + { + LOGWARNING("cUDPEndpoint:GetPort(): invalid endpoint object. Stack trace:"); + S.LogStackTrace(); + return 0; + } + auto Endpoint = *static_cast(lua_touserdata(L, 1)); + + // Get the Port: + S.Push(Endpoint->GetPort()); + return 1; +} + + + + + +/** Binds cLuaUDPEndpoint::IsOpen */ +static int tolua_cUDPEndpoint_IsOpen(lua_State * L) +{ + // Function signature: + // Endpoint:IsOpen() -> bool + + cLuaState S(L); + if ( + !S.CheckParamUserType(1, "cUDPEndpoint") || + !S.CheckParamEnd(2) + ) + { + return 0; + } + + // Get the endpoint: + if (lua_isnil(L, 1)) + { + LOGWARNING("cUDPEndpoint:IsListening(): invalid server handle object. Stack trace:"); + S.LogStackTrace(); + return 0; + } + auto Endpoint = *static_cast(lua_touserdata(L, 1)); // Close it: - S.Push(Srv->IsListening()); + S.Push(Endpoint->IsOpen()); + return 1; +} + + + + + +/** Binds cLuaUDPEndpoint::Send */ +static int tolua_cUDPEndpoint_Send(lua_State * L) +{ + // Function signature: + // LinkInstance:Send(DataString) + + cLuaState S(L); + if ( + !S.CheckParamUserType(1, "cUDPEndpoint") || + !S.CheckParamString(2, 3) || + !S.CheckParamNumber(4) || + !S.CheckParamEnd(5) + ) + { + return 0; + } + + // Get the link: + if (lua_isnil(L, 1)) + { + LOGWARNING("cUDPEndpoint:Send(): invalid endpoint object. Stack trace:"); + S.LogStackTrace(); + return 0; + } + auto Endpoint = *static_cast(lua_touserdata(L, 1)); + + // Get the data to send: + AString Data, RemotePeer; + int RemotePort; + S.GetStackValues(2, Data, RemotePeer, RemotePort); + + // Check the port: + if ((RemotePort < 0) || (RemotePort > USHRT_MAX)) + { + LOGWARNING("cUDPEndpoint:Send() called with invalid port (%d), failing.", RemotePort); + S.LogStackTrace(); + S.Push(false); + return 1; + } + + // Send the data: + S.Push(Endpoint->Send(Data, RemotePeer, static_cast(RemotePort))); return 1; } @@ -644,13 +898,21 @@ void ManualBindings::BindNetwork(lua_State * tolua_S) tolua_cclass(tolua_S, "cTCPLink", "cTCPLink", "", nullptr); tolua_usertype(tolua_S, "cServerHandle"); tolua_cclass(tolua_S, "cServerHandle", "cServerHandle", "", tolua_collect_cServerHandle); + tolua_usertype(tolua_S, "cUDPEndpoint"); + tolua_cclass(tolua_S, "cUDPEndpoint", "cUDPEndpoint", "", tolua_collect_cUDPEndpoint); // Fill in the functions (alpha-sorted): tolua_beginmodule(tolua_S, "cNetwork"); - tolua_function(tolua_S, "Connect", tolua_cNetwork_Connect); - tolua_function(tolua_S, "HostnameToIP", tolua_cNetwork_HostnameToIP); - tolua_function(tolua_S, "IPToHostname", tolua_cNetwork_IPToHostname); - tolua_function(tolua_S, "Listen", tolua_cNetwork_Listen); + tolua_function(tolua_S, "Connect", tolua_cNetwork_Connect); + tolua_function(tolua_S, "CreateUDPEndpoint", tolua_cNetwork_CreateUDPEndpoint); + tolua_function(tolua_S, "HostnameToIP", tolua_cNetwork_HostnameToIP); + tolua_function(tolua_S, "IPToHostname", tolua_cNetwork_IPToHostname); + tolua_function(tolua_S, "Listen", tolua_cNetwork_Listen); + tolua_endmodule(tolua_S); + + tolua_beginmodule(tolua_S, "cServerHandle"); + tolua_function(tolua_S, "Close", tolua_cServerHandle_Close); + tolua_function(tolua_S, "IsListening", tolua_cServerHandle_IsListening); tolua_endmodule(tolua_S); tolua_beginmodule(tolua_S, "cTCPLink"); @@ -665,10 +927,14 @@ void ManualBindings::BindNetwork(lua_State * tolua_S) tolua_function(tolua_S, "StartTLSServer", tolua_cTCPLink_StartTLSServer); tolua_endmodule(tolua_S); - tolua_beginmodule(tolua_S, "cServerHandle"); - tolua_function(tolua_S, "Close", tolua_cServerHandle_Close); - tolua_function(tolua_S, "IsListening", tolua_cServerHandle_IsListening); + tolua_beginmodule(tolua_S, "cUDPEndpoint"); + tolua_function(tolua_S, "Close", tolua_cUDPEndpoint_Close); + tolua_function(tolua_S, "EnableBroadcasts", tolua_cUDPEndpoint_EnableBroadcasts); + tolua_function(tolua_S, "GetPort", tolua_cUDPEndpoint_GetPort); + tolua_function(tolua_S, "IsOpen", tolua_cUDPEndpoint_IsOpen); + tolua_function(tolua_S, "Send", tolua_cUDPEndpoint_Send); tolua_endmodule(tolua_S); + } diff --git a/src/OSSupport/CMakeLists.txt b/src/OSSupport/CMakeLists.txt index 6f609c519..81b37ef0e 100644 --- a/src/OSSupport/CMakeLists.txt +++ b/src/OSSupport/CMakeLists.txt @@ -18,6 +18,7 @@ SET (SRCS ServerHandleImpl.cpp StackTrace.cpp TCPLinkImpl.cpp + UDPEndpointImpl.cpp ) SET (HDRS @@ -36,6 +37,7 @@ SET (HDRS ServerHandleImpl.h StackTrace.h TCPLinkImpl.h + UDPEndpointImpl.h ) if(NOT MSVC) diff --git a/src/OSSupport/HostnameLookup.cpp b/src/OSSupport/HostnameLookup.cpp index 3a2997ffd..0944153be 100644 --- a/src/OSSupport/HostnameLookup.cpp +++ b/src/OSSupport/HostnameLookup.cpp @@ -69,12 +69,24 @@ void cHostnameLookup::Callback(int a_ErrCode, evutil_addrinfo * a_Addr, void * a case AF_INET: // IPv4 { sockaddr_in * sin = reinterpret_cast(a_Addr->ai_addr); + if (!Self->m_Callbacks->OnNameResolvedV4(Self->m_Hostname, sin)) + { + // Callback indicated that the IP shouldn't be serialized to a string, just continue with the next address: + HasResolved = true; + continue; + } evutil_inet_ntop(AF_INET, &(sin->sin_addr), IP, sizeof(IP)); break; } case AF_INET6: // IPv6 { sockaddr_in6 * sin = reinterpret_cast(a_Addr->ai_addr); + if (!Self->m_Callbacks->OnNameResolvedV6(Self->m_Hostname, sin)) + { + // Callback indicated that the IP shouldn't be serialized to a string, just continue with the next address: + HasResolved = true; + continue; + } evutil_inet_ntop(AF_INET6, &(sin->sin6_addr), IP, sizeof(IP)); break; } diff --git a/src/OSSupport/Network.h b/src/OSSupport/Network.h index e883dfb29..5dd596223 100644 --- a/src/OSSupport/Network.h +++ b/src/OSSupport/Network.h @@ -130,6 +130,64 @@ public: +/** Interface that provides methods available on UDP communication endpoints. */ +class cUDPEndpoint +{ +public: + /** Interface for the callbacks for events that can happen on the endpoint. */ + class cCallbacks + { + public: + // Force a virtual destructor in all descendants: + virtual ~cCallbacks() {} + + /** Called when an error occurs on the endpoint. */ + virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) = 0; + + /** Called when there is an incoming datagram from a remote host. */ + virtual void OnReceivedData(const char * a_Data, size_t a_Size, const AString & a_RemoteHost, UInt16 a_RemotePort) = 0; + }; + + + // Force a virtual destructor for all descendants: + virtual ~cUDPEndpoint() {} + + /** Closes the underlying socket. + Note that there still might be callbacks in-flight after this method returns. */ + virtual void Close(void) = 0; + + /** Returns true if the endpoint is open. */ + virtual bool IsOpen(void) const = 0; + + /** Returns the local port to which the underlying socket is bound. */ + virtual UInt16 GetPort(void) const = 0; + + /** Sends the specified payload in a single UDP datagram to the specified host+port combination. + Note that in order to send to a broadcast address, you need to call EnableBroadcasts() first. */ + virtual bool Send(const AString & a_Payload, const AString & a_Host, UInt16 a_Port) = 0; + + /** Marks the socket as capable of sending broadcast, using whatever OS API is needed. + Without this call, sending to a broadcast address using Send() may fail. */ + virtual void EnableBroadcasts(void) = 0; + +protected: + /** The callbacks used for various events on the endpoint. */ + cCallbacks & m_Callbacks; + + + /** Creates a new instance of an endpoint, with the specified callbacks. */ + cUDPEndpoint(cCallbacks & a_Callbacks): + m_Callbacks(a_Callbacks) + { + } +}; + +typedef SharedPtr cUDPEndpointPtr; + + + + + class cNetwork { public: @@ -183,9 +241,22 @@ public: /** Called when the hostname is successfully resolved into an IP address. May be called multiple times if a name resolves to multiple addresses. - a_IP may be either an IPv4 or an IPv6 address with their proper formatting. */ + a_IP may be either an IPv4 or an IPv6 address with their proper formatting. + Each call to OnNameResolved() is preceded by a call to either OnNameResolvedV4() or OnNameResolvedV6(). */ virtual void OnNameResolved(const AString & a_Name, const AString & a_IP) = 0; + /** Called when the hostname is successfully resolved into an IPv4 address. + May be called multiple times if a name resolves to multiple addresses. + Each call to OnNameResolvedV4 is followed by OnNameResolved with the IP address serialized to a string. + If this callback returns false, the OnNameResolved() call is skipped for this address. */ + virtual bool OnNameResolvedV4(const AString & a_Name, const sockaddr_in * a_IP) { return true; } + + /** Called when the hostname is successfully resolved into an IPv6 address. + May be called multiple times if a name resolves to multiple addresses. + Each call to OnNameResolvedV4 is followed by OnNameResolved with the IP address serialized to a string. + If this callback returns false, the OnNameResolved() call is skipped for this address. */ + virtual bool OnNameResolvedV6(const AString & a_Name, const sockaddr_in6 * a_IP) { return true; } + /** Called when an error is encountered while resolving. If an error is reported, the OnFinished() callback is not called. */ virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) = 0; @@ -242,6 +313,11 @@ public: const AString & a_IP, cResolveNameCallbacksPtr a_Callbacks ); + + /** Opens up an UDP endpoint for sending and receiving UDP datagrams on the specified port. + If a_Port is 0, the OS is free to assign any port number it likes to the endpoint. + Returns the endpoint object that can be interacted with. */ + static cUDPEndpointPtr CreateUDPEndpoint(UInt16 a_Port, cUDPEndpoint::cCallbacks & a_Callbacks); }; diff --git a/src/OSSupport/UDPEndpointImpl.cpp b/src/OSSupport/UDPEndpointImpl.cpp new file mode 100644 index 000000000..c5190bcf6 --- /dev/null +++ b/src/OSSupport/UDPEndpointImpl.cpp @@ -0,0 +1,608 @@ + +// UDPEndpointImpl.cpp + +// Implements the cUDPEndpointImpl class representing an implementation of an endpoint in UDP communication + +#include "Globals.h" +#include "UDPEndpointImpl.h" +#include "NetworkSingleton.h" + + + + + +//////////////////////////////////////////////////////////////////////////////// +// Globals: + +static bool IsValidSocket(evutil_socket_t a_Socket) +{ + #ifdef _WIN32 + return (a_Socket != INVALID_SOCKET); + #else // _WIN32 + return (a_Socket >= 0); + #endif // else _WIN32 +} + + + + + +/** Converts a_SrcAddr in IPv4 format to a_DstAddr in IPv6 format (using IPv4-mapped IPv6). */ +static void ConvertIPv4ToMappedIPv6(sockaddr_in & a_SrcAddr, sockaddr_in6 & a_DstAddr) +{ + memset(&a_DstAddr, 0, sizeof(a_DstAddr)); + a_DstAddr.sin6_family = AF_INET6; + a_DstAddr.sin6_addr.s6_addr[10] = 0xff; + a_DstAddr.sin6_addr.s6_addr[11] = 0xff; + a_DstAddr.sin6_addr.s6_addr[12] = static_cast((a_SrcAddr.sin_addr.s_addr >> 0) & 0xff); + a_DstAddr.sin6_addr.s6_addr[13] = static_cast((a_SrcAddr.sin_addr.s_addr >> 8) & 0xff); + a_DstAddr.sin6_addr.s6_addr[14] = static_cast((a_SrcAddr.sin_addr.s_addr >> 16) & 0xff); + a_DstAddr.sin6_addr.s6_addr[15] = static_cast((a_SrcAddr.sin_addr.s_addr >> 24) & 0xff); + a_DstAddr.sin6_port = a_SrcAddr.sin_port; +} + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cUDPSendAfterLookup: + +/** A hostname-to-IP resolver callback that sends the data stored within to the resolved IP address. +This is used for sending UDP datagrams to hostnames, so that the cUDPEndpoint::Send() doesn't block. +Instead an instance of this callback is queued for resolving and the data is sent once the IP is resolved. */ +class cUDPSendAfterLookup: + public cNetwork::cResolveNameCallbacks +{ +public: + cUDPSendAfterLookup(const AString & a_Data, UInt16 a_Port, evutil_socket_t a_MainSock, evutil_socket_t a_SecondSock, bool a_IsMainSockIPv6): + m_Data(a_Data), + m_Port(a_Port), + m_MainSock(a_MainSock), + m_SecondSock(a_SecondSock), + m_IsMainSockIPv6(a_IsMainSockIPv6), + m_HasIPv4(false), + m_HasIPv6(false) + { + } + +protected: + /** The data to send after the hostname is resolved. */ + AString m_Data; + + /** The port to which to send the data. */ + UInt16 m_Port; + + /** The primary socket to use for sending. */ + evutil_socket_t m_MainSock; + + /** The secondary socket to use for sending, if needed by the OS. */ + evutil_socket_t m_SecondSock; + + /** True if m_MainSock is an IPv6 socket. */ + bool m_IsMainSockIPv6; + + /** The IPv4 address resolved, if any. */ + sockaddr_in m_AddrIPv4; + + /** Set to true if the name resolved to an IPv4 address. */ + bool m_HasIPv4; + + /** The IPv6 address resolved, if any. */ + sockaddr_in6 m_AddrIPv6; + + /** Set to true if the name resolved to an IPv6 address. */ + bool m_HasIPv6; + + + // cNetwork::cResolveNameCallbacks overrides: + virtual void OnNameResolved(const AString & a_Name, const AString & a_PI) override + { + // Not needed + } + + virtual bool OnNameResolvedV4(const AString & a_Name, const sockaddr_in * a_IP) override + { + if (!m_HasIPv4) + { + m_AddrIPv4 = *a_IP; + m_AddrIPv4.sin_port = htons(m_Port); + m_HasIPv4 = true; + } + + // Don't want OnNameResolved() callback + return false; + } + + virtual bool OnNameResolvedV6(const AString & a_Name, const sockaddr_in6 * a_IP) override + { + if (!m_HasIPv6) + { + m_AddrIPv6 = *a_IP; + m_AddrIPv6.sin6_port = htons(m_Port); + m_HasIPv6 = true; + } + + // Don't want OnNameResolved() callback + return false; + } + + virtual void OnFinished(void) override + { + // Send the actual data, through the correct socket and using the correct resolved address: + if (m_IsMainSockIPv6) + { + if (m_HasIPv6) + { + sendto(m_MainSock, m_Data.data(), static_cast(m_Data.size()), 0, reinterpret_cast(&m_AddrIPv6), static_cast(sizeof(m_AddrIPv6))); + } + else if (m_HasIPv4) + { + // If the secondary socket is valid, it is an IPv4 socket, so use that: + if (m_SecondSock != -1) + { + sendto(m_SecondSock, m_Data.data(), static_cast(m_Data.size()), 0, reinterpret_cast(&m_AddrIPv4), static_cast(sizeof(m_AddrIPv4))); + } + else + { + // Need an address conversion from IPv4 to IPv6-mapped-IPv4: + ConvertIPv4ToMappedIPv6(m_AddrIPv4, m_AddrIPv6); + sendto(m_MainSock, m_Data.data(), static_cast(m_Data.size()), 0, reinterpret_cast(&m_AddrIPv6), static_cast(sizeof(m_AddrIPv6))); + } + } + else + { + LOGD("UDP endpoint queued sendto: Name not resolved"); + return; + } + } + else // m_IsMainSockIPv6 + { + // Main socket is IPv4 only, only allow IPv4 dst address: + if (!m_HasIPv4) + { + LOGD("UDP endpoint queued sendto: Name not resolved to IPv4 for an IPv4-only socket"); + return; + } + sendto(m_MainSock, m_Data.data(), static_cast(m_Data.size()), 0, reinterpret_cast(&m_AddrIPv4), static_cast(sizeof(m_AddrIPv4))); + } + } + + virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override + { + // Nothing needed + } +}; + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cUDPEndpointImpl: + +cUDPEndpointImpl::cUDPEndpointImpl(UInt16 a_Port, cUDPEndpoint::cCallbacks & a_Callbacks): + super(a_Callbacks), + m_Port(0), + m_MainSock(-1), + m_IsMainSockIPv6(true), + m_SecondarySock(-1), + m_MainEvent(nullptr), + m_SecondaryEvent(nullptr) +{ + Open(a_Port); +} + + + + + +void cUDPEndpointImpl::Close(void) +{ + if (m_Port == 0) + { + // Already closed + return; + } + + // Close the LibEvent handles: + if (m_MainEvent != nullptr) + { + event_free(m_MainEvent); + m_MainEvent = nullptr; + } + if (m_SecondaryEvent != nullptr) + { + event_free(m_SecondaryEvent); + m_SecondaryEvent = nullptr; + } + + // Close the OS sockets: + evutil_closesocket(m_MainSock); + m_MainSock = -1; + evutil_closesocket(m_SecondarySock); + m_SecondarySock = -1; + + // Mark as closed: + m_Port = 0; +} + + + + + +bool cUDPEndpointImpl::IsOpen(void) const +{ + return (m_Port != 0); +} + + + + + +UInt16 cUDPEndpointImpl::GetPort(void) const +{ + return m_Port; +} + + + + + +bool cUDPEndpointImpl::Send(const AString & a_Payload, const AString & a_Host, UInt16 a_Port) +{ + // If a_Host is an IP address, send the data directly: + sockaddr_storage sa; + int salen = static_cast(sizeof(sa)); + memset(&sa, 0, sizeof(sa)); + if (evutil_parse_sockaddr_port(a_Host.c_str(), reinterpret_cast(&sa), &salen) != 0) + { + // a_Host is a hostname, we need to do a lookup first: + auto queue = std::make_shared(a_Payload, a_Port, m_MainSock, m_SecondarySock, m_IsMainSockIPv6); + return cNetwork::HostnameToIP(a_Host, queue); + } + + // a_Host is an IP address and has been parsed into "sa" + // Insert the correct port and send data: + int NumSent; + switch (sa.ss_family) + { + case AF_INET: + { + reinterpret_cast(&sa)->sin_port = htons(a_Port); + if (m_IsMainSockIPv6) + { + if (IsValidSocket(m_SecondarySock)) + { + // The secondary socket, which is always IPv4, is present: + NumSent = static_cast(sendto(m_SecondarySock, a_Payload.data(), static_cast(a_Payload.size()), 0, reinterpret_cast(&sa), static_cast(salen))); + } + else + { + // Need to convert IPv4 to IPv6 address before sending: + sockaddr_in6 IPv6; + ConvertIPv4ToMappedIPv6(*reinterpret_cast(&sa), IPv6); + NumSent = static_cast(sendto(m_MainSock, a_Payload.data(), static_cast(a_Payload.size()), 0, reinterpret_cast(&IPv6), static_cast(sizeof(IPv6)))); + } + } + else + { + NumSent = static_cast(sendto(m_MainSock, a_Payload.data(), static_cast(a_Payload.size()), 0, reinterpret_cast(&sa), static_cast(salen))); + } + break; + } + + case AF_INET6: + { + reinterpret_cast(&sa)->sin6_port = htons(a_Port); + NumSent = static_cast(sendto(m_MainSock, a_Payload.data(), static_cast(a_Payload.size()), 0, reinterpret_cast(&sa), static_cast(salen))); + break; + } + default: + { + LOGD("UDP sendto: Invalid address family for address \"%s\".", a_Host.c_str()); + return false; + } + } + return (NumSent > 0); +} + + + + + +void cUDPEndpointImpl::EnableBroadcasts(void) +{ + ASSERT(IsOpen()); + + // Enable broadcasts on the main socket: + // Some OSes use ints, others use chars, so we try both + int broadcastInt = 1; + char broadcastChar = 1; + // (Note that Windows uses const char * for option values, while Linux uses const void *) + if (setsockopt(m_MainSock, SOL_SOCKET, SO_BROADCAST, reinterpret_cast(&broadcastInt), sizeof(broadcastInt)) == -1) + { + if (setsockopt(m_MainSock, SOL_SOCKET, SO_BROADCAST, &broadcastChar, sizeof(broadcastChar)) == -1) + { + int err = EVUTIL_SOCKET_ERROR(); + LOGWARNING("Cannot enable broadcasts on port %d: %d (%s)", m_Port, err, evutil_socket_error_to_string(err)); + return; + } + + // Enable broadcasts on the secondary socket, if opened (use char, it worked for primary): + if (IsValidSocket(m_SecondarySock)) + { + if (setsockopt(m_SecondarySock, SOL_SOCKET, SO_BROADCAST, &broadcastChar, sizeof(broadcastChar)) == -1) + { + int err = EVUTIL_SOCKET_ERROR(); + LOGWARNING("Cannot enable broadcasts on port %d (secondary): %d (%s)", m_Port, err, evutil_socket_error_to_string(err)); + } + } + return; + } + + // Enable broadcasts on the secondary socket, if opened (use int, it worked for primary): + if (IsValidSocket(m_SecondarySock)) + { + if (setsockopt(m_SecondarySock, SOL_SOCKET, SO_BROADCAST, reinterpret_cast(&broadcastInt), sizeof(broadcastInt)) == -1) + { + int err = EVUTIL_SOCKET_ERROR(); + LOGWARNING("Cannot enable broadcasts on port %d (secondary): %d (%s)", m_Port, err, evutil_socket_error_to_string(err)); + } + } +} + + + + + +void cUDPEndpointImpl::Open(UInt16 a_Port) +{ + ASSERT(m_Port == 0); // Must not be already open + + // Make sure the cNetwork internals are innitialized: + cNetworkSingleton::Get(); + + // Set up the main socket: + // It should listen on IPv6 with IPv4 fallback, when available; IPv4 when IPv6 is not available. + bool NeedsTwoSockets = false; + m_IsMainSockIPv6 = true; + m_MainSock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + + int err; + if (!IsValidSocket(m_MainSock)) + { + // Failed to create IPv6 socket, create an IPv4 one instead: + m_IsMainSockIPv6 = false; + err = EVUTIL_SOCKET_ERROR(); + LOGD("Failed to create IPv6 MainSock: %d (%s)", err, evutil_socket_error_to_string(err)); + m_MainSock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (!IsValidSocket(m_MainSock)) + { + err = EVUTIL_SOCKET_ERROR(); + m_Callbacks.OnError(err, Printf("Cannot create UDP socket for port %d: %s", a_Port, evutil_socket_error_to_string(err))); + return; + } + + // Allow the port to be reused right after the socket closes: + if (evutil_make_listen_socket_reuseable(m_MainSock) != 0) + { + err = EVUTIL_SOCKET_ERROR(); + LOG("UDP Port %d cannot be made reusable: %d (%s). Restarting the server might not work.", + a_Port, err, evutil_socket_error_to_string(err) + ); + } + + // Bind to all interfaces: + sockaddr_in name; + memset(&name, 0, sizeof(name)); + name.sin_family = AF_INET; + name.sin_port = ntohs(a_Port); + if (bind(m_MainSock, reinterpret_cast(&name), sizeof(name)) != 0) + { + err = EVUTIL_SOCKET_ERROR(); + m_Callbacks.OnError(err, Printf("Cannot bind UDP port %d: %s", a_Port, evutil_socket_error_to_string(err))); + evutil_closesocket(m_MainSock); + return; + } + } + else + { + // IPv6 socket created, switch it into "dualstack" mode: + UInt32 Zero = 0; + #ifdef _WIN32 + // WinXP doesn't support this feature, so if the setting fails, create another socket later on: + int res = setsockopt(m_MainSock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast(&Zero), sizeof(Zero)); + err = EVUTIL_SOCKET_ERROR(); + NeedsTwoSockets = ((res == SOCKET_ERROR) && (err == WSAENOPROTOOPT)); + #else + setsockopt(m_MainSock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast(&Zero), sizeof(Zero)); + #endif + + // Allow the port to be reused right after the socket closes: + if (evutil_make_listen_socket_reuseable(m_MainSock) != 0) + { + err = EVUTIL_SOCKET_ERROR(); + LOG("UDP Port %d cannot be made reusable: %d (%s). Restarting the server might not work.", + a_Port, err, evutil_socket_error_to_string(err) + ); + } + + // Bind to all interfaces: + sockaddr_in6 name; + memset(&name, 0, sizeof(name)); + name.sin6_family = AF_INET6; + name.sin6_port = ntohs(a_Port); + if (bind(m_MainSock, reinterpret_cast(&name), sizeof(name)) != 0) + { + err = EVUTIL_SOCKET_ERROR(); + m_Callbacks.OnError(err, Printf("Cannot bind to UDP port %d: %s", a_Port, evutil_socket_error_to_string(err))); + evutil_closesocket(m_MainSock); + return; + } + } + if (evutil_make_socket_nonblocking(m_MainSock) != 0) + { + err = EVUTIL_SOCKET_ERROR(); + m_Callbacks.OnError(err, Printf("Cannot make socket on UDP port %d nonblocking: %s", a_Port, evutil_socket_error_to_string(err))); + evutil_closesocket(m_MainSock); + return; + } + m_MainEvent = event_new(cNetworkSingleton::Get().GetEventBase(), m_MainSock, EV_READ | EV_PERSIST, RawCallback, this); + event_add(m_MainEvent, nullptr); + + // Read the actual port number on which the socket is listening: + { + sockaddr_storage name; + socklen_t namelen = static_cast(sizeof(name)); + getsockname(m_MainSock, reinterpret_cast(&name), &namelen); + switch (name.ss_family) + { + case AF_INET: + { + sockaddr_in * sin = reinterpret_cast(&name); + m_Port = ntohs(sin->sin_port); + break; + } + case AF_INET6: + { + sockaddr_in6 * sin6 = reinterpret_cast(&name); + m_Port = ntohs(sin6->sin6_port); + break; + } + } + } + + // If we don't need to create another socket, bail out now: + if (!NeedsTwoSockets) + { + return; + } + + // If a secondary socket is required (WinXP dual-stack), create it here: + LOGD("Creating a second UDP socket for IPv4"); + m_SecondarySock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + + if (!IsValidSocket(m_SecondarySock)) + { + // Don't report as an error, the primary socket is working + err = EVUTIL_SOCKET_ERROR(); + LOGD("Socket creation failed for secondary UDP socket for port %d: %d, %s", m_Port, err, evutil_socket_error_to_string(err)); + return; + } + + // Allow the port to be reused right after the socket closes: + if (evutil_make_listen_socket_reuseable(m_SecondarySock) != 0) + { + // Don't report as an error, the primary socket is working + err = EVUTIL_SOCKET_ERROR(); + LOGD("UDP Port %d cannot be made reusable (second socket): %d (%s). Restarting the server might not work.", + a_Port, err, evutil_socket_error_to_string(err) + ); + evutil_closesocket(m_SecondarySock); + m_SecondarySock = -1; + return; + } + + // Make the secondary socket nonblocking: + if (evutil_make_socket_nonblocking(m_SecondarySock) != 0) + { + // Don't report as an error, the primary socket is working + err = EVUTIL_SOCKET_ERROR(); + LOGD("evutil_make_socket_nonblocking() failed for secondary UDP socket: %d, %s", err, evutil_socket_error_to_string(err)); + evutil_closesocket(m_SecondarySock); + m_SecondarySock = -1; + return; + } + + // Bind to all IPv4 interfaces: + sockaddr_in name; + memset(&name, 0, sizeof(name)); + name.sin_family = AF_INET; + name.sin_port = ntohs(m_Port); + if (bind(m_SecondarySock, reinterpret_cast(&name), sizeof(name)) != 0) + { + // Don't report as an error, the primary socket is working + err = EVUTIL_SOCKET_ERROR(); + LOGD("Cannot bind secondary socket to UDP port %d: %d (%s)", m_Port, err, evutil_socket_error_to_string(err)); + evutil_closesocket(m_SecondarySock); + m_SecondarySock = -1; + return; + } + + m_SecondaryEvent = event_new(cNetworkSingleton::Get().GetEventBase(), m_SecondarySock, EV_READ | EV_PERSIST, RawCallback, this); + event_add(m_SecondaryEvent, nullptr); +} + + + + + +void cUDPEndpointImpl::RawCallback(evutil_socket_t a_Socket, short a_What, void * a_Self) +{ + cUDPEndpointImpl * Self = reinterpret_cast(a_Self); + Self->Callback(a_Socket, a_What); +} + + + + + +void cUDPEndpointImpl::Callback(evutil_socket_t a_Socket, short a_What) +{ + if ((a_What & EV_READ) != 0) + { + // Receive datagram from the socket: + char buf[64 KiB]; + int buflen = static_cast(sizeof(buf)); + sockaddr_storage sa; + socklen_t salen = static_cast(sizeof(sa)); + int len = recvfrom(a_Socket, buf, buflen, 0, reinterpret_cast(&sa), &salen); + if (len >= 0) + { + // Convert the remote IP address to a string: + char RemoteHost[128]; + UInt16 RemotePort; + switch (sa.ss_family) + { + case AF_INET: + { + auto sin = reinterpret_cast(&sa); + evutil_inet_ntop(sa.ss_family, &sin->sin_addr, RemoteHost, sizeof(RemoteHost)); + RemotePort = ntohs(sin->sin_port); + break; + } + case AF_INET6: + { + auto sin = reinterpret_cast(&sa); + evutil_inet_ntop(sa.ss_family, &sin->sin6_addr, RemoteHost, sizeof(RemoteHost)); + RemotePort = ntohs(sin->sin6_port); + break; + } + default: + { + return; + } + } + + // Call the callback: + m_Callbacks.OnReceivedData(buf, len, RemoteHost, RemotePort); + } + } +} + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cNetwork API: + +cUDPEndpointPtr cNetwork::CreateUDPEndpoint(UInt16 a_Port, cUDPEndpoint::cCallbacks & a_Callbacks) +{ + return std::make_shared(a_Port, a_Callbacks); +} + + + + diff --git a/src/OSSupport/UDPEndpointImpl.h b/src/OSSupport/UDPEndpointImpl.h new file mode 100644 index 000000000..75942b0cf --- /dev/null +++ b/src/OSSupport/UDPEndpointImpl.h @@ -0,0 +1,81 @@ + +// UDPEndpointImpl.h + +// Declares the cUDPEndpointImpl class representing an implementation of an endpoint in UDP communication + + + + + +#pragma once + +#include "Network.h" +#include + + + + + +// fwd: +class cUDPEndpointImpl; +typedef SharedPtr cUDPEndpointImplPtr; + + + + + +class cUDPEndpointImpl: + public cUDPEndpoint +{ + typedef cUDPEndpoint super; + +public: + /** Creates a new instance of the endpoint, with the specified callbacks. + Tries to open on the specified port; if it fails, the endpoint is left in the "closed" state. + If a_Port is 0, the OS is free to assign any port number it likes to the endpoint. */ + cUDPEndpointImpl(UInt16 a_Port, cUDPEndpoint::cCallbacks & a_Callbacks); + + // cUDPEndpoint overrides: + virtual void Close(void) override; + virtual bool IsOpen(void) const override; + virtual UInt16 GetPort(void) const override; + virtual bool Send(const AString & a_Payload, const AString & a_Host, UInt16 a_Port) override; + virtual void EnableBroadcasts(void) override; + +protected: + /** The local port on which the endpoint is open. + If this is zero, it means the endpoint is closed - either opening has failed, or it has been closed explicitly. */ + UInt16 m_Port; + + /** The primary underlying OS socket. */ + evutil_socket_t m_MainSock; + + /** True if m_MainSock is in the IPv6 namespace (needs IPv6 addresses for sending). */ + bool m_IsMainSockIPv6; + + /** The secondary OS socket (if primary doesn't support dualstack). */ + evutil_socket_t m_SecondarySock; + + /** The LibEvent handle for the primary socket. */ + event * m_MainEvent; + + /** The LibEvent handle for the secondary socket. */ + event * m_SecondaryEvent; + + + /** Creates and opens the socket on the specified port. + If a_Port is 0, the OS is free to assign any port number it likes to the endpoint. + If the opening fails, the OnError() callback is called and the endpoint is left "closed" (IsOpen() returns false). */ + void Open(UInt16 a_Port); + + /** The callback that LibEvent calls when an event occurs on one of the sockets. + Calls Callback() on a_Self. */ + static void RawCallback(evutil_socket_t a_Socket, short a_What, void * a_Self); + + /** The callback that is called when an event occurs on one of the sockets. */ + void Callback(evutil_socket_t a_Socket, short a_What); +}; + + + + From e30ee8063d9e7b3471c9bbb197418d792d8fb468 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Fri, 20 Feb 2015 16:05:53 +0100 Subject: [PATCH 44/95] UDPEndpointImpl: Fixed clang warnings. --- src/OSSupport/UDPEndpointImpl.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/OSSupport/UDPEndpointImpl.cpp b/src/OSSupport/UDPEndpointImpl.cpp index c5190bcf6..ece521ab8 100644 --- a/src/OSSupport/UDPEndpointImpl.cpp +++ b/src/OSSupport/UDPEndpointImpl.cpp @@ -554,10 +554,10 @@ void cUDPEndpointImpl::Callback(evutil_socket_t a_Socket, short a_What) { // Receive datagram from the socket: char buf[64 KiB]; - int buflen = static_cast(sizeof(buf)); + socklen_t buflen = static_cast(sizeof(buf)); sockaddr_storage sa; socklen_t salen = static_cast(sizeof(sa)); - int len = recvfrom(a_Socket, buf, buflen, 0, reinterpret_cast(&sa), &salen); + auto len = recvfrom(a_Socket, buf, buflen, 0, reinterpret_cast(&sa), &salen); if (len >= 0) { // Convert the remote IP address to a string: @@ -586,7 +586,7 @@ void cUDPEndpointImpl::Callback(evutil_socket_t a_Socket, short a_What) } // Call the callback: - m_Callbacks.OnReceivedData(buf, len, RemoteHost, RemotePort); + m_Callbacks.OnReceivedData(buf, static_cast(len), RemoteHost, RemotePort); } } } From 591b4eeb8a82d1a15c22394683f151d4186489e4 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Fri, 20 Feb 2015 16:08:21 +0100 Subject: [PATCH 45/95] Network tests: Fixed clang warnings. --- tests/Network/EchoServer.cpp | 2 +- tests/Network/Google.cpp | 2 +- tests/Network/NameLookup.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/Network/EchoServer.cpp b/tests/Network/EchoServer.cpp index 49fb89122..2a420fbac 100644 --- a/tests/Network/EchoServer.cpp +++ b/tests/Network/EchoServer.cpp @@ -99,7 +99,7 @@ class cEchoServerCallbacks: -void DoTest(void) +static void DoTest(void) { LOGD("EchoServer: starting up"); cServerHandlePtr Server = cNetwork::Listen(9876, std::make_shared()); diff --git a/tests/Network/Google.cpp b/tests/Network/Google.cpp index 54bfa6de4..ab366f9e2 100644 --- a/tests/Network/Google.cpp +++ b/tests/Network/Google.cpp @@ -97,7 +97,7 @@ public: -void DoTest(void) +static void DoTest(void) { cEvent evtFinish; diff --git a/tests/Network/NameLookup.cpp b/tests/Network/NameLookup.cpp index 16fa8042b..4781a59ec 100644 --- a/tests/Network/NameLookup.cpp +++ b/tests/Network/NameLookup.cpp @@ -46,7 +46,7 @@ public: -void DoTest(void) +static void DoTest(void) { cEvent evtFinish; From e39d2d4605f5952dcd8dca2188b545680776796f Mon Sep 17 00:00:00 2001 From: Mattes D Date: Fri, 20 Feb 2015 16:14:44 +0100 Subject: [PATCH 46/95] APIDump: Added the UDP zero port policy. --- MCServer/Plugins/APIDump/Classes/Network.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MCServer/Plugins/APIDump/Classes/Network.lua b/MCServer/Plugins/APIDump/Classes/Network.lua index 82a8d52dc..797788661 100644 --- a/MCServer/Plugins/APIDump/Classes/Network.lua +++ b/MCServer/Plugins/APIDump/Classes/Network.lua @@ -288,7 +288,7 @@ g_Server = nil Functions = { Connect = { Params = "Host, Port, LinkCallbacks", Return = "bool", Notes = "(STATIC) Begins establishing a (client) TCP connection to the specified host. Uses the LinkCallbacks table to report progress, success, errors and incoming data. Returns false if it fails immediately (bad port value, bad hostname format), true otherwise. Host can be either an IP address or a hostname." }, - CreateUDPEndpoint = { Params = "Port, UDPCallbacks", Return = "{{cUDPEndpoint|UDPEndpoint}}", Notes = "(STATIC) Creates a UDP endpoint that listens for incoming datagrams on the specified port, and can be used to send or broadcast datagrams. Uses the UDPCallbacks to report incoming datagrams or errors. If the endpoint cannot be created, the OnError callback is called with the error details and the returned endpoint will report IsOpen() == false. The plugin needs to store the returned endpoint object for as long as it needs the UDP port open; if the endpoint is garbage-collected by Lua, the socket will be closed and no more incoming data will be reported." }, + CreateUDPEndpoint = { Params = "Port, UDPCallbacks", Return = "{{cUDPEndpoint|UDPEndpoint}}", Notes = "(STATIC) Creates a UDP endpoint that listens for incoming datagrams on the specified port, and can be used to send or broadcast datagrams. Uses the UDPCallbacks to report incoming datagrams or errors. If the endpoint cannot be created, the OnError callback is called with the error details and the returned endpoint will report IsOpen() == false. The plugin needs to store the returned endpoint object for as long as it needs the UDP port open; if the endpoint is garbage-collected by Lua, the socket will be closed and no more incoming data will be reported.
If the Port is zero, the OS chooses an available UDP port for the endpoint; use {{cUDPEndpoint}}:GetPort() to query the port number in such case." }, HostnameToIP = { Params = "Host, LookupCallbacks", Return = "bool", Notes = "(STATIC) Begins a DNS lookup to find the IP address(es) for the specified host. Uses the LookupCallbacks table to report progress, success or errors. Returns false if it fails immediately (bad hostname format), true if the lookup started successfully. Host can be either a hostname or an IP address." }, IPToHostname = { Params = "Address, LookupCallbacks", Return = "bool", Notes = "(STATIC) Begins a reverse-DNS lookup to find out the hostname for the specified IP address. Uses the LookupCallbacks table to report progress, success or errors. Returns false if it fails immediately (bad address format), true if the lookup started successfully." }, Listen = { Params = "Port, ListenCallbacks", Return = "{{cServerHandle|ServerHandle}}", Notes = "(STATIC) Starts listening on the specified port. Uses the ListenCallbacks to report incoming connections or errors. Returns a {{cServerHandle}} object representing the server. If the listen operation failed, the OnError callback is called with the error details and the returned server handle will report IsListening() == false. The plugin needs to store the server handle object for as long as it needs the server running, if the server handle is garbage-collected by Lua, the listening socket will be closed and all current connections dropped." }, From 22d3a6a47f89ae1bb36f4f5f93c1694658b92bed Mon Sep 17 00:00:00 2001 From: Mattes D Date: Fri, 20 Feb 2015 22:55:19 +0100 Subject: [PATCH 47/95] Fixed monster spawn randomness. Fixes #1699. --- src/MobSpawner.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/MobSpawner.cpp b/src/MobSpawner.cpp index 0a32d17ef..541135996 100644 --- a/src/MobSpawner.cpp +++ b/src/MobSpawner.cpp @@ -110,7 +110,8 @@ eMonsterType cMobSpawner::ChooseMobType(EMCSBiome a_Biome) if (allowedMobsSize > 0) { std::set::iterator itr = allowedMobs.begin(); - int iRandom = m_Random.NextInt((int)allowedMobsSize, a_Biome); + static int Counter = 0; + int iRandom = m_Random.NextInt((int)allowedMobsSize, Counter++); for (int i = 0; i < iRandom; i++) { From b9e4fe0a3b2a6a4d688d1a0807f15944361fb585 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sat, 21 Feb 2015 09:41:14 +0100 Subject: [PATCH 48/95] Added cCryptoHash namespace to Lua API. --- MCServer/Plugins/APIDump/APIDesc.lua | 24 ++++- MCServer/Plugins/Debuggers/Debuggers.lua | 60 ++++++++++--- MCServer/Plugins/Debuggers/Info.lua | 16 +++- src/Bindings/ManualBindings.cpp | 109 ++++++++++++++++++++++- 4 files changed, 190 insertions(+), 19 deletions(-) diff --git a/MCServer/Plugins/APIDump/APIDesc.lua b/MCServer/Plugins/APIDump/APIDesc.lua index 4c8dbd1e9..61fc69d32 100644 --- a/MCServer/Plugins/APIDump/APIDesc.lua +++ b/MCServer/Plugins/APIDump/APIDesc.lua @@ -685,6 +685,28 @@ end }, }, -- cCraftingRecipe + cCryptoHash = + { + Desc = + [[ + Provides functions for generating cryptographic hashes.

+

+ Note that all functions in this class are static, so they should be called in the dot convention: +

+local Hash = cCryptoHash.sha1HexString("DataToHash")
+

+

Each cryptographic hash has two variants, one returns the hash as a raw binary string, the other returns the hash as a hex-encoded string twice as long as the binary string. + ]], + + Functions = + { + md5 = { Params = "Data", Return = "string", Notes = "(STATIC) Calculates the md5 hash of the data, returns it as a raw (binary) string of 16 characters." }, + md5HexString = { Params = "Data", Return = "string", Notes = "(STATIC) Calculates the md5 hash of the data, returns it as a hex-encoded string of 32 characters." }, + sha1 = { Params = "Data", Return = "string", Notes = "(STATIC) Calculates the sha1 hash of the data, returns it as a raw (binary) string of 20 characters." }, + sha1HexString = { Params = "Data", Return = "string", Notes = "(STATIC) Calculates the sha1 hash of the data, returns it as a hex-encoded string of 40 characters." }, + }, + }, -- cCryptoHash + cEnchantments = { Desc = [[ @@ -2929,7 +2951,7 @@ end StringToMobType = {Params = "string", Return = "{{Globals#MobType|MobType}}", Notes = "DEPRECATED! Please use cMonster:StringToMobType(). Converts a string representation to a {{Globals#MobType|MobType}} enumerated value"}, StripColorCodes = {Params = "string", Return = "string", Notes = "Removes all control codes used by MC for colors and styles"}, TrimString = {Params = "string", Return = "string", Notes = "Trims whitespace at both ends of the string"}, - md5 = {Params = "string", Return = "string", Notes = "converts a string to an md5 hash"}, + md5 = {Params = "string", Return = "string", Notes = "OBSOLETE, use the {{cCryptoHash}} functions instead.
Converts a string to a raw binary md5 hash."}, }, ConstantGroups = { diff --git a/MCServer/Plugins/Debuggers/Debuggers.lua b/MCServer/Plugins/Debuggers/Debuggers.lua index a46072324..a28ffa552 100644 --- a/MCServer/Plugins/Debuggers/Debuggers.lua +++ b/MCServer/Plugins/Debuggers/Debuggers.lua @@ -1476,7 +1476,7 @@ function HandleWESel(a_Split, a_Player) SelCuboid:Expand(NumBlocks, NumBlocks, 0, 0, NumBlocks, NumBlocks) -- Set the selection: - local IsSuccess = cPluginManager:CallPlugin("WorldEdit", "SetPlayerCuboidSelection", a_Player, SelCuboid) + IsSuccess = cPluginManager:CallPlugin("WorldEdit", "SetPlayerCuboidSelection", a_Player, SelCuboid) if not(IsSuccess) then a_Player:SendMessage(cCompositeChat():SetMessageType(mtFailure):AddTextPart("Cannot adjust selection, WorldEdit reported failure while setting new selection")) return true @@ -1606,17 +1606,36 @@ end -function HandleConsoleSchedule(a_Split) - local prev = os.clock() - LOG("Scheduling a task for 2 seconds in the future (current os.clock is " .. prev .. ")") - cRoot:Get():GetDefaultWorld():ScheduleTask(40, - function () - local current = os.clock() - local diff = current - prev - LOG("Scheduled function is called. Current os.clock is " .. current .. ", difference is " .. diff .. ")") - end - ) - return true, "Task scheduled" +-- List of hashing functions to test: +local HashFunctions = +{ + {"md5", md5 }, + {"cCryptoHash.md5", cCryptoHash.md5 }, + {"cCryptoHash.md5HexString", cCryptoHash.md5HexString }, + {"cCryptoHash.sha1", cCryptoHash.sha1 }, + {"cCryptoHash.sha1HexString", cCryptoHash.sha1HexString }, +} + +-- List of strings to try hashing: +local HashExamples = +{ + "", + "\0", + "test", +} + +function HandleConsoleHash(a_Split) + for _, str in ipairs(HashExamples) do + LOG("Hashing string \"" .. str .. "\":") + for _, hash in ipairs(HashFunctions) do + if not(hash[2]) then + LOG("Hash function " .. hash[1] .. " doesn't exist in the API!") + else + LOG(hash[1] .. "() = " .. hash[2](str)) + end + end -- for hash - HashFunctions[] + end -- for str - HashExamples[] + return true end @@ -1704,3 +1723,20 @@ end + +function HandleConsoleSchedule(a_Split) + local prev = os.clock() + LOG("Scheduling a task for 2 seconds in the future (current os.clock is " .. prev .. ")") + cRoot:Get():GetDefaultWorld():ScheduleTask(40, + function () + local current = os.clock() + local diff = current - prev + LOG("Scheduled function is called. Current os.clock is " .. current .. ", difference is " .. diff .. ")") + end + ) + return true, "Task scheduled" +end + + + + diff --git a/MCServer/Plugins/Debuggers/Info.lua b/MCServer/Plugins/Debuggers/Info.lua index b96ef3de5..63a4b9177 100644 --- a/MCServer/Plugins/Debuggers/Info.lua +++ b/MCServer/Plugins/Debuggers/Info.lua @@ -200,21 +200,29 @@ g_PluginInfo = ConsoleCommands = { - ["sched"] = + ["hash"] = { - Handler = HandleConsoleSchedule, - HelpString = "Tests the world scheduling", + Handler = HandleConsoleHash, + HelpString = "Tests the crypto hashing functions", }, + ["loadchunk"] = { Handler = HandleConsoleLoadChunk, HelpString = "Loads the specified chunk into memory", }, + ["preparechunk"] = { Handler = HandleConsolePrepareChunk, HelpString = "Prepares the specified chunk completely (load / gen / light)", - } + }, + + ["sched"] = + { + Handler = HandleConsoleSchedule, + HelpString = "Tests the world scheduling", + }, }, -- ConsoleCommands } -- g_PluginInfo diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp index 30bce6525..69d16ac2b 100644 --- a/src/Bindings/ManualBindings.cpp +++ b/src/Bindings/ManualBindings.cpp @@ -3,8 +3,11 @@ #include "ManualBindings.h" #undef TOLUA_TEMPLATE_BIND +#include +#include #include "tolua++/include/tolua++.h" #include "polarssl/md5.h" +#include "polarssl/sha1.h" #include "PluginLua.h" #include "PluginManager.h" #include "LuaWindow.h" @@ -2203,11 +2206,16 @@ static int tolua_cPlugin_Call(lua_State * tolua_S) -static int tolua_md5(lua_State* tolua_S) +static int tolua_md5(lua_State * tolua_S) { + // Calculate the raw md5 checksum byte array: unsigned char Output[16]; size_t len = 0; const unsigned char * SourceString = (const unsigned char *)lua_tolstring(tolua_S, 1, &len); + if (SourceString == nullptr) + { + return 0; + } md5(SourceString, len, Output); lua_pushlstring(tolua_S, (const char *)Output, ARRAYCOUNT(Output)); return 1; @@ -2217,6 +2225,91 @@ static int tolua_md5(lua_State* tolua_S) +/** Does the same as tolua_md5, but reports that the usage is obsolete and the plugin should use cCrypto.md5(). */ +static int tolua_md5_obsolete(lua_State * tolua_S) +{ + LOGWARNING("Using md5() is obsolete, please change your plugin to use cCryptoHash.md5()"); + cLuaState::LogStackTrace(tolua_S); + return tolua_md5(tolua_S); +} + + + + + +static int tolua_md5HexString(lua_State * tolua_S) +{ + // Calculate the raw md5 checksum byte array: + unsigned char md5Output[16]; + size_t len = 0; + const unsigned char * SourceString = (const unsigned char *)lua_tolstring(tolua_S, 1, &len); + if (SourceString == nullptr) + { + return 0; + } + md5(SourceString, len, md5Output); + + // Convert the md5 checksum to hex string: + std::stringstream Output; + Output << std::hex << std::setw(2) << std::setfill('0'); + for (size_t i = 0; i < ARRAYCOUNT(md5Output); i++) + { + Output << static_cast(md5Output[i]); // Need to cast to a number, otherwise a char is output + } + lua_pushlstring(tolua_S, Output.str().c_str(), Output.str().size()); + return 1; +} + + + + + +static int tolua_sha1(lua_State * tolua_S) +{ + // Calculate the raw SHA1 checksum byte array from the input string: + unsigned char Output[20]; + size_t len = 0; + const unsigned char * SourceString = (const unsigned char *)lua_tolstring(tolua_S, 1, &len); + if (SourceString == nullptr) + { + return 0; + } + sha1(SourceString, len, Output); + lua_pushlstring(tolua_S, (const char *)Output, ARRAYCOUNT(Output)); + return 1; +} + + + + + +static int tolua_sha1HexString(lua_State * tolua_S) +{ + // Calculate the raw SHA1 checksum byte array from the input string: + unsigned char sha1Output[20]; + size_t len = 0; + const unsigned char * SourceString = (const unsigned char *)lua_tolstring(tolua_S, 1, &len); + if (SourceString == nullptr) + { + return 0; + } + sha1(SourceString, len, sha1Output); + + // Convert the sha1 checksum to hex string: + std::stringstream Output; + Output << std::hex << std::setw(2) << std::setfill('0'); + for (size_t i = 0; i < ARRAYCOUNT(sha1Output); i++) + { + Output << static_cast(sha1Output[i]); // Need to cast to a number, otherwise a char is output + } + lua_pushlstring(tolua_S, Output.str().c_str(), Output.str().size()); + return 1; +} + + + + + static int tolua_push_StringStringMap(lua_State* tolua_S, std::map< std::string, std::string >& a_StringStringMap) { lua_newtable(tolua_S); @@ -3419,6 +3512,12 @@ static int tolua_cCompositeChat_UnderlineUrls(lua_State * tolua_S) void ManualBindings::Bind(lua_State * tolua_S) { tolua_beginmodule(tolua_S, nullptr); + + // Create the new classes: + tolua_usertype(tolua_S, "cCryptoHash"); + tolua_cclass(tolua_S, "cCryptoHash", "cCryptoHash", "", nullptr); + + // Globals: tolua_function(tolua_S, "Clamp", tolua_Clamp); tolua_function(tolua_S, "StringSplit", tolua_StringSplit); tolua_function(tolua_S, "StringSplitAndTrim", tolua_StringSplitAndTrim); @@ -3429,6 +3528,7 @@ void ManualBindings::Bind(lua_State * tolua_S) tolua_function(tolua_S, "LOGERROR", tolua_LOGERROR); tolua_function(tolua_S, "Base64Encode", tolua_Base64Encode); tolua_function(tolua_S, "Base64Decode", tolua_Base64Decode); + tolua_function(tolua_S, "md5", tolua_md5_obsolete); // OBSOLETE, use cCryptoHash.md5() instead tolua_beginmodule(tolua_S, "cFile"); tolua_function(tolua_S, "GetFolderContents", tolua_cFile_GetFolderContents); @@ -3585,7 +3685,12 @@ void ManualBindings::Bind(lua_State * tolua_S) tolua_function(tolua_S, "GetSlotCoords", Lua_ItemGrid_GetSlotCoords); tolua_endmodule(tolua_S); - tolua_function(tolua_S, "md5", tolua_md5); + tolua_beginmodule(tolua_S, "cCryptoHash"); + tolua_function(tolua_S, "md5", tolua_md5); + tolua_function(tolua_S, "md5HexString", tolua_md5HexString); + tolua_function(tolua_S, "sha1", tolua_sha1); + tolua_function(tolua_S, "sha1HexString", tolua_sha1HexString); + tolua_endmodule(tolua_S); BindRankManager(tolua_S); BindNetwork(tolua_S); From b165ab63633cd8c45d505a2fd2165807ea2b50ec Mon Sep 17 00:00:00 2001 From: Freddie Wang Date: Wed, 4 Feb 2015 23:10:46 -0800 Subject: [PATCH 49/95] Fix door placement check --- src/Items/ItemDoor.h | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/Items/ItemDoor.h b/src/Items/ItemDoor.h index dacf286e5..6d8d97f6f 100644 --- a/src/Items/ItemDoor.h +++ b/src/Items/ItemDoor.h @@ -62,10 +62,10 @@ public: return false; } } - + // Check the two blocks that will get replaced by the door: - BLOCKTYPE LowerBlockType = a_World.GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ); - BLOCKTYPE UpperBlockType = a_World.GetBlock(a_BlockX, a_BlockY + 2, a_BlockZ); + BLOCKTYPE LowerBlockType = a_World.GetBlock(a_BlockX, a_BlockY, a_BlockZ); + BLOCKTYPE UpperBlockType = a_World.GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ); if ( !cBlockDoorHandler::CanReplaceBlock(LowerBlockType) || !cBlockDoorHandler::CanReplaceBlock(UpperBlockType)) @@ -106,7 +106,3 @@ public: return true; } } ; - - - - From 1bcc4abd68ad7cff943b1aa785f9d7bcb62c6b5d Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sat, 21 Feb 2015 14:23:37 +0100 Subject: [PATCH 50/95] Door handler: Removed needless check. The Y coord has already been checked above. --- src/Items/ItemDoor.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Items/ItemDoor.h b/src/Items/ItemDoor.h index 6d8d97f6f..18289be03 100644 --- a/src/Items/ItemDoor.h +++ b/src/Items/ItemDoor.h @@ -40,7 +40,7 @@ public: } // The door needs a compatible block below it: - if ((a_BlockY > 0) && !cBlockDoorHandler::CanBeOn(a_World.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ))) + if (!cBlockDoorHandler::CanBeOn(a_World.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ))) { return false; } From 0ee5fff6494f398eb72f4f00f6b34b17c4d93885 Mon Sep 17 00:00:00 2001 From: Samvbnm Date: Sat, 21 Feb 2015 17:32:47 -0400 Subject: [PATCH 51/95] Update COMPILING.md Tell the reader to make sure they have the latest buildtools installed. --- COMPILING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/COMPILING.md b/COMPILING.md index f8b292ebc..0ac4728fa 100644 --- a/COMPILING.md +++ b/COMPILING.md @@ -71,7 +71,7 @@ After doing so, run the command `xcodebuild lib/polarssl/POLARSSL.xcodeproj` in Install git, make, cmake and gcc or clang, using your platform's package manager: ``` -sudo apt-get install git make cmake gcc g++ +sudo apt-get install git make cmake gcc g++ (Ensure you have the latest versions installed to avoid problems) ``` ### Getting the sources ### From 5d77a3ba6d5278a59e3404b63fdcb7cd652472f5 Mon Sep 17 00:00:00 2001 From: Samvbnm Date: Sat, 21 Feb 2015 18:27:55 -0400 Subject: [PATCH 52/95] Update COMPILING.md --- COMPILING.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/COMPILING.md b/COMPILING.md index 0ac4728fa..bbe1586f1 100644 --- a/COMPILING.md +++ b/COMPILING.md @@ -71,8 +71,9 @@ After doing so, run the command `xcodebuild lib/polarssl/POLARSSL.xcodeproj` in Install git, make, cmake and gcc or clang, using your platform's package manager: ``` -sudo apt-get install git make cmake gcc g++ (Ensure you have the latest versions installed to avoid problems) +sudo apt-get install git make cmake gcc g++ ``` +(Ensure you have the latest versions installed to avoid errors during compilation) ### Getting the sources ### ``` From b958848e7feb6dd20ba3f87a3172fecc78922ee3 Mon Sep 17 00:00:00 2001 From: Samvbnm Date: Sun, 22 Feb 2015 12:17:11 -0400 Subject: [PATCH 53/95] Update COMPILING.md Changed to include specific version requirement --- COMPILING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/COMPILING.md b/COMPILING.md index bbe1586f1..89f88bbb5 100644 --- a/COMPILING.md +++ b/COMPILING.md @@ -73,7 +73,7 @@ Install git, make, cmake and gcc or clang, using your platform's package manager ``` sudo apt-get install git make cmake gcc g++ ``` -(Ensure you have the latest versions installed to avoid errors during compilation) +(Ensure you have gcc/g++ 4.8 or higher installed to avoid errors during compilation) ### Getting the sources ### ``` From 13f81a051d948fd5acb278c5d8679efa5fb3297c Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Sun, 22 Feb 2015 17:34:20 +0100 Subject: [PATCH 54/95] Exported CompressString and UncompressString to Lua --- src/Bindings/ManualBindings.cpp | 37 +++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp index 69d16ac2b..ee3d81014 100644 --- a/src/Bindings/ManualBindings.cpp +++ b/src/Bindings/ManualBindings.cpp @@ -31,6 +31,7 @@ #include "../LineBlockTracer.h" #include "../WorldStorage/SchematicFileSerializer.h" #include "../CompositeChat.h" +#include "../StringCompression.h" @@ -110,6 +111,40 @@ static int tolua_Clamp(lua_State * tolua_S) +static int tolua_CompressString(lua_State * tolua_S) +{ + cLuaState LuaState(tolua_S); + const char * ToCompress = tolua_tocppstring(LuaState, 1, 0); + int Length = (int)tolua_tonumber(LuaState, 2, 0); + int Factor = (int)tolua_tonumber(LuaState, 3, 0); + AString res; + + CompressString(ToCompress, Length, res, Factor); + LuaState.Push(res); + return 1; +} + + + + + +static int tolua_UncompressString(lua_State * tolua_S) +{ + cLuaState LuaState(tolua_S); + const char * ToUncompress = tolua_tocppstring(LuaState, 1, 0); + int Length = (int)tolua_tonumber(LuaState, 2, 0); + int UncompressedSize = (int)tolua_tonumber(LuaState, 3, 0); + AString res; + + UncompressString(ToUncompress, Length, res, UncompressedSize); + LuaState.Push(res); + return 1; +} + + + + + static int tolua_StringSplit(lua_State * tolua_S) { cLuaState LuaState(tolua_S); @@ -3519,6 +3554,8 @@ void ManualBindings::Bind(lua_State * tolua_S) // Globals: tolua_function(tolua_S, "Clamp", tolua_Clamp); + tolua_function(tolua_S, "CompressString", tolua_CompressString); + tolua_function(tolua_S, "UncompressString", tolua_UncompressString); tolua_function(tolua_S, "StringSplit", tolua_StringSplit); tolua_function(tolua_S, "StringSplitAndTrim", tolua_StringSplitAndTrim); tolua_function(tolua_S, "LOG", tolua_LOG); From f073636805682ff5018f876ff3ae4a0fa34cdff8 Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Sun, 22 Feb 2015 17:40:44 +0100 Subject: [PATCH 55/95] Documented CompressString and UncompressString --- MCServer/Plugins/APIDump/APIDesc.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MCServer/Plugins/APIDump/APIDesc.lua b/MCServer/Plugins/APIDump/APIDesc.lua index 61fc69d32..02ac537ee 100644 --- a/MCServer/Plugins/APIDump/APIDesc.lua +++ b/MCServer/Plugins/APIDump/APIDesc.lua @@ -2900,6 +2900,7 @@ end BlockStringToType = {Params = "BlockTypeString", Return = "BLOCKTYPE", Notes = "Returns the block type parsed from the given string"}, Clamp = {Params = "Number, Min, Max", Return = "number", Notes = "Clamp the number to the specified range."}, ClickActionToString = {Params = "{{Globals#ClickAction|ClickAction}}", Return = "string", Notes = "Returns a string description of the ClickAction enumerated value"}, + CompressString = {Params = "string, length, factor", Return = "string", Notes = "Compresses a string using ZLIB"}, DamageTypeToString = {Params = "{{Globals#DamageType|DamageType}}", Return = "string", Notes = "Converts the {{Globals#DamageType|DamageType}} enumerated value to a string representation "}, EscapeString = {Params = "string", Return = "string", Notes = "Returns a copy of the string with all quotes and backslashes escaped by a backslash"}, GetChar = {Params = "String, Pos", Return = "string", Notes = "Returns one character from the string, specified by position "}, @@ -2951,6 +2952,7 @@ end StringToMobType = {Params = "string", Return = "{{Globals#MobType|MobType}}", Notes = "DEPRECATED! Please use cMonster:StringToMobType(). Converts a string representation to a {{Globals#MobType|MobType}} enumerated value"}, StripColorCodes = {Params = "string", Return = "string", Notes = "Removes all control codes used by MC for colors and styles"}, TrimString = {Params = "string", Return = "string", Notes = "Trims whitespace at both ends of the string"}, + UncompressString = {Params = "string, length, uncompressed length", Return = "string", Notes = "Uncompresses a string using ZLIB"}, md5 = {Params = "string", Return = "string", Notes = "OBSOLETE, use the {{cCryptoHash}} functions instead.
Converts a string to a raw binary md5 hash."}, }, ConstantGroups = From b474b9fb5d95f3c511ce0f01f4a6c3e205f13629 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sun, 22 Feb 2015 19:05:43 +0100 Subject: [PATCH 56/95] Fixed race condition for TCP link deleting. This could have caused crashes when a client disconnected from the server. --- src/ClientHandle.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index c774a92c2..2e0e86653 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -1892,9 +1892,13 @@ void cClientHandle::Tick(float a_Dt) cCSLock Lock(m_CSOutgoingData); std::swap(OutgoingData, m_OutgoingData); } - if ((m_Link != nullptr) && !OutgoingData.empty()) + if (!OutgoingData.empty()) { - m_Link->Send(OutgoingData.data(), OutgoingData.size()); + cTCPLinkPtr Link(m_Link); // Grab a copy of the link in a multithread-safe way + if ((Link != nullptr)) + { + Link->Send(OutgoingData.data(), OutgoingData.size()); + } } m_TicksSinceLastPacket += 1; From 54410bfe4dac8793d6b46df725c400f7ce8d365e Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Mon, 23 Feb 2015 12:53:02 +0100 Subject: [PATCH 57/95] Exported all compression functions in a new class. --- src/Bindings/ManualBindings.cpp | 149 ++++++++++++++++++++++++++++---- 1 file changed, 131 insertions(+), 18 deletions(-) diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp index ee3d81014..451161d87 100644 --- a/src/Bindings/ManualBindings.cpp +++ b/src/Bindings/ManualBindings.cpp @@ -111,16 +111,30 @@ static int tolua_Clamp(lua_State * tolua_S) -static int tolua_CompressString(lua_State * tolua_S) +static int tolua_CompressStringZLIB(lua_State * tolua_S) { - cLuaState LuaState(tolua_S); - const char * ToCompress = tolua_tocppstring(LuaState, 1, 0); - int Length = (int)tolua_tonumber(LuaState, 2, 0); - int Factor = (int)tolua_tonumber(LuaState, 3, 0); - AString res; + cLuaState S(tolua_S); + if ( + !lua_isstring(tolua_S, 1) || + ( + !lua_isnumber(tolua_S, 2) && + !lua_isnil(tolua_S, 2) + ) + ) + { + cLuaState::LogStackTrace(tolua_S); + return 0; + } - CompressString(ToCompress, Length, res, Factor); - LuaState.Push(res); + // Get the params: + AString ToCompress; + int CompressionLevel = 5; + S.GetStackValues(1, ToCompress, CompressionLevel); + + // Compress the string: + AString res; + CompressString(ToCompress.data(), ToCompress.size(), res, CompressionLevel); + S.Push(res); return 1; } @@ -128,16 +142,107 @@ static int tolua_CompressString(lua_State * tolua_S) -static int tolua_UncompressString(lua_State * tolua_S) +static int tolua_UncompressStringZLIB(lua_State * tolua_S) { - cLuaState LuaState(tolua_S); - const char * ToUncompress = tolua_tocppstring(LuaState, 1, 0); - int Length = (int)tolua_tonumber(LuaState, 2, 0); - int UncompressedSize = (int)tolua_tonumber(LuaState, 3, 0); - AString res; + cLuaState S(tolua_S); + if ( + !lua_isstring(tolua_S, 1) || + !lua_isnumber(tolua_S, 2) + ) + { + cLuaState::LogStackTrace(tolua_S); + return 0; + } - UncompressString(ToUncompress, Length, res, UncompressedSize); - LuaState.Push(res); + // Get the params: + AString ToUncompress; + int UncompressedSize; + S.GetStackValues(1, ToUncompress, UncompressedSize); + + // Compress the string: + AString res; + UncompressString(ToUncompress.data(), ToUncompress.size(), res, UncompressedSize); + S.Push(res); + return 1; +} + + + + + +static int tolua_CompressStringGZIP(lua_State * tolua_S) +{ + cLuaState S(tolua_S); + if (!lua_isstring(tolua_S, 1)) + { + cLuaState::LogStackTrace(tolua_S); + return 0; + } + + // Get the params: + AString ToCompress; + S.GetStackValues(1, ToCompress); + + // Compress the string: + AString res; + CompressStringGZIP(ToCompress.data(), ToCompress.size(), res); + S.Push(res); + return 1; +} + + + + + +static int tolua_UncompressStringGZIP(lua_State * tolua_S) +{ + cLuaState S(tolua_S); + if ( + !lua_isstring(tolua_S, 1) + ) + { + cLuaState::LogStackTrace(tolua_S); + return 0; + } + + + + // Get the params: + AString ToUncompress; + S.GetStackValues(1, ToUncompress); + + // Compress the string: + AString res; + UncompressStringGZIP(ToUncompress.data(), ToUncompress.size(), res); + S.Push(res); + return 1; +} + + + + + +static int tolua_InflateString(lua_State * tolua_S) +{ + cLuaState S(tolua_S); + if ( + !lua_isstring(tolua_S, 1) + ) + { + cLuaState::LogStackTrace(tolua_S); + return 0; + } + + + + // Get the params: + AString ToUncompress; + S.GetStackValues(1, ToUncompress); + + // Compress the string: + AString res; + InflateString(ToUncompress.data(), ToUncompress.size(), res); + S.Push(res); return 1; } @@ -3551,11 +3656,11 @@ void ManualBindings::Bind(lua_State * tolua_S) // Create the new classes: tolua_usertype(tolua_S, "cCryptoHash"); tolua_cclass(tolua_S, "cCryptoHash", "cCryptoHash", "", nullptr); + tolua_usertype(tolua_S, "cStringCompression"); + tolua_cclass(tolua_S, "cStringCompression", "cStringCompression", "", nullptr); // Globals: tolua_function(tolua_S, "Clamp", tolua_Clamp); - tolua_function(tolua_S, "CompressString", tolua_CompressString); - tolua_function(tolua_S, "UncompressString", tolua_UncompressString); tolua_function(tolua_S, "StringSplit", tolua_StringSplit); tolua_function(tolua_S, "StringSplitAndTrim", tolua_StringSplitAndTrim); tolua_function(tolua_S, "LOG", tolua_LOG); @@ -3729,6 +3834,14 @@ void ManualBindings::Bind(lua_State * tolua_S) tolua_function(tolua_S, "sha1HexString", tolua_sha1HexString); tolua_endmodule(tolua_S); + tolua_beginmodule(tolua_S, "cStringCompression"); + tolua_function(tolua_S, "CompressStringZLIB", tolua_CompressStringZLIB); + tolua_function(tolua_S, "UncompressStringZLIB", tolua_UncompressStringZLIB); + tolua_function(tolua_S, "CompressStringGZIP", tolua_CompressStringGZIP); + tolua_function(tolua_S, "UncompressStringGZIP", tolua_UncompressStringGZIP); + tolua_function(tolua_S, "InflateString", tolua_InflateString); + tolua_endmodule(tolua_S); + BindRankManager(tolua_S); BindNetwork(tolua_S); From 174f5080210aafb5856813c5e178aa7347b8ef8d Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Mon, 23 Feb 2015 12:53:34 +0100 Subject: [PATCH 58/95] Documented cStringCompression --- MCServer/Plugins/APIDump/APIDesc.lua | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/MCServer/Plugins/APIDump/APIDesc.lua b/MCServer/Plugins/APIDump/APIDesc.lua index 02ac537ee..c214434b5 100644 --- a/MCServer/Plugins/APIDump/APIDesc.lua +++ b/MCServer/Plugins/APIDump/APIDesc.lua @@ -2254,6 +2254,27 @@ end ShouldAuthenticate = { Params = "", Return = "bool", Notes = "Returns true iff the server is set to authenticate players (\"online mode\")." }, }, }, -- cServer + + cStringCompression = + { + Desc = [[ + Provides functions to compress or decompress string +

+ All functions in this class are static, so they should be called in the dot convention: +

+local CompressedString = cStringCompression.CompressStringGZIP("DataToCompress")
+
+ ]], + + Functions = + { + CompressStringGZIP = {Params = "string", Return = "string", Notes = "Compress a string using GZIP"}, + CompressStringZLIB = {Params = "string, factor", Return = "string", Notes = "Compresses a string using ZLIB"}, + InflateString = {Params = "string", Return = "string", Notes = "Uncompresses a string using Inflate"}, + UncompressStringGZIP = {Params = "string", Return = "string", Notes = "Uncompress a string using GZIP"}, + UncompressStringZLIB = {Params = "string, uncompressed length", Return = "string", Notes = "Uncompresses a string using ZLIB"}, + }, + }, cTeam = { @@ -2900,7 +2921,6 @@ end BlockStringToType = {Params = "BlockTypeString", Return = "BLOCKTYPE", Notes = "Returns the block type parsed from the given string"}, Clamp = {Params = "Number, Min, Max", Return = "number", Notes = "Clamp the number to the specified range."}, ClickActionToString = {Params = "{{Globals#ClickAction|ClickAction}}", Return = "string", Notes = "Returns a string description of the ClickAction enumerated value"}, - CompressString = {Params = "string, length, factor", Return = "string", Notes = "Compresses a string using ZLIB"}, DamageTypeToString = {Params = "{{Globals#DamageType|DamageType}}", Return = "string", Notes = "Converts the {{Globals#DamageType|DamageType}} enumerated value to a string representation "}, EscapeString = {Params = "string", Return = "string", Notes = "Returns a copy of the string with all quotes and backslashes escaped by a backslash"}, GetChar = {Params = "String, Pos", Return = "string", Notes = "Returns one character from the string, specified by position "}, @@ -2952,7 +2972,6 @@ end StringToMobType = {Params = "string", Return = "{{Globals#MobType|MobType}}", Notes = "DEPRECATED! Please use cMonster:StringToMobType(). Converts a string representation to a {{Globals#MobType|MobType}} enumerated value"}, StripColorCodes = {Params = "string", Return = "string", Notes = "Removes all control codes used by MC for colors and styles"}, TrimString = {Params = "string", Return = "string", Notes = "Trims whitespace at both ends of the string"}, - UncompressString = {Params = "string, length, uncompressed length", Return = "string", Notes = "Uncompresses a string using ZLIB"}, md5 = {Params = "string", Return = "string", Notes = "OBSOLETE, use the {{cCryptoHash}} functions instead.
Converts a string to a raw binary md5 hash."}, }, ConstantGroups = From 8c8ec1094d8ba6df410f1fb0d0fba216b98a8f9a Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Mon, 23 Feb 2015 15:29:07 +0100 Subject: [PATCH 59/95] Replaced lua_isXYZ with cLuaState::CheckParamXYZ --- src/Bindings/ManualBindings.cpp | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp index 451161d87..7255f73a4 100644 --- a/src/Bindings/ManualBindings.cpp +++ b/src/Bindings/ManualBindings.cpp @@ -115,10 +115,10 @@ static int tolua_CompressStringZLIB(lua_State * tolua_S) { cLuaState S(tolua_S); if ( - !lua_isstring(tolua_S, 1) || + !S.CheckParamString(1) && ( - !lua_isnumber(tolua_S, 2) && - !lua_isnil(tolua_S, 2) + !S.CheckParamNumber(2) || + !S.CheckParamEnd(2) ) ) { @@ -146,8 +146,8 @@ static int tolua_UncompressStringZLIB(lua_State * tolua_S) { cLuaState S(tolua_S); if ( - !lua_isstring(tolua_S, 1) || - !lua_isnumber(tolua_S, 2) + !S.CheckParamString(1) && + !S.CheckParamNumber(2) ) { cLuaState::LogStackTrace(tolua_S); @@ -173,7 +173,10 @@ static int tolua_UncompressStringZLIB(lua_State * tolua_S) static int tolua_CompressStringGZIP(lua_State * tolua_S) { cLuaState S(tolua_S); - if (!lua_isstring(tolua_S, 1)) + if ( + !S.CheckParamString(1) && + !S.CheckParamEnd(2) + ) { cLuaState::LogStackTrace(tolua_S); return 0; @@ -198,15 +201,14 @@ static int tolua_UncompressStringGZIP(lua_State * tolua_S) { cLuaState S(tolua_S); if ( - !lua_isstring(tolua_S, 1) + !S.CheckParamString(1) && + !S.CheckParamEnd(2) ) { cLuaState::LogStackTrace(tolua_S); return 0; } - - // Get the params: AString ToUncompress; S.GetStackValues(1, ToUncompress); @@ -226,15 +228,14 @@ static int tolua_InflateString(lua_State * tolua_S) { cLuaState S(tolua_S); if ( - !lua_isstring(tolua_S, 1) + !S.CheckParamString(1) && + !S.CheckParamEnd(2) ) { cLuaState::LogStackTrace(tolua_S); return 0; } - - // Get the params: AString ToUncompress; S.GetStackValues(1, ToUncompress); From d39d2ca5e9592760246b11bdb0336e067ed8ee8a Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Mon, 23 Feb 2015 15:40:31 +0100 Subject: [PATCH 60/95] Added forgotten indent --- src/Bindings/ManualBindings.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp index 7255f73a4..2e9d49ef0 100644 --- a/src/Bindings/ManualBindings.cpp +++ b/src/Bindings/ManualBindings.cpp @@ -117,8 +117,8 @@ static int tolua_CompressStringZLIB(lua_State * tolua_S) if ( !S.CheckParamString(1) && ( - !S.CheckParamNumber(2) || - !S.CheckParamEnd(2) + !S.CheckParamNumber(2) || + !S.CheckParamEnd(2) ) ) { From 9e1db16ba497fd527b95fc00c69229de3957876d Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Mon, 23 Feb 2015 16:09:35 +0100 Subject: [PATCH 61/95] Fixed operators --- src/Bindings/ManualBindings.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp index 2e9d49ef0..a6ae4869b 100644 --- a/src/Bindings/ManualBindings.cpp +++ b/src/Bindings/ManualBindings.cpp @@ -115,9 +115,9 @@ static int tolua_CompressStringZLIB(lua_State * tolua_S) { cLuaState S(tolua_S); if ( - !S.CheckParamString(1) && + !S.CheckParamString(1) || ( - !S.CheckParamNumber(2) || + !S.CheckParamNumber(2) && !S.CheckParamEnd(2) ) ) @@ -146,7 +146,7 @@ static int tolua_UncompressStringZLIB(lua_State * tolua_S) { cLuaState S(tolua_S); if ( - !S.CheckParamString(1) && + !S.CheckParamString(1) || !S.CheckParamNumber(2) ) { @@ -174,7 +174,7 @@ static int tolua_CompressStringGZIP(lua_State * tolua_S) { cLuaState S(tolua_S); if ( - !S.CheckParamString(1) && + !S.CheckParamString(1) || !S.CheckParamEnd(2) ) { @@ -201,7 +201,7 @@ static int tolua_UncompressStringGZIP(lua_State * tolua_S) { cLuaState S(tolua_S); if ( - !S.CheckParamString(1) && + !S.CheckParamString(1) || !S.CheckParamEnd(2) ) { @@ -228,7 +228,7 @@ static int tolua_InflateString(lua_State * tolua_S) { cLuaState S(tolua_S); if ( - !S.CheckParamString(1) && + !S.CheckParamString(1) || !S.CheckParamEnd(2) ) { From 056b42cb94acd50029baf1526aca4d246df25814 Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Mon, 23 Feb 2015 18:43:01 +0100 Subject: [PATCH 62/95] Added documentation for CompressStringZLIB There was no info about the factor. --- MCServer/Plugins/APIDump/APIDesc.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MCServer/Plugins/APIDump/APIDesc.lua b/MCServer/Plugins/APIDump/APIDesc.lua index c214434b5..f2ec2546a 100644 --- a/MCServer/Plugins/APIDump/APIDesc.lua +++ b/MCServer/Plugins/APIDump/APIDesc.lua @@ -2269,7 +2269,7 @@ local CompressedString = cStringCompression.CompressStringGZIP("DataToCompress") Functions = { CompressStringGZIP = {Params = "string", Return = "string", Notes = "Compress a string using GZIP"}, - CompressStringZLIB = {Params = "string, factor", Return = "string", Notes = "Compresses a string using ZLIB"}, + CompressStringZLIB = {Params = "string, factor", Return = "string", Notes = "Compresses a string using ZLIB. Factor 0 is no compression and factor 9 is maximum compression"}, InflateString = {Params = "string", Return = "string", Notes = "Uncompresses a string using Inflate"}, UncompressStringGZIP = {Params = "string", Return = "string", Notes = "Uncompress a string using GZIP"}, UncompressStringZLIB = {Params = "string, uncompressed length", Return = "string", Notes = "Uncompresses a string using ZLIB"}, From c286b186c4905b0a36a6bae7bc2af6d672cd82e2 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Tue, 24 Feb 2015 10:04:43 +0100 Subject: [PATCH 63/95] 1.8 Protocol: Fixed a possible race condition. Fixes #1759. --- src/Protocol/Protocol18x.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/Protocol/Protocol18x.cpp b/src/Protocol/Protocol18x.cpp index 9b0f1c37c..22280f800 100644 --- a/src/Protocol/Protocol18x.cpp +++ b/src/Protocol/Protocol18x.cpp @@ -874,11 +874,15 @@ void cProtocol180::SendPlayerListUpdatePing(const cPlayer & a_Player) { ASSERT(m_State == 3); // In game mode? - cPacketizer Pkt(*this, 0x38); // Playerlist Item packet - Pkt.WriteVarInt(2); - Pkt.WriteVarInt(1); - Pkt.WriteUUID(a_Player.GetUUID()); - Pkt.WriteVarInt((UInt32)a_Player.GetClientHandle()->GetPing()); + auto ClientHandle = a_Player.GetClientHandlePtr(); + if (ClientHandle != nullptr) + { + cPacketizer Pkt(*this, 0x38); // Playerlist Item packet + Pkt.WriteVarInt(2); + Pkt.WriteVarInt(1); + Pkt.WriteUUID(a_Player.GetUUID()); + Pkt.WriteVarInt(static_cast(ClientHandle->GetPing())); + } } From a56b6906d60f5b511aeb93cf765359634e4bcf6e Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Wed, 25 Feb 2015 17:26:48 +0100 Subject: [PATCH 64/95] Fixed flowing water turning into ice when snowing --- src/Chunk.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Chunk.cpp b/src/Chunk.cpp index a4198c322..08cb237c3 100644 --- a/src/Chunk.cpp +++ b/src/Chunk.cpp @@ -925,7 +925,7 @@ void cChunk::ApplyWeatherToTop() { SetBlock(X, Height + 1, Z, E_BLOCK_SNOW, 0); } - else if ((TopBlock == E_BLOCK_WATER) || (TopBlock == E_BLOCK_STATIONARY_WATER)) + else if (IsBlockWater(TopBlock) && (TopMeta == 0)) { SetBlock(X, Height, Z, E_BLOCK_ICE, 0); } From f1f23b09ae20fa4d6288a9139462e49ff77d98c4 Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Wed, 25 Feb 2015 19:00:52 +0100 Subject: [PATCH 65/95] Added GetSnowStartHeight returns the height of a biome where it starts snowing --- src/BiomeDef.cpp | 127 +++++++++++++++++++++++++++++++++++++++++++++++ src/BiomeDef.h | 3 ++ 2 files changed, 130 insertions(+) diff --git a/src/BiomeDef.cpp b/src/BiomeDef.cpp index 188e06173..43f3ef0b1 100644 --- a/src/BiomeDef.cpp +++ b/src/BiomeDef.cpp @@ -222,3 +222,130 @@ bool IsBiomeCold(EMCSBiome a_Biome) + +int GetSnowStartHeight(EMCSBiome a_Biome) +{ + switch (a_Biome) + { + case biIcePlainsSpikes: + case biIcePlains: + case biIceMountains: + case biFrozenRiver: + case biColdBeach: + case biColdTaiga: + case biColdTaigaHills: + case biColdTaigaM: + { + // Always snow + return -1; + } + + case biExtremeHills: + case biExtremeHillsM: + case biExtremeHillsPlus: + case biExtremeHillsPlusM: + case biStoneBeach: + { + // Starts snowing at 96 + return 96; + } + + case biTaiga: + case biTaigaHills: + case biTaigaM: + { + // Start snowing at 130 + return 130; + } + + case biMegaTaiga: + case biMegaSpruceTaiga: + case biMegaTaigaHills: + case biMegaSpruceTaigaHills: + { + // Start snowing at 160 + return 160; + } + + case biRiver: + case biOcean: + case biDeepOcean: + { + // Starts snowing at 280 + return 280; + } + + case biBirchForest: + case biBirchForestHills: + case biBirchForestM: + case biBirchForestHillsM: + { + // Starts snowing at 335 + return 335; + } + + case biForest: + case biForestHills: + case biFlowerForest: + case biRoofedForest: + case biRoofedForestM: + { + // Starts snowing at 400 + return 400; + } + + case biPlains: + case biSunflowerPlains: + case biSwampland: + case biSwamplandM: + case biBeach: + { + // Starts snowing at 460 + return 460; + } + + case biMushroomIsland: + case biMushroomShore: + { + // Starts snowing at 520 + return 520; + } + + case biJungle: + case biJungleHills: + case biJungleM: + case biJungleEdge: + case biJungleEdgeM: + { + // Starts snowing at 550 + return 550; + } + + case biDesert: + case biDesertHills: + case biDesertM: + case biSavanna: + case biSavannaM: + case biSavannaPlateau: + case biSavannaPlateauM: + case biMesa: + case biMesaBryce: + case biMesaPlateau: + case biMesaPlateauF: + case biMesaPlateauFM: + case biMesaPlateauM: + { + // These biomes don't actualy have any downfall. + return 1000; + } + + default: + { + return -1; + } + } +} + + + + diff --git a/src/BiomeDef.h b/src/BiomeDef.h index 84751cfd7..cda12556a 100644 --- a/src/BiomeDef.h +++ b/src/BiomeDef.h @@ -129,4 +129,7 @@ extern bool IsBiomeVeryCold(EMCSBiome a_Biome); Doesn't report Very Cold biomes, use IsBiomeVeryCold() for those. */ extern bool IsBiomeCold(EMCSBiome a_Biome); +/** Returns the height when a biome when a biome starts snowing.*/ +extern int GetSnowStartHeight(EMCSBiome a_Biome); + // tolua_end From b3f07511306080545fb3e763e99ce31796d7de7d Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Wed, 25 Feb 2015 19:02:08 +0100 Subject: [PATCH 66/95] Weather: Snow starts forming when the top block is at the right height or higher --- src/Chunk.cpp | 121 +++++++++++++++++++++++--------------------------- 1 file changed, 56 insertions(+), 65 deletions(-) diff --git a/src/Chunk.cpp b/src/Chunk.cpp index 08cb237c3..e05fa4a99 100644 --- a/src/Chunk.cpp +++ b/src/Chunk.cpp @@ -874,80 +874,71 @@ void cChunk::ApplyWeatherToTop() int X = m_World->GetTickRandomNumber(15); int Z = m_World->GetTickRandomNumber(15); - switch (GetBiomeAt(X, Z)) + + // TODO: Check light levels, don't snow over when the BlockLight is higher than (7?) + int Height = GetHeight(X, Z); + + if (GetSnowStartHeight(GetBiomeAt(X, Z)) > Height) { - case biTaiga: - case biFrozenOcean: - case biFrozenRiver: - case biIcePlains: - case biIceMountains: - case biTaigaHills: + return; + } + + BLOCKTYPE TopBlock = GetBlock(X, Height, Z); + NIBBLETYPE TopMeta = GetMeta (X, Height, Z); + if (m_World->IsDeepSnowEnabled() && (TopBlock == E_BLOCK_SNOW)) + { + int MaxSize = 7; + BLOCKTYPE BlockType[4]; + NIBBLETYPE BlockMeta[4]; + UnboundedRelGetBlock(X - 1, Height, Z, BlockType[0], BlockMeta[0]); + UnboundedRelGetBlock(X + 1, Height, Z, BlockType[1], BlockMeta[1]); + UnboundedRelGetBlock(X, Height, Z - 1, BlockType[2], BlockMeta[2]); + UnboundedRelGetBlock(X, Height, Z + 1, BlockType[3], BlockMeta[3]); + for (int i = 0; i < 4; i++) { - // TODO: Check light levels, don't snow over when the BlockLight is higher than (7?) - int Height = GetHeight(X, Z); - BLOCKTYPE TopBlock = GetBlock(X, Height, Z); - NIBBLETYPE TopMeta = GetMeta (X, Height, Z); - if (m_World->IsDeepSnowEnabled() && (TopBlock == E_BLOCK_SNOW)) + switch (BlockType[i]) { - int MaxSize = 7; - BLOCKTYPE BlockType[4]; - NIBBLETYPE BlockMeta[4]; - UnboundedRelGetBlock(X - 1, Height, Z, BlockType[0], BlockMeta[0]); - UnboundedRelGetBlock(X + 1, Height, Z, BlockType[1], BlockMeta[1]); - UnboundedRelGetBlock(X, Height, Z - 1, BlockType[2], BlockMeta[2]); - UnboundedRelGetBlock(X, Height, Z + 1, BlockType[3], BlockMeta[3]); - for (int i = 0; i < 4; i++) + case E_BLOCK_AIR: { - switch (BlockType[i]) - { - case E_BLOCK_AIR: - { - MaxSize = 0; - break; - } - case E_BLOCK_SNOW: - { - MaxSize = std::min(BlockMeta[i] + 1, MaxSize); - break; - } - } + MaxSize = 0; + break; } - if (TopMeta < MaxSize) + case E_BLOCK_SNOW: { - FastSetBlock(X, Height, Z, E_BLOCK_SNOW, TopMeta + 1); - } - else if (TopMeta > MaxSize) - { - FastSetBlock(X, Height, Z, E_BLOCK_SNOW, TopMeta - 1); + MaxSize = std::min(BlockMeta[i] + 1, MaxSize); + break; } } - else if (cBlockInfo::IsSnowable(TopBlock) && (Height + 1 < cChunkDef::Height)) - { - SetBlock(X, Height + 1, Z, E_BLOCK_SNOW, 0); - } - else if (IsBlockWater(TopBlock) && (TopMeta == 0)) - { - SetBlock(X, Height, Z, E_BLOCK_ICE, 0); - } - else if ( - (m_World->IsDeepSnowEnabled()) && - ( - (TopBlock == E_BLOCK_RED_ROSE) || - (TopBlock == E_BLOCK_YELLOW_FLOWER) || - (TopBlock == E_BLOCK_RED_MUSHROOM) || - (TopBlock == E_BLOCK_BROWN_MUSHROOM) - ) - ) - { - SetBlock(X, Height, Z, E_BLOCK_SNOW, 0); - } - break; - } // case (snowy biomes) - default: - { - break; } - } // switch (biome) + if (TopMeta < MaxSize) + { + FastSetBlock(X, Height, Z, E_BLOCK_SNOW, TopMeta + 1); + } + else if (TopMeta > MaxSize) + { + FastSetBlock(X, Height, Z, E_BLOCK_SNOW, TopMeta - 1); + } + } + else if (cBlockInfo::IsSnowable(TopBlock) && (Height + 1 < cChunkDef::Height)) + { + SetBlock(X, Height + 1, Z, E_BLOCK_SNOW, 0); + } + else if (IsBlockWater(TopBlock) && (TopMeta == 0)) + { + SetBlock(X, Height, Z, E_BLOCK_ICE, 0); + } + else if ( + (m_World->IsDeepSnowEnabled()) && + ( + (TopBlock == E_BLOCK_RED_ROSE) || + (TopBlock == E_BLOCK_YELLOW_FLOWER) || + (TopBlock == E_BLOCK_RED_MUSHROOM) || + (TopBlock == E_BLOCK_BROWN_MUSHROOM) + ) + ) + { + SetBlock(X, Height, Z, E_BLOCK_SNOW, 0); + } } From ba3eaf922377c4b19d66b205a47c0698ea02f5b4 Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Wed, 25 Feb 2015 19:12:53 +0100 Subject: [PATCH 67/95] Snow finisher uses GetSnowStartHeight instead of specific biomes --- src/Generating/FinishGen.cpp | 36 ++++++++++++++---------------------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/src/Generating/FinishGen.cpp b/src/Generating/FinishGen.cpp index e10cb00f8..548c50563 100644 --- a/src/Generating/FinishGen.cpp +++ b/src/Generating/FinishGen.cpp @@ -470,30 +470,22 @@ void cFinishGenSnow::GenFinish(cChunkDesc & a_ChunkDesc) { for (int x = 0; x < cChunkDef::Width; x++) { - switch (a_ChunkDesc.GetBiome(x, z)) + int Height = a_ChunkDesc.GetHeight(x, z); + if (GetSnowStartHeight(a_ChunkDesc.GetBiome(x, z)) > Height) { - case biIcePlains: - case biIceMountains: - case biTaiga: - case biTaigaHills: - case biFrozenRiver: - case biFrozenOcean: - { - int Height = a_ChunkDesc.GetHeight(x, z); - if (cBlockInfo::IsSnowable(a_ChunkDesc.GetBlockType(x, Height, z)) && (Height < cChunkDef::Height - 1)) - { - a_ChunkDesc.SetBlockType(x, Height + 1, z, E_BLOCK_SNOW); - a_ChunkDesc.SetHeight(x, z, Height + 1); - } - break; - } - default: - { - // There's no snow in the other biomes. - break; - } + // Height isn't high enough for snow to start forming. + continue; } - } + + if (!cBlockInfo::IsSnowable(a_ChunkDesc.GetBlockType(x, Height, z)) && (Height < cChunkDef::Height - 1)) + { + // The top block can't be snown over. + continue; + } + + a_ChunkDesc.SetBlockType(x, Height + 1, z, E_BLOCK_SNOW); + a_ChunkDesc.SetHeight(x, z, Height + 1); + } // for x } // for z } From ac2c88b4510c8504cf015bfa69fc844aa9293e34 Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Wed, 25 Feb 2015 19:22:44 +0100 Subject: [PATCH 68/95] Ice finisher uses GetSnowStartHeight instead of specific biomes --- src/Generating/FinishGen.cpp | 45 +++++++++++++++--------------------- 1 file changed, 19 insertions(+), 26 deletions(-) diff --git a/src/Generating/FinishGen.cpp b/src/Generating/FinishGen.cpp index 548c50563..d8fb9c8c0 100644 --- a/src/Generating/FinishGen.cpp +++ b/src/Generating/FinishGen.cpp @@ -503,34 +503,27 @@ void cFinishGenIce::GenFinish(cChunkDesc & a_ChunkDesc) { for (int x = 0; x < cChunkDef::Width; x++) { - switch (a_ChunkDesc.GetBiome(x, z)) + int Height = a_ChunkDesc.GetHeight(x, z); + if (GetSnowStartHeight(a_ChunkDesc.GetBiome(x, z)) > Height) { - case biIcePlains: - case biIceMountains: - case biTaiga: - case biTaigaHills: - case biFrozenRiver: - case biFrozenOcean: - { - int Height = a_ChunkDesc.GetHeight(x, z); - switch (a_ChunkDesc.GetBlockType(x, Height, z)) - { - case E_BLOCK_WATER: - case E_BLOCK_STATIONARY_WATER: - { - a_ChunkDesc.SetBlockType(x, Height, z, E_BLOCK_ICE); - break; - } - } - break; - } - default: - { - // No icy water in other biomes. - break; - } + // Height isn't high enough for snow to start forming. + continue; } - } + + if (!IsBlockWater(a_ChunkDesc.GetBlockType(x, Height, z))) + { + // The block isn't a water block. + continue; + } + + if (a_ChunkDesc.GetBlockMeta(x, Height, z) != 0) + { + // The water block isn't a source block. + continue; + } + + a_ChunkDesc.SetBlockType(x, Height, z, E_BLOCK_ICE); + } // for x } // for z } From 378528136c921cc520289c2753e1edb5f25d3c7c Mon Sep 17 00:00:00 2001 From: Raekye Date: Wed, 25 Feb 2015 20:56:45 -0500 Subject: [PATCH 69/95] use DoSetSpeed in AddSpeed* in Entity.cpp --- src/Entities/Entity.cpp | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp index c51a27961..07cfb97b2 100644 --- a/src/Entities/Entity.cpp +++ b/src/Entities/Entity.cpp @@ -1913,10 +1913,7 @@ void cEntity::AddPosition(double a_AddPosX, double a_AddPosY, double a_AddPosZ) void cEntity::AddSpeed(double a_AddSpeedX, double a_AddSpeedY, double a_AddSpeedZ) { - m_Speed.x += a_AddSpeedX; - m_Speed.y += a_AddSpeedY; - m_Speed.z += a_AddSpeedZ; - WrapSpeed(); + DoSetSpeed(m_Speed.x + a_AddSpeedX, m_Speed.y + a_AddSpeedY, m_Speed.z + a_AddSpeedZ); } @@ -1925,8 +1922,7 @@ void cEntity::AddSpeed(double a_AddSpeedX, double a_AddSpeedY, double a_AddSpeed void cEntity::AddSpeedX(double a_AddSpeedX) { - m_Speed.x += a_AddSpeedX; - WrapSpeed(); + AddSpeed(a_AddSpeedX, 0, 0); } @@ -1935,8 +1931,7 @@ void cEntity::AddSpeedX(double a_AddSpeedX) void cEntity::AddSpeedY(double a_AddSpeedY) { - m_Speed.y += a_AddSpeedY; - WrapSpeed(); + AddSpeed(0, a_AddSpeedY, 0); } @@ -1945,8 +1940,7 @@ void cEntity::AddSpeedY(double a_AddSpeedY) void cEntity::AddSpeedZ(double a_AddSpeedZ) { - m_Speed.z += a_AddSpeedZ; - WrapSpeed(); + AddSpeed(0, 0, a_AddSpeedZ); } From 81e8577cfd9d1a8dd40ca1e9fc25c83f990b7f82 Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Thu, 26 Feb 2015 20:26:45 +0100 Subject: [PATCH 70/95] changed int to unsigned And return 0 instead of -1 --- src/BiomeDef.cpp | 6 +++--- src/BiomeDef.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/BiomeDef.cpp b/src/BiomeDef.cpp index 43f3ef0b1..bdc582863 100644 --- a/src/BiomeDef.cpp +++ b/src/BiomeDef.cpp @@ -223,7 +223,7 @@ bool IsBiomeCold(EMCSBiome a_Biome) -int GetSnowStartHeight(EMCSBiome a_Biome) +unsigned GetSnowStartHeight(EMCSBiome a_Biome) { switch (a_Biome) { @@ -237,7 +237,7 @@ int GetSnowStartHeight(EMCSBiome a_Biome) case biColdTaigaM: { // Always snow - return -1; + return 0; } case biExtremeHills: @@ -341,7 +341,7 @@ int GetSnowStartHeight(EMCSBiome a_Biome) default: { - return -1; + return 0; } } } diff --git a/src/BiomeDef.h b/src/BiomeDef.h index cda12556a..9b2369e5f 100644 --- a/src/BiomeDef.h +++ b/src/BiomeDef.h @@ -130,6 +130,6 @@ Doesn't report Very Cold biomes, use IsBiomeVeryCold() for those. */ extern bool IsBiomeCold(EMCSBiome a_Biome); /** Returns the height when a biome when a biome starts snowing.*/ -extern int GetSnowStartHeight(EMCSBiome a_Biome); +extern unsigned GetSnowStartHeight(EMCSBiome a_Biome); // tolua_end From e63b4f4913f93e2346bab32d079e7de289aac257 Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Sat, 28 Feb 2015 16:34:17 +0100 Subject: [PATCH 71/95] Added experience drops when mining ores --- src/Blocks/BlockOre.h | 59 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/src/Blocks/BlockOre.h b/src/Blocks/BlockOre.h index f6ea3aa3c..08d79f435 100644 --- a/src/Blocks/BlockOre.h +++ b/src/Blocks/BlockOre.h @@ -11,6 +11,7 @@ class cBlockOreHandler : public cBlockHandler { + typedef cBlockHandler super; public: cBlockOreHandler(BLOCKTYPE a_BlockType) : cBlockHandler(a_BlockType) @@ -56,6 +57,64 @@ public: } } } + + virtual void OnDestroyedByPlayer(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) override + { + super::OnDestroyedByPlayer(a_ChunkInterface, a_WorldInterface, a_Player, a_BlockX, a_BlockY, a_BlockZ); + + if (a_Player->IsGameModeCreative()) + { + // Don't drop XP when the player is in creative mode. + return; + } + + if (a_Player->GetEquippedItem().m_Enchantments.GetLevel(cEnchantments::enchSilkTouch) != 0) + { + // Don't drop XP when the ore is mined with the Silk Touch enchantment + return; + } + + cFastRandom Random; + int Reward = 0; + + switch (m_BlockType) + { + case E_BLOCK_NETHER_QUARTZ_ORE: + case E_BLOCK_LAPIS_ORE: + { + // Lapis and nether quartz get 2 - 5 experience + Reward = Random.NextInt(4) + 2; + break; + } + case E_BLOCK_REDSTONE_ORE: + case E_BLOCK_REDSTONE_ORE_GLOWING: + { + // Redstone gets 1 - 5 experience + Reward = Random.NextInt(5) + 1; + break; + } + case E_BLOCK_DIAMOND_ORE: + case E_BLOCK_EMERALD_ORE: + { + // Diamond and emerald get 3 - 7 experience + Reward = Random.NextInt(5) + 3; + break; + } + case E_BLOCK_COAL_ORE: + { + // Coal gets 0 - 2 experience + Reward = Random.NextInt(3); + break; + } + + default: break; + } + + if (Reward != 0) + { + a_WorldInterface.SpawnExperienceOrb(a_BlockX, a_BlockY, a_BlockZ, Reward); + } + } } ; From 88fc70a06a9e2d15f9a672f792e0029a79be136f Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sat, 28 Feb 2015 17:16:21 +0100 Subject: [PATCH 72/95] Fixed door placement. Doors now have hinges on the correct side, based on what the surroundings are when placing them. --- src/Items/ItemDoor.h | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/Items/ItemDoor.h b/src/Items/ItemDoor.h index 18289be03..71143d5a8 100644 --- a/src/Items/ItemDoor.h +++ b/src/Items/ItemDoor.h @@ -77,19 +77,32 @@ public: NIBBLETYPE LowerBlockMeta = cBlockDoorHandler::PlayerYawToMetaData(a_Player.GetYaw()); Vector3i RelDirToOutside = cBlockDoorHandler::GetRelativeDirectionToOutside(LowerBlockMeta); Vector3i LeftNeighborPos = RelDirToOutside; - LeftNeighborPos.TurnCCW(); + LeftNeighborPos.TurnCW(); LeftNeighborPos.Move(a_BlockX, a_BlockY, a_BlockZ); Vector3i RightNeighborPos = RelDirToOutside; - RightNeighborPos.TurnCW(); + RightNeighborPos.TurnCCW(); RightNeighborPos.Move(a_BlockX, a_BlockY, a_BlockZ); // Decide whether the hinge is on the left (default) or on the right: NIBBLETYPE UpperBlockMeta = 0x08; + BLOCKTYPE LeftNeighborBlock = a_World.GetBlock(LeftNeighborPos); + BLOCKTYPE RightNeighborBlock = a_World.GetBlock(RightNeighborPos); + /* + // DEBUG: + LOGD("Door being placed at {%d, %d, %d}", a_BlockX, a_BlockY, a_BlockZ); + LOGD("RelDirToOutside: {%d, %d, %d}", RelDirToOutside.x, RelDirToOutside.y, RelDirToOutside.z); + LOGD("Left neighbor at {%d, %d, %d}: %d (%s)", LeftNeighborPos.x, LeftNeighborPos.y, LeftNeighborPos.z, LeftNeighborBlock, ItemTypeToString(LeftNeighborBlock).c_str()); + LOGD("Right neighbor at {%d, %d, %d}: %d (%s)", RightNeighborPos.x, RightNeighborPos.y, RightNeighborPos.z, RightNeighborBlock, ItemTypeToString(RightNeighborBlock).c_str()); + */ if ( - cBlockDoorHandler::IsDoorBlockType(a_World.GetBlock(LeftNeighborPos)) || // The block to the left is a door block - cBlockInfo::IsSolid(a_World.GetBlock(RightNeighborPos)) // The block to the right is solid + cBlockDoorHandler::IsDoorBlockType(LeftNeighborBlock) || // The block to the left is a door block + ( + cBlockInfo::IsSolid(RightNeighborBlock) && // The block to the right is solid... + !cBlockDoorHandler::IsDoorBlockType(RightNeighborBlock) // ... but not a door + ) ) { + // DEBUG: LOGD("Setting hinge to right side"); UpperBlockMeta = 0x09; // Upper block | hinge on right } From 224df08d30b556c0d7214e451fd4322ca75f32ea Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Sat, 28 Feb 2015 17:27:28 +0100 Subject: [PATCH 73/95] GetSnowStartHeight returns an int --- src/BiomeDef.cpp | 2 +- src/BiomeDef.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/BiomeDef.cpp b/src/BiomeDef.cpp index bdc582863..3e34ebdbe 100644 --- a/src/BiomeDef.cpp +++ b/src/BiomeDef.cpp @@ -223,7 +223,7 @@ bool IsBiomeCold(EMCSBiome a_Biome) -unsigned GetSnowStartHeight(EMCSBiome a_Biome) +int GetSnowStartHeight(EMCSBiome a_Biome) { switch (a_Biome) { diff --git a/src/BiomeDef.h b/src/BiomeDef.h index 9b2369e5f..cda12556a 100644 --- a/src/BiomeDef.h +++ b/src/BiomeDef.h @@ -130,6 +130,6 @@ Doesn't report Very Cold biomes, use IsBiomeVeryCold() for those. */ extern bool IsBiomeCold(EMCSBiome a_Biome); /** Returns the height when a biome when a biome starts snowing.*/ -extern unsigned GetSnowStartHeight(EMCSBiome a_Biome); +extern int GetSnowStartHeight(EMCSBiome a_Biome); // tolua_end From b65a6ef210a590c6d265cdd71efe58b81a4ae836 Mon Sep 17 00:00:00 2001 From: DevToaster Date: Sun, 1 Mar 2015 03:03:41 +1030 Subject: [PATCH 74/95] modified: src/Entities/Player.cpp modified: src/Entities/Player.h --- src/Entities/Player.cpp | 34 +++++++++++++++++----------------- src/Entities/Player.h | 20 ++++++++++---------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index 527380761..57f31752e 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -296,7 +296,7 @@ void cPlayer::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) -short cPlayer::CalcLevelFromXp(short a_XpTotal) +int cPlayer::CalcLevelFromXp(int a_XpTotal) { // level 0 to 15 if (a_XpTotal <= XP_TO_LEVEL15) @@ -307,18 +307,18 @@ short cPlayer::CalcLevelFromXp(short a_XpTotal) // level 30+ if (a_XpTotal > XP_TO_LEVEL30) { - return (short) (151.5 + sqrt( 22952.25 - (14 * (2220 - a_XpTotal)))) / 7; + return (int) (151.5 + sqrt( 22952.25 - (14 * (2220 - a_XpTotal)))) / 7; } // level 16 to 30 - return (short) ( 29.5 + sqrt( 870.25 - (6 * ( 360 - a_XpTotal)))) / 3; + return (int) ( 29.5 + sqrt( 870.25 - (6 * ( 360 - a_XpTotal)))) / 3; } -short cPlayer::XpForLevel(short a_Level) +int cPlayer::XpForLevel(int a_Level) { // level 0 to 15 if (a_Level <= 15) @@ -329,18 +329,18 @@ short cPlayer::XpForLevel(short a_Level) // level 30+ if (a_Level >= 31) { - return (short) ( (3.5 * a_Level * a_Level) - (151.5 * a_Level) + 2220); + return (int) ( (3.5 * a_Level * a_Level) - (151.5 * a_Level) + 2220); } // level 16 to 30 - return (short) ( (1.5 * a_Level * a_Level) - (29.5 * a_Level) + 360); + return (int) ( (1.5 * a_Level * a_Level) - (29.5 * a_Level) + 360); } -short cPlayer::GetXpLevel() +int cPlayer::GetXpLevel() { return CalcLevelFromXp(m_CurrentXp); } @@ -351,8 +351,8 @@ short cPlayer::GetXpLevel() float cPlayer::GetXpPercentage() { - short int currentLevel = CalcLevelFromXp(m_CurrentXp); - short int currentLevel_XpBase = XpForLevel(currentLevel); + int currentLevel = CalcLevelFromXp(m_CurrentXp); + int currentLevel_XpBase = XpForLevel(currentLevel); return (float)(m_CurrentXp - currentLevel_XpBase) / (float)(XpForLevel(1+currentLevel) - currentLevel_XpBase); @@ -362,9 +362,9 @@ float cPlayer::GetXpPercentage() -bool cPlayer::SetCurrentExperience(short int a_CurrentXp) +bool cPlayer::SetCurrentExperience(int a_CurrentXp) { - if (!(a_CurrentXp >= 0) || (a_CurrentXp > (std::numeric_limits().max() - m_LifetimeTotalXp))) + if (!(a_CurrentXp >= 0) || (a_CurrentXp > (std::numeric_limits().max() - m_LifetimeTotalXp))) { LOGWARNING("Tried to update experiece with an invalid Xp value: %d", a_CurrentXp); return false; // oops, they gave us a dodgey number @@ -382,19 +382,19 @@ bool cPlayer::SetCurrentExperience(short int a_CurrentXp) -short cPlayer::DeltaExperience(short a_Xp_delta) +int cPlayer::DeltaExperience(int a_Xp_delta) { - if (a_Xp_delta > (std::numeric_limits().max() - m_CurrentXp)) + if (a_Xp_delta > (std::numeric_limits().max() - m_CurrentXp)) { // Value was bad, abort and report - LOGWARNING("Attempt was made to increment Xp by %d, which overflowed the short datatype. Ignoring.", a_Xp_delta); + LOGWARNING("Attempt was made to increment Xp by %d, which overflowed the int datatype. Ignoring.", a_Xp_delta); return -1; // Should we instead just return the current Xp? } m_CurrentXp += a_Xp_delta; // Make sure they didn't subtract too much - m_CurrentXp = std::max(m_CurrentXp, 0); + m_CurrentXp = std::max(m_CurrentXp, 0); // Update total for score calculation if (a_Xp_delta > 0) @@ -1725,8 +1725,8 @@ bool cPlayer::LoadFromFile(const AString & a_FileName, cWorldPtr & a_World) m_FoodSaturationLevel = root.get("foodSaturation", MAX_FOOD_LEVEL).asDouble(); m_FoodTickTimer = root.get("foodTickTimer", 0).asInt(); m_FoodExhaustionLevel = root.get("foodExhaustion", 0).asDouble(); - m_LifetimeTotalXp = (short) root.get("xpTotal", 0).asInt(); - m_CurrentXp = (short) root.get("xpCurrent", 0).asInt(); + m_LifetimeTotalXp = (int) root.get("xpTotal", 0).asInt(); + m_CurrentXp = (int) root.get("xpCurrent", 0).asInt(); m_IsFlying = root.get("isflying", 0).asBool(); m_GameMode = (eGameMode) root.get("gamemode", eGameMode_NotSet).asInt(); diff --git a/src/Entities/Player.h b/src/Entities/Player.h index 7abb1b98a..021b6fb0a 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -72,22 +72,22 @@ public: Returns true on success "should" really only be called at init or player death, plugins excepted */ - bool SetCurrentExperience(short a_XpTotal); + bool SetCurrentExperience(int a_XpTotal); /* changes Xp by Xp_delta, you "shouldn't" inc more than MAX_EXPERIENCE_ORB_SIZE Wont't allow xp to go negative Returns the new current experience, -1 on error */ - short DeltaExperience(short a_Xp_delta); + int DeltaExperience(int a_Xp_delta); /** Gets the experience total - XpTotal for score on death */ - inline short GetXpLifetimeTotal(void) { return m_LifetimeTotalXp; } + inline int GetXpLifetimeTotal(void) { return m_LifetimeTotalXp; } /** Gets the currrent experience */ - inline short GetCurrentXp(void) { return m_CurrentXp; } + inline int GetCurrentXp(void) { return m_CurrentXp; } /** Gets the current level - XpLevel */ - short GetXpLevel(void); + int GetXpLevel(void); /** Gets the experience bar percentage - XpP */ float GetXpPercentage(void); @@ -95,13 +95,13 @@ public: /** Caculates the amount of XP needed for a given level Ref: http://minecraft.gamepedia.com/XP */ - static short XpForLevel(short int a_Level); + static int XpForLevel(int a_Level); /** Inverse of XpForLevel Ref: http://minecraft.gamepedia.com/XP values are as per this with pre-calculations */ - static short CalcLevelFromXp(short int a_CurrentXp); + static int CalcLevelFromXp(int a_CurrentXp); // tolua_end @@ -581,8 +581,8 @@ protected: Int64 m_EatingFinishTick; /** Player Xp level */ - short int m_LifetimeTotalXp; - short int m_CurrentXp; + int m_LifetimeTotalXp; + int m_CurrentXp; // flag saying we need to send a xp update to client bool m_bDirtyExperience; @@ -609,7 +609,7 @@ protected: */ bool m_bIsTeleporting; - /** The short UUID (no dashes) of the player, as read from the ClientHandle. + /** The int UUID (no dashes) of the player, as read from the ClientHandle. If no ClientHandle is given, the UUID is initialized to empty. */ AString m_UUID; From e012c06281624e97fc79fa90e18e51a42fb002af Mon Sep 17 00:00:00 2001 From: DevToaster Date: Sun, 1 Mar 2015 03:09:36 +1030 Subject: [PATCH 75/95] Replaced short int with int for Player experience --- src/Entities/Player.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Entities/Player.h b/src/Entities/Player.h index 021b6fb0a..e02c66bd3 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -609,7 +609,7 @@ protected: */ bool m_bIsTeleporting; - /** The int UUID (no dashes) of the player, as read from the ClientHandle. + /** The short UUID (no dashes) of the player, as read from the ClientHandle. If no ClientHandle is given, the UUID is initialized to empty. */ AString m_UUID; From f5a216cabc14e6b2381ea881a12ce4db24841c9e Mon Sep 17 00:00:00 2001 From: DevToaster Date: Sun, 1 Mar 2015 03:45:06 +1030 Subject: [PATCH 76/95] Changed C-styled casts to static_cast and removed unneeded casts --- src/Entities/Player.cpp | 68 ++++++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index 57f31752e..e1d9f4550 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -105,15 +105,15 @@ cPlayer::cPlayer(cClientHandlePtr a_Client, const AString & a_PlayerName) : SetPosX(World->GetSpawnX()); SetPosY(World->GetSpawnY()); SetPosZ(World->GetSpawnZ()); - SetBedPos(Vector3i((int)World->GetSpawnX(), (int)World->GetSpawnY(), (int)World->GetSpawnZ())); + SetBedPos(Vector3i(static_cast(World->GetSpawnX()), static_cast(World->GetSpawnY()), static_cast(World->GetSpawnZ()))); LOGD("Player \"%s\" is connecting for the first time, spawning at default world spawn {%.2f, %.2f, %.2f}", a_PlayerName.c_str(), GetPosX(), GetPosY(), GetPosZ() ); } - m_LastJumpHeight = (float)(GetPosY()); - m_LastGroundHeight = (float)(GetPosY()); + m_LastJumpHeight = static_cast(GetPosY()); + m_LastGroundHeight = static_cast(GetPosY()); m_Stance = GetPosY() + 1.62; if (m_GameMode == gmNotSet) @@ -278,7 +278,7 @@ void cPlayer::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) if (IsFlying()) { - m_LastGroundHeight = (float)GetPosY(); + m_LastGroundHeight = static_cast(GetPosY()); } if (m_TicksUntilNextSave == 0) @@ -307,11 +307,11 @@ int cPlayer::CalcLevelFromXp(int a_XpTotal) // level 30+ if (a_XpTotal > XP_TO_LEVEL30) { - return (int) (151.5 + sqrt( 22952.25 - (14 * (2220 - a_XpTotal)))) / 7; + return static_cast((151.5 + sqrt( 22952.25 - (14 * (2220 - a_XpTotal)))) / 7); } // level 16 to 30 - return (int) ( 29.5 + sqrt( 870.25 - (6 * ( 360 - a_XpTotal)))) / 3; + return static_cast((29.5 + sqrt( 870.25 - (6 * ( 360 - a_XpTotal)))) / 3); } @@ -329,11 +329,11 @@ int cPlayer::XpForLevel(int a_Level) // level 30+ if (a_Level >= 31) { - return (int) ( (3.5 * a_Level * a_Level) - (151.5 * a_Level) + 2220); + return static_cast((3.5 * a_Level * a_Level) - (151.5 * a_Level) + 2220); } // level 16 to 30 - return (int) ( (1.5 * a_Level * a_Level) - (29.5 * a_Level) + 360); + return static_cast((1.5 * a_Level * a_Level) - (29.5 * a_Level) + 360); } @@ -354,8 +354,8 @@ float cPlayer::GetXpPercentage() int currentLevel = CalcLevelFromXp(m_CurrentXp); int currentLevel_XpBase = XpForLevel(currentLevel); - return (float)(m_CurrentXp - currentLevel_XpBase) / - (float)(XpForLevel(1+currentLevel) - currentLevel_XpBase); + return static_cast(m_CurrentXp - currentLevel_XpBase) / + static_cast(XpForLevel(1+currentLevel) - currentLevel_XpBase); } @@ -394,7 +394,7 @@ int cPlayer::DeltaExperience(int a_Xp_delta) m_CurrentXp += a_Xp_delta; // Make sure they didn't subtract too much - m_CurrentXp = std::max(m_CurrentXp, 0); + m_CurrentXp = std::max(m_CurrentXp, 0); // Update total for score calculation if (a_Xp_delta > 0) @@ -466,7 +466,7 @@ void cPlayer::SetTouchGround(bool a_bTouchGround) { if (GetPosY() > m_LastJumpHeight) { - m_LastJumpHeight = (float)GetPosY(); + m_LastJumpHeight = static_cast(GetPosY()); } cWorld * World = GetWorld(); if ((GetPosY() >= 0) && (GetPosY() < cChunkDef::Height)) @@ -483,13 +483,13 @@ void cPlayer::SetTouchGround(bool a_bTouchGround) (BlockType == E_BLOCK_VINES) ) { - m_LastGroundHeight = (float)GetPosY(); + m_LastGroundHeight = static_cast(GetPosY()); } } } else { - float Dist = (float)(m_LastGroundHeight - floor(GetPosY())); + float Dist = static_cast(m_LastGroundHeight - floor(GetPosY())); if (Dist >= 2.0) // At least two blocks - TODO: Use m_LastJumpHeight instead of m_LastGroundHeight above { @@ -497,12 +497,12 @@ void cPlayer::SetTouchGround(bool a_bTouchGround) m_Stats.AddValue(statDistFallen, (StatValue)floor(Dist * 100 + 0.5)); } - int Damage = (int)(Dist - 3.f); + int Damage = static_cast(Dist - 3.f); if (m_LastJumpHeight > m_LastGroundHeight) { Damage++; } - m_LastJumpHeight = (float)GetPosY(); + m_LastJumpHeight = static_cast(GetPosY()); if (Damage > 0) { @@ -510,10 +510,10 @@ void cPlayer::SetTouchGround(bool a_bTouchGround) TakeDamage(dtFalling, nullptr, Damage, Damage, 0); // Fall particles - GetWorld()->BroadcastSoundParticleEffect(2006, POSX_TOINT, (int)GetPosY() - 1, POSZ_TOINT, Damage /* Used as particle effect speed modifier */); + GetWorld()->BroadcastSoundParticleEffect(2006, POSX_TOINT, static_cast(GetPosY()) - 1, POSZ_TOINT, Damage /* Used as particle effect speed modifier */); } - m_LastGroundHeight = (float)GetPosY(); + m_LastGroundHeight = static_cast(GetPosY()); } } @@ -551,7 +551,7 @@ void cPlayer::SetFoodLevel(int a_FoodLevel) void cPlayer::SetFoodSaturationLevel(double a_FoodSaturationLevel) { - m_FoodSaturationLevel = Clamp(a_FoodSaturationLevel, 0.0, (double) m_FoodLevel); + m_FoodSaturationLevel = Clamp(a_FoodSaturationLevel, 0.0, static_cast(m_FoodLevel)); } @@ -1275,8 +1275,8 @@ unsigned int cPlayer::AwardAchievement(const eStatistic a_Ach) void cPlayer::TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ) { SetPosition(a_PosX, a_PosY, a_PosZ); - m_LastGroundHeight = (float)a_PosY; - m_LastJumpHeight = (float)a_PosY; + m_LastGroundHeight = static_cast(a_PosY); + m_LastJumpHeight = static_cast(a_PosY); m_bIsTeleporting = true; m_World->BroadcastTeleportEntity(*this, GetClientHandle()); @@ -1714,9 +1714,9 @@ bool cPlayer::LoadFromFile(const AString & a_FileName, cWorldPtr & a_World) Json::Value & JSON_PlayerRotation = root["rotation"]; if (JSON_PlayerRotation.size() == 3) { - SetYaw ((float)JSON_PlayerRotation[(unsigned)0].asDouble()); - SetPitch ((float)JSON_PlayerRotation[(unsigned)1].asDouble()); - SetRoll ((float)JSON_PlayerRotation[(unsigned)2].asDouble()); + SetYaw (static_cast(JSON_PlayerRotation[(unsigned)0].asDouble())); + SetPitch (static_cast(JSON_PlayerRotation[(unsigned)1].asDouble())); + SetRoll (static_cast(JSON_PlayerRotation[(unsigned)2].asDouble())); } m_Health = root.get("health", 0).asInt(); @@ -1725,8 +1725,8 @@ bool cPlayer::LoadFromFile(const AString & a_FileName, cWorldPtr & a_World) m_FoodSaturationLevel = root.get("foodSaturation", MAX_FOOD_LEVEL).asDouble(); m_FoodTickTimer = root.get("foodTickTimer", 0).asInt(); m_FoodExhaustionLevel = root.get("foodExhaustion", 0).asDouble(); - m_LifetimeTotalXp = (int) root.get("xpTotal", 0).asInt(); - m_CurrentXp = (int) root.get("xpCurrent", 0).asInt(); + m_LifetimeTotalXp = root.get("xpTotal", 0).asInt(); + m_CurrentXp = root.get("xpCurrent", 0).asInt(); m_IsFlying = root.get("isflying", 0).asBool(); m_GameMode = (eGameMode) root.get("gamemode", eGameMode_NotSet).asInt(); @@ -1812,18 +1812,18 @@ bool cPlayer::SaveToDisk() root["world"] = m_World->GetName(); if (m_GameMode == m_World->GetGameMode()) { - root["gamemode"] = (int) eGameMode_NotSet; + root["gamemode"] = static_cast(eGameMode_NotSet); } else { - root["gamemode"] = (int) m_GameMode; + root["gamemode"] = static_cast(m_GameMode); } } else { // This happens if the player is saved to new format after loading from the old format root["world"] = m_LoadedWorldName; - root["gamemode"] = (int) eGameMode_NotSet; + root["gamemode"] = static_cast(eGameMode_NotSet); } Json::StyledWriter writer; @@ -1839,7 +1839,7 @@ bool cPlayer::SaveToDisk() ); return false; } - if (f.Write(JsonData.c_str(), JsonData.size()) != (int)JsonData.size()) + if (f.Write(JsonData.c_str(), JsonData.size()) != static_cast(JsonData.size())) { LOGWARNING("Error writing player \"%s\" to file \"%s\" - cannot save data. Player will lose their progress. ", GetName().c_str(), SourceFile.c_str() @@ -1894,7 +1894,7 @@ void cPlayer::UseEquippedItem(int a_Amount) if (GetInventory().DamageEquippedItem(a_Amount)) { - m_World->BroadcastSoundEffect("random.break", GetPosX(), GetPosY(), GetPosZ(), 0.5f, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64)); + m_World->BroadcastSoundEffect("random.break", GetPosX(), GetPosY(), GetPosZ(), 0.5f, static_cast(0.75 + (static_cast((GetUniqueID() * 23) % 32)) / 64)); } } @@ -2042,17 +2042,17 @@ void cPlayer::UpdateMovementStats(const Vector3d & a_DeltaPos) else if (IsSubmerged()) { m_Stats.AddValue(statDistDove, Value); - AddFoodExhaustion(0.00015 * (double)Value); + AddFoodExhaustion(0.00015 * static_cast(Value)); } else if (IsSwimming()) { m_Stats.AddValue(statDistSwum, Value); - AddFoodExhaustion(0.00015 * (double)Value); + AddFoodExhaustion(0.00015 * static_cast(Value)); } else if (IsOnGround()) { m_Stats.AddValue(statDistWalked, Value); - AddFoodExhaustion((m_IsSprinting ? 0.001 : 0.0001) * (double)Value); + AddFoodExhaustion((m_IsSprinting ? 0.001 : 0.0001) * static_cast(Value)); } else { From 19d7ec51a0a4cb70d5f1e4a62fcf3e944e072a5d Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Sat, 28 Feb 2015 22:37:33 +0100 Subject: [PATCH 77/95] Implemented a vines finisher that creates vines in jungle biomes --- src/Generating/ComposableGenerator.cpp | 4 ++ src/Generating/FinishGen.cpp | 94 ++++++++++++++++++++++++++ src/Generating/FinishGen.h | 21 ++++++ 3 files changed, 119 insertions(+) diff --git a/src/Generating/ComposableGenerator.cpp b/src/Generating/ComposableGenerator.cpp index bda45ad92..09c1c3949 100644 --- a/src/Generating/ComposableGenerator.cpp +++ b/src/Generating/ComposableGenerator.cpp @@ -616,6 +616,10 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile) int MaxDensity = a_IniFile.GetValueSetI("Generator", "VillageMaxDensity", 80); m_FinishGens.push_back(std::make_shared(Seed, GridSize, MaxOffset, MaxDepth, MaxSize, MinDensity, MaxDensity, m_BiomeGen, m_CompositedHeightCache)); } + else if (NoCaseCompare(*itr, "Vines") == 0) + { + m_FinishGens.push_back(cFinishGenPtr(new cFinishGenVines(Seed))); + } else if (NoCaseCompare(*itr, "WaterLakes") == 0) { int Probability = a_IniFile.GetValueSetI("Generator", "WaterLakesProbability", 25); diff --git a/src/Generating/FinishGen.cpp b/src/Generating/FinishGen.cpp index d8fb9c8c0..5b8965906 100644 --- a/src/Generating/FinishGen.cpp +++ b/src/Generating/FinishGen.cpp @@ -243,6 +243,100 @@ void cFinishGenTallGrass::GenFinish(cChunkDesc & a_ChunkDesc) +//////////////////////////////////////////////////////////////////////////////// +// cFinishGenVines + +bool cFinishGenVines::IsJungleVariant(EMCSBiome a_Biome) +{ + switch (a_Biome) + { + case biJungle: + case biJungleEdge: + case biJungleEdgeM: + case biJungleHills: + case biJungleM: + { + return true; + } + } + + return false; +} + + + + + +void cFinishGenVines::GenFinish(cChunkDesc & a_ChunkDesc) +{ + for (int x = 0; x < cChunkDef::Width; x++) + { + int xx = x + a_ChunkDesc.GetChunkX() * cChunkDef::Width; + for (int z = 0; z < cChunkDef::Width; z++) + { + int zz = z + a_ChunkDesc.GetChunkZ() * cChunkDef::Width; + if (!IsJungleVariant(a_ChunkDesc.GetBiome(x, z))) + { + // Current biome isn't a jungle + continue; + } + + if (m_Noise.IntNoise2D(xx, zz) < 0.5) + { + continue; + } + + int Height = a_ChunkDesc.GetHeight(x, z); + for (int y = Height; y > 40; y--) + { + if (a_ChunkDesc.GetBlockType(x, y, z) != E_BLOCK_AIR) + { + // Can't place vines in non-air blocks + continue; + } + + if (m_Noise.IntNoise3D(xx, y, zz) < 0.5) + { + continue; + } + + std::vector Places; + if ((x + 1 < cChunkDef::Width) && cBlockInfo::FullyOccupiesVoxel(a_ChunkDesc.GetBlockType(x + 1, y, z))) + { + Places.push_back(8); + } + + if ((x - 1 > 0) && cBlockInfo::FullyOccupiesVoxel(a_ChunkDesc.GetBlockType(x - 1, y, z))) + { + Places.push_back(2); + } + + if ((z + 1 < cChunkDef::Width) && cBlockInfo::FullyOccupiesVoxel(a_ChunkDesc.GetBlockType(x, y, z + 1))) + { + Places.push_back(1); + } + + if ((z - 1 > 0) && cBlockInfo::FullyOccupiesVoxel(a_ChunkDesc.GetBlockType(x, y, z - 1))) + { + Places.push_back(4); + } + + if (Places.size() == 0) + { + continue; + } + + NIBBLETYPE Meta = Places[m_Noise.IntNoise3DInt(xx, y, zz) % Places.size()]; + a_ChunkDesc.SetBlockTypeMeta(x, y, z, E_BLOCK_VINES, Meta); + } + } + } +} + + + + + //////////////////////////////////////////////////////////////////////////////// // cFinishGenSprinkleFoliage: diff --git a/src/Generating/FinishGen.h b/src/Generating/FinishGen.h index ae6dee590..023d26f10 100644 --- a/src/Generating/FinishGen.h +++ b/src/Generating/FinishGen.h @@ -118,6 +118,27 @@ protected: +class cFinishGenVines : + public cFinishGen +{ +public: + cFinishGenVines(int a_Seed) : + m_Noise(a_Seed) + { + } + + bool IsJungleVariant(EMCSBiome a_Biome); + +protected: + cNoise m_Noise; + + virtual void GenFinish(cChunkDesc & a_ChunkDesc) override; +}; + + + + + class cFinishGenSoulsandRims : public cFinishGen { From 0394acfc0ca25e0b3497a4494d98c0b21062ad47 Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Sat, 28 Feb 2015 22:40:13 +0100 Subject: [PATCH 78/95] Made the minimum vine level configurable --- src/Generating/ComposableGenerator.cpp | 3 ++- src/Generating/FinishGen.cpp | 2 +- src/Generating/FinishGen.h | 6 ++++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Generating/ComposableGenerator.cpp b/src/Generating/ComposableGenerator.cpp index 09c1c3949..42a805760 100644 --- a/src/Generating/ComposableGenerator.cpp +++ b/src/Generating/ComposableGenerator.cpp @@ -618,7 +618,8 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile) } else if (NoCaseCompare(*itr, "Vines") == 0) { - m_FinishGens.push_back(cFinishGenPtr(new cFinishGenVines(Seed))); + int Level = a_IniFile.GetValueSetI("Generator", "VinesLevel", 40); + m_FinishGens.push_back(cFinishGenPtr(new cFinishGenVines(Seed, Level))); } else if (NoCaseCompare(*itr, "WaterLakes") == 0) { diff --git a/src/Generating/FinishGen.cpp b/src/Generating/FinishGen.cpp index 5b8965906..8790ac0e8 100644 --- a/src/Generating/FinishGen.cpp +++ b/src/Generating/FinishGen.cpp @@ -287,7 +287,7 @@ void cFinishGenVines::GenFinish(cChunkDesc & a_ChunkDesc) } int Height = a_ChunkDesc.GetHeight(x, z); - for (int y = Height; y > 40; y--) + for (int y = Height; y > m_Level; y--) { if (a_ChunkDesc.GetBlockType(x, y, z) != E_BLOCK_AIR) { diff --git a/src/Generating/FinishGen.h b/src/Generating/FinishGen.h index 023d26f10..950406872 100644 --- a/src/Generating/FinishGen.h +++ b/src/Generating/FinishGen.h @@ -122,8 +122,9 @@ class cFinishGenVines : public cFinishGen { public: - cFinishGenVines(int a_Seed) : - m_Noise(a_Seed) + cFinishGenVines(int a_Seed, int a_Level) : + m_Noise(a_Seed), + m_Level(a_Level) { } @@ -131,6 +132,7 @@ public: protected: cNoise m_Noise; + int m_Level; virtual void GenFinish(cChunkDesc & a_ChunkDesc) override; }; From f53e2251898bcceaedf79c72a1f69a66ab2d4c20 Mon Sep 17 00:00:00 2001 From: Alexander Harkness Date: Sun, 1 Mar 2015 10:10:34 +0000 Subject: [PATCH 79/95] Fixed raspi easyinstall. Fixes #1782 --- easyinstall.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/easyinstall.sh b/easyinstall.sh index 9b4007144..40fa85cfe 100755 --- a/easyinstall.sh +++ b/easyinstall.sh @@ -7,7 +7,7 @@ case $PLATFORM in "i686") DOWNLOADURL="http://builds.cuberite.org/job/MCServer%20Linux%20x86/lastSuccessfulBuild/artifact/MCServer.tar" ;; "x86_64") DOWNLOADURL="http://builds.cuberite.org/job/MCServer%20Linux%20x64/lastSuccessfulBuild/artifact/MCServer.tar" ;; # Assume that all arm devices are a raspi for now. - "arm*") DOWNLOADURL="http://builds.cuberite.org/job/MCServer%20Linux%20armhf/lastSuccessfulBuild/artifact/MCServer/MCServer.tar" + arm*) DOWNLOADURL="http://builds.cuberite.org/job/MCServer%20Linux%20armhf/lastSuccessfulBuild/artifact/MCServer/MCServer.tar" esac echo "Downloading precompiled binaries." From a22a81ef4ba9bb1d195a5b820a5f708f3b1030f0 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sun, 1 Mar 2015 12:11:51 +0100 Subject: [PATCH 80/95] Debuggers plugin: Disabled WECUI manipulation. --- MCServer/Plugins/Debuggers/Debuggers.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MCServer/Plugins/Debuggers/Debuggers.lua b/MCServer/Plugins/Debuggers/Debuggers.lua index a28ffa552..c8069a411 100644 --- a/MCServer/Plugins/Debuggers/Debuggers.lua +++ b/MCServer/Plugins/Debuggers/Debuggers.lua @@ -25,13 +25,14 @@ function Initialize(Plugin) PM:AddHook(cPluginManager.HOOK_PLAYER_RIGHT_CLICKING_ENTITY, OnPlayerRightClickingEntity); PM:AddHook(cPluginManager.HOOK_WORLD_TICK, OnWorldTick); PM:AddHook(cPluginManager.HOOK_PLUGINS_LOADED, OnPluginsLoaded); - PM:AddHook(cPluginManager.HOOK_PLUGIN_MESSAGE, OnPluginMessage); PM:AddHook(cPluginManager.HOOK_PLAYER_JOINED, OnPlayerJoined); PM:AddHook(cPluginManager.HOOK_PROJECTILE_HIT_BLOCK, OnProjectileHitBlock); PM:AddHook(cPluginManager.HOOK_CHUNK_UNLOADING, OnChunkUnloading); PM:AddHook(cPluginManager.HOOK_WORLD_STARTED, OnWorldStarted); PM:AddHook(cPluginManager.HOOK_PROJECTILE_HIT_BLOCK, OnProjectileHitBlock); + -- _X: Disabled WECUI manipulation: + -- PM:AddHook(cPluginManager.HOOK_PLUGIN_MESSAGE, OnPluginMessage); -- _X: Disabled so that the normal operation doesn't interfere with anything -- PM:AddHook(cPluginManager.HOOK_CHUNK_GENERATED, OnChunkGenerated); From 780a9ae9d121ebbf52276dea6545006822ad1c62 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sun, 1 Mar 2015 12:13:39 +0100 Subject: [PATCH 81/95] Added Steppy height generator. --- src/Generating/BioGen.cpp | 2 +- src/Generating/HeiGen.cpp | 64 +++++++ src/Generating/ProtIntGen.h | 344 ++++++++++++++++++++++++++++++++++++ 3 files changed, 409 insertions(+), 1 deletion(-) diff --git a/src/Generating/BioGen.cpp b/src/Generating/BioGen.cpp index 378ece6a3..265db30ad 100644 --- a/src/Generating/BioGen.cpp +++ b/src/Generating/BioGen.cpp @@ -1036,7 +1036,7 @@ protected: //////////////////////////////////////////////////////////////////////////////// -// cBioGenGrown: +// cBioGenProtGrown: class cBioGenProtGrown: public cBiomeGen diff --git a/src/Generating/HeiGen.cpp b/src/Generating/HeiGen.cpp index 61d087c17..defe053d9 100644 --- a/src/Generating/HeiGen.cpp +++ b/src/Generating/HeiGen.cpp @@ -10,6 +10,66 @@ #include "DistortedHeightmap.h" #include "EndGen.h" #include "Noise3DGenerator.h" +#include "ProtIntGen.h" + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cHeiGenSteppy: + +class cHeiGenSteppy: + public cTerrainHeightGen +{ +public: + cHeiGenSteppy(int a_Seed) : + m_Seed(a_Seed) + { + m_Gen = + std::make_shared>( + std::make_shared (a_Seed + 1, + std::make_shared (a_Seed + 2, + std::make_shared (a_Seed + 3, + std::make_shared (a_Seed + 4, + std::make_shared (a_Seed + 5, 1, + std::make_shared (a_Seed + 6, + std::make_shared (a_Seed + 7, + std::make_shared (a_Seed + 8, 60, + std::make_shared (a_Seed + 9, 1, + std::make_shared (a_Seed + 1, + std::make_shared (a_Seed + 2, + std::make_shared (a_Seed + 3, 60, + std::make_shared (a_Seed + 4, + std::make_shared (a_Seed + 5, + std::make_shared (a_Seed + 6, 60, + std::make_shared (a_Seed + 7, 10, 50, 50, + std::make_shared (a_Seed + 8, + std::make_shared (a_Seed + 9, + std::make_shared (a_Seed + 1, 10, 50, 50, + std::make_shared (a_Seed + 2, 2, + std::make_shared (a_Seed + 3, + std::make_shared (a_Seed + 4, + std::make_shared (a_Seed + 5, 10) + ))))))))))))))))))))))); + } + + // cTerrainHeightGen overrides: + virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override + { + int heights[cChunkDef::Width * cChunkDef::Width]; + m_Gen->GetInts(a_ChunkX * cChunkDef::Width, a_ChunkZ * cChunkDef::Width, cChunkDef::Width, cChunkDef::Width, heights); + for (auto i = 0; i < ARRAYCOUNT(heights); i++) + { + a_HeightMap[i] = static_cast(std::max(std::min(60 + heights[i], cChunkDef::Height - 60), 40)); + } + } + +protected: + int m_Seed; + + SharedPtr m_Gen; +}; @@ -821,6 +881,10 @@ cTerrainHeightGenPtr cTerrainHeightGen::CreateHeightGen(cIniFile & a_IniFile, cB // Return an empty pointer, the caller will create the proper generator: return cTerrainHeightGenPtr(); } + else if (NoCaseCompare(HeightGenName, "Steppy") == 0) + { + res = std::make_shared(a_Seed); + } else if (NoCaseCompare(HeightGenName, "Noise3D") == 0) { // Not a heightmap-based generator, but it used to be accessible via HeightGen, so we need to skip making the default out of it diff --git a/src/Generating/ProtIntGen.h b/src/Generating/ProtIntGen.h index 73ed27096..e709222fe 100644 --- a/src/Generating/ProtIntGen.h +++ b/src/Generating/ProtIntGen.h @@ -318,6 +318,350 @@ protected: +/** Averages the values of the underlying 2 * 2 neighbors. */ +class cProtIntGenAvgValues : + public cProtIntGen +{ + typedef cProtIntGen super; + +public: + cProtIntGenAvgValues(Underlying a_Underlying) : + m_Underlying(a_Underlying) + { + } + + + virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override + { + // Generate the underlying values: + int lowerSizeX = a_SizeX + 1; + int lowerSizeZ = a_SizeZ + 1; + ASSERT(lowerSizeX * lowerSizeZ <= m_BufferSize); + int lowerData[m_BufferSize]; + m_Underlying->GetInts(a_MinX, a_MinZ, lowerSizeX, lowerSizeZ, lowerData); + + // Average - add all 4 "neighbors" and divide by 4: + for (int z = 0; z < a_SizeZ; z++) + { + for (int x = 0; x < a_SizeX; x++) + { + int idxLower = x + lowerSizeX * z; + a_Values[x + a_SizeX * z] = ( + lowerData[idxLower] + lowerData[idxLower + 1] + + lowerData[idxLower + lowerSizeX] + lowerData[idxLower + lowerSizeX + 1] + ) / 4; + } + } + } + +protected: + Underlying m_Underlying; +}; + + + + + +/** Averages the values of the underlying 4 * 4 neighbors. */ +class cProtIntGenAvg4Values : + public cProtIntGen +{ + typedef cProtIntGen super; + +public: + cProtIntGenAvg4Values(Underlying a_Underlying) : + m_Underlying(a_Underlying) + { + } + + + virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override + { + // Generate the underlying values: + int lowerSizeX = a_SizeX + 4; + int lowerSizeZ = a_SizeZ + 4; + ASSERT(lowerSizeX * lowerSizeZ <= m_BufferSize); + int lowerData[m_BufferSize]; + m_Underlying->GetInts(a_MinX - 1, a_MinZ - 1, lowerSizeX, lowerSizeZ, lowerData); + + // Calculate the weighted average of all 16 "neighbors": + for (int z = 0; z < a_SizeZ; z++) + { + for (int x = 0; x < a_SizeX; x++) + { + int idxLower1 = x + lowerSizeX * z; + int idxLower2 = idxLower1 + lowerSizeX; + int idxLower3 = idxLower1 + 2 * lowerSizeX; + int idxLower4 = idxLower1 + 3 * lowerSizeX; + a_Values[x + a_SizeX * z] = ( + 1 * lowerData[idxLower1] + 2 * lowerData[idxLower1 + 1] + 2 * lowerData[idxLower1 + 2] + 1 * lowerData[idxLower1 + 3] + + 2 * lowerData[idxLower2] + 32 * lowerData[idxLower2 + 1] + 32 * lowerData[idxLower2 + 2] + 2 * lowerData[idxLower2 + 3] + + 2 * lowerData[idxLower3] + 32 * lowerData[idxLower3 + 1] + 32 * lowerData[idxLower3 + 2] + 2 * lowerData[idxLower3 + 3] + + 1 * lowerData[idxLower4] + 2 * lowerData[idxLower4 + 1] + 2 * lowerData[idxLower4 + 2] + 1 * lowerData[idxLower4 + 3] + ) / 148; + } + } + } + +protected: + Underlying m_Underlying; +}; + + + + + +/** Averages the values of the underlying 3 * 3 neighbors with custom weight. */ +template +class cProtIntGenWeightAvg : + public cProtIntGen +{ + typedef cProtIntGen super; + +public: + cProtIntGenWeightAvg(Underlying a_Underlying) : + m_Underlying(a_Underlying) + { + } + + + virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override + { + // Generate the underlying values: + int lowerSizeX = a_SizeX + 3; + int lowerSizeZ = a_SizeZ + 3; + ASSERT(lowerSizeX * lowerSizeZ <= m_BufferSize); + int lowerData[m_BufferSize]; + m_Underlying->GetInts(a_MinX, a_MinZ, lowerSizeX, lowerSizeZ, lowerData); + + // Calculate the weighted average the neighbors: + for (int z = 0; z < a_SizeZ; z++) + { + for (int x = 0; x < a_SizeX; x++) + { + int idxLower1 = x + lowerSizeX * z; + int idxLower2 = idxLower1 + lowerSizeX; + int idxLower3 = idxLower1 + 2 * lowerSizeX; + a_Values[x + a_SizeX * z] = ( + WeightDiagonal * lowerData[idxLower1] + WeightCardinal * lowerData[idxLower1 + 1] + WeightDiagonal * lowerData[idxLower1 + 2] + + WeightCardinal * lowerData[idxLower2] + WeightCenter * lowerData[idxLower2 + 1] + WeightCardinal * lowerData[idxLower2 + 2] + + WeightDiagonal * lowerData[idxLower3] + WeightCardinal * lowerData[idxLower3 + 1] + WeightDiagonal * lowerData[idxLower3 + 2] + ) / (4 * WeightDiagonal + 4 * WeightCardinal + WeightCenter); + } + } + } + +protected: + Underlying m_Underlying; +}; + + + + + +/** Replaces random values of the underlying data with random integers in the specified range [Min .. Min + Range). */ +class cProtIntGenRndChoice : + public cProtIntGenWithNoise +{ + typedef cProtIntGenWithNoise super; + +public: + cProtIntGenRndChoice(int a_Seed, int a_ChancePct, int a_Min, int a_Range, Underlying a_Underlying) : + super(a_Seed), + m_ChancePct(a_ChancePct), + m_Min(a_Min), + m_Range(a_Range), + m_Underlying(a_Underlying) + { + } + + + virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override + { + // Generate the underlying values: + m_Underlying->GetInts(a_MinX, a_MinZ, a_SizeX, a_SizeZ, a_Values); + + // Replace random values: + for (int z = 0; z < a_SizeZ; z++) + { + int BaseZ = a_MinZ + z; + for (int x = 0; x < a_SizeX; x++) + { + if (((super::m_Noise.IntNoise2DInt(BaseZ, a_MinX + x) / 13) % 101) < m_ChancePct) + { + a_Values[x + a_SizeX * z] = m_Min + (super::m_Noise.IntNoise2DInt(a_MinX + x, BaseZ) / 7) % m_Range; + } + } // for x + } // for z + } + +protected: + int m_ChancePct; + int m_Min; + int m_Range; + Underlying m_Underlying; +}; + + + + + +/** Adds a random value in range [-a_HalfRange, +a_HalfRange] to each of the underlying values. */ +class cProtIntGenAddRnd : + public cProtIntGenWithNoise +{ + typedef cProtIntGenWithNoise super; + +public: + cProtIntGenAddRnd(int a_Seed, int a_HalfRange, Underlying a_Underlying) : + super(a_Seed), + m_Range(a_HalfRange * 2 + 1), + m_HalfRange(a_HalfRange), + m_Underlying(a_Underlying) + { + } + + + virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override + { + // Generate the underlying values: + m_Underlying->GetInts(a_MinX, a_MinZ, a_SizeX, a_SizeZ, a_Values); + + // Add the random values: + for (int z = 0; z < a_SizeZ; z++) + { + int NoiseZ = a_MinZ + z; + for (int x = 0; x < a_SizeX; x++) + { + int noiseVal = ((super::m_Noise.IntNoise2DInt(a_MinX + x, NoiseZ) / 7) % m_Range) - m_HalfRange; + a_Values[x + z * a_SizeX] += noiseVal; + } + } + } + +protected: + int m_Range; + int m_HalfRange; + Underlying m_Underlying; +}; + + + + + +/** Replaces random underlying values with the average of the neighbors. */ +class cProtIntGenRndAvg : + public cProtIntGenWithNoise +{ + typedef cProtIntGenWithNoise super; + +public: + cProtIntGenRndAvg(int a_Seed, int a_AvgChancePct, Underlying a_Underlying) : + super(a_Seed), + m_AvgChancePct(a_AvgChancePct), + m_Underlying(a_Underlying) + { + } + + + virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override + { + // Generate the underlying values: + int lowerSizeX = a_SizeX + 2; + int lowerSizeZ = a_SizeZ + 2; + ASSERT(lowerSizeX * lowerSizeZ <= m_BufferSize); + int lowerData[m_BufferSize]; + m_Underlying->GetInts(a_MinX - 1, a_MinZ - 1, lowerSizeX, lowerSizeZ, lowerData); + + // Average random values: + for (int z = 0; z < a_SizeZ; z++) + { + int NoiseZ = a_MinZ + z; + for (int x = 0; x < a_SizeX; x++) + { + int idxLower = x + 1 + lowerSizeX * (z + 1); + if (((super::m_Noise.IntNoise2DInt(a_MinX + x, NoiseZ) / 7) % 100) > m_AvgChancePct) + { + // Average the 4 neighbors: + a_Values[x + z * a_SizeX] = ( + lowerData[idxLower - 1] + lowerData[idxLower + 1] + + lowerData[idxLower - lowerSizeX] + lowerData[idxLower + lowerSizeX] + ) / 4; + } + else + { + // Keep the underlying value: + a_Values[x + z * a_SizeX] = lowerData[idxLower]; + } + } + } + } + +protected: + int m_AvgChancePct; + Underlying m_Underlying; +}; + + + + + +/** Replaces random underlying values with a random value in between the max and min of the neighbors. */ +class cProtIntGenRndBetween : + public cProtIntGenWithNoise +{ + typedef cProtIntGenWithNoise super; + +public: + cProtIntGenRndBetween(int a_Seed, int a_AvgChancePct, Underlying a_Underlying) : + super(a_Seed), + m_AvgChancePct(a_AvgChancePct), + m_Underlying(a_Underlying) + { + } + + + virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override + { + // Generate the underlying values: + int lowerSizeX = a_SizeX + 2; + int lowerSizeZ = a_SizeZ + 2; + ASSERT(lowerSizeX * lowerSizeZ <= m_BufferSize); + int lowerData[m_BufferSize]; + m_Underlying->GetInts(a_MinX - 1, a_MinZ - 1, lowerSizeX, lowerSizeZ, lowerData); + + // Average random values: + for (int z = 0; z < a_SizeZ; z++) + { + int NoiseZ = a_MinZ + z; + for (int x = 0; x < a_SizeX; x++) + { + int idxLower = x + 1 + lowerSizeX * (z + 1); + if (((super::m_Noise.IntNoise2DInt(a_MinX + x, NoiseZ) / 7) % 100) > m_AvgChancePct) + { + // Chose a value in between the min and max neighbor: + int min = std::min(std::min(lowerData[idxLower - 1], lowerData[idxLower + 1]), std::min(lowerData[idxLower - lowerSizeX], lowerData[idxLower + lowerSizeX])); + int max = std::max(std::max(lowerData[idxLower - 1], lowerData[idxLower + 1]), std::max(lowerData[idxLower - lowerSizeX], lowerData[idxLower + lowerSizeX])); + a_Values[x + z * a_SizeX] = min + ((super::m_Noise.IntNoise2DInt(a_MinX + x, NoiseZ + 10) / 7) % (max - min + 1)); + } + else + { + // Keep the underlying value: + a_Values[x + z * a_SizeX] = lowerData[idxLower]; + } + } + } + } + +protected: + int m_AvgChancePct; + Underlying m_Underlying; +}; + + + + + /** Converts land biomes at the edge of an ocean into the respective beach biome. */ class cProtIntGenBeaches : public cProtIntGen From d2e1ed3a3be0da9244e12740c28f40cd19b9ccab Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sun, 1 Mar 2015 12:40:53 +0100 Subject: [PATCH 82/95] Steppy HeiGen: Fixed Linux compilation. --- src/Generating/HeiGen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Generating/HeiGen.cpp b/src/Generating/HeiGen.cpp index defe053d9..54567cb4d 100644 --- a/src/Generating/HeiGen.cpp +++ b/src/Generating/HeiGen.cpp @@ -59,7 +59,7 @@ public: { int heights[cChunkDef::Width * cChunkDef::Width]; m_Gen->GetInts(a_ChunkX * cChunkDef::Width, a_ChunkZ * cChunkDef::Width, cChunkDef::Width, cChunkDef::Width, heights); - for (auto i = 0; i < ARRAYCOUNT(heights); i++) + for (size_t i = 0; i < ARRAYCOUNT(heights); i++) { a_HeightMap[i] = static_cast(std::max(std::min(60 + heights[i], cChunkDef::Height - 60), 40)); } From e63f9bdc1a2cdb32e5e49fe325d3bad7317b5418 Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Sun, 1 Mar 2015 20:06:44 +0100 Subject: [PATCH 83/95] Replaced cFinishGenPtr with std::make_shared --- src/Generating/ComposableGenerator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Generating/ComposableGenerator.cpp b/src/Generating/ComposableGenerator.cpp index 42a805760..4a670b064 100644 --- a/src/Generating/ComposableGenerator.cpp +++ b/src/Generating/ComposableGenerator.cpp @@ -619,7 +619,7 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile) else if (NoCaseCompare(*itr, "Vines") == 0) { int Level = a_IniFile.GetValueSetI("Generator", "VinesLevel", 40); - m_FinishGens.push_back(cFinishGenPtr(new cFinishGenVines(Seed, Level))); + m_FinishGens.push_back(std::make_shared(Seed, Level)); } else if (NoCaseCompare(*itr, "WaterLakes") == 0) { From 3d3735a37864b3abf416ec5f9ae962f68392589d Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Sun, 1 Mar 2015 20:08:05 +0100 Subject: [PATCH 84/95] Replaced IntNoiseXX with IntNoiseXXInt --- src/Generating/FinishGen.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Generating/FinishGen.cpp b/src/Generating/FinishGen.cpp index 8790ac0e8..260253d62 100644 --- a/src/Generating/FinishGen.cpp +++ b/src/Generating/FinishGen.cpp @@ -281,7 +281,7 @@ void cFinishGenVines::GenFinish(cChunkDesc & a_ChunkDesc) continue; } - if (m_Noise.IntNoise2D(xx, zz) < 0.5) + if ((m_Noise.IntNoise2DInt(xx, zz) % 101) < 50) { continue; } @@ -295,7 +295,7 @@ void cFinishGenVines::GenFinish(cChunkDesc & a_ChunkDesc) continue; } - if (m_Noise.IntNoise3D(xx, y, zz) < 0.5) + if ((m_Noise.IntNoise3DInt(xx, y, zz) % 101) < 50) { continue; } From db2a406c13711ac751117793c9655255ec70731c Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Sun, 1 Mar 2015 20:09:44 +0100 Subject: [PATCH 85/95] Removed trailing whitespace --- src/Generating/FinishGen.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Generating/FinishGen.h b/src/Generating/FinishGen.h index 950406872..70696c4f8 100644 --- a/src/Generating/FinishGen.h +++ b/src/Generating/FinishGen.h @@ -122,7 +122,7 @@ class cFinishGenVines : public cFinishGen { public: - cFinishGenVines(int a_Seed, int a_Level) : + cFinishGenVines(int a_Seed, int a_Level) : m_Noise(a_Seed), m_Level(a_Level) { From d4b505db025b05379ac9a5df923a60f0d37eed9a Mon Sep 17 00:00:00 2001 From: Mattes D Date: Tue, 3 Mar 2015 01:28:58 +0100 Subject: [PATCH 86/95] Lua API: Fixed md5 and sha1 hex formatting. std::setw() is only valid for one output operation and needs to be set again in each loop repetition. --- src/Bindings/ManualBindings.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp index a6ae4869b..cac81f325 100644 --- a/src/Bindings/ManualBindings.cpp +++ b/src/Bindings/ManualBindings.cpp @@ -2392,10 +2392,10 @@ static int tolua_md5HexString(lua_State * tolua_S) // Convert the md5 checksum to hex string: std::stringstream Output; - Output << std::hex << std::setw(2) << std::setfill('0'); + Output << std::hex << std::setfill('0'); for (size_t i = 0; i < ARRAYCOUNT(md5Output); i++) { - Output << static_cast(md5Output[i]); // Need to cast to a number, otherwise a char is output + Output << std::setw(2) << static_cast(md5Output[i]); // Need to cast to a number, otherwise a char is output } lua_pushlstring(tolua_S, Output.str().c_str(), Output.str().size()); return 1; @@ -2438,10 +2438,10 @@ static int tolua_sha1HexString(lua_State * tolua_S) // Convert the sha1 checksum to hex string: std::stringstream Output; - Output << std::hex << std::setw(2) << std::setfill('0'); + Output << std::hex << std::setfill('0'); for (size_t i = 0; i < ARRAYCOUNT(sha1Output); i++) { - Output << static_cast(sha1Output[i]); // Need to cast to a number, otherwise a char is output + Output << std::setw(2) << static_cast(sha1Output[i]); // Need to cast to a number, otherwise a char is output } lua_pushlstring(tolua_S, Output.str().c_str(), Output.str().size()); return 1; From f71b1fe799eb944b9488019da134a6cc34675605 Mon Sep 17 00:00:00 2001 From: joshi07 Date: Thu, 5 Mar 2015 11:52:42 +0100 Subject: [PATCH 87/95] Added OnTeleportEntity hook for plugins. Plugins may or may not allow teleport to the new position. Updated the HookNotify plugin with it. --- MCServer/Plugins/HookNotify/HookNotify.lua | 17 +++++++++++++++ src/Bindings/Plugin.h | 1 + src/Bindings/PluginLua.cpp | 21 ++++++++++++++++++ src/Bindings/PluginLua.h | 1 + src/Bindings/PluginManager.cpp | 18 ++++++++++++++++ src/Bindings/PluginManager.h | 2 ++ src/Entities/Entity.cpp | 8 +++++-- src/Entities/Player.cpp | 25 +++++++++++++--------- 8 files changed, 81 insertions(+), 12 deletions(-) diff --git a/MCServer/Plugins/HookNotify/HookNotify.lua b/MCServer/Plugins/HookNotify/HookNotify.lua index ed791090e..1d3d5088e 100644 --- a/MCServer/Plugins/HookNotify/HookNotify.lua +++ b/MCServer/Plugins/HookNotify/HookNotify.lua @@ -23,6 +23,7 @@ function Initialize(Plugin) cPluginManager.AddHook(cPluginManager.HOOK_COLLECTING_PICKUP, OnCollectingPickup); cPluginManager.AddHook(cPluginManager.HOOK_CRAFTING_NO_RECIPE, OnCraftingNoRecipe); cPluginManager.AddHook(cPluginManager.HOOK_DISCONNECT, OnDisconnect); + cPluginManager.AddHook(cPluginManager.HOOK_ENTITY_TELEPORT, OnEntityTeleport); cPluginManager.AddHook(cPluginManager.HOOK_EXECUTE_COMMAND, OnExecuteCommand); cPluginManager.AddHook(cPluginManager.HOOK_HANDSHAKE, OnHandshake); cPluginManager.AddHook(cPluginManager.HOOK_KILLING, OnKilling); @@ -179,6 +180,22 @@ end +function OnEntityTeleport(arg1,arg2,arg3) + if arg1.IsPlayer() then + -- if it's a player, get his name + LOG("OnEntityTeleport: Player: " .. arg1.GetName()); + else + -- if it's a entity, get its type + LOG("OnEntityTeleport: EntityType: " .. arg1.GetEntityType()); + end + LOG("OldPos: " .. arg2.x .. " / " .. arg2.y .. " / " .. arg2.z); + LOG("NewPos: " .. arg3.x .. " / " .. arg3.y .. " / " .. arg3.z); +end + + + + + function OnExecuteCommand(...) LogHook("OnExecuteCommand", unpack(arg)); diff --git a/src/Bindings/Plugin.h b/src/Bindings/Plugin.h index 6210dbed4..6ade8ef9f 100644 --- a/src/Bindings/Plugin.h +++ b/src/Bindings/Plugin.h @@ -57,6 +57,7 @@ public: virtual bool OnCraftingNoRecipe (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe) = 0; virtual bool OnDisconnect (cClientHandle & a_Client, const AString & a_Reason) = 0; virtual bool OnEntityAddEffect (cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier) = 0; + virtual bool OnEntityTeleport (cEntity & a_Entity, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition) = 0; virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split) = 0; virtual bool OnExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) = 0; virtual bool OnExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) = 0; diff --git a/src/Bindings/PluginLua.cpp b/src/Bindings/PluginLua.cpp index 500913e76..fb7650d42 100644 --- a/src/Bindings/PluginLua.cpp +++ b/src/Bindings/PluginLua.cpp @@ -857,6 +857,26 @@ bool cPluginLua::OnPlayerMoving(cPlayer & a_Player, const Vector3d & a_OldPositi +bool cPluginLua::OnEntityTeleport(cEntity & a_Entity, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_ENTITY_TELEPORT]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_Entity, a_OldPosition, a_NewPosition, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + bool cPluginLua::OnPlayerPlacedBlock(cPlayer & a_Player, const sSetBlock & a_BlockChange) { cCSLock Lock(m_CriticalSection); @@ -1577,6 +1597,7 @@ const char * cPluginLua::GetHookFnName(int a_HookType) case cPluginManager::HOOK_DISCONNECT: return "OnDisconnect"; case cPluginManager::HOOK_PLAYER_ANIMATION: return "OnPlayerAnimation"; case cPluginManager::HOOK_ENTITY_ADD_EFFECT: return "OnEntityAddEffect"; + case cPluginManager::HOOK_ENTITY_TELEPORT: return "OnEntityTeleport"; case cPluginManager::HOOK_EXECUTE_COMMAND: return "OnExecuteCommand"; case cPluginManager::HOOK_HANDSHAKE: return "OnHandshake"; case cPluginManager::HOOK_KILLING: return "OnKilling"; diff --git a/src/Bindings/PluginLua.h b/src/Bindings/PluginLua.h index f443f5fc0..7b528501b 100644 --- a/src/Bindings/PluginLua.h +++ b/src/Bindings/PluginLua.h @@ -106,6 +106,7 @@ public: virtual bool OnPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Entity) override; virtual bool OnPlayerShooting (cPlayer & a_Player) override; virtual bool OnPlayerSpawned (cPlayer & a_Player) override; + virtual bool OnEntityTeleport (cEntity & a_Entity, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition) override; virtual bool OnPlayerTossingItem (cPlayer & a_Player) override; virtual bool OnPlayerUsedBlock (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 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; diff --git a/src/Bindings/PluginManager.cpp b/src/Bindings/PluginManager.cpp index 9d86c64a2..41b36337e 100644 --- a/src/Bindings/PluginManager.cpp +++ b/src/Bindings/PluginManager.cpp @@ -505,6 +505,24 @@ bool cPluginManager::CallHookEntityAddEffect(cEntity & a_Entity, int a_EffectTyp +bool cPluginManager::CallHookEntityTeleport(cEntity & a_Entity, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition) +{ + FIND_HOOK(HOOK_ENTITY_TELEPORT); + VERIFY_HOOK; + + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnEntityTeleport(a_Entity, a_OldPosition, a_NewPosition)) + { + return true; + } + } + return false; +} + + + + bool cPluginManager::CallHookExecuteCommand(cPlayer * a_Player, const AStringVector & a_Split) { FIND_HOOK(HOOK_EXECUTE_COMMAND); diff --git a/src/Bindings/PluginManager.h b/src/Bindings/PluginManager.h index 97e91c1df..c8b4de9d6 100644 --- a/src/Bindings/PluginManager.h +++ b/src/Bindings/PluginManager.h @@ -109,6 +109,7 @@ public: HOOK_PLAYER_RIGHT_CLICKING_ENTITY, HOOK_PLAYER_SHOOTING, HOOK_PLAYER_SPAWNED, + HOOK_ENTITY_TELEPORT, HOOK_PLAYER_TOSSING_ITEM, HOOK_PLAYER_USED_BLOCK, HOOK_PLAYER_USED_ITEM, @@ -190,6 +191,7 @@ public: bool CallHookCraftingNoRecipe (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe); bool CallHookDisconnect (cClientHandle & a_Client, const AString & a_Reason); bool CallHookEntityAddEffect (cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier); + bool CallHookEntityTeleport (cEntity & a_Entity, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition); bool CallHookExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split); // If a_Player == nullptr, it is a console cmd bool CallHookExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData); bool CallHookExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData); diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp index 07cfb97b2..1bc4690e1 100644 --- a/src/Entities/Entity.cpp +++ b/src/Entities/Entity.cpp @@ -1632,8 +1632,12 @@ void cEntity::TeleportToEntity(cEntity & a_Entity) void cEntity::TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ) { - SetPosition(a_PosX, a_PosY, a_PosZ); - m_World->BroadcastTeleportEntity(*this); + // ask the plugins to allow teleport to the new position. + if (!cRoot::Get()->GetPluginManager()->CallHookEntityTeleport(*this, m_LastPos, Vector3d(a_PosX, a_PosY, a_PosZ))) + { + SetPosition(a_PosX, a_PosY, a_PosZ); + m_World->BroadcastTeleportEntity(*this); + } } diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index e1d9f4550..0d36d0b23 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -232,7 +232,7 @@ void cPlayer::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) } bool CanMove = true; - if (!GetPosition().EqualsEps(m_LastPos, 0.01)) // Non negligible change in position from last tick? + if (!GetPosition().EqualsEps(m_LastPos, 0.02)) // Non negligible change in position from last tick? 0.02 tp prevent continous calling while floating sometimes. { // Apply food exhaustion from movement: ApplyFoodExhaustionFromMovement(); @@ -396,6 +396,7 @@ int cPlayer::DeltaExperience(int a_Xp_delta) // Make sure they didn't subtract too much m_CurrentXp = std::max(m_CurrentXp, 0); + // Update total for score calculation if (a_Xp_delta > 0) { @@ -1274,13 +1275,17 @@ unsigned int cPlayer::AwardAchievement(const eStatistic a_Ach) void cPlayer::TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ) { - SetPosition(a_PosX, a_PosY, a_PosZ); - m_LastGroundHeight = static_cast(a_PosY); - m_LastJumpHeight = static_cast(a_PosY); - m_bIsTeleporting = true; + // ask plugins to allow teleport to the new position. + if (!cRoot::Get()->GetPluginManager()->CallHookEntityTeleport(*this, m_LastPos, Vector3d(a_PosX, a_PosY, a_PosZ))) + { + SetPosition(a_PosX, a_PosY, a_PosZ); + m_LastGroundHeight = static_cast(a_PosY); + m_LastJumpHeight = static_cast(a_PosY); + m_bIsTeleporting = true; - m_World->BroadcastTeleportEntity(*this, GetClientHandle()); - m_ClientHandle->SendPlayerMoveLook(); + m_World->BroadcastTeleportEntity(*this, GetClientHandle()); + m_ClientHandle->SendPlayerMoveLook(); + } } @@ -1725,9 +1730,9 @@ bool cPlayer::LoadFromFile(const AString & a_FileName, cWorldPtr & a_World) m_FoodSaturationLevel = root.get("foodSaturation", MAX_FOOD_LEVEL).asDouble(); m_FoodTickTimer = root.get("foodTickTimer", 0).asInt(); m_FoodExhaustionLevel = root.get("foodExhaustion", 0).asDouble(); - m_LifetimeTotalXp = root.get("xpTotal", 0).asInt(); - m_CurrentXp = root.get("xpCurrent", 0).asInt(); - m_IsFlying = root.get("isflying", 0).asBool(); + m_LifetimeTotalXp = root.get("xpTotal", 0).asInt(); + m_CurrentXp = root.get("xpCurrent", 0).asInt(); + m_IsFlying = root.get("isflying", 0).asBool(); m_GameMode = (eGameMode) root.get("gamemode", eGameMode_NotSet).asInt(); From a5e1f970a6369121a88a96e0f37328d88587dea8 Mon Sep 17 00:00:00 2001 From: joshi07 Date: Thu, 5 Mar 2015 19:44:15 +0100 Subject: [PATCH 88/95] Added description to APIDump for OnEntityTeleport --- MCServer/Plugins/APIDump/APIDesc.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MCServer/Plugins/APIDump/APIDesc.lua b/MCServer/Plugins/APIDump/APIDesc.lua index f2ec2546a..a7a597fb7 100644 --- a/MCServer/Plugins/APIDump/APIDesc.lua +++ b/MCServer/Plugins/APIDump/APIDesc.lua @@ -925,8 +925,8 @@ local Hash = cCryptoHash.sha1HexString("DataToHash") { Params = "DamageType, AttackerEntity, RawDamage, KnockbackAmount", Return = "", Notes = "Causes this entity to take damage of the specified type, from the specified attacker (may be nil). The final damage is calculated from RawDamage using the currently equipped armor." }, { Params = "DamageType, ArrackerEntity, RawDamage, FinalDamage, KnockbackAmount", Return = "", Notes = "Causes this entity to take damage of the specified type, from the specified attacker (may be nil). The values are wrapped into a {{TakeDamageInfo}} structure and applied directly." }, }, - TeleportToCoords = { Params = "PosX, PosY, PosZ", Return = "", Notes = "Teleports the entity to the specified coords." }, - TeleportToEntity = { Params = "DestEntity", Return = "", Notes = "Teleports this entity to the specified destination entity." }, + TeleportToCoords = { Params = "PosX, PosY, PosZ", Return = "", Notes = "Teleports the entity to the specified coords. Asks plugins if the teleport is allowed." }, + TeleportToEntity = { Params = "DestEntity", Return = "", Notes = "Teleports this entity to the specified destination entity. Asks plugins if the teleport is allowed." }, }, Constants = { From 3cef52a7f722e9ecc0e71f6e7a7e7b991dbf6dac Mon Sep 17 00:00:00 2001 From: joshi07 Date: Thu, 5 Mar 2015 20:08:32 +0100 Subject: [PATCH 89/95] Added OnEntityTeleport.lua hook to APIDump --- .../APIDump/Hooks/OnEntityTeleport.lua | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 MCServer/Plugins/APIDump/Hooks/OnEntityTeleport.lua diff --git a/MCServer/Plugins/APIDump/Hooks/OnEntityTeleport.lua b/MCServer/Plugins/APIDump/Hooks/OnEntityTeleport.lua new file mode 100644 index 000000000..cdeb0947f --- /dev/null +++ b/MCServer/Plugins/APIDump/Hooks/OnEntityTeleport.lua @@ -0,0 +1,29 @@ +return +{ + HOOK_ENTITY_TELEPORT = + { + CalledWhen = "Any entity teleports. Plugin may refuse teleport.", + DefaultFnName = "OnEntityTeleport", -- also used as pagename + Desc = [[ + This function is called in each server tick for each {{cEntity|Entity}} that has + teleported. Plugins may refuse the teleport. + ]], + Params = + { + { Name = "Entity", Type = "{{cEntity}}", Notes = "The entity who has teleported. New position is set in the object after successfull teleport" }, + { Name = "OldPosition", Type = "{{Vector3d}}", Notes = "The old position." }, + { Name = "NewPosition", Type = "{{Vector3d}}", Notes = "The new position." }, + }, + Returns = [[ + If the function returns true, teleport is prohibited.

+

+ If the function returns false or no value, other plugins' callbacks are called and finally the new + position is permanently stored in the cEntity object.

+ ]], + }, -- HOOK_ENTITY_TELEPORT +} + + + + + From 7348bf38534bfdfa0c82a103474c4488bf21fe9e Mon Sep 17 00:00:00 2001 From: Alexander Harkness Date: Thu, 5 Mar 2015 19:29:57 +0000 Subject: [PATCH 90/95] Grass checks for sufficient light before spreading --- src/Blocks/BlockDirt.h | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/Blocks/BlockDirt.h b/src/Blocks/BlockDirt.h index aae6719e2..12bca92dd 100644 --- a/src/Blocks/BlockDirt.h +++ b/src/Blocks/BlockDirt.h @@ -52,7 +52,19 @@ public: return; } } - + + // Make sure that there is enough light at the source block to spread + if (!a_Chunk.GetWorld()->IsChunkLighted(a_Chunk.GetPosX(), a_Chunk.GetPosZ())) + { + a_Chunk.GetWorld()->QueueLightChunk(a_Chunk.GetPosX(), a_Chunk.GetPosZ()); + return; + } + else if (std::max(a_Chunk.GetBlockLight(a_RelX, a_RelY + 1, a_RelZ), a_Chunk.GetTimeAlteredLight(a_Chunk.GetSkyLight(a_RelX, a_RelY + 1, a_RelZ))) < 9) + { + // Source block is not bright enough to spread + return; + } + // Grass spreads to adjacent dirt blocks: cFastRandom rand; for (int i = 0; i < 2; i++) // Pick two blocks to grow to From ebaf1a870c92c6eb272c86bfe70921288387435e Mon Sep 17 00:00:00 2001 From: Alexander Harkness Date: Thu, 5 Mar 2015 21:05:05 +0000 Subject: [PATCH 91/95] Updated license year. --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 69b3c7390..d8a8ec1e4 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,7 @@ MCServer: A performant C++ Minecraft Server www: http://mc-server.org/ -Copyright 2014 MCServer Team +Copyright 2011-2015 MCServer Team ------ From 134246fb152740c5f08abb583d399ed7d097faae Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Sun, 8 Mar 2015 13:29:49 +0100 Subject: [PATCH 92/95] Added grass/course dirt layer on MesaPlateauF(M) --- src/Generating/CompoGenBiomal.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/Generating/CompoGenBiomal.cpp b/src/Generating/CompoGenBiomal.cpp index 030c2baa5..315ccae73 100644 --- a/src/Generating/CompoGenBiomal.cpp +++ b/src/Generating/CompoGenBiomal.cpp @@ -542,6 +542,20 @@ protected: HasHadWater = true; } // for y a_ChunkDesc.SetBlockType(a_RelX, 0, a_RelZ, E_BLOCK_BEDROCK); + + EMCSBiome MesaVersion = a_ChunkDesc.GetBiome(a_RelX, a_RelZ); + if ((MesaVersion == biMesaPlateauF) || (MesaVersion == biMesaPlateauFM)) + { + if (Top < 95 + static_cast(m_MesaFloor.CubicNoise2D(NoiseY * 2, NoiseX * 2) * 6)) + { + return; + } + + BLOCKTYPE Block = m_MesaFloor.CubicNoise2D(NoiseX * 4, NoiseY * 4) < 0 ? E_BLOCK_DIRT : E_BLOCK_GRASS; + NIBBLETYPE Meta = Block == E_BLOCK_GRASS ? 0 : 1; + + a_ChunkDesc.SetBlockTypeMeta(a_RelX, Top, a_RelZ, Block, Meta); + } } From 2bbfd0341f1faae711064f64372a74b8c98c5840 Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Sun, 8 Mar 2015 13:30:21 +0100 Subject: [PATCH 93/95] Added proper trees to Mesa biomes Mesa only has small apple trees. --- src/Generating/Trees.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/Generating/Trees.cpp b/src/Generating/Trees.cpp index a10e0f4f1..9e72a688f 100644 --- a/src/Generating/Trees.cpp +++ b/src/Generating/Trees.cpp @@ -224,9 +224,6 @@ void GetTreeImageByBiome(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_No case biMegaTaiga: case biMegaTaigaHills: case biExtremeHillsPlus: - case biMesa: - case biMesaPlateauF: - case biMesaPlateau: case biSunflowerPlains: case biDesertM: case biExtremeHillsM: @@ -239,9 +236,6 @@ void GetTreeImageByBiome(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_No case biMegaSpruceTaiga: case biMegaSpruceTaigaHills: case biExtremeHillsPlusM: - case biMesaBryce: - case biMesaPlateauFM: - case biMesaPlateauM: { // TODO: These need their special trees GetBirchTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks); @@ -264,6 +258,16 @@ void GetTreeImageByBiome(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_No return; } + case biMesa: + case biMesaPlateauF: + case biMesaPlateau: + case biMesaBryce: + case biMesaPlateauFM: + case biMesaPlateauM: + { + GetSmallAppleTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks); + } + case biDesert: case biDesertHills: case biRiver: From d19f2a472b2ba4d7ae32a2b5cf2507a446ea7806 Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Sun, 8 Mar 2015 15:22:01 +0100 Subject: [PATCH 94/95] Added parenthesis around the comparisons --- src/Generating/CompoGenBiomal.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Generating/CompoGenBiomal.cpp b/src/Generating/CompoGenBiomal.cpp index 315ccae73..3140bd754 100644 --- a/src/Generating/CompoGenBiomal.cpp +++ b/src/Generating/CompoGenBiomal.cpp @@ -551,8 +551,8 @@ protected: return; } - BLOCKTYPE Block = m_MesaFloor.CubicNoise2D(NoiseX * 4, NoiseY * 4) < 0 ? E_BLOCK_DIRT : E_BLOCK_GRASS; - NIBBLETYPE Meta = Block == E_BLOCK_GRASS ? 0 : 1; + BLOCKTYPE Block = (m_MesaFloor.CubicNoise2D(NoiseX * 4, NoiseY * 4) < 0) ? E_BLOCK_DIRT : E_BLOCK_GRASS; + NIBBLETYPE Meta = (Block == E_BLOCK_GRASS) ? 0 : 1; a_ChunkDesc.SetBlockTypeMeta(a_RelX, Top, a_RelZ, Block, Meta); } From ce6219530a2a3e6550967b21bad8ad9a2145c465 Mon Sep 17 00:00:00 2001 From: Howaner Date: Mon, 9 Mar 2015 22:32:12 +0100 Subject: [PATCH 95/95] Fixed client kick/crash if many block changes happend --- src/Chunk.cpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/Chunk.cpp b/src/Chunk.cpp index e05fa4a99..00ac1fdb1 100644 --- a/src/Chunk.cpp +++ b/src/Chunk.cpp @@ -776,10 +776,22 @@ void cChunk::BroadcastPendingBlockChanges(void) { return; } - - for (cClientHandleList::iterator itr = m_LoadedByClient.begin(), end = m_LoadedByClient.end(); itr != end; ++itr) + + if (m_PendingSendBlocks.size() >= 10240) { - (*itr)->SendBlockChanges(m_PosX, m_PosZ, m_PendingSendBlocks); + // Resend the full chunk + for (cClientHandleList::iterator itr = m_LoadedByClient.begin(), end = m_LoadedByClient.end(); itr != end; ++itr) + { + m_World->ForceSendChunkTo(m_PosX, m_PosZ, cChunkSender::E_CHUNK_PRIORITY_MEDIUM, (*itr)); + } + } + else + { + // Only send block changes + for (cClientHandleList::iterator itr = m_LoadedByClient.begin(), end = m_LoadedByClient.end(); itr != end; ++itr) + { + (*itr)->SendBlockChanges(m_PosX, m_PosZ, m_PendingSendBlocks); + } } m_PendingSendBlocks.clear(); }