From 08d84080fa50426b77f79f5b55bbf5fb5d3ef718 Mon Sep 17 00:00:00 2001 From: Raekye Date: Sun, 25 Jan 2015 18:23:30 -0500 Subject: filter out leaves meta changes from being sent to the client --- src/Chunk.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src') diff --git a/src/Chunk.cpp b/src/Chunk.cpp index 979492b46..f5a1f9900 100644 --- a/src/Chunk.cpp +++ b/src/Chunk.cpp @@ -1570,6 +1570,10 @@ void cChunk::FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockT if ( a_SendToClients && // ... we are told to do so AND ... ( + !( // ... the old and new blocktypes AREN'T leaves (because the client doesn't need meta updates) + ((OldBlockType == E_BLOCK_LEAVES) && (a_BlockType == E_BLOCK_LEAVES)) || + ((OldBlockType == E_BLOCK_NEW_LEAVES) && (a_BlockType == E_BLOCK_NEW_LEAVES)) + ) || // ... OR ... (OldBlockMeta != a_BlockMeta) || // ... the meta value is different OR ... !( // ... the old and new blocktypes AREN'T liquids (because client doesn't need to distinguish betwixt them): ((OldBlockType == E_BLOCK_STATIONARY_WATER) && (a_BlockType == E_BLOCK_WATER)) || // Replacing stationary water with water -- cgit v1.2.3 From 82a10116ddc60ecf1fac33ee89c0c234a80b5cfb Mon Sep 17 00:00:00 2001 From: Raekye Date: Sun, 25 Jan 2015 19:13:55 -0500 Subject: fix logic bug in cChunk::FastSetBlock filtering out leaves meta changes - send update if it's not a leaves block and (the original checks) --- src/Chunk.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/Chunk.cpp b/src/Chunk.cpp index f5a1f9900..a4198c322 100644 --- a/src/Chunk.cpp +++ b/src/Chunk.cpp @@ -1573,13 +1573,15 @@ void cChunk::FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockT !( // ... the old and new blocktypes AREN'T leaves (because the client doesn't need meta updates) ((OldBlockType == E_BLOCK_LEAVES) && (a_BlockType == E_BLOCK_LEAVES)) || ((OldBlockType == E_BLOCK_NEW_LEAVES) && (a_BlockType == E_BLOCK_NEW_LEAVES)) - ) || // ... OR ... - (OldBlockMeta != a_BlockMeta) || // ... the meta value is different OR ... - !( // ... the old and new blocktypes AREN'T liquids (because client doesn't need to distinguish betwixt them): - ((OldBlockType == E_BLOCK_STATIONARY_WATER) && (a_BlockType == E_BLOCK_WATER)) || // Replacing stationary water with water - ((OldBlockType == E_BLOCK_WATER) && (a_BlockType == E_BLOCK_STATIONARY_WATER)) || // Replacing water with stationary water - ((OldBlockType == E_BLOCK_STATIONARY_LAVA) && (a_BlockType == E_BLOCK_LAVA)) || // Replacing stationary water with water - ((OldBlockType == E_BLOCK_LAVA) && (a_BlockType == E_BLOCK_STATIONARY_LAVA)) // Replacing water with stationary water + ) && // ... AND ... + ( + (OldBlockMeta != a_BlockMeta) || // ... the meta value is different OR ... + !( // ... the old and new blocktypes AREN'T liquids (because client doesn't need to distinguish betwixt them): + ((OldBlockType == E_BLOCK_STATIONARY_WATER) && (a_BlockType == E_BLOCK_WATER)) || // Replacing stationary water with water + ((OldBlockType == E_BLOCK_WATER) && (a_BlockType == E_BLOCK_STATIONARY_WATER)) || // Replacing water with stationary water + ((OldBlockType == E_BLOCK_STATIONARY_LAVA) && (a_BlockType == E_BLOCK_LAVA)) || // Replacing stationary water with water + ((OldBlockType == E_BLOCK_LAVA) && (a_BlockType == E_BLOCK_STATIONARY_LAVA)) // Replacing water with stationary water + ) ) ) ) -- cgit v1.2.3 From 40e231bc29e453ecb685b02204cec46c458b80d7 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sat, 24 Jan 2015 09:39:14 +0100 Subject: StringUtils: Added string vector manipulation. --- src/StringUtils.cpp | 44 ++++++++++++++++++++++++++++++++++++++++++++ src/StringUtils.h | 11 ++++++++++- 2 files changed, 54 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/StringUtils.cpp b/src/StringUtils.cpp index a63525356..48486a762 100644 --- a/src/StringUtils.cpp +++ b/src/StringUtils.cpp @@ -905,3 +905,47 @@ bool SplitZeroTerminatedStrings(const AString & a_Strings, AStringVector & a_Out + +AStringVector MergeStringVectors(const AStringVector & a_Strings1, const AStringVector & a_Strings2) +{ + // Initialize the resulting vector by the first vector: + AStringVector res = a_Strings1; + + // Add each item from strings2 that is not already present: + for (auto item : a_Strings2) + { + if (std::find(res.begin(), res.end(), item) != res.end()) + { + res.push_back(item); + } + } // for item - a_Strings2[] + + return res; +} + + + + + +AString StringsConcat(const AStringVector & a_Strings, char a_Separator) +{ + // If the vector is empty, return an empty string: + if (a_Strings.empty()) + { + return ""; + } + + // Concatenate the strings in the vector: + AString res; + res.append(a_Strings[0]); + for (auto itr = a_Strings.cbegin() + 1, end = a_Strings.cend(); itr != end; ++itr) + { + res.push_back(a_Separator); + res.append(*itr); + } + return res; +} + + + + diff --git a/src/StringUtils.h b/src/StringUtils.h index bfe2a41fa..bc3bb7a2c 100644 --- a/src/StringUtils.h +++ b/src/StringUtils.h @@ -115,7 +115,16 @@ a_Output is first cleared and then each separate string is pushed back into a_Ou Returns true if there are at least two strings in a_Output (there was at least one \0 separator). */ extern bool SplitZeroTerminatedStrings(const AString & a_Strings, AStringVector & a_Output); -/// Parses any integer type. Checks bounds and returns errors out of band. +/** Merges the two vectors of strings, removing duplicate entries from the second vector. +The resulting vector contains items from a_Strings1 first, then from a_Strings2. +The order of items doesn't change, only the duplicates are removed. +If a_Strings1 contains duplicates, the result will still contain those duplicates. */ +extern AStringVector MergeStringVectors(const AStringVector & a_Strings1, const AStringVector & a_Strings2); + +/** Concatenates the specified strings into a single string, separated by the specified separator. */ +extern AString StringsConcat(const AStringVector & a_Strings, char a_Separator); + +/** Parses any integer type. Checks bounds and returns errors out of band. */ template bool StringToInteger(const AString & a_str, T & a_Num) { -- cgit v1.2.3 From 7990d223eaef21e095c8b25389ffa20ff3137316 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sat, 24 Jan 2015 09:45:07 +0100 Subject: IniFile: Added common code to migrate ports. Old settings have IPv4 and IPv6 ports as separate values, new settings have a single value for the ports. --- src/IniFile.cpp | 36 ++++++++++++++++++++++++++++++++++++ src/IniFile.h | 24 ++++++++++++++++++++---- 2 files changed, 56 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/IniFile.cpp b/src/IniFile.cpp index ded7e4199..3a213a90e 100644 --- a/src/IniFile.cpp +++ b/src/IniFile.cpp @@ -888,3 +888,39 @@ void cIniFile::RemoveBom(AString & a_line) const + +AStringVector ReadUpgradeIniPorts( + cIniFile & a_IniFile, + const AString & a_KeyName, + const AString & a_PortsValueName, + const AString & a_OldIPv4ValueName, + const AString & a_OldIPv6ValueName, + const AString & a_DefaultValue +) +{ + // Read the regular value, but don't use the default (in order to detect missing value for upgrade): + AStringVector Ports = StringSplitAndTrim(a_IniFile.GetValue(a_KeyName, a_PortsValueName), ";,"); + + if (Ports.empty()) + { + // Historically there were two separate entries for IPv4 and IPv6, merge them and migrate: + AString Ports4 = a_IniFile.GetValue(a_KeyName, a_OldIPv4ValueName, a_DefaultValue); + AString Ports6 = a_IniFile.GetValue(a_KeyName, a_OldIPv6ValueName); + Ports = MergeStringVectors(StringSplitAndTrim(Ports4, ";,"), StringSplitAndTrim(Ports6, ";,")); + a_IniFile.DeleteValue(a_KeyName, a_OldIPv4ValueName); + a_IniFile.DeleteValue(a_KeyName, a_OldIPv6ValueName); + + // If those weren't present or were empty, use the default:" + if (Ports.empty()) + { + Ports = StringSplitAndTrim(a_DefaultValue, ";,"); + } + a_IniFile.SetValue(a_KeyName, a_PortsValueName, StringsConcat(Ports, ',')); + } + + return Ports; +} + + + + diff --git a/src/IniFile.h b/src/IniFile.h index e5879f46c..3e717723f 100644 --- a/src/IniFile.h +++ b/src/IniFile.h @@ -15,9 +15,7 @@ !! MODIFIED BY FAKETRUTH and madmaxoft!! */ -#ifndef CIniFile_H -#define CIniFile_H - +#pragma once @@ -215,4 +213,22 @@ public: // tolua_end -#endif + + + + +/** Reads the list of ports from the INI file, possibly upgrading from IPv4/IPv6-specific values into new version-agnostic value. +Reads the list of ports from a_PortsValueName. If that value doesn't exist or is empty, the list is combined from values +in a_OldIPv4ValueName and a_OldIPv6ValueName; in this case the old values are removed from the INI file. +If there is none of the three values or they are all empty, the default is used and stored in the Ports value. */ +AStringVector ReadUpgradeIniPorts( + cIniFile & a_IniFile, + const AString & a_KeyName, + const AString & a_PortsValueName, + const AString & a_OldIPv4ValueName, + const AString & a_OldIPv6ValueName, + const AString & a_DefaultValue +); + + + -- cgit v1.2.3 From f477b524bb2ebcacab705fca036d555cfa8f8e8d Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sat, 24 Jan 2015 10:11:34 +0100 Subject: WebAdmin uses the new cNetwork API. --- src/HTTPServer/HTTPConnection.cpp | 86 +++++++++++++--------- src/HTTPServer/HTTPConnection.h | 39 ++++++---- src/HTTPServer/HTTPServer.cpp | 134 +++++++++++++++++------------------ src/HTTPServer/HTTPServer.h | 52 ++++++-------- src/HTTPServer/SslHTTPConnection.cpp | 33 ++++----- src/HTTPServer/SslHTTPConnection.h | 4 +- src/WebAdmin.cpp | 75 +++++++++++--------- src/WebAdmin.h | 17 +++-- 8 files changed, 237 insertions(+), 203 deletions(-) (limited to 'src') diff --git a/src/HTTPServer/HTTPConnection.cpp b/src/HTTPServer/HTTPConnection.cpp index d5dbf0199..de12b36ce 100644 --- a/src/HTTPServer/HTTPConnection.cpp +++ b/src/HTTPServer/HTTPConnection.cpp @@ -38,8 +38,7 @@ cHTTPConnection::~cHTTPConnection() void cHTTPConnection::SendStatusAndReason(int a_StatusCode, const AString & a_Response) { - AppendPrintf(m_OutgoingData, "%d %s\r\nContent-Length: 0\r\n\r\n", a_StatusCode, a_Response.c_str()); - m_HTTPServer.NotifyConnectionWrite(*this); + SendData(Printf("%d %s\r\nContent-Length: 0\r\n\r\n", a_StatusCode, a_Response.c_str())); m_State = wcsRecvHeaders; } @@ -49,8 +48,7 @@ void cHTTPConnection::SendStatusAndReason(int a_StatusCode, const AString & a_Re void cHTTPConnection::SendNeedAuth(const AString & a_Realm) { - AppendPrintf(m_OutgoingData, "HTTP/1.1 401 Unauthorized\r\nWWW-Authenticate: Basic realm=\"%s\"\r\nContent-Length: 0\r\n\r\n", a_Realm.c_str()); - m_HTTPServer.NotifyConnectionWrite(*this); + SendData(Printf("HTTP/1.1 401 Unauthorized\r\nWWW-Authenticate: Basic realm=\"%s\"\r\nContent-Length: 0\r\n\r\n", a_Realm.c_str())); m_State = wcsRecvHeaders; } @@ -61,9 +59,10 @@ void cHTTPConnection::SendNeedAuth(const AString & a_Realm) void cHTTPConnection::Send(const cHTTPResponse & a_Response) { ASSERT(m_State == wcsRecvIdle); - a_Response.AppendToData(m_OutgoingData); + AString toSend; + a_Response.AppendToData(toSend); m_State = wcsSendingResp; - m_HTTPServer.NotifyConnectionWrite(*this); + SendData(toSend); } @@ -73,10 +72,10 @@ void cHTTPConnection::Send(const cHTTPResponse & a_Response) void cHTTPConnection::Send(const void * a_Data, size_t a_Size) { ASSERT(m_State == wcsSendingResp); - AppendPrintf(m_OutgoingData, SIZE_T_FMT_HEX "\r\n", a_Size); - m_OutgoingData.append((const char *)a_Data, a_Size); - m_OutgoingData.append("\r\n"); - m_HTTPServer.NotifyConnectionWrite(*this); + // We're sending in Chunked transfer encoding + SendData(Printf(SIZE_T_FMT_HEX "\r\n", a_Size)); + SendData(a_Data, a_Size); + SendData("\r\n"); } @@ -86,9 +85,8 @@ void cHTTPConnection::Send(const void * a_Data, size_t a_Size) void cHTTPConnection::FinishResponse(void) { ASSERT(m_State == wcsSendingResp); - m_OutgoingData.append("0\r\n\r\n"); + SendData("0\r\n\r\n"); m_State = wcsRecvHeaders; - m_HTTPServer.NotifyConnectionWrite(*this); } @@ -108,8 +106,7 @@ void cHTTPConnection::AwaitNextRequest(void) case wcsRecvIdle: { // The client is waiting for a response, send an "Internal server error": - m_OutgoingData.append("HTTP/1.1 500 Internal Server Error\r\n\r\n"); - m_HTTPServer.NotifyConnectionWrite(*this); + SendData("HTTP/1.1 500 Internal Server Error\r\n\r\n"); m_State = wcsRecvHeaders; break; } @@ -117,7 +114,7 @@ void cHTTPConnection::AwaitNextRequest(void) case wcsSendingResp: { // The response headers have been sent, we need to terminate the response body: - m_OutgoingData.append("0\r\n\r\n"); + SendData("0\r\n\r\n"); m_State = wcsRecvHeaders; break; } @@ -140,15 +137,27 @@ void cHTTPConnection::Terminate(void) { m_HTTPServer.RequestFinished(*this, *m_CurrentRequest); } - m_HTTPServer.CloseConnection(*this); + m_Link.reset(); } -bool cHTTPConnection::DataReceived(const char * a_Data, size_t a_Size) +void cHTTPConnection::OnLinkCreated(cTCPLinkPtr a_Link) { + ASSERT(m_Link == nullptr); + m_Link = a_Link; +} + + + + + +void cHTTPConnection::OnReceivedData(const char * a_Data, size_t a_Size) +{ + ASSERT(m_Link != nullptr); + switch (m_State) { case wcsRecvHeaders: @@ -164,13 +173,14 @@ bool cHTTPConnection::DataReceived(const char * a_Data, size_t a_Size) delete m_CurrentRequest; m_CurrentRequest = nullptr; m_State = wcsInvalid; - m_HTTPServer.CloseConnection(*this); - return true; + m_Link->Close(); + m_Link.reset(); + return; } if (m_CurrentRequest->IsInHeaders()) { // The request headers are not yet complete - return false; + return; } // The request has finished parsing its headers successfully, notify of it: @@ -186,11 +196,13 @@ bool cHTTPConnection::DataReceived(const char * a_Data, size_t a_Size) // Process the rest of the incoming data into the request body: if (a_Size > BytesConsumed) { - return cHTTPConnection::DataReceived(a_Data + BytesConsumed, a_Size - BytesConsumed); + cHTTPConnection::OnReceivedData(a_Data + BytesConsumed, a_Size - BytesConsumed); + return; } else { - return cHTTPConnection::DataReceived("", 0); // If the request has zero body length, let it be processed right-away + cHTTPConnection::OnReceivedData("", 0); // If the request has zero body length, let it be processed right-away + return; } } @@ -210,8 +222,9 @@ bool cHTTPConnection::DataReceived(const char * a_Data, size_t a_Size) if (!m_CurrentRequest->DoesAllowKeepAlive()) { m_State = wcsInvalid; - m_HTTPServer.CloseConnection(*this); - return true; + m_Link->Close(); + m_Link.reset(); + return; } delete m_CurrentRequest; m_CurrentRequest = nullptr; @@ -225,32 +238,39 @@ bool cHTTPConnection::DataReceived(const char * a_Data, size_t a_Size) break; } } - return false; } -void cHTTPConnection::GetOutgoingData(AString & a_Data) +void cHTTPConnection::OnRemoteClosed(void) { - std::swap(a_Data, m_OutgoingData); + if (m_CurrentRequest != nullptr) + { + m_HTTPServer.RequestFinished(*this, *m_CurrentRequest); + } + m_Link.reset(); } -void cHTTPConnection::SocketClosed(void) + +void cHTTPConnection::OnError(int a_ErrorCode, const AString & a_ErrorMsg) { - if (m_CurrentRequest != nullptr) - { - m_HTTPServer.RequestFinished(*this, *m_CurrentRequest); - } - m_HTTPServer.CloseConnection(*this); + OnRemoteClosed(); } +void cHTTPConnection::SendData(const void * a_Data, size_t a_Size) +{ + m_Link->Send(a_Data, a_Size); +} + + + diff --git a/src/HTTPServer/HTTPConnection.h b/src/HTTPServer/HTTPConnection.h index ccbf26466..8ecc4a4d4 100644 --- a/src/HTTPServer/HTTPConnection.h +++ b/src/HTTPServer/HTTPConnection.h @@ -9,7 +9,7 @@ #pragma once -#include "../OSSupport/SocketThreads.h" +#include "../OSSupport/Network.h" @@ -25,7 +25,7 @@ class cHTTPRequest; class cHTTPConnection : - public cSocketThreads::cCallback + public cTCPLink::cCallbacks { public: @@ -78,9 +78,6 @@ protected: /** Status in which the request currently is */ eState m_State; - /** Data that is queued for sending, once the socket becomes writable */ - AString m_OutgoingData; - /** The request being currently received Valid only between having parsed the headers and finishing receiving the body. */ cHTTPRequest * m_CurrentRequest; @@ -88,18 +85,34 @@ protected: /** Number of bytes that remain to read for the complete body of the message to be received. Valid only in wcsRecvBody */ size_t m_CurrentRequestBodyRemaining; + + /** The network link attached to this connection. */ + cTCPLinkPtr m_Link; - // cSocketThreads::cCallback overrides: - /** Data is received from the client. - Returns true if the connection has been closed as the result of parsing the data. */ - virtual bool DataReceived(const char * a_Data, size_t a_Size) override; + // cTCPLink::cCallbacks overrides: + /** The link instance has been created, remember it. */ + virtual void OnLinkCreated(cTCPLinkPtr a_Link) override; + + /** Data is received from the client. */ + virtual void OnReceivedData(const char * a_Data, size_t a_Size) override; - /** Data can be sent to client */ - virtual void GetOutgoingData(AString & a_Data) override; + /** The socket has been closed for any reason. */ + virtual void OnRemoteClosed(void) override; - /** The socket has been closed for any reason */ - virtual void SocketClosed(void) override; + /** An error has occurred on the socket. */ + virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override; + + // Overridable: + /** Called to send raw data over the link. Descendants may provide data transformations (SSL etc.) */ + virtual void SendData(const void * a_Data, size_t a_Size); + + /** Sends the raw data over the link. + Descendants may provide data transformations (SSL etc.) via the overridable SendData() function. */ + void SendData(const AString & a_Data) + { + SendData(a_Data.data(), a_Data.size()); + } } ; typedef std::vector cHTTPConnections; diff --git a/src/HTTPServer/HTTPServer.cpp b/src/HTTPServer/HTTPServer.cpp index 9ab030a1f..3bcf0783a 100644 --- a/src/HTTPServer/HTTPServer.cpp +++ b/src/HTTPServer/HTTPServer.cpp @@ -118,12 +118,46 @@ class cDebugCallbacks : +//////////////////////////////////////////////////////////////////////////////// +// cHTTPServerListenCallbacks: + +class cHTTPServerListenCallbacks: + public cNetwork::cListenCallbacks +{ +public: + cHTTPServerListenCallbacks(cHTTPServer & a_HTTPServer, UInt16 a_Port): + m_HTTPServer(a_HTTPServer), + m_Port(a_Port) + { + } + +protected: + /** The HTTP server instance that we're attached to. */ + cHTTPServer & m_HTTPServer; + + /** The port for which this instance is responsible. */ + UInt16 m_Port; + + // cNetwork::cListenCallbacks overrides: + virtual cTCPLink::cCallbacksPtr OnIncomingConnection(const AString & a_RemoteIPAddress, UInt16 a_RemotePort) override + { + return m_HTTPServer.OnIncomingConnection(a_RemoteIPAddress, a_RemotePort); + } + virtual void OnAccepted(cTCPLink & a_Link) override {} + virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override + { + LOGWARNING("HTTP server error on port %d: %d (%s)", m_Port, a_ErrorCode, a_ErrorMsg.c_str()); + } +}; + + + + + //////////////////////////////////////////////////////////////////////////////// // cHTTPServer: cHTTPServer::cHTTPServer(void) : - m_ListenThreadIPv4(*this, cSocket::IPv4, "WebServer"), - m_ListenThreadIPv6(*this, cSocket::IPv6, "WebServer"), m_Callbacks(nullptr) { } @@ -141,7 +175,7 @@ cHTTPServer::~cHTTPServer() -bool cHTTPServer::Initialize(const AString & a_PortsIPv4, const AString & a_PortsIPv6) +bool cHTTPServer::Initialize(void) { // Read the HTTPS cert + key: AString CertFile = cFile::ReadWholeFile("webadmin/httpscert.crt"); @@ -177,18 +211,6 @@ bool cHTTPServer::Initialize(const AString & a_PortsIPv4, const AString & a_Port { LOGINFO("WebServer: The server is running in secure HTTPS mode."); } - - // Open up requested ports: - bool HasAnyPort; - m_ListenThreadIPv4.SetReuseAddr(true); - m_ListenThreadIPv6.SetReuseAddr(true); - HasAnyPort = m_ListenThreadIPv4.Initialize(a_PortsIPv4); - HasAnyPort = m_ListenThreadIPv6.Initialize(a_PortsIPv6) || HasAnyPort; - if (!HasAnyPort) - { - return false; - } - return true; } @@ -196,19 +218,28 @@ bool cHTTPServer::Initialize(const AString & a_PortsIPv4, const AString & a_Port -bool cHTTPServer::Start(cCallbacks & a_Callbacks) +bool cHTTPServer::Start(cCallbacks & a_Callbacks, const AStringVector & a_Ports) { m_Callbacks = &a_Callbacks; - if (!m_ListenThreadIPv4.Start()) - { - return false; - } - if (!m_ListenThreadIPv6.Start()) + + // Open up requested ports: + for (auto port : a_Ports) { - m_ListenThreadIPv4.Stop(); - return false; - } - return true; + UInt16 PortNum = static_cast(atoi(port.c_str())); + if (PortNum == 0) + { + LOGWARNING("WebServer: Invalid port value: \"%s\". Ignoring.", port.c_str()); + continue; + } + auto Handle = cNetwork::Listen(PortNum, std::make_shared(*this, PortNum)); + if (Handle->IsListening()) + { + m_ServerHandles.push_back(Handle); + } + } // for port - a_Ports[] + + // Report success if at least one port opened successfully: + return !m_ServerHandles.empty(); } @@ -217,63 +248,30 @@ bool cHTTPServer::Start(cCallbacks & a_Callbacks) void cHTTPServer::Stop(void) { - m_ListenThreadIPv4.Stop(); - m_ListenThreadIPv6.Stop(); - - // Drop all current connections: - cCSLock Lock(m_CSConnections); - while (!m_Connections.empty()) + for (auto handle : m_ServerHandles) { - m_Connections.front()->Terminate(); - } // for itr - m_Connections[] + handle->Close(); + } + m_ServerHandles.clear(); } -void cHTTPServer::OnConnectionAccepted(cSocket & a_Socket) +cTCPLink::cCallbacksPtr cHTTPServer::OnIncomingConnection(const AString & a_RemoteIPAddress, UInt16 a_RemotePort) { - cHTTPConnection * Connection; + UNUSED(a_RemoteIPAddress); + UNUSED(a_RemotePort); + if (m_Cert.get() != nullptr) { - Connection = new cSslHTTPConnection(*this, m_Cert, m_CertPrivKey); + return std::make_shared(*this, m_Cert, m_CertPrivKey); } else { - Connection = new cHTTPConnection(*this); + return std::make_shared(*this); } - m_SocketThreads.AddClient(a_Socket, Connection); - cCSLock Lock(m_CSConnections); - m_Connections.push_back(Connection); -} - - - - - -void cHTTPServer::CloseConnection(cHTTPConnection & a_Connection) -{ - m_SocketThreads.RemoveClient(&a_Connection); - cCSLock Lock(m_CSConnections); - for (cHTTPConnections::iterator itr = m_Connections.begin(), end = m_Connections.end(); itr != end; ++itr) - { - if (*itr == &a_Connection) - { - m_Connections.erase(itr); - break; - } - } - delete &a_Connection; -} - - - - - -void cHTTPServer::NotifyConnectionWrite(cHTTPConnection & a_Connection) -{ - m_SocketThreads.NotifyWrite(&a_Connection); } diff --git a/src/HTTPServer/HTTPServer.h b/src/HTTPServer/HTTPServer.h index 73d4cbdd0..d626fb475 100644 --- a/src/HTTPServer/HTTPServer.h +++ b/src/HTTPServer/HTTPServer.h @@ -9,8 +9,7 @@ #pragma once -#include "../OSSupport/ListenThread.h" -#include "../OSSupport/SocketThreads.h" +#include "../OSSupport/Network.h" #include "../IniFile.h" #include "PolarSSL++/RsaPrivateKey.h" #include "PolarSSL++/CryptoKey.h" @@ -33,8 +32,7 @@ typedef std::vector cHTTPConnections; -class cHTTPServer : - public cListenThread::cCallback +class cHTTPServer { public: class cCallbacks @@ -42,44 +40,39 @@ public: public: virtual ~cCallbacks() {} - /** Called when a new request arrives over a connection and its headers have been parsed. - The request body needn't have arrived yet. - */ + /** Called when a new request arrives over a connection and all its headers have been parsed. + The request body needn't have arrived yet. */ virtual void OnRequestBegun(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) = 0; /** Called when another part of request body has arrived. May be called multiple times for a single request. */ virtual void OnRequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, size_t a_Size) = 0; - /// Called when the request body has been fully received in previous calls to OnRequestBody() + /** Called when the request body has been fully received in previous calls to OnRequestBody() */ virtual void OnRequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) = 0; } ; cHTTPServer(void); virtual ~cHTTPServer(); - /// Initializes the server on the specified ports - bool Initialize(const AString & a_PortsIPv4, const AString & a_PortsIPv6); + /** Initializes the server - reads the cert files etc. */ + bool Initialize(void); - /// Starts the server and assigns the callbacks to use for incoming requests - bool Start(cCallbacks & a_Callbacks); + /** Starts the server and assigns the callbacks to use for incoming requests */ + bool Start(cCallbacks & a_Callbacks, const AStringVector & a_Ports); - /// Stops the server, drops all current connections + /** Stops the server, drops all current connections */ void Stop(void); protected: friend class cHTTPConnection; friend class cSslHTTPConnection; + friend class cHTTPServerListenCallbacks; - cListenThread m_ListenThreadIPv4; - cListenThread m_ListenThreadIPv6; + /** The cNetwork API handle for the listening socket. */ + cServerHandlePtrs m_ServerHandles; - cSocketThreads m_SocketThreads; - - cCriticalSection m_CSConnections; - cHTTPConnections m_Connections; ///< All the connections that are currently being serviced - - /// The callbacks to call for various events + /** The callbacks to call for various events */ cCallbacks * m_Callbacks; /** The server certificate to use for the SSL connections */ @@ -89,23 +82,18 @@ protected: cCryptoKeyPtr m_CertPrivKey; - // cListenThread::cCallback overrides: - virtual void OnConnectionAccepted(cSocket & a_Socket) override; - - /// Called by cHTTPConnection to close the connection (presumably due to an error) - void CloseConnection(cHTTPConnection & a_Connection); - - /// Called by cHTTPConnection to notify SocketThreads that there's data to be sent for the connection - void NotifyConnectionWrite(cHTTPConnection & a_Connection); - - /// Called by cHTTPConnection when it finishes parsing the request header + /** Called by cHTTPServerListenCallbacks when there's a new incoming connection. + Returns the connection instance to be used as the cTCPLink callbacks. */ + cTCPLink::cCallbacksPtr OnIncomingConnection(const AString & a_RemoteIPAddress, UInt16 a_RemotePort); + + /** Called by cHTTPConnection when it finishes parsing the request header */ void NewRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request); /** Called by cHTTPConenction when it receives more data for the request body. May be called multiple times for a single request. */ void RequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, size_t a_Size); - /// Called by cHTTPConnection when it detects that the request has finished (all of its body has been received) + /** Called by cHTTPConnection when it detects that the request has finished (all of its body has been received) */ void RequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request); } ; diff --git a/src/HTTPServer/SslHTTPConnection.cpp b/src/HTTPServer/SslHTTPConnection.cpp index d237089d9..f09daac8f 100644 --- a/src/HTTPServer/SslHTTPConnection.cpp +++ b/src/HTTPServer/SslHTTPConnection.cpp @@ -25,14 +25,8 @@ cSslHTTPConnection::cSslHTTPConnection(cHTTPServer & a_HTTPServer, const cX509Ce -bool cSslHTTPConnection::DataReceived(const char * a_Data, size_t a_Size) +void cSslHTTPConnection::OnReceivedData(const char * a_Data, size_t a_Size) { - // If there is outgoing data in the queue, notify the server that it should write it out: - if (!m_OutgoingData.empty()) - { - m_HTTPServer.NotifyConnectionWrite(*this); - } - // Process the received data: const char * Data = a_Data; size_t Size = a_Size; @@ -52,17 +46,18 @@ bool cSslHTTPConnection::DataReceived(const char * a_Data, size_t a_Size) int NumRead = m_Ssl.ReadPlain(Buffer, sizeof(Buffer)); if (NumRead > 0) { - if (super::DataReceived(Buffer, (size_t)NumRead)) - { - // The socket has been closed, and the object is already deleted. Bail out. - return true; - } + super::OnReceivedData(Buffer, (size_t)NumRead); + } + else if (NumRead == POLARSSL_ERR_NET_WANT_READ) + { + // SSL requires us to send data to peer first, do so by "sending" empty data: + SendData(nullptr, 0); } // If both failed, bail out: if ((BytesWritten == 0) && (NumRead <= 0)) { - return false; + return; } } } @@ -71,18 +66,20 @@ bool cSslHTTPConnection::DataReceived(const char * a_Data, size_t a_Size) -void cSslHTTPConnection::GetOutgoingData(AString & a_Data) +void cSslHTTPConnection::SendData(const void * a_Data, size_t a_Size) { + const char * OutgoingData = reinterpret_cast(a_Data); + size_t pos = 0; for (;;) { // Write as many bytes from our buffer to SSL's encryption as possible: int NumWritten = 0; - if (!m_OutgoingData.empty()) + if (pos < a_Size) { - NumWritten = m_Ssl.WritePlain(m_OutgoingData.data(), m_OutgoingData.size()); + NumWritten = m_Ssl.WritePlain(OutgoingData + pos, a_Size - pos); if (NumWritten > 0) { - m_OutgoingData.erase(0, (size_t)NumWritten); + pos += static_cast(NumWritten); } } @@ -91,7 +88,7 @@ void cSslHTTPConnection::GetOutgoingData(AString & a_Data) size_t NumBytes = m_Ssl.ReadOutgoing(Buffer, sizeof(Buffer)); if (NumBytes > 0) { - a_Data.append(Buffer, NumBytes); + m_Link->Send(Buffer, NumBytes); } // If both failed, bail out: diff --git a/src/HTTPServer/SslHTTPConnection.h b/src/HTTPServer/SslHTTPConnection.h index c2c1585cd..dc54b1eff 100644 --- a/src/HTTPServer/SslHTTPConnection.h +++ b/src/HTTPServer/SslHTTPConnection.h @@ -36,8 +36,8 @@ protected: cCryptoKeyPtr m_PrivateKey; // cHTTPConnection overrides: - virtual bool DataReceived (const char * a_Data, size_t a_Size) override; // Data is received from the client - virtual void GetOutgoingData(AString & a_Data) override; // Data can be sent to client + virtual void OnReceivedData(const char * a_Data, size_t a_Size) override; // Data is received from the client + virtual void SendData(const void * a_Data, size_t a_Size) override; // Data is to be sent to client } ; diff --git a/src/WebAdmin.cpp b/src/WebAdmin.cpp index dbf600c25..13cf3cc41 100644 --- a/src/WebAdmin.cpp +++ b/src/WebAdmin.cpp @@ -19,7 +19,16 @@ -/// Helper class - appends all player names together in a HTML list +static const char DEFAULT_WEBADMIN_PORTS[] = "8080"; + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cPlayerAccum: + +/** Helper class - appends all player names together in an HTML list */ class cPlayerAccum : public cPlayerListCallback { @@ -40,11 +49,12 @@ public: +//////////////////////////////////////////////////////////////////////////////// +// cWebAdmin: + cWebAdmin::cWebAdmin(void) : m_IsInitialized(false), m_IsRunning(false), - m_PortsIPv4("8080"), - m_PortsIPv6(""), m_TemplateScript("") { } @@ -91,8 +101,7 @@ bool cWebAdmin::Init(void) m_IniFile.AddHeaderComment(" Password format: Password=*password*; for example:"); m_IniFile.AddHeaderComment(" [User:admin]"); m_IniFile.AddHeaderComment(" Password=admin"); - m_IniFile.SetValue("WebAdmin", "Port", m_PortsIPv4); - m_IniFile.SetValue("WebAdmin", "PortsIPv6", m_PortsIPv6); + m_IniFile.SetValue("WebAdmin", "Ports", DEFAULT_WEBADMIN_PORTS); m_IniFile.WriteFile("webadmin.ini"); } @@ -104,32 +113,6 @@ bool cWebAdmin::Init(void) LOGD("Initialising WebAdmin..."); - m_PortsIPv4 = m_IniFile.GetValueSet("WebAdmin", "Port", m_PortsIPv4); - m_PortsIPv6 = m_IniFile.GetValueSet("WebAdmin", "PortsIPv6", m_PortsIPv6); - - if (!m_HTTPServer.Initialize(m_PortsIPv4, m_PortsIPv6)) - { - return false; - } - m_IsInitialized = true; - m_IniFile.WriteFile("webadmin.ini"); - return true; -} - - - - - -bool cWebAdmin::Start(void) -{ - if (!m_IsInitialized) - { - // Not initialized - return false; - } - - LOGD("Starting WebAdmin..."); - // Initialize the WebAdmin template script and load the file m_TemplateScript.Create(); m_TemplateScript.RegisterAPILibs(); @@ -141,6 +124,7 @@ bool cWebAdmin::Start(void) return false; } + // Load the login template, provide a fallback default if not found: if (!LoadLoginTemplate()) { LOGWARN("Could not load WebAdmin login template \"%s\", using fallback template.", FILE_IO_PREFIX "webadmin/login_template.html"); @@ -155,7 +139,34 @@ bool cWebAdmin::Start(void) ""; } - m_IsRunning = m_HTTPServer.Start(*this); + // Read the ports to be used: + // Note that historically the ports were stored in the "Port" and "PortsIPv6" values + m_Ports = ReadUpgradeIniPorts(m_IniFile, "WebAdmin", "Ports", "Port", "PortsIPv6", DEFAULT_WEBADMIN_PORTS); + + if (!m_HTTPServer.Initialize()) + { + return false; + } + m_IsInitialized = true; + m_IniFile.WriteFile("webadmin.ini"); + return true; +} + + + + + +bool cWebAdmin::Start(void) +{ + if (!m_IsInitialized) + { + // Not initialized + return false; + } + + LOGD("Starting WebAdmin..."); + + m_IsRunning = m_HTTPServer.Start(*this, m_Ports); return m_IsRunning; } diff --git a/src/WebAdmin.h b/src/WebAdmin.h index a85fb1f0c..86a8a9a4b 100644 --- a/src/WebAdmin.h +++ b/src/WebAdmin.h @@ -5,7 +5,6 @@ #pragma once -#include "OSSupport/Socket.h" #include "Bindings/LuaState.h" #include "IniFile.h" #include "HTTPServer/HTTPServer.h" @@ -135,8 +134,16 @@ public: /** Returns the prefix needed for making a link point to the webadmin root from the given URL ("../../../webadmin"-style) */ AString GetBaseURL(const AString & a_URL); - AString GetIPv4Ports(void) const { return m_PortsIPv4; } - AString GetIPv6Ports(void) const { return m_PortsIPv6; } + /** Returns the list of ports used for the webadmin. */ + AString GetPorts(void) const { return StringsConcat(m_Ports, ','); } + + /** OBSOLETE: Returns the list of IPv4 ports used for the webadmin. + Currently there is no distinction between IPv4 and IPv6; use GetPorts() instead. */ + AString GetIPv4Ports(void) const { return GetPorts(); } + + /** OBSOLETE: Returns the list of IPv6 ports used for the webadmin. + Currently there is no distinction between IPv4 and IPv6; use GetPorts() instead. */ + AString GetIPv6Ports(void) const { return GetPorts(); } // tolua_end @@ -205,8 +212,8 @@ protected: PluginList m_Plugins; - AString m_PortsIPv4; - AString m_PortsIPv6; + /** The ports on which the webadmin is running. */ + AStringVector m_Ports; /** The Lua template script to provide templates: */ cLuaState m_TemplateScript; -- cgit v1.2.3 From 80a386cbc477e234171c357d4aa035a00e3c1273 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sat, 24 Jan 2015 12:58:27 +0100 Subject: Fixed missing LibEvent reference in OSSupport. --- src/OSSupport/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/OSSupport/CMakeLists.txt b/src/OSSupport/CMakeLists.txt index 9424b63da..c8e96cbec 100644 --- a/src/OSSupport/CMakeLists.txt +++ b/src/OSSupport/CMakeLists.txt @@ -52,6 +52,6 @@ if(NOT MSVC) target_link_libraries(OSSupport rt) endif() - target_link_libraries(OSSupport pthread) + target_link_libraries(OSSupport pthread event_core event_extra) endif() endif() -- cgit v1.2.3 From 059af2efdc882e95492752c13393e0374338350a Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sat, 24 Jan 2015 13:31:24 +0100 Subject: Fixed a crash in RCON server. --- src/Server.cpp | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/Server.cpp b/src/Server.cpp index 4dbe59ac6..df6e5ec8a 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -640,7 +640,6 @@ void cServer::PrintHelp(const AStringVector & a_Split, cCommandOutputCallback & const AStringPair & cmd = *itr; a_Output.Out(Printf("%-*s%s\n", static_cast(Callback.m_MaxLen), cmd.first.c_str(), cmd.second.c_str())); } // for itr - Callback.m_Commands[] - a_Output.Finished(); } -- cgit v1.2.3 From 9e61ad8c74191210b0c0151a4b5bdbd88956a74d Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sat, 24 Jan 2015 13:31:42 +0100 Subject: Migrated RCON server to cNetwork API. --- src/RCONServer.cpp | 155 +++++++++++++++++++++++++++++++---------------------- src/RCONServer.h | 61 +++++++++------------ 2 files changed, 116 insertions(+), 100 deletions(-) (limited to 'src') diff --git a/src/RCONServer.cpp b/src/RCONServer.cpp index 49ca4fc61..2ec7f191f 100644 --- a/src/RCONServer.cpp +++ b/src/RCONServer.cpp @@ -38,6 +38,43 @@ enum +//////////////////////////////////////////////////////////////////////////////// +// cRCONListenCallbacks: + +class cRCONListenCallbacks: + public cNetwork::cListenCallbacks +{ +public: + cRCONListenCallbacks(cRCONServer & a_RCONServer, UInt16 a_Port): + m_RCONServer(a_RCONServer), + m_Port(a_Port) + { + } + +protected: + /** The RCON server instance that we're attached to. */ + cRCONServer & m_RCONServer; + + /** The port for which this instance is responsible. */ + UInt16 m_Port; + + // cNetwork::cListenCallbacks overrides: + virtual cTCPLink::cCallbacksPtr OnIncomingConnection(const AString & a_RemoteIPAddress, UInt16 a_RemotePort) override + { + LOG("RCON Client \"%s\" connected!", a_RemoteIPAddress.c_str()); + return std::make_shared(m_RCONServer, a_RemoteIPAddress); + } + virtual void OnAccepted(cTCPLink & a_Link) override {} + virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override + { + LOGWARNING("RCON server error on port %d: %d (%s)", m_Port, a_ErrorCode, a_ErrorMsg); + } +}; + + + + + //////////////////////////////////////////////////////////////////////////////// // cRCONCommandOutput: @@ -77,9 +114,7 @@ protected: // cRCONServer: cRCONServer::cRCONServer(cServer & a_Server) : - m_Server(a_Server), - m_ListenThread4(*this, cSocket::IPv4, "RCON"), - m_ListenThread6(*this, cSocket::IPv6, "RCON") + m_Server(a_Server) { } @@ -89,8 +124,10 @@ cRCONServer::cRCONServer(cServer & a_Server) : cRCONServer::~cRCONServer() { - m_ListenThread4.Stop(); - m_ListenThread6.Stop(); + for (auto srv: m_ListenServers) + { + srv->Close(); + } } @@ -112,24 +149,28 @@ void cRCONServer::Initialize(cIniFile & a_IniFile) return; } - // Read and initialize both IPv4 and IPv6 ports for RCON - bool HasAnyPorts = false; - AString Ports4 = a_IniFile.GetValueSet("RCON", "PortsIPv4", "25575"); - if (m_ListenThread4.Initialize(Ports4)) - { - HasAnyPorts = true; - m_ListenThread4.Start(); - } - AString Ports6 = a_IniFile.GetValueSet("RCON", "PortsIPv6", "25575"); - if (m_ListenThread6.Initialize(Ports6)) + // Read the listening ports for RCON from config: + AStringVector Ports = ReadUpgradeIniPorts(a_IniFile, "RCON", "Ports", "PortsIPv4", "PortsIPv6", "25575"); + + // Start listening on each specified port: + for (auto port: Ports) { - HasAnyPorts = true; - m_ListenThread6.Start(); + UInt16 PortNum = static_cast(atol(port.c_str())); + if (PortNum == 0) + { + LOGINFO("Invalid RCON port value: \"%s\". Ignoring.", port.c_str()); + continue; + } + auto Handle = cNetwork::Listen(PortNum, std::make_shared(*this, PortNum)); + if (Handle->IsListening()) + { + m_ListenServers.push_back(Handle); + } } - if (!HasAnyPorts) + + if (m_ListenServers.empty()) { - LOGWARNING("RCON is requested, but no ports are specified. Specify at least one port in PortsIPv4 or PortsIPv6. RCON is now disabled."); - return; + LOGWARNING("RCON is enabled but no valid ports were found. RCON is not accessible."); } } @@ -137,40 +178,33 @@ void cRCONServer::Initialize(cIniFile & a_IniFile) -void cRCONServer::OnConnectionAccepted(cSocket & a_Socket) -{ - if (!a_Socket.IsValid()) - { - return; - } +//////////////////////////////////////////////////////////////////////////////// +// cRCONServer::cConnection: - LOG("RCON Client \"%s\" connected!", a_Socket.GetIPString().c_str()); - - // Create a new cConnection object, it will be deleted when the connection is closed - m_SocketThreads.AddClient(a_Socket, new cConnection(*this, a_Socket)); +cRCONServer::cConnection::cConnection(cRCONServer & a_RCONServer, const AString & a_IPAddress) : + m_IsAuthenticated(false), + m_RCONServer(a_RCONServer), + m_IPAddress(a_IPAddress) +{ } -//////////////////////////////////////////////////////////////////////////////// -// cRCONServer::cConnection: - -cRCONServer::cConnection::cConnection(cRCONServer & a_RCONServer, cSocket & a_Socket) : - m_IsAuthenticated(false), - m_RCONServer(a_RCONServer), - m_Socket(a_Socket), - m_IPAddress(a_Socket.GetIPString()) +void cRCONServer::cConnection::OnLinkCreated(cTCPLinkPtr a_Link) { + m_Link = a_Link; } -bool cRCONServer::cConnection::DataReceived(const char * a_Data, size_t a_Size) +void cRCONServer::cConnection::OnReceivedData(const char * a_Data, size_t a_Size) { + ASSERT(m_Link != nullptr); + // Append data to the buffer: m_Buffer.append(a_Data, a_Size); @@ -184,49 +218,45 @@ bool cRCONServer::cConnection::DataReceived(const char * a_Data, size_t a_Size) LOGWARNING("Received an invalid RCON packet length (%d), dropping RCON connection to %s.", Length, m_IPAddress.c_str() ); - m_RCONServer.m_SocketThreads.RemoveClient(this); - m_Socket.CloseSocket(); - delete this; - return false; + m_Link->Close(); + m_Link.reset(); + return; } - if (Length > (int)(m_Buffer.size() + 4)) + if (Length > static_cast(m_Buffer.size() + 4)) { // Incomplete packet yet, wait for more data to come - return false; + return; } int RequestID = IntFromBuffer(m_Buffer.data() + 4); int PacketType = IntFromBuffer(m_Buffer.data() + 8); if (!ProcessPacket(RequestID, PacketType, Length - 10, m_Buffer.data() + 12)) { - m_RCONServer.m_SocketThreads.RemoveClient(this); - m_Socket.CloseSocket(); - delete this; - return false; + m_Link->Close(); + m_Link.reset(); + return; } m_Buffer.erase(0, Length + 4); } // while (m_Buffer.size() >= 14) - return false; } -void cRCONServer::cConnection::GetOutgoingData(AString & a_Data) +void cRCONServer::cConnection::OnRemoteClosed(void) { - a_Data.assign(m_Outgoing); - m_Outgoing.clear(); + m_Link.reset(); } -void cRCONServer::cConnection::SocketClosed(void) +void cRCONServer::cConnection::OnError(int a_ErrorCode, const AString & a_ErrorMsg) { - m_RCONServer.m_SocketThreads.RemoveClient(this); - delete this; + LOGD("Error in RCON connection %s: %d (%s)", m_IPAddress.c_str(), a_ErrorCode, a_ErrorMsg.c_str()); + m_Link.reset(); } @@ -311,22 +341,21 @@ void cRCONServer::cConnection::IntToBuffer(int a_Value, char * a_Buffer) void cRCONServer::cConnection::SendResponse(int a_RequestID, int a_PacketType, int a_PayloadLength, const char * a_Payload) { ASSERT((a_PayloadLength == 0) || (a_Payload != nullptr)); // Either zero data to send, or a valid payload ptr + ASSERT(m_Link != nullptr); char Buffer[4]; int Length = a_PayloadLength + 10; IntToBuffer(Length, Buffer); - m_Outgoing.append(Buffer, 4); + m_Link->Send(Buffer, 4); IntToBuffer(a_RequestID, Buffer); - m_Outgoing.append(Buffer, 4); + m_Link->Send(Buffer, 4); IntToBuffer(a_PacketType, Buffer); - m_Outgoing.append(Buffer, 4); + m_Link->Send(Buffer, 4); if (a_PayloadLength > 0) { - m_Outgoing.append(a_Payload, a_PayloadLength); + m_Link->Send(a_Payload, a_PayloadLength); } - m_Outgoing.push_back(0); - m_Outgoing.push_back(0); - m_RCONServer.m_SocketThreads.NotifyWrite(this); + m_Link->Send("\0", 2); // Send two zero chars as the padding } diff --git a/src/RCONServer.h b/src/RCONServer.h index 47c746736..1d599b279 100644 --- a/src/RCONServer.h +++ b/src/RCONServer.h @@ -9,8 +9,7 @@ #pragma once -#include "OSSupport/SocketThreads.h" -#include "OSSupport/ListenThread.h" +#include "OSSupport/Network.h" @@ -24,8 +23,7 @@ class cIniFile; -class cRCONServer : - public cListenThread::cCallback +class cRCONServer { public: cRCONServer(cServer & a_Server); @@ -35,72 +33,61 @@ public: protected: friend class cRCONCommandOutput; + friend class cRCONListenCallbacks; class cConnection : - public cSocketThreads::cCallback + public cTCPLink::cCallbacks { public: - cConnection(cRCONServer & a_RCONServer, cSocket & a_Socket); + cConnection(cRCONServer & a_RCONServer, const AString & a_IPAddress); protected: friend class cRCONCommandOutput; - /// Set to true if the client has successfully authenticated + /** Set to true if the client has successfully authenticated */ bool m_IsAuthenticated; - /// Buffer for the incoming data + /** Buffer for the incoming data */ AString m_Buffer; - /// Buffer for the outgoing data - AString m_Outgoing; - - /// Server that owns this connection and processes requests + /** Server that owns this connection and processes requests */ cRCONServer & m_RCONServer; - /// The socket belonging to the client - cSocket & m_Socket; + /** The TCP link to the client */ + cTCPLinkPtr m_Link; - /// Address of the client + /** Address of the client */ AString m_IPAddress; - // cSocketThreads::cCallback overrides: - virtual bool DataReceived(const char * a_Data, size_t a_Size) override; - virtual void GetOutgoingData(AString & a_Data) override; - virtual void SocketClosed(void) override; + // cTCPLink::cCallbacks overrides: + virtual void OnLinkCreated(cTCPLinkPtr a_Link); + virtual void OnReceivedData(const char * a_Data, size_t a_Length) override; + virtual void OnRemoteClosed(void) override; + virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override; - /// Processes the given packet and sends the response; returns true if successful, false if the connection is to be dropped + /** Processes the given packet and sends the response; returns true if successful, false if the connection is to be dropped */ bool ProcessPacket(int a_RequestID, int a_PacketType, int a_PayloadLength, const char * a_Payload); - /// Reads 4 bytes from a_Buffer and returns the int they represent + /** Reads 4 bytes from a_Buffer and returns the int they represent */ int IntFromBuffer(const char * a_Buffer); - /// Puts 4 bytes representing the int into the buffer + /** Puts 4 bytes representing the int into the buffer */ void IntToBuffer(int a_Value, char * a_Buffer); - /// Sends a RCON packet back to the client + /** Sends a RCON packet back to the client */ void SendResponse(int a_RequestID, int a_PacketType, int a_PayloadLength, const char * a_Payload); } ; - /// The server object that will process the commands received + /** The server object that will process the commands received */ cServer & m_Server; - /// The thread(s) that take care of all the traffic on the RCON ports - cSocketThreads m_SocketThreads; - - /// The thread for accepting IPv4 RCON connections - cListenThread m_ListenThread4; - - /// The thread for accepting IPv6 RCON connections - cListenThread m_ListenThread6; + /** The sockets for accepting RCON connections (one socket per port). */ + cServerHandlePtrs m_ListenServers; - /// Password for authentication + /** Password for authentication */ AString m_Password; - - - // cListenThread::cCallback overrides: - virtual void OnConnectionAccepted(cSocket & a_Socket) override; } ; -- cgit v1.2.3 From 28ffc55d895172ef68d41a1f831c4c4f766225fd Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sat, 24 Jan 2015 15:08:11 +0100 Subject: RCON server: fixed compilation. --- src/RCONServer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/RCONServer.cpp b/src/RCONServer.cpp index 2ec7f191f..63bc97fe3 100644 --- a/src/RCONServer.cpp +++ b/src/RCONServer.cpp @@ -67,7 +67,7 @@ protected: virtual void OnAccepted(cTCPLink & a_Link) override {} virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override { - LOGWARNING("RCON server error on port %d: %d (%s)", m_Port, a_ErrorCode, a_ErrorMsg); + LOGWARNING("RCON server error on port %d: %d (%s)", m_Port, a_ErrorCode, a_ErrorMsg.c_str()); } }; -- cgit v1.2.3 From 7cff25f0ffa70bf142663ccdc320bf86dfb623dd Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sat, 24 Jan 2015 15:58:40 +0100 Subject: StringUtils: Fixed bad predicate in MergeStringVectors(). Instead of preventing duplicates it was allowing only duplicates. --- src/StringUtils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/StringUtils.cpp b/src/StringUtils.cpp index 48486a762..4eb2d48b6 100644 --- a/src/StringUtils.cpp +++ b/src/StringUtils.cpp @@ -914,7 +914,7 @@ AStringVector MergeStringVectors(const AStringVector & a_Strings1, const AString // Add each item from strings2 that is not already present: for (auto item : a_Strings2) { - if (std::find(res.begin(), res.end(), item) != res.end()) + if (std::find(res.begin(), res.end(), item) == res.end()) { res.push_back(item); } -- cgit v1.2.3 From f1f12495b2fe33e6ae424e1654a66e220162e926 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sat, 24 Jan 2015 15:59:06 +0100 Subject: cFile: Added ReplaceFileNameInvalidChars(). --- src/OSSupport/File.cpp | 23 +++++++++++++++++++++++ src/OSSupport/File.h | 3 +++ 2 files changed, 26 insertions(+) (limited to 'src') diff --git a/src/OSSupport/File.cpp b/src/OSSupport/File.cpp index 8957dfaef..1ee9feafe 100644 --- a/src/OSSupport/File.cpp +++ b/src/OSSupport/File.cpp @@ -453,6 +453,29 @@ AString cFile::ReadWholeFile(const AString & a_FileName) +AString cFile::ReplaceFileNameInvalidChars(const AString & a_FileName, char a_Replacement) +{ + AString res(a_FileName); + for (auto & ch: res) + { + switch (ch) + { + case ':': + case '\\': + case '/': + { + ch = a_Replacement; + break; + } + } + } + return res; +} + + + + + int cFile::Printf(const char * a_Fmt, ...) { AString buf; diff --git a/src/OSSupport/File.h b/src/OSSupport/File.h index dfb38e839..b39f71e28 100644 --- a/src/OSSupport/File.h +++ b/src/OSSupport/File.h @@ -126,6 +126,9 @@ public: /** Returns the entire contents of the specified file as a string. Returns empty string on error. */ static AString ReadWholeFile(const AString & a_FileName); + + /** Replaces characters that cannot be in a file name with the specified char. */ + static AString ReplaceFileNameInvalidChars(const AString & a_FileName, char a_Replacement = '_'); // tolua_end -- cgit v1.2.3 From 978fc6a9886d322f7b5749e22c129e04a9bc72d7 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sat, 24 Jan 2015 15:59:41 +0100 Subject: Protocol18: Fixed failure with invalig logfile names. --- src/Protocol/Protocol18x.cpp | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/Protocol/Protocol18x.cpp b/src/Protocol/Protocol18x.cpp index 3c4e049bd..5d722d9fc 100644 --- a/src/Protocol/Protocol18x.cpp +++ b/src/Protocol/Protocol18x.cpp @@ -108,8 +108,15 @@ cProtocol180::cProtocol180(cClientHandle * a_Client, const AString & a_ServerAdd { static int sCounter = 0; cFile::CreateFolder("CommLogs"); - AString FileName = Printf("CommLogs/%x_%d__%s.log", (unsigned)time(nullptr), sCounter++, a_Client->GetIPString().c_str()); - m_CommLogFile.Open(FileName, cFile::fmWrite); + AString FileName = Printf("CommLogs/%x_%d__%s.log", + static_cast(time(nullptr)), + sCounter++, + cFile::ReplaceFileNameInvalidChars(a_Client->GetIPString()).c_str() + ); + if (!m_CommLogFile.Open(FileName, cFile::fmWrite)) + { + LOG("Cannot log communication to file, the log file \"%s\" cannot be opened for writing.", FileName.c_str()); + } } } @@ -1659,7 +1666,7 @@ void cProtocol180::FixItemFramePositions(int a_ObjectData, double & a_PosX, doub void cProtocol180::AddReceivedData(const char * a_Data, size_t a_Size) { // Write the incoming data into the comm log file: - if (g_ShouldLogCommIn) + if (g_ShouldLogCommIn && m_CommLogFile.IsOpen()) { if (m_ReceivedData.GetReadableSpace() > 0) { @@ -1764,7 +1771,7 @@ void cProtocol180::AddReceivedData(const char * a_Data, size_t a_Size) bb.Write("\0", 1); // Log the packet info into the comm log file: - if (g_ShouldLogCommIn) + if (g_ShouldLogCommIn && m_CommLogFile.IsOpen()) { AString PacketData; bb.ReadAll(PacketData); @@ -1796,7 +1803,7 @@ void cProtocol180::AddReceivedData(const char * a_Data, size_t a_Size) #endif // _DEBUG // Put a message in the comm log: - if (g_ShouldLogCommIn) + if (g_ShouldLogCommIn && m_CommLogFile.IsOpen()) { m_CommLogFile.Printf("^^^^^^ Unhandled packet ^^^^^^\n\n\n"); } @@ -1813,7 +1820,7 @@ void cProtocol180::AddReceivedData(const char * a_Data, size_t a_Size) ); // Put a message in the comm log: - if (g_ShouldLogCommIn) + if (g_ShouldLogCommIn && m_CommLogFile.IsOpen()) { m_CommLogFile.Printf("^^^^^^ Wrong number of bytes read for this packet (exp %d left, got " SIZE_T_FMT " left) ^^^^^^\n\n\n", 1, bb.GetReadableSpace() @@ -1827,7 +1834,7 @@ void cProtocol180::AddReceivedData(const char * a_Data, size_t a_Size) } // for (ever) // Log any leftover bytes into the logfile: - if (g_ShouldLogCommIn && (m_ReceivedData.GetReadableSpace() > 0)) + if (g_ShouldLogCommIn && (m_ReceivedData.GetReadableSpace() > 0) && m_CommLogFile.IsOpen()) { AString AllData; size_t OldReadableSpace = m_ReceivedData.GetReadableSpace(); @@ -2798,7 +2805,7 @@ cProtocol180::cPacketizer::~cPacketizer() } // Log the comm into logfile: - if (g_ShouldLogCommOut) + if (g_ShouldLogCommOut && m_Protocol.m_CommLogFile.IsOpen()) { AString Hex; ASSERT(PacketData.size() > 0); -- cgit v1.2.3 From 1f2f8b553b2f5080beed9f995066a1ec863228ca Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sat, 24 Jan 2015 20:03:29 +0100 Subject: cNetwork: Fixed IP address reading for incoming connections. --- src/OSSupport/ServerHandleImpl.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/OSSupport/ServerHandleImpl.cpp b/src/OSSupport/ServerHandleImpl.cpp index ba38dbf2e..2485c8e1b 100644 --- a/src/OSSupport/ServerHandleImpl.cpp +++ b/src/OSSupport/ServerHandleImpl.cpp @@ -256,19 +256,20 @@ void cServerHandleImpl::Callback(evconnlistener * a_Listener, evutil_socket_t a_ // Get the textual IP address and port number out of a_Addr: char IPAddress[128]; - evutil_inet_ntop(a_Addr->sa_family, a_Addr->sa_data, IPAddress, ARRAYCOUNT(IPAddress)); UInt16 Port = 0; switch (a_Addr->sa_family) { case AF_INET: { sockaddr_in * sin = reinterpret_cast(a_Addr); + evutil_inet_ntop(AF_INET, sin, IPAddress, ARRAYCOUNT(IPAddress)); Port = ntohs(sin->sin_port); break; } case AF_INET6: { sockaddr_in6 * sin6 = reinterpret_cast(a_Addr); + evutil_inet_ntop(AF_INET, sin6, IPAddress, ARRAYCOUNT(IPAddress)); Port = ntohs(sin6->sin6_port); break; } -- cgit v1.2.3 From 7dfeb67f016135c6da8373b96c32d447e8bf027e Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sat, 24 Jan 2015 20:17:00 +0100 Subject: Converted cServer to use the cNetwork API. --- src/ClientHandle.cpp | 214 +++++++++++++++++++------------------- src/ClientHandle.h | 80 ++++++++------ src/Entities/Player.cpp | 24 +++-- src/Entities/Player.h | 18 +++- src/Server.cpp | 270 ++++++++++++++---------------------------------- src/Server.h | 97 ++++++++--------- src/World.cpp | 31 +++--- src/World.h | 9 +- 8 files changed, 330 insertions(+), 413 deletions(-) (limited to 'src') diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index 387cc4628..e90114d1b 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -15,7 +15,6 @@ #include "Item.h" #include "Mobs/Monster.h" #include "ChatColor.h" -#include "OSSupport/Socket.h" #include "Items/ItemHandler.h" #include "Blocks/BlockHandler.h" #include "Blocks/BlockSlab.h" @@ -56,16 +55,15 @@ int cClientHandle::s_ClientCount = 0; //////////////////////////////////////////////////////////////////////////////// // cClientHandle: -cClientHandle::cClientHandle(const cSocket * a_Socket, int a_ViewDistance) : +cClientHandle::cClientHandle(const AString & a_IPString, int a_ViewDistance) : m_CurrentViewDistance(a_ViewDistance), m_RequestedViewDistance(a_ViewDistance), - m_IPString(a_Socket->GetIPString()), - m_OutgoingData(64 KiB), + m_IPString(a_IPString), m_Player(nullptr), m_HasSentDC(false), m_LastStreamedChunkX(0x7fffffff), // bogus chunk coords to force streaming upon login m_LastStreamedChunkZ(0x7fffffff), - m_TimeSinceLastPacket(0), + m_TicksSinceLastPacket(0), m_Ping(1000), m_PingID(1), m_BlockDigAnimStage(-1), @@ -135,9 +133,6 @@ cClientHandle::~cClientHandle() SendDisconnect("Server shut down? Kthnxbai"); } - // Close the socket as soon as it sends all outgoing data: - cRoot::Get()->GetServer()->RemoveClient(this); - delete m_Protocol; m_Protocol = nullptr; @@ -150,6 +145,10 @@ cClientHandle::~cClientHandle() void cClientHandle::Destroy(void) { + { + cCSLock Lock(m_CSOutgoingData); + m_Link.reset(); + } { cCSLock Lock(m_CSDestroyingState); if (m_State >= csDestroying) @@ -168,6 +167,10 @@ void cClientHandle::Destroy(void) RemoveFromAllChunks(); m_Player->GetWorld()->RemoveClientFromChunkSender(this); } + if (m_Player != nullptr) + { + m_Player->RemoveClientHandle(); + } m_State = csDestroyed; } @@ -326,7 +329,8 @@ void cClientHandle::Authenticate(const AString & a_Name, const AString & a_UUID, m_Protocol->SendLoginSuccess(); // Spawn player (only serversided, so data is loaded) - m_Player = new cPlayer(this, GetUsername()); + m_Player = new cPlayer(m_Self, GetUsername()); + m_Self.reset(); cWorld * World = cRoot::Get()->GetWorld(m_Player->GetLoadedWorldName()); if (World == nullptr) @@ -689,6 +693,47 @@ void cClientHandle::HandleCreativeInventory(short a_SlotNum, const cItem & a_Hel +void cClientHandle::HandleEnchantItem(Byte a_WindowID, Byte a_Enchantment) +{ + if (a_Enchantment > 2) + { + LOGWARNING("%s attempt to crash the server with invalid enchanting selection!", GetUsername().c_str()); + Kick("Invalid enchanting!"); + return; + } + + if ( + (m_Player->GetWindow() == nullptr) || + (m_Player->GetWindow()->GetWindowID() != a_WindowID) || + (m_Player->GetWindow()->GetWindowType() != cWindow::wtEnchantment) + ) + { + return; + } + + cEnchantingWindow * Window = (cEnchantingWindow*) m_Player->GetWindow(); + cItem Item = *Window->m_SlotArea->GetSlot(0, *m_Player); + int BaseEnchantmentLevel = Window->GetPropertyValue(a_Enchantment); + + if (Item.EnchantByXPLevels(BaseEnchantmentLevel)) + { + if (m_Player->IsGameModeCreative() || m_Player->DeltaExperience(-m_Player->XpForLevel(BaseEnchantmentLevel)) >= 0) + { + Window->m_SlotArea->SetSlot(0, *m_Player, Item); + Window->SendSlot(*m_Player, Window->m_SlotArea, 0); + Window->BroadcastWholeWindow(); + + Window->SetProperty(0, 0, *m_Player); + Window->SetProperty(1, 0, *m_Player); + Window->SetProperty(2, 0, *m_Player); + } + } +} + + + + + void cClientHandle::HandlePlayerAbilities(bool a_CanFly, bool a_IsFlying, float FlyingSpeed, float WalkingSpeed) { UNUSED(FlyingSpeed); // Ignore the client values for these @@ -1777,44 +1822,12 @@ void cClientHandle::SendData(const char * a_Data, size_t a_Size) // This could crash the client, because they've already unloaded the world etc., and suddenly a wild packet appears (#31) return; } - + + cCSLock Lock(m_CSOutgoingData); + if (m_Link != nullptr) { - cCSLock Lock(m_CSOutgoingData); - - // _X 2012_09_06: We need an overflow buffer, usually when streaming the initial chunks - if (m_OutgoingDataOverflow.empty()) - { - // No queued overflow data; if this packet fits into the ringbuffer, put it in, otherwise put it in the overflow buffer: - size_t CanFit = m_OutgoingData.GetFreeSpace(); - if (CanFit > a_Size) - { - CanFit = a_Size; - } - if (CanFit > 0) - { - m_OutgoingData.Write(a_Data, CanFit); - } - if (a_Size > CanFit) - { - m_OutgoingDataOverflow.append(a_Data + CanFit, a_Size - CanFit); - } - } - else - { - // There is a queued overflow. Append to it, then send as much from its front as possible - m_OutgoingDataOverflow.append(a_Data, a_Size); - size_t CanFit = m_OutgoingData.GetFreeSpace(); - if (CanFit > 128) - { - // No point in moving the data over if it's not large enough - too much effort for too little an effect - m_OutgoingData.Write(m_OutgoingDataOverflow.data(), CanFit); - m_OutgoingDataOverflow.erase(0, CanFit); - } - } - } // Lock(m_CSOutgoingData) - - // Notify SocketThreads that we have something to write: - cRoot::Get()->GetServer()->NotifyClientWrite(this); + m_Link->Send(a_Data, a_Size); + } } @@ -1873,8 +1886,8 @@ void cClientHandle::Tick(float a_Dt) } m_Protocol->DataReceived(IncomingData.data(), IncomingData.size()); - m_TimeSinceLastPacket += a_Dt; - if (m_TimeSinceLastPacket > 30000.f) // 30 seconds time-out + m_TicksSinceLastPacket += 1; + if (m_TicksSinceLastPacket > 600) // 30 seconds time-out { SendDisconnect("Nooooo!! You timed out! D: Come back!"); Destroy(); @@ -1970,8 +1983,8 @@ void cClientHandle::ServerTick(float a_Dt) return; } - m_TimeSinceLastPacket += a_Dt; - if (m_TimeSinceLastPacket > 30000.f) // 30 seconds time-out + m_TicksSinceLastPacket += 1; + if (m_TicksSinceLastPacket > 600) // 30 seconds { SendDisconnect("Nooooo!! You timed out! D: Come back!"); Destroy(); @@ -2843,94 +2856,79 @@ void cClientHandle::PacketError(UInt32 a_PacketType) -bool cClientHandle::DataReceived(const char * a_Data, size_t a_Size) +void cClientHandle::SocketClosed(void) { - // Data is received from the client, store it in the buffer to be processed by the Tick thread: - m_TimeSinceLastPacket = 0; - cCSLock Lock(m_CSIncomingData); - m_IncomingData.append(a_Data, a_Size); - return false; + // The socket has been closed for any reason + + if (!m_Username.empty()) // Ignore client pings + { + LOGD("Client %s @ %s disconnected", m_Username.c_str(), m_IPString.c_str()); + cRoot::Get()->GetPluginManager()->CallHookDisconnect(*this, "Player disconnected"); + } + + Destroy(); } -void cClientHandle::GetOutgoingData(AString & a_Data) +void cClientHandle::SetSelf(cClientHandlePtr a_Self) { - // Data can be sent to client - { - cCSLock Lock(m_CSOutgoingData); - m_OutgoingData.ReadAll(a_Data); - m_OutgoingData.CommitRead(); - a_Data.append(m_OutgoingDataOverflow); - m_OutgoingDataOverflow.clear(); - } - - // Disconnect player after all packets have been sent - if (m_HasSentDC && a_Data.empty()) - { - Destroy(); - } + ASSERT(m_Self == nullptr); + m_Self = a_Self; } -void cClientHandle::SocketClosed(void) +void cClientHandle::OnLinkCreated(cTCPLinkPtr a_Link) { - // The socket has been closed for any reason - - LOGD("Player %s @ %s disconnected", m_Username.c_str(), m_IPString.c_str()); + m_Link = a_Link; +} - if (!m_Username.empty()) // Ignore client pings - { - cRoot::Get()->GetPluginManager()->CallHookDisconnect(*this, "Player disconnected"); - } - Destroy(); + + + +void cClientHandle::OnReceivedData(const char * a_Data, size_t a_Length) +{ + // Reset the timeout: + m_TicksSinceLastPacket = 0; + + // Queue the incoming data to be processed in the tick thread: + cCSLock Lock(m_CSIncomingData); + m_IncomingData.append(a_Data, a_Length); } -void cClientHandle::HandleEnchantItem(Byte & a_WindowID, Byte & a_Enchantment) +void cClientHandle::OnRemoteClosed(void) { - if (a_Enchantment > 2) { - LOGWARNING("%s attempt to crash the server with invalid enchanting selection!", GetUsername().c_str()); - Kick("Invalid enchanting!"); - return; + cCSLock Lock(m_CSOutgoingData); + m_Link.reset(); } + SocketClosed(); +} - if ( - (m_Player->GetWindow() == nullptr) || - (m_Player->GetWindow()->GetWindowID() != a_WindowID) || - (m_Player->GetWindow()->GetWindowType() != cWindow::wtEnchantment) - ) - { - return; - } - - cEnchantingWindow * Window = (cEnchantingWindow*) m_Player->GetWindow(); - cItem Item = *Window->m_SlotArea->GetSlot(0, *m_Player); - int BaseEnchantmentLevel = Window->GetPropertyValue(a_Enchantment); - if (Item.EnchantByXPLevels(BaseEnchantmentLevel)) - { - if (m_Player->IsGameModeCreative() || m_Player->DeltaExperience(-m_Player->XpForLevel(BaseEnchantmentLevel)) >= 0) - { - Window->m_SlotArea->SetSlot(0, *m_Player, Item); - Window->SendSlot(*m_Player, Window->m_SlotArea, 0); - Window->BroadcastWholeWindow(); - Window->SetProperty(0, 0, *m_Player); - Window->SetProperty(1, 0, *m_Player); - Window->SetProperty(2, 0, *m_Player); - } + + +void cClientHandle::OnError(int a_ErrorCode, const AString & a_ErrorMsg) +{ + LOGD("An error has occurred on client link for %s @ %s: %d (%s). Client disconnected.", + m_Username.c_str(), m_IPString.c_str(), a_ErrorCode, a_ErrorMsg.c_str() + ); + { + cCSLock Lock(m_CSOutgoingData); + m_Link.reset(); } + SocketClosed(); } diff --git a/src/ClientHandle.h b/src/ClientHandle.h index 03ae38cfd..f5b7faede 100644 --- a/src/ClientHandle.h +++ b/src/ClientHandle.h @@ -8,12 +8,10 @@ #pragma once -#ifndef CCLIENTHANDLE_H_INCLUDED -#define CCLIENTHANDLE_H_INCLUDED +#include "OSSupport/Network.h" #include "Defines.h" #include "Vector3.h" -#include "OSSupport/SocketThreads.h" #include "ChunkDef.h" #include "ByteBuffer.h" #include "Scoreboard.h" @@ -27,6 +25,7 @@ +// fwd: class cChunkDataSerializer; class cInventory; class cMonster; @@ -42,25 +41,29 @@ class cItemHandler; class cWorld; class cCompositeChat; class cStatManager; +class cClientHandle; +typedef SharedPtr cClientHandlePtr; -class cClientHandle : // tolua_export - public cSocketThreads::cCallback +class cClientHandle // tolua_export + : public cTCPLink::cCallbacks { // tolua_export -public: - -#if defined(ANDROID_NDK) - static const int DEFAULT_VIEW_DISTANCE = 4; // The default ViewDistance (used when no value is set in Settings.ini) -#else - static const int DEFAULT_VIEW_DISTANCE = 10; -#endif +public: // tolua_export + + #if defined(ANDROID_NDK) + static const int DEFAULT_VIEW_DISTANCE = 4; // The default ViewDistance (used when no value is set in Settings.ini) + #else + static const int DEFAULT_VIEW_DISTANCE = 10; + #endif static const int MAX_VIEW_DISTANCE = 32; static const int MIN_VIEW_DISTANCE = 1; - cClientHandle(const cSocket * a_Socket, int a_ViewDistance); + /** Creates a new client with the specified IP address in its description and the specified initial view distance. */ + cClientHandle(const AString & a_IPString, int a_ViewDistance); + virtual ~cClientHandle(); const AString & GetIPString(void) const { return m_IPString; } // tolua_export @@ -276,6 +279,10 @@ public: void HandleCommandBlockEntityChange(int a_EntityID, const AString & a_NewCommand); void HandleCreativeInventory (short a_SlotNum, const cItem & a_HeldItem); + + /** Called when the player enchants an Item in the Enchanting table UI. */ + void HandleEnchantItem(Byte a_WindowID, Byte a_Enchantment); + void HandleEntityCrouch (int a_EntityID, bool a_IsCrouching); void HandleEntityLeaveBed (int a_EntityID); void HandleEntitySprinting (int a_EntityID, bool a_IsSprinting); @@ -329,9 +336,6 @@ public: Sends an UnloadChunk packet for each loaded chunk and resets the streamed chunks. */ void RemoveFromWorld(void); - /** Called when the player will enchant a Item */ - void HandleEnchantItem(Byte & a_WindowID, Byte & a_Enchantment); - /** Called by the protocol recognizer when the protocol version is known. */ void SetProtocolVersion(UInt32 a_ProtocolVersion) { m_ProtocolVersion = a_ProtocolVersion; } @@ -340,6 +344,9 @@ public: private: + friend class cServer; // Needs access to SetSelf() + + /** The type used for storing the names of registered plugin channels. */ typedef std::set cChannels; @@ -361,13 +368,16 @@ private: cChunkCoordsList m_SentChunks; // Chunks that are currently sent to the client cProtocol * m_Protocol; - + + /** Protects m_IncomingData against multithreaded access. */ cCriticalSection m_CSIncomingData; - AString m_IncomingData; - + + /** Queue for the incoming data received on the link until it is processed in Tick(). + Protected by m_CSIncomingData. */ + AString m_IncomingData; + + /** Protects data going out through m_Link against multi-threaded sending. */ cCriticalSection m_CSOutgoingData; - cByteBuffer m_OutgoingData; - AString m_OutgoingDataOverflow; ///< For data that didn't fit into the m_OutgoingData ringbuffer temporarily Vector3d m_ConfirmPosition; @@ -379,8 +389,8 @@ private: int m_LastStreamedChunkX; int m_LastStreamedChunkZ; - /** Seconds since the last packet data was received (updated in Tick(), reset in DataReceived()) */ - float m_TimeSinceLastPacket; + /** Number of ticks since the last network packet was received (increased in Tick(), reset in OnReceivedData()) */ + int m_TicksSinceLastPacket; /** Duration of the last completed client ping. */ std::chrono::steady_clock::duration m_Ping; @@ -458,6 +468,13 @@ private: /** The version of the protocol that the client is talking, or 0 if unknown. */ UInt32 m_ProtocolVersion; + /** The link that is used for network communication. + m_CSOutgoingData is used to synchronize access for sending data. */ + cTCPLinkPtr m_Link; + + /** Shared pointer to self, so that this instance can keep itself alive when needed. */ + cClientHandlePtr m_Self; + /** Returns true if the rate block interactions is within a reasonable limit (bot protection) */ bool CheckBlockInteractionsRate(void); @@ -483,16 +500,19 @@ private: /** Removes all of the channels from the list of current plugin channels. Ignores channels that are not found. */ void UnregisterPluginChannels(const AStringVector & a_ChannelList); - // cSocketThreads::cCallback overrides: - virtual bool DataReceived (const char * a_Data, size_t a_Size) override; // Data is received from the client - virtual void GetOutgoingData(AString & a_Data) override; // Data can be sent to client - virtual void SocketClosed (void) override; // The socket has been closed for any reason -}; // tolua_export - + /** Called when the network socket has been closed. */ + void SocketClosed(void); + /** Called right after the instance is created to store its SharedPtr inside. */ + void SetSelf(cClientHandlePtr a_Self); + // 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; + virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override; +}; // tolua_export -#endif // CCLIENTHANDLE_H_INCLUDED diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index 1ca131375..527380761 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -47,7 +47,7 @@ const int cPlayer::EATING_TICKS = 30; -cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName) : +cPlayer::cPlayer(cClientHandlePtr a_Client, const AString & a_PlayerName) : super(etPlayer, 0.6, 1.8), m_bVisible(true), m_FoodLevel(MAX_FOOD_LEVEL), @@ -174,7 +174,7 @@ void cPlayer::Destroyed() void cPlayer::SpawnOn(cClientHandle & a_Client) { - if (!m_bVisible || (m_ClientHandle == (&a_Client))) + if (!m_bVisible || (m_ClientHandle.get() == (&a_Client))) { return; } @@ -246,7 +246,7 @@ void cPlayer::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) if (CanMove) { - BroadcastMovementUpdate(m_ClientHandle); + BroadcastMovementUpdate(m_ClientHandle.get()); } if (m_Health > 0) // make sure player is alive @@ -419,7 +419,7 @@ void cPlayer::StartChargingBow(void) LOGD("Player \"%s\" started charging their bow", GetName().c_str()); m_IsChargingBow = true; m_BowCharge = 0; - m_World->BroadcastEntityMetadata(*this, m_ClientHandle); + m_World->BroadcastEntityMetadata(*this, m_ClientHandle.get()); } @@ -432,7 +432,7 @@ int cPlayer::FinishChargingBow(void) int res = m_BowCharge; m_IsChargingBow = false; m_BowCharge = 0; - m_World->BroadcastEntityMetadata(*this, m_ClientHandle); + m_World->BroadcastEntityMetadata(*this, m_ClientHandle.get()); return res; } @@ -446,7 +446,7 @@ void cPlayer::CancelChargingBow(void) LOGD("Player \"%s\" cancelled charging their bow at a charge of %d", GetName().c_str(), m_BowCharge); m_IsChargingBow = false; m_BowCharge = 0; - m_World->BroadcastEntityMetadata(*this, m_ClientHandle); + m_World->BroadcastEntityMetadata(*this, m_ClientHandle.get()); } @@ -1391,7 +1391,7 @@ void cPlayer::SetVisible(bool a_bVisible) if (!a_bVisible && m_bVisible) { m_bVisible = false; - m_World->BroadcastDestroyEntity(*this, m_ClientHandle); // Destroy on all clients + m_World->BroadcastDestroyEntity(*this, m_ClientHandle.get()); // Destroy on all clients } } @@ -2294,6 +2294,16 @@ void cPlayer::Detach() +void cPlayer::RemoveClientHandle(void) +{ + ASSERT(m_ClientHandle != nullptr); + m_ClientHandle.reset(); +} + + + + + AString cPlayer::GetUUIDFileName(const AString & a_UUID) { AString UUID = cMojangAPI::MakeUUIDDashed(a_UUID); diff --git a/src/Entities/Player.h b/src/Entities/Player.h index d3ed46db6..fa9ac7cad 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -40,7 +40,7 @@ public: CLASS_PROTODEF(cPlayer) - cPlayer(cClientHandle * a_Client, const AString & a_PlayerName); + cPlayer(cClientHandlePtr a_Client, const AString & a_PlayerName); virtual ~cPlayer(); @@ -222,7 +222,15 @@ public: /** Closes the current window if it matches the specified ID, resets current window to m_InventoryWindow */ void CloseWindowIfID(char a_WindowID, bool a_CanRefuse = true); - cClientHandle * GetClientHandle(void) const { return m_ClientHandle; } + /** Returns the raw client handle associated with the player. */ + cClientHandle * GetClientHandle(void) const { return m_ClientHandle.get(); } + + // tolua_end + + /** Returns the SharedPtr to client handle associated with the player. */ + cClientHandlePtr GetClientHandlePtr(void) const { return m_ClientHandle; } + + // tolua_begin void SendMessage (const AString & a_Message) { m_ClientHandle->SendChat(a_Message, mtCustom); } void SendMessageInfo (const AString & a_Message) { m_ClientHandle->SendChat(a_Message, mtInformation); } @@ -467,6 +475,10 @@ public: virtual bool IsRclking (void) const { return IsEating() || IsChargingBow(); } virtual void Detach(void); + + /** Called by cClientHandle when the client is being destroyed. + The player removes its m_ClientHandle ownership so that the ClientHandle gets deleted. */ + void RemoveClientHandle(void); protected: @@ -537,7 +549,7 @@ protected: std::chrono::steady_clock::time_point m_LastPlayerListTime; - cClientHandle * m_ClientHandle; + cClientHandlePtr m_ClientHandle; cSlotNums m_InventoryPaintSlots; diff --git a/src/Server.cpp b/src/Server.cpp index df6e5ec8a..783b12947 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -5,7 +5,6 @@ #include "Server.h" #include "ClientHandle.h" #include "Mobs/Monster.h" -#include "OSSupport/Socket.h" #include "Root.h" #include "World.h" #include "ChunkDef.h" @@ -57,6 +56,39 @@ typedef std::list< cClientHandle* > ClientList; +//////////////////////////////////////////////////////////////////////////////// +// cServerListenCallbacks: + +class cServerListenCallbacks: + public cNetwork::cListenCallbacks +{ + cServer & m_Server; + UInt16 m_Port; + + virtual cTCPLink::cCallbacksPtr OnIncomingConnection(const AString & a_RemoteIPAddress, UInt16 a_RemotePort) override + { + return m_Server.OnConnectionAccepted(a_RemoteIPAddress); + } + + virtual void OnAccepted(cTCPLink & a_Link) override {} + + virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) + { + LOGWARNING("Cannot listen on port %d: %d (%s).", m_Port, a_ErrorCode, a_ErrorMsg.c_str()); + } + +public: + cServerListenCallbacks(cServer & a_Server, UInt16 a_Port): + m_Server(a_Server), + m_Port(a_Port) + { + } +}; + + + + + //////////////////////////////////////////////////////////////////////////////// // cServer::cTickThread: @@ -100,8 +132,6 @@ void cServer::cTickThread::Execute(void) // cServer: cServer::cServer(void) : - m_ListenThreadIPv4(*this, cSocket::IPv4, "Client"), - m_ListenThreadIPv6(*this, cSocket::IPv6, "Client"), m_PlayerCount(0), m_PlayerCountDiff(0), m_ClientViewDistance(0), @@ -121,42 +151,6 @@ cServer::cServer(void) : -void cServer::ClientDestroying(const cClientHandle * a_Client) -{ - m_SocketThreads.RemoveClient(a_Client); -} - - - - - -void cServer::NotifyClientWrite(const cClientHandle * a_Client) -{ - m_NotifyWriteThread.NotifyClientWrite(a_Client); -} - - - - - -void cServer::WriteToClient(const cClientHandle * a_Client, const AString & a_Data) -{ - m_SocketThreads.Write(a_Client, a_Data); -} - - - - - -void cServer::RemoveClient(const cClientHandle * a_Client) -{ - m_SocketThreads.RemoveClient(a_Client); -} - - - - - void cServer::ClientMovedToWorld(const cClientHandle * a_Client) { cCSLock Lock(m_CSClients); @@ -211,33 +205,8 @@ bool cServer::InitServer(cIniFile & a_SettingsIni, bool a_ShouldAuth) LOGINFO("Compatible clients: %s", MCS_CLIENT_VERSIONS); LOGINFO("Compatible protocol versions %s", MCS_PROTOCOL_VERSIONS); - if (cSocket::WSAStartup() != 0) // Only does anything on Windows, but whatever - { - LOGERROR("WSAStartup() != 0"); - return false; - } - - bool HasAnyPorts = false; - AString Ports = a_SettingsIni.GetValueSet("Server", "Port", "25565"); - m_ListenThreadIPv4.SetReuseAddr(true); - if (m_ListenThreadIPv4.Initialize(Ports)) - { - HasAnyPorts = true; - } - - Ports = a_SettingsIni.GetValueSet("Server", "PortsIPv6", "25565"); - m_ListenThreadIPv6.SetReuseAddr(true); - if (m_ListenThreadIPv6.Initialize(Ports)) - { - HasAnyPorts = true; - } + m_Ports = ReadUpgradeIniPorts(a_SettingsIni, "Server", "Ports", "Port", "PortsIPv6", "25565"); - if (!HasAnyPorts) - { - LOGERROR("Couldn't open any ports. Aborting the server"); - return false; - } - m_RCONServer.Initialize(a_SettingsIni); m_bIsConnected = true; @@ -278,8 +247,6 @@ bool cServer::InitServer(cIniFile & a_SettingsIni, bool a_ShouldAuth) LOGINFO("Setting default viewdistance to the maximum of %d", m_ClientViewDistance); } - m_NotifyWriteThread.Start(this); - PrepareKeys(); return true; @@ -327,36 +294,14 @@ void cServer::PrepareKeys(void) -void cServer::OnConnectionAccepted(cSocket & a_Socket) +cTCPLink::cCallbacksPtr cServer::OnConnectionAccepted(const AString & a_RemoteIPAddress) { - if (!a_Socket.IsValid()) - { - return; - } - - const AString & ClientIP = a_Socket.GetIPString(); - if (ClientIP.empty()) - { - LOGWARN("cServer: A client connected, but didn't present its IP, disconnecting."); - a_Socket.CloseSocket(); - return; - } - - LOGD("Client \"%s\" connected!", ClientIP.c_str()); - - cClientHandle * NewHandle = new cClientHandle(&a_Socket, m_ClientViewDistance); - if (!m_SocketThreads.AddClient(a_Socket, NewHandle)) - { - // For some reason SocketThreads have rejected the handle, clean it up - LOGERROR("Client \"%s\" cannot be handled, server probably unstable", ClientIP.c_str()); - a_Socket.CloseSocket(); - delete NewHandle; - NewHandle = nullptr; - return; - } - + LOGD("Client \"%s\" connected!", a_RemoteIPAddress.c_str()); + cClientHandlePtr NewHandle = std::make_shared(a_RemoteIPAddress, m_ClientViewDistance); + NewHandle->SetSelf(NewHandle); cCSLock Lock(m_CSClients); m_Clients.push_back(NewHandle); + return NewHandle; } @@ -403,23 +348,30 @@ bool cServer::Tick(float a_Dt) void cServer::TickClients(float a_Dt) { - cClientHandleList RemoveClients; + cClientHandlePtrs RemoveClients; { cCSLock Lock(m_CSClients); // Remove clients that have moved to a world (the world will be ticking them from now on) - for (cClientHandleList::const_iterator itr = m_ClientsToRemove.begin(), end = m_ClientsToRemove.end(); itr != end; ++itr) + for (auto itr = m_ClientsToRemove.begin(), end = m_ClientsToRemove.end(); itr != end; ++itr) { - m_Clients.remove(*itr); + for (auto itrC = m_Clients.begin(), endC = m_Clients.end(); itrC != endC; ++itrC) + { + if (itrC->get() == *itr) + { + m_Clients.erase(itrC); + break; + } + } } // for itr - m_ClientsToRemove[] m_ClientsToRemove.clear(); // Tick the remaining clients, take out those that have been destroyed into RemoveClients - for (cClientHandleList::iterator itr = m_Clients.begin(); itr != m_Clients.end();) + for (auto itr = m_Clients.begin(); itr != m_Clients.end();) { if ((*itr)->IsDestroyed()) { - // Remove the client later, when CS is not held, to avoid deadlock: http://forum.mc-server.org/showthread.php?tid=374 + // Delete the client later, when CS is not held, to avoid deadlock: http://forum.mc-server.org/showthread.php?tid=374 RemoveClients.push_back(*itr); itr = m_Clients.erase(itr); continue; @@ -430,10 +382,7 @@ void cServer::TickClients(float a_Dt) } // Delete the clients that have been destroyed - for (cClientHandleList::iterator itr = RemoveClients.begin(); itr != RemoveClients.end(); ++itr) - { - delete *itr; - } // for itr - RemoveClients[] + RemoveClients.clear(); } @@ -442,12 +391,23 @@ void cServer::TickClients(float a_Dt) bool cServer::Start(void) { - if (!m_ListenThreadIPv4.Start()) + for (auto port: m_Ports) { - return false; - } - if (!m_ListenThreadIPv6.Start()) + UInt16 PortNum = static_cast(atoi(port.c_str())); + if (PortNum == 0) + { + LOGWARNING("Invalid port specified for server: \"%s\". Ignoring.", port.c_str()); + continue; + } + auto Handle = cNetwork::Listen(PortNum, std::make_shared(*this, PortNum)); + if (Handle->IsListening()) + { + m_ServerHandles.push_back(Handle); + } + } // for port - Ports[] + if (m_ServerHandles.empty()) { + LOGERROR("Couldn't open any ports. Aborting the server"); return false; } if (!m_TickThread.Start()) @@ -669,19 +629,24 @@ void cServer::BindBuiltInConsoleCommands(void) void cServer::Shutdown(void) { - m_ListenThreadIPv4.Stop(); - m_ListenThreadIPv6.Stop(); + // Stop listening on all sockets: + for (auto srv: m_ServerHandles) + { + srv->Close(); + } + m_ServerHandles.clear(); + // Notify the tick thread and wait for it to terminate: m_bRestarting = true; m_RestartEvent.Wait(); cRoot::Get()->SaveAllChunks(); + // Remove all clients: cCSLock Lock(m_CSClients); - for (ClientList::iterator itr = m_Clients.begin(); itr != m_Clients.end(); ++itr) + for (auto itr = m_Clients.begin(); itr != m_Clients.end(); ++itr) { (*itr)->Destroy(); - delete *itr; } m_Clients.clear(); } @@ -693,7 +658,7 @@ void cServer::Shutdown(void) void cServer::KickUser(int a_ClientID, const AString & a_Reason) { cCSLock Lock(m_CSClients); - for (ClientList::iterator itr = m_Clients.begin(); itr != m_Clients.end(); ++itr) + for (auto itr = m_Clients.begin(); itr != m_Clients.end(); ++itr) { if ((*itr)->GetUniqueID() == a_ClientID) { @@ -709,7 +674,7 @@ void cServer::KickUser(int a_ClientID, const AString & a_Reason) void cServer::AuthenticateUser(int a_ClientID, const AString & a_Name, const AString & a_UUID, const Json::Value & a_Properties) { cCSLock Lock(m_CSClients); - for (ClientList::iterator itr = m_Clients.begin(); itr != m_Clients.end(); ++itr) + for (auto itr = m_Clients.begin(); itr != m_Clients.end(); ++itr) { if ((*itr)->GetUniqueID() == a_ClientID) { @@ -723,82 +688,3 @@ void cServer::AuthenticateUser(int a_ClientID, const AString & a_Name, const ASt -//////////////////////////////////////////////////////////////////////////////// -// cServer::cNotifyWriteThread: - -cServer::cNotifyWriteThread::cNotifyWriteThread(void) : - super("ClientPacketThread"), - m_Server(nullptr) -{ -} - - - - - -cServer::cNotifyWriteThread::~cNotifyWriteThread() -{ - m_ShouldTerminate = true; - m_Event.Set(); - Wait(); -} - - - - - -bool cServer::cNotifyWriteThread::Start(cServer * a_Server) -{ - m_Server = a_Server; - return super::Start(); -} - - - - - -void cServer::cNotifyWriteThread::Execute(void) -{ - cClientHandleList Clients; - while (!m_ShouldTerminate) - { - cCSLock Lock(m_CS); - while (m_Clients.empty()) - { - cCSUnlock Unlock(Lock); - m_Event.Wait(); - if (m_ShouldTerminate) - { - return; - } - } - - // Copy the clients to notify and unlock the CS: - Clients.splice(Clients.begin(), m_Clients); - Lock.Unlock(); - - for (cClientHandleList::iterator itr = Clients.begin(); itr != Clients.end(); ++itr) - { - m_Server->m_SocketThreads.NotifyWrite(*itr); - } // for itr - Clients[] - Clients.clear(); - } // while (!mShouldTerminate) -} - - - - - -void cServer::cNotifyWriteThread::NotifyClientWrite(const cClientHandle * a_Client) -{ - { - cCSLock Lock(m_CS); - m_Clients.remove(const_cast(a_Client)); // Put it there only once - m_Clients.push_back(const_cast(a_Client)); - } - m_Event.Set(); -} - - - - diff --git a/src/Server.h b/src/Server.h index aab47987f..1f30295b7 100644 --- a/src/Server.h +++ b/src/Server.h @@ -9,10 +9,9 @@ #pragma once -#include "OSSupport/SocketThreads.h" -#include "OSSupport/ListenThread.h" - #include "RCONServer.h" +#include "OSSupport/IsThread.h" +#include "OSSupport/Network.h" #ifdef _MSC_VER #pragma warning(push) @@ -36,10 +35,12 @@ // fwd: class cPlayer; class cClientHandle; +typedef SharedPtr cClientHandlePtr; +typedef std::list cClientHandlePtrs; +typedef std::list cClientHandles; class cIniFile; class cCommandOutputCallback; -typedef std::list cClientHandleList; namespace Json { @@ -50,10 +51,11 @@ namespace Json -class cServer // tolua_export - : public cListenThread::cCallback -{ // tolua_export -public: // tolua_export +// tolua_begin +class cServer +{ +public: + // tolua_end virtual ~cServer() {} bool InitServer(cIniFile & a_SettingsIni, bool a_ShouldAuth); @@ -105,13 +107,6 @@ public: // tolua_export /** Called by cClientHandle's destructor; stop m_SocketThreads from calling back into a_Client */ void ClientDestroying(const cClientHandle * a_Client); - /** Notifies m_SocketThreads that client has something to be written */ - void NotifyClientWrite(const cClientHandle * a_Client); - - void WriteToClient(const cClientHandle * a_Client, const AString & a_Data); // Queues outgoing data for the client through m_SocketThreads - - void RemoveClient(const cClientHandle * a_Client); // Removes the clienthandle from m_SocketThreads - /** Don't tick a_Client anymore, it will be ticked from its cPlayer instead */ void ClientMovedToWorld(const cClientHandle * a_Client); @@ -147,30 +142,7 @@ public: // tolua_export private: friend class cRoot; // so cRoot can create and destroy cServer - - /** When NotifyClientWrite() is called, it is queued for this thread to process (to avoid deadlocks between cSocketThreads, cClientHandle and cChunkMap) */ - class cNotifyWriteThread : - public cIsThread - { - typedef cIsThread super; - - cEvent m_Event; // Set when m_Clients gets appended - cServer * m_Server; - - cCriticalSection m_CS; - cClientHandleList m_Clients; - - virtual void Execute(void); - - public: - - cNotifyWriteThread(void); - ~cNotifyWriteThread(); - - bool Start(cServer * a_Server); - - void NotifyClientWrite(const cClientHandle * a_Client); - } ; + friend class cServerListenCallbacks; // Accessing OnConnectionAccepted() /** The server tick thread takes care of the players who aren't yet spawned in a world */ class cTickThread : @@ -189,21 +161,29 @@ private: } ; - cNotifyWriteThread m_NotifyWriteThread; - - cListenThread m_ListenThreadIPv4; - cListenThread m_ListenThreadIPv6; - - cCriticalSection m_CSClients; ///< Locks client lists - cClientHandleList m_Clients; ///< Clients that are connected to the server and not yet assigned to a cWorld - cClientHandleList m_ClientsToRemove; ///< Clients that have just been moved into a world and are to be removed from m_Clients in the next Tick() - - mutable cCriticalSection m_CSPlayerCount; ///< Locks the m_PlayerCount - int m_PlayerCount; ///< Number of players currently playing in the server - cCriticalSection m_CSPlayerCountDiff; ///< Locks the m_PlayerCountDiff - int m_PlayerCountDiff; ///< Adjustment to m_PlayerCount to be applied in the Tick thread + /** The network sockets listening for client connections. */ + cServerHandlePtrs m_ServerHandles; + + /** Protects m_Clients and m_ClientsToRemove against multithreaded access. */ + cCriticalSection m_CSClients; + + /** Clients that are connected to the server and not yet assigned to a cWorld. */ + cClientHandlePtrs m_Clients; + + /** Clients that have just been moved into a world and are to be removed from m_Clients in the next Tick(). */ + cClientHandles m_ClientsToRemove; - cSocketThreads m_SocketThreads; + /** Protects m_PlayerCount against multithreaded access. */ + mutable cCriticalSection m_CSPlayerCount; + + /** Number of players currently playing in the server. */ + int m_PlayerCount; + + /** Protects m_PlayerCountDiff against multithreaded access. */ + cCriticalSection m_CSPlayerCountDiff; + + /** Adjustment to m_PlayerCount to be applied in the Tick thread. */ + int m_PlayerCountDiff; int m_ClientViewDistance; // The default view distance for clients; settable in Settings.ini @@ -250,19 +230,24 @@ private: /** True if BungeeCord handshake packets (with player UUID) should be accepted. */ bool m_ShouldAllowBungeeCord; + /** The list of ports on which the server should listen for connections. + Initialized in InitServer(), used in Start(). */ + AStringVector m_Ports; + cServer(void); /** Loads, or generates, if missing, RSA keys for protocol encryption */ void PrepareKeys(void); + + /** Creates a new cClientHandle instance and adds it to the list of clients. + Returns the cClientHandle reinterpreted as cTCPLink callbacks. */ + cTCPLink::cCallbacksPtr OnConnectionAccepted(const AString & a_RemoteIPAddress); bool Tick(float a_Dt); /** Ticks the clients in m_Clients, manages the list in respect to removing clients */ void TickClients(float a_Dt); - - // cListenThread::cCallback overrides: - virtual void OnConnectionAccepted(cSocket & a_Socket) override; }; // tolua_export diff --git a/src/World.cpp b/src/World.cpp index 24b1a9b40..474f77b81 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -815,10 +815,9 @@ void cWorld::Stop(void) // Delete the clients that have been in this world: { cCSLock Lock(m_CSClients); - for (cClientHandleList::iterator itr = m_Clients.begin(); itr != m_Clients.end(); ++itr) + for (auto itr = m_Clients.begin(); itr != m_Clients.end(); ++itr) { (*itr)->Destroy(); - delete *itr; } // for itr - m_Clients[] m_Clients.clear(); } @@ -1093,19 +1092,26 @@ void cWorld::TickScheduledTasks(void) void cWorld::TickClients(float a_Dt) { - cClientHandleList RemoveClients; + cClientHandlePtrs RemoveClients; { cCSLock Lock(m_CSClients); // Remove clients scheduled for removal: - for (cClientHandleList::iterator itr = m_ClientsToRemove.begin(), end = m_ClientsToRemove.end(); itr != end; ++itr) + for (auto itr = m_ClientsToRemove.begin(), end = m_ClientsToRemove.end(); itr != end; ++itr) { - m_Clients.remove(*itr); + for (auto itrC = m_Clients.begin(), endC = m_Clients.end(); itrC != endC; ++itrC) + { + if (itrC->get() == *itr) + { + m_Clients.erase(itrC); + break; + } + } } // for itr - m_ClientsToRemove[] m_ClientsToRemove.clear(); // Add clients scheduled for adding: - for (cClientHandleList::iterator itr = m_ClientsToAdd.begin(), end = m_ClientsToAdd.end(); itr != end; ++itr) + for (auto itr = m_ClientsToAdd.begin(), end = m_ClientsToAdd.end(); itr != end; ++itr) { ASSERT(std::find(m_Clients.begin(), m_Clients.end(), *itr) == m_Clients.end()); m_Clients.push_back(*itr); @@ -1113,7 +1119,7 @@ void cWorld::TickClients(float a_Dt) m_ClientsToAdd.clear(); // Tick the clients, take out those that have been destroyed into RemoveClients - for (cClientHandleList::iterator itr = m_Clients.begin(); itr != m_Clients.end();) + for (auto itr = m_Clients.begin(); itr != m_Clients.end();) { if ((*itr)->IsDestroyed()) { @@ -1126,12 +1132,9 @@ void cWorld::TickClients(float a_Dt) ++itr; } // for itr - m_Clients[] } - - // Delete the clients that have been destroyed - for (cClientHandleList::iterator itr = RemoveClients.begin(); itr != RemoveClients.end(); ++itr) - { - delete *itr; - } // for itr - RemoveClients[] + + // Delete the clients queued for removal: + RemoveClients.clear(); } @@ -3525,7 +3528,7 @@ void cWorld::AddQueuedPlayers(void) cCSLock Lock(m_CSClients); for (cPlayerList::iterator itr = PlayersToAdd.begin(), end = PlayersToAdd.end(); itr != end; ++itr) { - cClientHandle * Client = (*itr)->GetClientHandle(); + cClientHandlePtr Client = (*itr)->GetClientHandlePtr(); if (Client != nullptr) { m_Clients.push_back(Client); diff --git a/src/World.h b/src/World.h index e7519dab8..3cac71a36 100644 --- a/src/World.h +++ b/src/World.h @@ -38,6 +38,9 @@ class cRedstoneSimulator; class cItem; class cPlayer; class cClientHandle; +typedef SharedPtr cClientHandlePtr; +typedef std::list cClientHandlePtrs; +typedef std::list cClientHandles; class cEntity; class cBlockEntity; class cWorldGenerator; // The generator that actually generates the chunks for a single world @@ -1019,13 +1022,13 @@ private: cCriticalSection m_CSClients; /** List of clients in this world, these will be ticked by this world */ - cClientHandleList m_Clients; + cClientHandlePtrs m_Clients; /** Clients that are scheduled for removal (ticked in another world), waiting for TickClients() to remove them */ - cClientHandleList m_ClientsToRemove; + cClientHandles m_ClientsToRemove; /** Clients that are scheduled for adding, waiting for TickClients to add them */ - cClientHandleList m_ClientsToAdd; + cClientHandlePtrs m_ClientsToAdd; /** Guards m_EntitiesToAdd */ cCriticalSection m_CSEntitiesToAdd; -- cgit v1.2.3 From 86f2f82d2a81bac4739d135959b08c011d007303 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sat, 24 Jan 2015 23:17:13 +0100 Subject: BlockingSslClientSocket: Migrated to cNetwork API. --- src/PolarSSL++/BlockingSslClientSocket.cpp | 176 +++++++++++++++++++++++++++-- src/PolarSSL++/BlockingSslClientSocket.h | 35 +++++- src/PolarSSL++/SslContext.cpp | 2 +- 3 files changed, 198 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/PolarSSL++/BlockingSslClientSocket.cpp b/src/PolarSSL++/BlockingSslClientSocket.cpp index 59e1281ac..821125b31 100644 --- a/src/PolarSSL++/BlockingSslClientSocket.cpp +++ b/src/PolarSSL++/BlockingSslClientSocket.cpp @@ -10,6 +10,80 @@ +//////////////////////////////////////////////////////////////////////////////// +// cBlockingSslClientSocketConnectCallbacks: + +class cBlockingSslClientSocketConnectCallbacks: + public cNetwork::cConnectCallbacks +{ + /** The socket object that is using this instance of the callbacks. */ + cBlockingSslClientSocket & m_Socket; + + virtual void OnConnected(cTCPLink & a_Link) override + { + m_Socket.OnConnected(); + } + + virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override + { + m_Socket.OnConnectError(a_ErrorMsg); + } + +public: + cBlockingSslClientSocketConnectCallbacks(cBlockingSslClientSocket & a_Socket): + m_Socket(a_Socket) + { + } +}; + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cBlockingSslClientSocketLinkCallbacks: + +class cBlockingSslClientSocketLinkCallbacks: + public cTCPLink::cCallbacks +{ + cBlockingSslClientSocket & m_Socket; + + virtual void OnLinkCreated(cTCPLinkPtr a_Link) override + { + m_Socket.SetLink(a_Link); + } + + + virtual void OnReceivedData(const char * a_Data, size_t a_Length) + { + m_Socket.OnReceivedData(a_Data, a_Length); + } + + + virtual void OnRemoteClosed(void) + { + m_Socket.OnDisconnected(); + } + + + virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) + { + m_Socket.OnDisconnected(); + } +public: + cBlockingSslClientSocketLinkCallbacks(cBlockingSslClientSocket & a_Socket): + m_Socket(a_Socket) + { + } +}; + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cBlockingSslClientSocket: + cBlockingSslClientSocket::cBlockingSslClientSocket(void) : m_Ssl(*this), m_IsConnected(false) @@ -32,10 +106,19 @@ bool cBlockingSslClientSocket::Connect(const AString & a_ServerName, UInt16 a_Po } // Connect the underlying socket: - m_Socket.CreateSocket(cSocket::IPv4); - if (!m_Socket.ConnectIPv4(a_ServerName.c_str(), a_Port)) + m_ServerName = a_ServerName; + if (!cNetwork::Connect(a_ServerName, a_Port, + std::make_shared(*this), + std::make_shared(*this)) + ) + { + return false; + } + + // Wait for the connection to succeed or fail: + m_Event.Wait(); + if (!m_IsConnected) { - Printf(m_LastErrorText, "Socket connect failed: %s", m_Socket.GetLastErrorString().c_str()); return false; } @@ -102,7 +185,7 @@ bool cBlockingSslClientSocket::Send(const void * a_Data, size_t a_NumBytes) ASSERT(m_IsConnected); // Keep sending the data until all of it is sent: - const char * Data = (const char *)a_Data; + const char * Data = reinterpret_cast(a_Data); size_t NumBytes = a_NumBytes; for (;;) { @@ -156,7 +239,8 @@ void cBlockingSslClientSocket::Disconnect(void) } m_Ssl.NotifyClose(); - m_Socket.CloseSocket(); + m_Socket->Close(); + m_Socket.reset(); m_IsConnected = false; } @@ -166,13 +250,25 @@ void cBlockingSslClientSocket::Disconnect(void) int cBlockingSslClientSocket::ReceiveEncrypted(unsigned char * a_Buffer, size_t a_NumBytes) { - int res = m_Socket.Receive((char *)a_Buffer, a_NumBytes, 0); - if (res < 0) + // Wait for any incoming data, if there is none: + cCSLock Lock(m_CSIncomingData); + while (m_IsConnected && m_IncomingData.empty()) + { + cCSUnlock Unlock(Lock); + m_Event.Wait(); + } + + // If we got disconnected, report an error after processing all data: + if (!m_IsConnected && m_IncomingData.empty()) { - // PolarSSL's net routines distinguish between connection reset and general failure, we don't need to return POLARSSL_ERR_NET_RECV_FAILED; } - return res; + + // Copy the data from the incoming buffer into the specified space: + size_t NumToCopy = std::min(a_NumBytes, m_IncomingData.size()); + memcpy(a_Buffer, m_IncomingData.data(), NumToCopy); + m_IncomingData.erase(0, NumToCopy); + return static_cast(NumToCopy); } @@ -181,13 +277,69 @@ int cBlockingSslClientSocket::ReceiveEncrypted(unsigned char * a_Buffer, size_t int cBlockingSslClientSocket::SendEncrypted(const unsigned char * a_Buffer, size_t a_NumBytes) { - int res = m_Socket.Send((const char *)a_Buffer, a_NumBytes); - if (res < 0) + cTCPLinkPtr Socket(m_Socket); // Make a copy so that multiple threads don't race on deleting the socket. + if (Socket == nullptr) + { + return POLARSSL_ERR_NET_SEND_FAILED; + } + if (!Socket->Send(a_Buffer, a_NumBytes)) { // PolarSSL's net routines distinguish between connection reset and general failure, we don't need to return POLARSSL_ERR_NET_SEND_FAILED; } - return res; + return static_cast(a_NumBytes); +} + + + + +void cBlockingSslClientSocket::OnConnected(void) +{ + m_IsConnected = true; + m_Event.Set(); +} + + + + + +void cBlockingSslClientSocket::OnConnectError(const AString & a_ErrorMsg) +{ + LOG("Cannot connect to %s: %s", m_ServerName.c_str(), a_ErrorMsg.c_str()); + m_Event.Set(); +} + + + + + +void cBlockingSslClientSocket::OnReceivedData(const char * a_Data, size_t a_Size) +{ + { + cCSLock Lock(m_CSIncomingData); + m_IncomingData.append(a_Data, a_Size); + } + m_Event.Set(); +} + + + + + +void cBlockingSslClientSocket::SetLink(cTCPLinkPtr a_Link) +{ + m_Socket = a_Link; +} + + + + + +void cBlockingSslClientSocket::OnDisconnected(void) +{ + m_Socket.reset(); + m_IsConnected = false; + m_Event.Set(); } diff --git a/src/PolarSSL++/BlockingSslClientSocket.h b/src/PolarSSL++/BlockingSslClientSocket.h index 7af897582..319e82bf2 100644 --- a/src/PolarSSL++/BlockingSslClientSocket.h +++ b/src/PolarSSL++/BlockingSslClientSocket.h @@ -9,8 +9,8 @@ #pragma once +#include "OSSupport/Network.h" #include "CallbackSslContext.h" -#include "../OSSupport/Socket.h" @@ -51,25 +51,56 @@ public: const AString & GetLastErrorText(void) const { return m_LastErrorText; } protected: + friend class cBlockingSslClientSocketConnectCallbacks; + friend class cBlockingSslClientSocketLinkCallbacks; + /** The SSL context used for the socket */ cCallbackSslContext m_Ssl; /** The underlying socket to the SSL server */ - cSocket m_Socket; + cTCPLinkPtr m_Socket; + + /** The object used to signal state changes in the socket (the cause of the blocking). */ + cEvent m_Event; /** The trusted CA root cert store, if we are to verify the cert strictly. Set by SetTrustedRootCertsFromString(). */ cX509CertPtr m_CACerts; /** The expected SSL peer's name, if we are to verify the cert strictly. Set by SetTrustedRootCertsFromString(). */ AString m_ExpectedPeerName; + + /** The hostname to which the socket is connecting (stored for error reporting). */ + AString m_ServerName; /** Text of the last error that has occurred. */ AString m_LastErrorText; /** Set to true if the connection established successfully. */ bool m_IsConnected; + + /** Protects m_IncomingData against multithreaded access. */ + cCriticalSection m_CSIncomingData; + + /** Buffer for the data incoming on the network socket. + Protected by m_CSIncomingData. */ + AString m_IncomingData; + /** Called when the connection is established successfully. */ + void OnConnected(void); + + /** Called when an error occurs while connecting the socket. */ + void OnConnectError(const AString & a_ErrorMsg); + + /** Called when there's incoming data from the socket. */ + void OnReceivedData(const char * a_Data, size_t a_Size); + + /** Called when the link for the connection is created. */ + void SetLink(cTCPLinkPtr a_Link); + + /** Called when the link is disconnected, either gracefully or by an error. */ + void OnDisconnected(void); + // cCallbackSslContext::cDataCallbacks 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; diff --git a/src/PolarSSL++/SslContext.cpp b/src/PolarSSL++/SslContext.cpp index 902267f90..66dfefc65 100644 --- a/src/PolarSSL++/SslContext.cpp +++ b/src/PolarSSL++/SslContext.cpp @@ -70,7 +70,7 @@ int cSslContext::Initialize(bool a_IsClient, const SharedPtr & // so they're disabled until someone needs them ssl_set_dbg(&m_Ssl, &SSLDebugMessage, this); ssl_set_verify(&m_Ssl, &SSLVerifyCert, this); - */ + //*/ /* // Set ciphersuite to the easiest one to decode, so that the connection can be wireshark-decoded: -- cgit v1.2.3 From f243aa387c0c76c46abef90292cab0aab0c63962 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sat, 24 Jan 2015 23:21:18 +0100 Subject: Removed ListenThread and SocketThreads. They have been replaced by the cNetwork API. Socket.cpp is still used by RCONClient. --- src/OSSupport/CMakeLists.txt | 6 - src/OSSupport/ListenThread.cpp | 238 -------------- src/OSSupport/ListenThread.h | 85 ----- src/OSSupport/SocketThreads.cpp | 702 ---------------------------------------- src/OSSupport/SocketThreads.h | 194 ----------- 5 files changed, 1225 deletions(-) delete mode 100644 src/OSSupport/ListenThread.cpp delete mode 100644 src/OSSupport/ListenThread.h delete mode 100644 src/OSSupport/SocketThreads.cpp delete mode 100644 src/OSSupport/SocketThreads.h (limited to 'src') diff --git a/src/OSSupport/CMakeLists.txt b/src/OSSupport/CMakeLists.txt index c8e96cbec..6f609c519 100644 --- a/src/OSSupport/CMakeLists.txt +++ b/src/OSSupport/CMakeLists.txt @@ -13,12 +13,9 @@ SET (SRCS HostnameLookup.cpp IPLookup.cpp IsThread.cpp - ListenThread.cpp NetworkSingleton.cpp Semaphore.cpp ServerHandleImpl.cpp - Socket.cpp - SocketThreads.cpp StackTrace.cpp TCPLinkImpl.cpp ) @@ -32,14 +29,11 @@ SET (HDRS HostnameLookup.h IPLookup.h IsThread.h - ListenThread.h Network.h NetworkSingleton.h Queue.h Semaphore.h ServerHandleImpl.h - Socket.h - SocketThreads.h StackTrace.h TCPLinkImpl.h ) diff --git a/src/OSSupport/ListenThread.cpp b/src/OSSupport/ListenThread.cpp deleted file mode 100644 index b029634e9..000000000 --- a/src/OSSupport/ListenThread.cpp +++ /dev/null @@ -1,238 +0,0 @@ - -// ListenThread.cpp - -// Implements the cListenThread class representing the thread that listens for client connections - -#include "Globals.h" -#include "ListenThread.h" - - - - - -cListenThread::cListenThread(cCallback & a_Callback, cSocket::eFamily a_Family, const AString & a_ServiceName) : - super(Printf("ListenThread %s", a_ServiceName.c_str())), - m_Callback(a_Callback), - m_Family(a_Family), - m_ShouldReuseAddr(false), - m_ServiceName(a_ServiceName) -{ -} - - - - - -cListenThread::~cListenThread() -{ - Stop(); -} - - - - - -bool cListenThread::Initialize(const AString & a_PortsString) -{ - ASSERT(m_Sockets.empty()); // Not yet started - - if (!CreateSockets(a_PortsString)) - { - return false; - } - - return true; -} - - - - - -bool cListenThread::Start(void) -{ - if (m_Sockets.empty()) - { - // There are no sockets listening, either forgotten to initialize or the user specified no listening ports - // Report as successful, though - return true; - } - return super::Start(); -} - - - - - -void cListenThread::Stop(void) -{ - if (m_Sockets.empty()) - { - // No sockets means no thread was running in the first place - return; - } - - m_ShouldTerminate = true; - - // Close one socket to wake the thread up from the select() call - m_Sockets[0].CloseSocket(); - - // Wait for the thread to finish - super::Wait(); - - // Close all the listening sockets: - for (cSockets::iterator itr = m_Sockets.begin() + 1, end = m_Sockets.end(); itr != end; ++itr) - { - itr->CloseSocket(); - } // for itr - m_Sockets[] - m_Sockets.clear(); -} - - - - - -void cListenThread::SetReuseAddr(bool a_Reuse) -{ - ASSERT(m_Sockets.empty()); // Must not have been Initialize()d yet - - m_ShouldReuseAddr = a_Reuse; -} - - - - - -bool cListenThread::CreateSockets(const AString & a_PortsString) -{ - AStringVector Ports = StringSplitAndTrim(a_PortsString, ","); - - if (Ports.empty()) - { - return false; - } - - AString FamilyStr = m_ServiceName; - switch (m_Family) - { - case cSocket::IPv4: FamilyStr.append(" IPv4"); break; - case cSocket::IPv6: FamilyStr.append(" IPv6"); break; - default: - { - ASSERT(!"Unknown address family"); - break; - } - } - - for (AStringVector::const_iterator itr = Ports.begin(), end = Ports.end(); itr != end; ++itr) - { - int Port = atoi(itr->c_str()); - if ((Port <= 0) || (Port > 65535)) - { - LOGWARNING("%s: Invalid port specified: \"%s\".", FamilyStr.c_str(), itr->c_str()); - continue; - } - m_Sockets.push_back(cSocket::CreateSocket(m_Family)); - if (!m_Sockets.back().IsValid()) - { - LOGWARNING("%s: Cannot create listening socket for port %d: \"%s\"", FamilyStr.c_str(), Port, cSocket::GetLastErrorString().c_str()); - m_Sockets.pop_back(); - continue; - } - - if (m_ShouldReuseAddr) - { - if (!m_Sockets.back().SetReuseAddress()) - { - LOG("%s: Port %d cannot reuse addr, syscall failed: \"%s\".", FamilyStr.c_str(), Port, cSocket::GetLastErrorString().c_str()); - } - } - - // Bind to port: - bool res = false; - switch (m_Family) - { - case cSocket::IPv4: res = m_Sockets.back().BindToAnyIPv4(Port); break; - case cSocket::IPv6: res = m_Sockets.back().BindToAnyIPv6(Port); break; - default: - { - ASSERT(!"Unknown address family"); - res = false; - } - } - if (!res) - { - LOGWARNING("%s: Cannot bind port %d: \"%s\".", FamilyStr.c_str(), Port, cSocket::GetLastErrorString().c_str()); - m_Sockets.pop_back(); - continue; - } - - if (!m_Sockets.back().Listen()) - { - LOGWARNING("%s: Cannot listen on port %d: \"%s\".", FamilyStr.c_str(), Port, cSocket::GetLastErrorString().c_str()); - m_Sockets.pop_back(); - continue; - } - - LOGINFO("%s: Port %d is open for connections", FamilyStr.c_str(), Port); - } // for itr - Ports[] - - return !(m_Sockets.empty()); -} - - - - - -void cListenThread::Execute(void) -{ - if (m_Sockets.empty()) - { - LOGD("Empty cListenThread, ending thread now."); - return; - } - - // Find the highest socket number: - cSocket::xSocket Highest = m_Sockets[0].GetSocket(); - for (cSockets::iterator itr = m_Sockets.begin(), end = m_Sockets.end(); itr != end; ++itr) - { - if (itr->GetSocket() > Highest) - { - Highest = itr->GetSocket(); - } - } // for itr - m_Sockets[] - - while (!m_ShouldTerminate) - { - // Put all sockets into a FD set: - fd_set fdRead; - FD_ZERO(&fdRead); - for (cSockets::iterator itr = m_Sockets.begin(), end = m_Sockets.end(); itr != end; ++itr) - { - FD_SET(itr->GetSocket(), &fdRead); - } // for itr - m_Sockets[] - - timeval tv; // On Linux select() doesn't seem to wake up when socket is closed, so let's kinda busy-wait: - tv.tv_sec = 1; - tv.tv_usec = 0; - if (select((int)Highest + 1, &fdRead, nullptr, nullptr, &tv) == -1) - { - LOG("select(R) call failed in cListenThread: \"%s\"", cSocket::GetLastErrorString().c_str()); - continue; - } - for (cSockets::iterator itr = m_Sockets.begin(), end = m_Sockets.end(); itr != end; ++itr) - { - if (itr->IsValid() && FD_ISSET(itr->GetSocket(), &fdRead)) - { - cSocket Client = (m_Family == cSocket::IPv4) ? itr->AcceptIPv4() : itr->AcceptIPv6(); - if (Client.IsValid()) - { - m_Callback.OnConnectionAccepted(Client); - } - } - } // for itr - m_Sockets[] - } // while (!m_ShouldTerminate) -} - - - - diff --git a/src/OSSupport/ListenThread.h b/src/OSSupport/ListenThread.h deleted file mode 100644 index b2d806c82..000000000 --- a/src/OSSupport/ListenThread.h +++ /dev/null @@ -1,85 +0,0 @@ - -// ListenThread.h - -// Declares the cListenThread class representing the thread that listens for client connections - - - - - -#pragma once - -#include "IsThread.h" -#include "Socket.h" - - - - - -// fwd: -class cServer; - - - - - -class cListenThread : - public cIsThread -{ - typedef cIsThread super; - -public: - /** Used as the callback for connection events */ - class cCallback - { - public: - virtual ~cCallback() {} - - /** This callback is called whenever a socket connection is accepted */ - virtual void OnConnectionAccepted(cSocket & a_Socket) = 0; - } ; - - cListenThread(cCallback & a_Callback, cSocket::eFamily a_Family, const AString & a_ServiceName = ""); - ~cListenThread(); - - /** Creates all the sockets, returns trus if successful, false if not. */ - bool Initialize(const AString & a_PortsString); - - bool Start(void); - - void Stop(void); - - /** Call before Initialize() to set the "reuse" flag on the sockets */ - void SetReuseAddr(bool a_Reuse = true); - -protected: - typedef std::vector cSockets; - - /** The callback which to notify of incoming connections */ - cCallback & m_Callback; - - /** Socket address family to use */ - cSocket::eFamily m_Family; - - /** Sockets that are being monitored */ - cSockets m_Sockets; - - /** If set to true, the SO_REUSEADDR socket option is set to true */ - bool m_ShouldReuseAddr; - - /** Name of the service that's listening on the ports; for logging purposes only */ - AString m_ServiceName; - - - /** Fills in m_Sockets with individual sockets, each for one port specified in a_PortsString. - Returns true if successful and at least one socket has been created - */ - bool CreateSockets(const AString & a_PortsString); - - // cIsThread override: - virtual void Execute(void) override; -} ; - - - - diff --git a/src/OSSupport/SocketThreads.cpp b/src/OSSupport/SocketThreads.cpp deleted file mode 100644 index 153d6ed1d..000000000 --- a/src/OSSupport/SocketThreads.cpp +++ /dev/null @@ -1,702 +0,0 @@ - -// cSocketThreads.cpp - -// Implements the cSocketThreads class representing the heart of MCS's client networking. -// This object takes care of network communication, groups sockets into threads and uses as little threads as possible for full read / write support -// For more detail, see http://forum.mc-server.org/showthread.php?tid=327 - -#include "Globals.h" -#include "SocketThreads.h" -#include "Errors.h" - - - - - -//////////////////////////////////////////////////////////////////////////////// -// cSocketThreads: - -cSocketThreads::cSocketThreads(void) -{ -} - - - - - -cSocketThreads::~cSocketThreads() -{ - for (cSocketThreadList::iterator itr = m_Threads.begin(); itr != m_Threads.end(); ++itr) - { - delete *itr; - } // for itr - m_Threads[] - m_Threads.clear(); -} - - - - - - -bool cSocketThreads::AddClient(const cSocket & a_Socket, cCallback * a_Client) -{ - // Add a (socket, client) pair for processing, data from a_Socket is to be sent to a_Client - - // Try to add to existing threads: - cCSLock Lock(m_CS); - for (cSocketThreadList::iterator itr = m_Threads.begin(); itr != m_Threads.end(); ++itr) - { - if ((*itr)->IsValid() && (*itr)->HasEmptySlot()) - { - (*itr)->AddClient(a_Socket, a_Client); - return true; - } - } - - // No thread has free space, create a new one: - LOGD("Creating a new cSocketThread (currently have " SIZE_T_FMT ")", m_Threads.size()); - cSocketThread * Thread = new cSocketThread(this); - if (!Thread->Start()) - { - // There was an error launching the thread (but it was already logged along with the reason) - LOGERROR("A new cSocketThread failed to start"); - delete Thread; - Thread = nullptr; - return false; - } - Thread->AddClient(a_Socket, a_Client); - m_Threads.push_back(Thread); - return true; -} - - - - - -void cSocketThreads::RemoveClient(const cCallback * a_Client) -{ - // Remove the associated socket and the client from processing - - cCSLock Lock(m_CS); - for (cSocketThreadList::iterator itr = m_Threads.begin(); itr != m_Threads.end(); ++itr) - { - if ((*itr)->RemoveClient(a_Client)) - { - return; - } - } // for itr - m_Threads[] - - // This client wasn't found. - // It's not an error, because it may have been removed by a different thread in the meantime. -} - - - - - -void cSocketThreads::NotifyWrite(const cCallback * a_Client) -{ - // Notifies the thread responsible for a_Client that the client has something to write - - cCSLock Lock(m_CS); - for (cSocketThreadList::iterator itr = m_Threads.begin(); itr != m_Threads.end(); ++itr) - { - if ((*itr)->NotifyWrite(a_Client)) - { - return; - } - } // for itr - m_Threads[] - - // Cannot assert - this normally happens if a client disconnects and has pending packets, the cServer::cNotifyWriteThread will call this on invalid clients too - // ASSERT(!"Notifying write to an unknown client"); -} - - - - - -void cSocketThreads::Write(const cCallback * a_Client, const AString & a_Data) -{ - // Puts a_Data into outgoing data queue for a_Client - cCSLock Lock(m_CS); - for (cSocketThreadList::iterator itr = m_Threads.begin(); itr != m_Threads.end(); ++itr) - { - if ((*itr)->Write(a_Client, a_Data)) - { - return; - } - } // for itr - m_Threads[] - - // This may be perfectly legal, if the socket has been destroyed and the client is finishing up - // ASSERT(!"Writing to an unknown socket"); -} - - - - - -//////////////////////////////////////////////////////////////////////////////// -// cSocketThreads::cSocketThread: - -cSocketThreads::cSocketThread::cSocketThread(cSocketThreads * a_Parent) : - cIsThread("cSocketThread"), - m_Parent(a_Parent), - m_NumSlots(0) -{ - // Nothing needed yet -} - - - - - -cSocketThreads::cSocketThread::~cSocketThread() -{ - m_ShouldTerminate = true; - - // Notify the thread: - ASSERT(m_ControlSocket2.IsValid()); - m_ControlSocket2.Send("a", 1); - - // Wait for the thread to finish: - Wait(); - - // Close the control sockets: - m_ControlSocket1.CloseSocket(); - m_ControlSocket2.CloseSocket(); -} - - - - - -void cSocketThreads::cSocketThread::AddClient(const cSocket & a_Socket, cCallback * a_Client) -{ - ASSERT(m_Parent->m_CS.IsLockedByCurrentThread()); - ASSERT(m_NumSlots < MAX_SLOTS); // Use HasEmptySlot() to check before adding - - m_Slots[m_NumSlots].m_Client = a_Client; - m_Slots[m_NumSlots].m_Socket = a_Socket; - m_Slots[m_NumSlots].m_Socket.SetNonBlocking(); - m_Slots[m_NumSlots].m_Outgoing.clear(); - m_Slots[m_NumSlots].m_State = sSlot::ssNormal; - m_NumSlots++; - - // Notify the thread of the change: - ASSERT(m_ControlSocket2.IsValid()); - m_ControlSocket2.Send("a", 1); -} - - - - - -bool cSocketThreads::cSocketThread::RemoveClient(const cCallback * a_Client) -{ - ASSERT(m_Parent->m_CS.IsLockedByCurrentThread()); - - if (m_NumSlots == 0) - { - return false; - } - - for (int i = m_NumSlots - 1; i >= 0 ; --i) - { - if (m_Slots[i].m_Client != a_Client) - { - continue; - } - - // Found the slot: - if (m_Slots[i].m_State == sSlot::ssRemoteClosed) - { - // The remote has already closed the socket, remove the slot altogether: - if (m_Slots[i].m_Socket.IsValid()) - { - m_Slots[i].m_Socket.CloseSocket(); - } - m_Slots[i] = m_Slots[--m_NumSlots]; - } - else - { - // Query and queue the last batch of outgoing data: - AString Data; - m_Slots[i].m_Client->GetOutgoingData(Data); - m_Slots[i].m_Outgoing.append(Data); - if (m_Slots[i].m_Outgoing.empty()) - { - // No more outgoing data, shut the socket down immediately: - m_Slots[i].m_Socket.ShutdownReadWrite(); - m_Slots[i].m_State = sSlot::ssShuttingDown; - } - else - { - // More data to send, shut down reading and wait for the rest to get sent: - m_Slots[i].m_State = sSlot::ssWritingRestOut; - } - m_Slots[i].m_Client = nullptr; - } - - // Notify the thread of the change: - ASSERT(m_ControlSocket2.IsValid()); - m_ControlSocket2.Send("r", 1); - return true; - } // for i - m_Slots[] - - // Not found - return false; -} - - - - - -bool cSocketThreads::cSocketThread::HasClient(const cCallback * a_Client) const -{ - ASSERT(m_Parent->m_CS.IsLockedByCurrentThread()); - - for (int i = m_NumSlots - 1; i >= 0; --i) - { - if (m_Slots[i].m_Client == a_Client) - { - return true; - } - } // for i - m_Slots[] - return false; -} - - - - - -bool cSocketThreads::cSocketThread::HasSocket(const cSocket * a_Socket) const -{ - for (int i = m_NumSlots - 1; i >= 0; --i) - { - if (m_Slots[i].m_Socket == *a_Socket) - { - return true; - } - } // for i - m_Slots[] - return false; -} - - - - - -bool cSocketThreads::cSocketThread::NotifyWrite(const cCallback * a_Client) -{ - ASSERT(m_Parent->m_CS.IsLockedByCurrentThread()); - - if (HasClient(a_Client)) - { - // Notify the thread that there's another packet in the queue: - ASSERT(m_ControlSocket2.IsValid()); - m_ControlSocket2.Send("q", 1); - return true; - } - return false; -} - - - - - -bool cSocketThreads::cSocketThread::Write(const cCallback * a_Client, const AString & a_Data) -{ - ASSERT(m_Parent->m_CS.IsLockedByCurrentThread()); - for (int i = m_NumSlots - 1; i >= 0; --i) - { - if (m_Slots[i].m_Client == a_Client) - { - m_Slots[i].m_Outgoing.append(a_Data); - - // Notify the thread that there's data in the queue: - ASSERT(m_ControlSocket2.IsValid()); - m_ControlSocket2.Send("q", 1); - - return true; - } - } // for i - m_Slots[] - return false; -} - - - - - -bool cSocketThreads::cSocketThread::Start(void) -{ - // Create the control socket listener - m_ControlSocket2 = cSocket::CreateSocket(cSocket::IPv4); - if (!m_ControlSocket2.IsValid()) - { - LOGERROR("Cannot create a Control socket for a cSocketThread (\"%s\"); continuing, but server may be unreachable from now on.", cSocket::GetLastErrorString().c_str()); - return false; - } - if (!m_ControlSocket2.BindToLocalhostIPv4(cSocket::ANY_PORT)) - { - LOGERROR("Cannot bind a Control socket for a cSocketThread (\"%s\"); continuing, but server may be unreachable from now on.", cSocket::GetLastErrorString().c_str()); - m_ControlSocket2.CloseSocket(); - return false; - } - if (!m_ControlSocket2.Listen(1)) - { - LOGERROR("Cannot listen on a Control socket for a cSocketThread (\"%s\"); continuing, but server may be unreachable from now on.", cSocket::GetLastErrorString().c_str()); - m_ControlSocket2.CloseSocket(); - return false; - } - if (m_ControlSocket2.GetPort() == 0) - { - LOGERROR("Cannot determine Control socket port (\"%s\"); conitnuing, but the server may be unreachable from now on.", cSocket::GetLastErrorString().c_str()); - m_ControlSocket2.CloseSocket(); - return false; - } - - // Start the thread - if (!super::Start()) - { - LOGERROR("Cannot start new cSocketThread"); - m_ControlSocket2.CloseSocket(); - return false; - } - - // Finish connecting the control socket by accepting connection from the thread's socket - cSocket tmp = m_ControlSocket2.AcceptIPv4(); - if (!tmp.IsValid()) - { - LOGERROR("Cannot link Control sockets for a cSocketThread (\"%s\"); continuing, but server may be unreachable from now on.", cSocket::GetLastErrorString().c_str()); - m_ControlSocket2.CloseSocket(); - return false; - } - m_ControlSocket2.CloseSocket(); - m_ControlSocket2 = tmp; - - return true; -} - - - - - -void cSocketThreads::cSocketThread::Execute(void) -{ - // Connect the "client" part of the Control socket: - m_ControlSocket1 = cSocket::CreateSocket(cSocket::IPv4); - ASSERT(m_ControlSocket2.GetPort() != 0); // We checked in the Start() method, but let's be sure - if (!m_ControlSocket1.ConnectToLocalhostIPv4(m_ControlSocket2.GetPort())) - { - LOGERROR("Cannot connect Control sockets for a cSocketThread (\"%s\"); continuing, but the server may be unreachable from now on.", cSocket::GetLastErrorString().c_str()); - m_ControlSocket2.CloseSocket(); - return; - } - - // The main thread loop: - while (!m_ShouldTerminate) - { - // Read outgoing data from the clients: - QueueOutgoingData(); - - // Put sockets into the sets - fd_set fdRead; - fd_set fdWrite; - cSocket::xSocket Highest = m_ControlSocket1.GetSocket(); - PrepareSets(&fdRead, &fdWrite, Highest); - - // Wait for the sockets: - timeval Timeout; - Timeout.tv_sec = 5; - Timeout.tv_usec = 0; - if (select((int)Highest + 1, &fdRead, &fdWrite, nullptr, &Timeout) == -1) - { - LOG("select() call failed in cSocketThread: \"%s\"", cSocket::GetLastErrorString().c_str()); - continue; - } - - // Perform the IO: - ReadFromSockets(&fdRead); - WriteToSockets(&fdWrite); - CleanUpShutSockets(); - } // while (!mShouldTerminate) -} - - - - - -void cSocketThreads::cSocketThread::PrepareSets(fd_set * a_Read, fd_set * a_Write, cSocket::xSocket & a_Highest) -{ - FD_ZERO(a_Read); - FD_ZERO(a_Write); - FD_SET(m_ControlSocket1.GetSocket(), a_Read); - - cCSLock Lock(m_Parent->m_CS); - for (int i = m_NumSlots - 1; i >= 0; --i) - { - if (!m_Slots[i].m_Socket.IsValid()) - { - continue; - } - if (m_Slots[i].m_State == sSlot::ssRemoteClosed) - { - // This socket won't provide nor consume any data anymore, don't put it in the Set - continue; - } - cSocket::xSocket s = m_Slots[i].m_Socket.GetSocket(); - FD_SET(s, a_Read); - if (s > a_Highest) - { - a_Highest = s; - } - if (!m_Slots[i].m_Outgoing.empty()) - { - // There's outgoing data for the socket, put it in the Write set - FD_SET(s, a_Write); - } - } // for i - m_Slots[] -} - - - - - -void cSocketThreads::cSocketThread::ReadFromSockets(fd_set * a_Read) -{ - // Read on available sockets: - - // Reset Control socket state: - if (FD_ISSET(m_ControlSocket1.GetSocket(), a_Read)) - { - char Dummy[128]; - m_ControlSocket1.Receive(Dummy, sizeof(Dummy), 0); - } - - // Read from clients: - cCSLock Lock(m_Parent->m_CS); - for (int i = m_NumSlots - 1; i >= 0; --i) - { - cSocket::xSocket Socket = m_Slots[i].m_Socket.GetSocket(); - if (!cSocket::IsValidSocket(Socket) || !FD_ISSET(Socket, a_Read)) - { - continue; - } - char Buffer[1024]; - int Received = m_Slots[i].m_Socket.Receive(Buffer, ARRAYCOUNT(Buffer), 0); - if (Received <= 0) - { - if (cSocket::GetLastError() != cSocket::ErrWouldBlock) - { - // The socket has been closed by the remote party - switch (m_Slots[i].m_State) - { - case sSlot::ssNormal: - { - // Close the socket on our side: - m_Slots[i].m_State = sSlot::ssRemoteClosed; - m_Slots[i].m_Socket.CloseSocket(); - - // Notify the callback that the remote has closed the socket, *after* removing the socket: - cCallback * client = m_Slots[i].m_Client; - m_Slots[i] = m_Slots[--m_NumSlots]; - if (client != nullptr) - { - client->SocketClosed(); - } - break; - } - case sSlot::ssWritingRestOut: - case sSlot::ssShuttingDown: - case sSlot::ssShuttingDown2: - { - // Force-close the socket and remove the slot: - m_Slots[i].m_Socket.CloseSocket(); - m_Slots[i] = m_Slots[--m_NumSlots]; - break; - } - default: - { - LOG("%s: Unexpected socket state: %d (%s)", - __FUNCTION__, m_Slots[i].m_Socket.GetSocket(), m_Slots[i].m_Socket.GetIPString().c_str() - ); - ASSERT(!"Unexpected socket state"); - break; - } - } // switch (m_Slots[i].m_State) - } - } - else - { - if (m_Slots[i].m_Client != nullptr) - { - m_Slots[i].m_Client->DataReceived(Buffer, Received); - } - } - } // for i - m_Slots[] -} - - - - - -void cSocketThreads::cSocketThread::WriteToSockets(fd_set * a_Write) -{ - // Write to available client sockets: - cCSLock Lock(m_Parent->m_CS); - for (int i = m_NumSlots - 1; i >= 0; --i) - { - cSocket::xSocket Socket = m_Slots[i].m_Socket.GetSocket(); - if (!cSocket::IsValidSocket(Socket) || !FD_ISSET(Socket, a_Write)) - { - continue; - } - if (m_Slots[i].m_Outgoing.empty()) - { - // Request another chunk of outgoing data: - if (m_Slots[i].m_Client != nullptr) - { - AString Data; - m_Slots[i].m_Client->GetOutgoingData(Data); - m_Slots[i].m_Outgoing.append(Data); - } - if (m_Slots[i].m_Outgoing.empty()) - { - // No outgoing data is ready - if (m_Slots[i].m_State == sSlot::ssWritingRestOut) - { - m_Slots[i].m_State = sSlot::ssShuttingDown; - m_Slots[i].m_Socket.ShutdownReadWrite(); - } - continue; - } - } // if (outgoing data is empty) - - if (m_Slots[i].m_State == sSlot::ssRemoteClosed) - { - continue; - } - - if (!SendDataThroughSocket(m_Slots[i].m_Socket, m_Slots[i].m_Outgoing)) - { - int Err = cSocket::GetLastError(); - LOGWARNING("Error %d while writing to client \"%s\", disconnecting. \"%s\"", Err, m_Slots[i].m_Socket.GetIPString().c_str(), GetOSErrorString(Err).c_str()); - m_Slots[i].m_Socket.CloseSocket(); - if (m_Slots[i].m_Client != nullptr) - { - m_Slots[i].m_Client->SocketClosed(); - } - continue; - } - - if (m_Slots[i].m_Outgoing.empty() && (m_Slots[i].m_State == sSlot::ssWritingRestOut)) - { - m_Slots[i].m_State = sSlot::ssShuttingDown; - m_Slots[i].m_Socket.ShutdownReadWrite(); - } - - // _X: If there's data left, it means the client is not reading fast enough, the server would unnecessarily spin in the main loop with zero actions taken; so signalling is disabled - // This means that if there's data left, it will be sent only when there's incoming data or someone queues another packet (for any socket handled by this thread) - /* - // If there's any data left, signalize the Control socket: - if (!m_Slots[i].m_Outgoing.empty()) - { - ASSERT(m_ControlSocket2.IsValid()); - m_ControlSocket2.Send("q", 1); - } - */ - } // for i - m_Slots[i] -} - - - - - -bool cSocketThreads::cSocketThread::SendDataThroughSocket(cSocket & a_Socket, AString & a_Data) -{ - // Send data in smaller chunks, so that the OS send buffers aren't overflown easily - while (!a_Data.empty()) - { - size_t NumToSend = std::min(a_Data.size(), (size_t)1024); - int Sent = a_Socket.Send(a_Data.data(), NumToSend); - if (Sent < 0) - { - int Err = cSocket::GetLastError(); - if (Err == cSocket::ErrWouldBlock) - { - // The OS send buffer is full, leave the outgoing data for the next time - return true; - } - // An error has occured - return false; - } - if (Sent == 0) - { - a_Socket.CloseSocket(); - return true; - } - a_Data.erase(0, Sent); - } - return true; -} - - - - - -void cSocketThreads::cSocketThread::CleanUpShutSockets(void) -{ - cCSLock Lock(m_Parent->m_CS); - for (int i = m_NumSlots - 1; i >= 0; i--) - { - switch (m_Slots[i].m_State) - { - case sSlot::ssShuttingDown2: - { - // The socket has reached the shutdown timeout, close it and clear its slot: - m_Slots[i].m_Socket.CloseSocket(); - m_Slots[i] = m_Slots[--m_NumSlots]; - break; - } - case sSlot::ssShuttingDown: - { - // The socket has been shut down for a single thread loop, let it loop once more before closing: - m_Slots[i].m_State = sSlot::ssShuttingDown2; - break; - } - default: break; - } - } // for i - m_Slots[] -} - - - - -void cSocketThreads::cSocketThread::QueueOutgoingData(void) -{ - cCSLock Lock(m_Parent->m_CS); - for (int i = 0; i < m_NumSlots; i++) - { - if (m_Slots[i].m_Client != nullptr) - { - AString Data; - m_Slots[i].m_Client->GetOutgoingData(Data); - m_Slots[i].m_Outgoing.append(Data); - } - if (m_Slots[i].m_Outgoing.empty()) - { - // No outgoing data is ready - if (m_Slots[i].m_State == sSlot::ssWritingRestOut) - { - // The socket doesn't want to be kept alive anymore, and doesn't have any remaining data to send. - // Shut it down and then close it after a timeout, or when the other side agrees - m_Slots[i].m_State = sSlot::ssShuttingDown; - m_Slots[i].m_Socket.ShutdownReadWrite(); - } - continue; - } - } -} - - - - diff --git a/src/OSSupport/SocketThreads.h b/src/OSSupport/SocketThreads.h deleted file mode 100644 index df819468d..000000000 --- a/src/OSSupport/SocketThreads.h +++ /dev/null @@ -1,194 +0,0 @@ - -// SocketThreads.h - -// Interfaces to the cSocketThreads class representing the heart of MCS's client networking. -// This object takes care of network communication, groups sockets into threads and uses as little threads as possible for full read / write support -// For more detail, see http://forum.mc-server.org/showthread.php?tid=327 - -/* -Additional details: -When a client wants to terminate the connection, they call the RemoveClient() function. This calls the -callback one last time to read all the available outgoing data, putting it in the slot's m_OutgoingData -buffer. Then it marks the slot as having no callback. The socket is kept alive until its outgoing data -queue is empty, then shutdown is called on it and finally the socket is closed after a timeout. -If at any time within this the remote end closes the socket, then the socket is closed directly. -As soon as the socket is closed, the slot is finally removed from the SocketThread. -The graph in $/docs/SocketThreads States.gv shows the state-machine transitions of the slot. -*/ - - - - - -/** How many clients should one thread handle? (must be less than FD_SETSIZE for your platform) */ -#define MAX_SLOTS 63 - - - - - -#pragma once - -#include "Socket.h" -#include "IsThread.h" - - - - -// Check MAX_SLOTS: -#if MAX_SLOTS >= FD_SETSIZE - #error "MAX_SLOTS must be less than FD_SETSIZE for your platform! (otherwise select() won't work)" -#endif - - - - - -// fwd: -class cSocket; -class cClientHandle; - - - - - -class cSocketThreads -{ -public: - - // Clients of cSocketThreads must implement this interface to be able to communicate - class cCallback - { - public: - // Force a virtual destructor in all subclasses: - virtual ~cCallback() {} - - /** Called when data is received from the remote party. - SocketThreads does not care about the return value, others can use it for their specific purpose - - for example HTTPServer uses it to signal if the connection was terminated as a result of the data received. */ - virtual bool DataReceived(const char * a_Data, size_t a_Size) = 0; - - /** Called when data can be sent to remote party - The function is supposed to *set* outgoing data to a_Data (overwrite) */ - virtual void GetOutgoingData(AString & a_Data) = 0; - - /** Called when the socket has been closed for any reason */ - virtual void SocketClosed(void) = 0; - } ; - - - cSocketThreads(void); - ~cSocketThreads(); - - /** Add a (socket, client) pair for processing, data from a_Socket is to be sent to a_Client; returns true if successful */ - bool AddClient(const cSocket & a_Socket, cCallback * a_Client); - - /** Remove the associated socket and the client from processing. - The socket is left to send its last outgoing data and is removed only after all its m_Outgoing is sent - and after the socket is properly shutdown (unless the remote disconnects before that) - */ - void RemoveClient(const cCallback * a_Client); - - /** Notify the thread responsible for a_Client that the client has something to write */ - void NotifyWrite(const cCallback * a_Client); - - /** Puts a_Data into outgoing data queue for a_Client */ - void Write(const cCallback * a_Client, const AString & a_Data); - -private: - - class cSocketThread : - public cIsThread - { - typedef cIsThread super; - - public: - - cSocketThread(cSocketThreads * a_Parent); - virtual ~cSocketThread(); - - // All these methods assume parent's m_CS is locked - bool HasEmptySlot(void) const {return m_NumSlots < MAX_SLOTS; } - bool IsEmpty (void) const {return m_NumSlots == 0; } - - void AddClient (const cSocket & a_Socket, cCallback * a_Client); // Takes ownership of the socket - bool RemoveClient(const cCallback * a_Client); // Returns true if removed, false if not found - bool HasClient (const cCallback * a_Client) const; - bool HasSocket (const cSocket * a_Socket) const; - bool NotifyWrite (const cCallback * a_Client); // Returns true if client handled by this thread - bool Write (const cCallback * a_Client, const AString & a_Data); // Returns true if client handled by this thread - - bool Start(void); // Hide the cIsThread's Start method, we need to provide our own startup to create the control socket - - bool IsValid(void) const {return m_ControlSocket2.IsValid(); } // If the Control socket dies, the thread is not valid anymore - - private: - - cSocketThreads * m_Parent; - - // Two ends of the control socket, the first is select()-ed, the second is written to for notifications - cSocket m_ControlSocket1; - cSocket m_ControlSocket2; - - // Socket-client-dataqueues-state quadruplets. - // Manipulation with these assumes that the parent's m_CS is locked - struct sSlot - { - /** The socket is primarily owned by this object */ - cSocket m_Socket; - - /** The callback to call for events. May be nullptr */ - cCallback * m_Client; - - /** If sending writes only partial data, the rest is stored here for another send. - Also used when the slot is being removed to store the last batch of outgoing data. */ - AString m_Outgoing; - - enum eState - { - ssNormal, ///< Normal read / write operations - ssWritingRestOut, ///< The client callback was removed, continue to send outgoing data - ssShuttingDown, ///< The last outgoing data has been sent, the socket has called shutdown() - ssShuttingDown2, ///< The shutdown has been done at least 1 thread loop ago (timeout detection) - ssRemoteClosed, ///< The remote end has closed the connection (and we still have a client callback) - } m_State; - } ; - - sSlot m_Slots[MAX_SLOTS]; - int m_NumSlots; // Number of slots actually used - - virtual void Execute(void) override; - - /** Prepares the Read and Write socket sets for select() - Puts all sockets into the read set, along with m_ControlSocket1. - Only sockets that have outgoing data queued on them are put in the write set.*/ - void PrepareSets(fd_set * a_ReadSet, fd_set * a_WriteSet, cSocket::xSocket & a_Highest); - - /** Reads from sockets indicated in a_Read */ - void ReadFromSockets(fd_set * a_Read); - - /** Writes to sockets indicated in a_Write */ - void WriteToSockets (fd_set * a_Write); - - /** Sends data through the specified socket, trying to fill the OS send buffer in chunks. - Returns true if there was no error while sending, false if an error has occured. - Modifies a_Data to contain only the unsent data. */ - bool SendDataThroughSocket(cSocket & a_Socket, AString & a_Data); - - /** Removes those slots in ssShuttingDown2 state, sets those with ssShuttingDown state to ssShuttingDown2 */ - void CleanUpShutSockets(void); - - /** Calls each client's callback to retrieve outgoing data for that client. */ - void QueueOutgoingData(void); - } ; - - typedef std::list cSocketThreadList; - - - cCriticalSection m_CS; - cSocketThreadList m_Threads; -} ; - - - - -- cgit v1.2.3 From 6ec5e8caa77829d7ea3593b08f1f91244d027601 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sun, 25 Jan 2015 16:25:15 +0100 Subject: Replaced atoi() with StringToInteger(). --- src/Generating/BioGen.cpp | 3 +-- src/HTTPServer/HTTPMessage.cpp | 5 ++++- src/HTTPServer/HTTPServer.cpp | 4 ++-- src/RCONServer.cpp | 4 ++-- src/Server.cpp | 4 ++-- 5 files changed, 11 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/Generating/BioGen.cpp b/src/Generating/BioGen.cpp index 2cc810d3b..378ece6a3 100644 --- a/src/Generating/BioGen.cpp +++ b/src/Generating/BioGen.cpp @@ -205,8 +205,7 @@ void cBiomeGenList::InitializeBiomes(const AString & a_Biomes) int Count = 1; if (Split2.size() >= 2) { - Count = atol(Split2[1].c_str()); - if (Count <= 0) + if (!StringToInteger(Split2[1], Count)) { LOGWARNING("Cannot decode biome count: \"%s\"; using 1.", Split2[1].c_str()); Count = 1; diff --git a/src/HTTPServer/HTTPMessage.cpp b/src/HTTPServer/HTTPMessage.cpp index d59ca438e..c87b3cc8b 100644 --- a/src/HTTPServer/HTTPMessage.cpp +++ b/src/HTTPServer/HTTPMessage.cpp @@ -55,7 +55,10 @@ void cHTTPMessage::AddHeader(const AString & a_Key, const AString & a_Value) } else if (Key == "content-length") { - m_ContentLength = static_cast(atol(m_Headers[Key].c_str())); + if (!StringToInteger(m_Headers[Key], m_ContentLength)) + { + m_ContentLength = 0; + } } } diff --git a/src/HTTPServer/HTTPServer.cpp b/src/HTTPServer/HTTPServer.cpp index 3bcf0783a..71f974a97 100644 --- a/src/HTTPServer/HTTPServer.cpp +++ b/src/HTTPServer/HTTPServer.cpp @@ -225,8 +225,8 @@ bool cHTTPServer::Start(cCallbacks & a_Callbacks, const AStringVector & a_Ports) // Open up requested ports: for (auto port : a_Ports) { - UInt16 PortNum = static_cast(atoi(port.c_str())); - if (PortNum == 0) + UInt16 PortNum; + if (!StringToInteger(port, PortNum)) { LOGWARNING("WebServer: Invalid port value: \"%s\". Ignoring.", port.c_str()); continue; diff --git a/src/RCONServer.cpp b/src/RCONServer.cpp index 63bc97fe3..cd8409367 100644 --- a/src/RCONServer.cpp +++ b/src/RCONServer.cpp @@ -155,8 +155,8 @@ void cRCONServer::Initialize(cIniFile & a_IniFile) // Start listening on each specified port: for (auto port: Ports) { - UInt16 PortNum = static_cast(atol(port.c_str())); - if (PortNum == 0) + UInt16 PortNum; + if (!StringToInteger(port, PortNum)) { LOGINFO("Invalid RCON port value: \"%s\". Ignoring.", port.c_str()); continue; diff --git a/src/Server.cpp b/src/Server.cpp index 783b12947..3f61be378 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -393,8 +393,8 @@ bool cServer::Start(void) { for (auto port: m_Ports) { - UInt16 PortNum = static_cast(atoi(port.c_str())); - if (PortNum == 0) + UInt16 PortNum; + if (!StringToInteger(port, PortNum)) { LOGWARNING("Invalid port specified for server: \"%s\". Ignoring.", port.c_str()); continue; -- cgit v1.2.3 From 13091e0fa0339a7e84b6a9e75cc91c96de0eae2b Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sun, 25 Jan 2015 16:33:49 +0100 Subject: RCONServer: Changed variables to UInt32. --- src/RCONServer.cpp | 51 ++++++++++++++++++++++++--------------------------- src/RCONServer.h | 10 +++++----- 2 files changed, 29 insertions(+), 32 deletions(-) (limited to 'src') diff --git a/src/RCONServer.cpp b/src/RCONServer.cpp index cd8409367..a02baf761 100644 --- a/src/RCONServer.cpp +++ b/src/RCONServer.cpp @@ -82,7 +82,7 @@ class cRCONCommandOutput : public cCommandOutputCallback { public: - cRCONCommandOutput(cRCONServer::cConnection & a_Connection, int a_RequestID) : + cRCONCommandOutput(cRCONServer::cConnection & a_Connection, UInt32 a_RequestID) : m_Connection(a_Connection), m_RequestID(a_RequestID) { @@ -96,13 +96,13 @@ public: virtual void Finished(void) override { - m_Connection.SendResponse(m_RequestID, RCON_PACKET_RESPONSE, (int)m_Buffer.size(), m_Buffer.c_str()); + m_Connection.SendResponse(m_RequestID, RCON_PACKET_RESPONSE, static_cast(m_Buffer.size()), m_Buffer.c_str()); delete this; } protected: cRCONServer::cConnection & m_Connection; - int m_RequestID; + UInt32 m_RequestID; AString m_Buffer; } ; @@ -211,7 +211,7 @@ void cRCONServer::cConnection::OnReceivedData(const char * a_Data, size_t a_Size // Process the packets in the buffer: while (m_Buffer.size() >= 14) { - int Length = IntFromBuffer(m_Buffer.data()); + UInt32 Length = UIntFromBuffer(m_Buffer.data()); if (Length > 1500) { // Too long, drop the connection @@ -222,14 +222,14 @@ void cRCONServer::cConnection::OnReceivedData(const char * a_Data, size_t a_Size m_Link.reset(); return; } - if (Length > static_cast(m_Buffer.size() + 4)) + if (Length > static_cast(m_Buffer.size() + 4)) { // Incomplete packet yet, wait for more data to come return; } - int RequestID = IntFromBuffer(m_Buffer.data() + 4); - int PacketType = IntFromBuffer(m_Buffer.data() + 8); + UInt32 RequestID = UIntFromBuffer(m_Buffer.data() + 4); + UInt32 PacketType = UIntFromBuffer(m_Buffer.data() + 8); if (!ProcessPacket(RequestID, PacketType, Length - 10, m_Buffer.data() + 12)) { m_Link->Close(); @@ -263,7 +263,7 @@ void cRCONServer::cConnection::OnError(int a_ErrorCode, const AString & a_ErrorM -bool cRCONServer::cConnection::ProcessPacket(int a_RequestID, int a_PacketType, int a_PayloadLength, const char * a_Payload) +bool cRCONServer::cConnection::ProcessPacket(UInt32 a_RequestID, UInt32 a_PacketType, UInt32 a_PayloadLength, const char * a_Payload) { switch (a_PacketType) { @@ -272,7 +272,7 @@ bool cRCONServer::cConnection::ProcessPacket(int a_RequestID, int a_PacketType, if (strncmp(a_Payload, m_RCONServer.m_Password.c_str(), a_PayloadLength) != 0) { LOGINFO("RCON: Invalid password from client %s, dropping connection.", m_IPAddress.c_str()); - SendResponse(-1, RCON_PACKET_RESPONSE, 0, nullptr); + SendResponse(0xffffffffU, RCON_PACKET_RESPONSE, 0, nullptr); return false; } m_IsAuthenticated = true; @@ -314,23 +314,22 @@ bool cRCONServer::cConnection::ProcessPacket(int a_RequestID, int a_PacketType, -/// Reads 4 bytes from a_Buffer and returns the int they represent -int cRCONServer::cConnection::IntFromBuffer(const char * a_Buffer) +UInt32 cRCONServer::cConnection::UIntFromBuffer(const char * a_Buffer) { - return ((unsigned char)a_Buffer[3] << 24) | ((unsigned char)a_Buffer[2] << 16) | ((unsigned char)a_Buffer[1] << 8) | (unsigned char)a_Buffer[0]; + const Byte * Buffer = reinterpret_cast(a_Buffer); + return (Buffer[3] << 24) | (Buffer[2] << 16) | (Buffer[1] << 8) | Buffer[0]; } -/// Puts 4 bytes representing the int into the buffer -void cRCONServer::cConnection::IntToBuffer(int a_Value, char * a_Buffer) +void cRCONServer::cConnection::UIntToBuffer(UInt32 a_Value, char * a_Buffer) { - a_Buffer[0] = a_Value & 0xff; - a_Buffer[1] = (a_Value >> 8) & 0xff; - a_Buffer[2] = (a_Value >> 16) & 0xff; - a_Buffer[3] = (a_Value >> 24) & 0xff; + a_Buffer[0] = static_cast(a_Value & 0xff); + a_Buffer[1] = static_cast((a_Value >> 8) & 0xff); + a_Buffer[2] = static_cast((a_Value >> 16) & 0xff); + a_Buffer[3] = static_cast((a_Value >> 24) & 0xff); } @@ -338,19 +337,17 @@ void cRCONServer::cConnection::IntToBuffer(int a_Value, char * a_Buffer) /// Sends a RCON packet back to the client -void cRCONServer::cConnection::SendResponse(int a_RequestID, int a_PacketType, int a_PayloadLength, const char * a_Payload) +void cRCONServer::cConnection::SendResponse(UInt32 a_RequestID, UInt32 a_PacketType, UInt32 a_PayloadLength, const char * a_Payload) { ASSERT((a_PayloadLength == 0) || (a_Payload != nullptr)); // Either zero data to send, or a valid payload ptr ASSERT(m_Link != nullptr); - char Buffer[4]; - int Length = a_PayloadLength + 10; - IntToBuffer(Length, Buffer); - m_Link->Send(Buffer, 4); - IntToBuffer(a_RequestID, Buffer); - m_Link->Send(Buffer, 4); - IntToBuffer(a_PacketType, Buffer); - m_Link->Send(Buffer, 4); + char Buffer[12]; + UInt32 Length = a_PayloadLength + 10; + UIntToBuffer(Length, Buffer); + UIntToBuffer(a_RequestID, Buffer + 4); + UIntToBuffer(a_PacketType, Buffer + 8); + m_Link->Send(Buffer, 12); if (a_PayloadLength > 0) { m_Link->Send(a_Payload, a_PayloadLength); diff --git a/src/RCONServer.h b/src/RCONServer.h index 1d599b279..352fa7b50 100644 --- a/src/RCONServer.h +++ b/src/RCONServer.h @@ -67,16 +67,16 @@ protected: virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override; /** Processes the given packet and sends the response; returns true if successful, false if the connection is to be dropped */ - bool ProcessPacket(int a_RequestID, int a_PacketType, int a_PayloadLength, const char * a_Payload); + bool ProcessPacket(UInt32 a_RequestID, UInt32 a_PacketType, UInt32 a_PayloadLength, const char * a_Payload); - /** Reads 4 bytes from a_Buffer and returns the int they represent */ - int IntFromBuffer(const char * a_Buffer); + /** Reads 4 bytes from a_Buffer and returns the LE UInt32 they represent */ + UInt32 UIntFromBuffer(const char * a_Buffer); /** Puts 4 bytes representing the int into the buffer */ - void IntToBuffer(int a_Value, char * a_Buffer); + void UIntToBuffer(UInt32 a_Value, char * a_Buffer); /** Sends a RCON packet back to the client */ - void SendResponse(int a_RequestID, int a_PacketType, int a_PayloadLength, const char * a_Payload); + void SendResponse(UInt32 a_RequestID, UInt32 a_PacketType, UInt32 a_PayloadLength, const char * a_Payload); } ; -- cgit v1.2.3 From edce2cbdcfdc7eae280bf27a91770b7009b31ebe Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sun, 25 Jan 2015 17:04:53 +0100 Subject: Fixed enchant packet handling. --- src/ClientHandle.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index e90114d1b..a06ddd045 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -711,9 +711,9 @@ void cClientHandle::HandleEnchantItem(Byte a_WindowID, Byte a_Enchantment) return; } - cEnchantingWindow * Window = (cEnchantingWindow*) m_Player->GetWindow(); - cItem Item = *Window->m_SlotArea->GetSlot(0, *m_Player); - int BaseEnchantmentLevel = Window->GetPropertyValue(a_Enchantment); + cEnchantingWindow * Window = reinterpret_cast(m_Player->GetWindow()); + cItem Item = *Window->m_SlotArea->GetSlot(0, *m_Player); // Make a copy of the item + short BaseEnchantmentLevel = Window->GetPropertyValue(a_Enchantment); if (Item.EnchantByXPLevels(BaseEnchantmentLevel)) { -- cgit v1.2.3 From 1b9c0f1030ec212eb39c5291f44d99313a2b638d Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sun, 25 Jan 2015 17:06:15 +0100 Subject: Removed cFile::ReplaceFileNameInvalidChars(). Its only usage in cProtocol18 has been changed to a simple string replace - only the IPv6's colons were causing problems. --- src/OSSupport/File.cpp | 23 ----------------------- src/OSSupport/File.h | 3 --- src/Protocol/Protocol18x.cpp | 4 +++- 3 files changed, 3 insertions(+), 27 deletions(-) (limited to 'src') diff --git a/src/OSSupport/File.cpp b/src/OSSupport/File.cpp index 1ee9feafe..8957dfaef 100644 --- a/src/OSSupport/File.cpp +++ b/src/OSSupport/File.cpp @@ -453,29 +453,6 @@ AString cFile::ReadWholeFile(const AString & a_FileName) -AString cFile::ReplaceFileNameInvalidChars(const AString & a_FileName, char a_Replacement) -{ - AString res(a_FileName); - for (auto & ch: res) - { - switch (ch) - { - case ':': - case '\\': - case '/': - { - ch = a_Replacement; - break; - } - } - } - return res; -} - - - - - int cFile::Printf(const char * a_Fmt, ...) { AString buf; diff --git a/src/OSSupport/File.h b/src/OSSupport/File.h index b39f71e28..ac6d1ab21 100644 --- a/src/OSSupport/File.h +++ b/src/OSSupport/File.h @@ -127,9 +127,6 @@ public: /** Returns the entire contents of the specified file as a string. Returns empty string on error. */ static AString ReadWholeFile(const AString & a_FileName); - /** Replaces characters that cannot be in a file name with the specified char. */ - static AString ReplaceFileNameInvalidChars(const AString & a_FileName, char a_Replacement = '_'); - // tolua_end /** Returns the list of all items in the specified folder (files, folders, nix pipes, whatever's there). */ diff --git a/src/Protocol/Protocol18x.cpp b/src/Protocol/Protocol18x.cpp index 5d722d9fc..7d954a297 100644 --- a/src/Protocol/Protocol18x.cpp +++ b/src/Protocol/Protocol18x.cpp @@ -108,10 +108,12 @@ cProtocol180::cProtocol180(cClientHandle * a_Client, const AString & a_ServerAdd { static int sCounter = 0; cFile::CreateFolder("CommLogs"); + AString IP(a_Client->GetIPString()); + ReplaceString(IP, ":", "_"); AString FileName = Printf("CommLogs/%x_%d__%s.log", static_cast(time(nullptr)), sCounter++, - cFile::ReplaceFileNameInvalidChars(a_Client->GetIPString()).c_str() + IP.c_str() ); if (!m_CommLogFile.Open(FileName, cFile::fmWrite)) { -- cgit v1.2.3 From 96def00c7c38a947e4c3bd8f1f0109c4b64e691e Mon Sep 17 00:00:00 2001 From: worktycho Date: Sun, 25 Jan 2015 17:07:51 +0000 Subject: Fix clang compile error --- src/RCONServer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/RCONServer.cpp b/src/RCONServer.cpp index a02baf761..685bd92f5 100644 --- a/src/RCONServer.cpp +++ b/src/RCONServer.cpp @@ -222,7 +222,7 @@ void cRCONServer::cConnection::OnReceivedData(const char * a_Data, size_t a_Size m_Link.reset(); return; } - if (Length > static_cast(m_Buffer.size() + 4)) + if (Length > static_cast(m_Buffer.size() + 4)) { // Incomplete packet yet, wait for more data to come return; -- cgit v1.2.3 From 4eecf9363a2a1a729a553cec2cd43d1736e9e7e8 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sun, 25 Jan 2015 22:52:40 +0100 Subject: Client: Only decode protocol when there's actual data incoming. --- src/ClientHandle.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index a06ddd045..10920496d 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -1884,7 +1884,10 @@ void cClientHandle::Tick(float a_Dt) cCSLock Lock(m_CSIncomingData); std::swap(IncomingData, m_IncomingData); } - m_Protocol->DataReceived(IncomingData.data(), IncomingData.size()); + if (!IncomingData.empty()) + { + m_Protocol->DataReceived(IncomingData.data(), IncomingData.size()); + } m_TicksSinceLastPacket += 1; if (m_TicksSinceLastPacket > 600) // 30 seconds time-out @@ -1968,7 +1971,10 @@ void cClientHandle::ServerTick(float a_Dt) cCSLock Lock(m_CSIncomingData); std::swap(IncomingData, m_IncomingData); } - m_Protocol->DataReceived(IncomingData.data(), IncomingData.size()); + if (!IncomingData.empty()) + { + m_Protocol->DataReceived(IncomingData.data(), IncomingData.size()); + } if (m_State == csAuthenticated) { -- cgit v1.2.3 From 2557f2867de3517dd800aabe302116f44912ea15 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sun, 25 Jan 2015 23:12:12 +0100 Subject: ServerHandleImpl: Removed needless diagnostic output. --- src/OSSupport/ServerHandleImpl.cpp | 4 ---- 1 file changed, 4 deletions(-) (limited to 'src') diff --git a/src/OSSupport/ServerHandleImpl.cpp b/src/OSSupport/ServerHandleImpl.cpp index 2485c8e1b..5244f3fd2 100644 --- a/src/OSSupport/ServerHandleImpl.cpp +++ b/src/OSSupport/ServerHandleImpl.cpp @@ -157,10 +157,6 @@ bool cServerHandleImpl::Listen(UInt16 a_Port) int res = setsockopt(MainSock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast(&Zero), sizeof(Zero)); err = EVUTIL_SOCKET_ERROR(); NeedsTwoSockets = ((res == SOCKET_ERROR) && (err == WSAENOPROTOOPT)); - LOGD("setsockopt(IPV6_V6ONLY) returned %d, err is %d (%s). %s", - res, err, evutil_socket_error_to_string(err), - NeedsTwoSockets ? "Second socket will be created" : "Second socket not needed" - ); #else setsockopt(MainSock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast(&Zero), sizeof(Zero)); #endif -- cgit v1.2.3 From 9a2200f8bb55cbce7a82f1d3d683371fd563e2b2 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sun, 25 Jan 2015 23:12:41 +0100 Subject: cTCPLinkImpl: Added the BEV_OPT_THREADSAFE flag. --- src/OSSupport/TCPLinkImpl.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/OSSupport/TCPLinkImpl.cpp b/src/OSSupport/TCPLinkImpl.cpp index b4cefa60c..f97db7582 100644 --- a/src/OSSupport/TCPLinkImpl.cpp +++ b/src/OSSupport/TCPLinkImpl.cpp @@ -17,8 +17,9 @@ cTCPLinkImpl::cTCPLinkImpl(cTCPLink::cCallbacksPtr a_LinkCallbacks): super(a_LinkCallbacks), - m_BufferEvent(bufferevent_socket_new(cNetworkSingleton::Get().GetEventBase(), -1, BEV_OPT_CLOSE_ON_FREE)) + m_BufferEvent(bufferevent_socket_new(cNetworkSingleton::Get().GetEventBase(), -1, BEV_OPT_CLOSE_ON_FREE | BEV_OPT_THREADSAFE)) { + LOGD("Created new cTCPLinkImpl at %p with BufferEvent at %p", this, m_BufferEvent); } @@ -27,9 +28,11 @@ 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)), + m_BufferEvent(bufferevent_socket_new(cNetworkSingleton::Get().GetEventBase(), a_Socket, BEV_OPT_CLOSE_ON_FREE | BEV_OPT_THREADSAFE)), m_Server(a_Server) { + LOGD("Created new cTCPLinkImpl at %p with BufferEvent at %p", this, m_BufferEvent); + // Update the endpoint addresses: UpdateLocalAddress(); UpdateAddress(a_Address, a_AddrLen, m_RemoteIP, m_RemotePort); @@ -41,6 +44,7 @@ cTCPLinkImpl::cTCPLinkImpl(evutil_socket_t a_Socket, cTCPLink::cCallbacksPtr a_L cTCPLinkImpl::~cTCPLinkImpl() { + LOGD("Deleting cTCPLinkImpl at %p with BufferEvent at %p", this, m_BufferEvent); bufferevent_free(m_BufferEvent); } @@ -179,6 +183,8 @@ void cTCPLinkImpl::ReadCallback(bufferevent * a_BufferEvent, void * a_Self) 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); + ASSERT(a_Self != nullptr); cTCPLinkImplPtr Self = static_cast(a_Self)->m_Self; -- cgit v1.2.3 From 053362e604c98c229df57337ba7752d4d6b97fa8 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Mon, 26 Jan 2015 14:46:20 +0100 Subject: Added network termination called at app exit. This fixes a crash in MSVC runtime caused by joining a thread in a global var's destructor. --- src/OSSupport/NetworkSingleton.cpp | 44 ++++++++++++++++++++++++++++---------- src/OSSupport/NetworkSingleton.h | 11 +++++++++- src/main.cpp | 11 ++++++++-- 3 files changed, 52 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/OSSupport/NetworkSingleton.cpp b/src/OSSupport/NetworkSingleton.cpp index 92f0604cd..000b17641 100644 --- a/src/OSSupport/NetworkSingleton.cpp +++ b/src/OSSupport/NetworkSingleton.cpp @@ -18,7 +18,8 @@ -cNetworkSingleton::cNetworkSingleton(void) +cNetworkSingleton::cNetworkSingleton(void): + m_HasTerminated(false) { // Windows: initialize networking: #ifdef _WIN32 @@ -72,6 +73,29 @@ cNetworkSingleton::cNetworkSingleton(void) cNetworkSingleton::~cNetworkSingleton() { + // Check that Terminate has been called already: + ASSERT(m_HasTerminated); +} + + + + + +cNetworkSingleton & cNetworkSingleton::Get(void) +{ + static cNetworkSingleton Instance; + return Instance; +} + + + + + +void cNetworkSingleton::Terminate(void) +{ + ASSERT(!m_HasTerminated); + m_HasTerminated = true; + // Wait for the LibEvent event loop to terminate: event_base_loopbreak(m_EventBase); m_EventLoopTerminated.Wait(); @@ -96,16 +120,6 @@ cNetworkSingleton::~cNetworkSingleton() -cNetworkSingleton & cNetworkSingleton::Get(void) -{ - static cNetworkSingleton Instance; - return Instance; -} - - - - - void cNetworkSingleton::LogCallback(int a_Severity, const char * a_Msg) { switch (a_Severity) @@ -138,6 +152,7 @@ void cNetworkSingleton::RunEventLoop(cNetworkSingleton * a_Self) void cNetworkSingleton::AddHostnameLookup(cHostnameLookupPtr a_HostnameLookup) { + ASSERT(!m_HasTerminated); cCSLock Lock(m_CS); m_HostnameLookups.push_back(a_HostnameLookup); } @@ -148,6 +163,7 @@ void cNetworkSingleton::AddHostnameLookup(cHostnameLookupPtr a_HostnameLookup) void cNetworkSingleton::RemoveHostnameLookup(const cHostnameLookup * a_HostnameLookup) { + ASSERT(!m_HasTerminated); cCSLock Lock(m_CS); for (auto itr = m_HostnameLookups.begin(), end = m_HostnameLookups.end(); itr != end; ++itr) { @@ -165,6 +181,7 @@ void cNetworkSingleton::RemoveHostnameLookup(const cHostnameLookup * a_HostnameL void cNetworkSingleton::AddIPLookup(cIPLookupPtr a_IPLookup) { + ASSERT(!m_HasTerminated); cCSLock Lock(m_CS); m_IPLookups.push_back(a_IPLookup); } @@ -175,6 +192,7 @@ void cNetworkSingleton::AddIPLookup(cIPLookupPtr a_IPLookup) void cNetworkSingleton::RemoveIPLookup(const cIPLookup * a_IPLookup) { + ASSERT(!m_HasTerminated); cCSLock Lock(m_CS); for (auto itr = m_IPLookups.begin(), end = m_IPLookups.end(); itr != end; ++itr) { @@ -192,6 +210,7 @@ void cNetworkSingleton::RemoveIPLookup(const cIPLookup * a_IPLookup) void cNetworkSingleton::AddLink(cTCPLinkImplPtr a_Link) { + ASSERT(!m_HasTerminated); cCSLock Lock(m_CS); m_Connections.push_back(a_Link); } @@ -202,6 +221,7 @@ void cNetworkSingleton::AddLink(cTCPLinkImplPtr a_Link) void cNetworkSingleton::RemoveLink(const cTCPLinkImpl * a_Link) { + ASSERT(!m_HasTerminated); cCSLock Lock(m_CS); for (auto itr = m_Connections.begin(), end = m_Connections.end(); itr != end; ++itr) { @@ -219,6 +239,7 @@ void cNetworkSingleton::RemoveLink(const cTCPLinkImpl * a_Link) void cNetworkSingleton::AddServer(cServerHandleImplPtr a_Server) { + ASSERT(!m_HasTerminated); cCSLock Lock(m_CS); m_Servers.push_back(a_Server); } @@ -229,6 +250,7 @@ void cNetworkSingleton::AddServer(cServerHandleImplPtr a_Server) void cNetworkSingleton::RemoveServer(const cServerHandleImpl * a_Server) { + ASSERT(!m_HasTerminated); cCSLock Lock(m_CS); for (auto itr = m_Servers.begin(), end = m_Servers.end(); itr != end; ++itr) { diff --git a/src/OSSupport/NetworkSingleton.h b/src/OSSupport/NetworkSingleton.h index 1d26fc8f4..e27e19012 100644 --- a/src/OSSupport/NetworkSingleton.h +++ b/src/OSSupport/NetworkSingleton.h @@ -4,7 +4,8 @@ // Declares the cNetworkSingleton class representing the storage for global data pertaining to network API // such as a list of all connections, all listening sockets and the LibEvent dispatch thread. -// This is an internal header, no-one outside OSSupport should need to include it; use Network.h instead +// This is an internal header, no-one outside OSSupport should need to include it; use Network.h instead; +// the only exception being the main app entrypoint that needs to call Terminate before quitting. @@ -48,6 +49,11 @@ public: /** Returns the singleton instance of this class */ static cNetworkSingleton & Get(void); + /** Terminates all network-related threads. + To be used only on app shutdown. + MSVC runtime requires that the LibEvent networking be shut down before the main() function is exitted; this is the way to do it. */ + void Terminate(void); + /** Returns the main LibEvent handle for event registering. */ event_base * GetEventBase(void) { return m_EventBase; } @@ -113,6 +119,9 @@ protected: /** Event that gets signalled when the event loop terminates. */ cEvent m_EventLoopTerminated; + /** Set to true if Terminate has been called. */ + volatile bool m_HasTerminated; + /** Initializes the LibEvent internals. */ cNetworkSingleton(void); diff --git a/src/main.cpp b/src/main.cpp index d4adc1ed9..20609a2f8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -11,13 +11,17 @@ #include #endif // _MSC_VER +#include "OSSupport/NetworkSingleton.h" -bool cRoot::m_TerminateEventRaised = false; // If something has told the server to stop; checked periodically in cRoot -static bool g_ServerTerminated = false; // Set to true when the server terminates, so our CTRL handler can then tell the OS to close the console +/** If something has told the server to stop; checked periodically in cRoot */ +bool cRoot::m_TerminateEventRaised = false; + +/** Set to true when the server terminates, so our CTRL handler can then tell the OS to close the console. */ +static bool g_ServerTerminated = false; /** If set to true, the protocols will log each player's incoming (C->S) communication to a per-connection logfile */ bool g_ShouldLogCommIn; @@ -305,6 +309,9 @@ int main( int argc, char **argv) g_ServerTerminated = true; + // Shutdown all of LibEvent: + cNetworkSingleton::Get().Terminate(); + return EXIT_SUCCESS; } -- cgit v1.2.3 From 19d463ae1c0faaf08a0783a01db81eae0a271b62 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Mon, 26 Jan 2015 14:49:51 +0100 Subject: Fixed a deadlock in cClientHandle network sending. --- src/ClientHandle.cpp | 27 +++++++++++++++++++++++---- src/ClientHandle.h | 6 +++++- 2 files changed, 28 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index 10920496d..c774a92c2 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -1824,10 +1824,7 @@ void cClientHandle::SendData(const char * a_Data, size_t a_Size) } cCSLock Lock(m_CSOutgoingData); - if (m_Link != nullptr) - { - m_Link->Send(a_Data, a_Size); - } + m_OutgoingData.append(a_Data, a_Size); } @@ -1888,6 +1885,17 @@ void cClientHandle::Tick(float a_Dt) { m_Protocol->DataReceived(IncomingData.data(), IncomingData.size()); } + + // Send any queued outgoing data: + AString OutgoingData; + { + cCSLock Lock(m_CSOutgoingData); + std::swap(OutgoingData, m_OutgoingData); + } + if ((m_Link != nullptr) && !OutgoingData.empty()) + { + m_Link->Send(OutgoingData.data(), OutgoingData.size()); + } m_TicksSinceLastPacket += 1; if (m_TicksSinceLastPacket > 600) // 30 seconds time-out @@ -1976,6 +1984,17 @@ void cClientHandle::ServerTick(float a_Dt) m_Protocol->DataReceived(IncomingData.data(), IncomingData.size()); } + // Send any queued outgoing data: + AString OutgoingData; + { + cCSLock Lock(m_CSOutgoingData); + std::swap(OutgoingData, m_OutgoingData); + } + if ((m_Link != nullptr) && !OutgoingData.empty()) + { + m_Link->Send(OutgoingData.data(), OutgoingData.size()); + } + if (m_State == csAuthenticated) { StreamNextChunk(); diff --git a/src/ClientHandle.h b/src/ClientHandle.h index f5b7faede..8129d6a50 100644 --- a/src/ClientHandle.h +++ b/src/ClientHandle.h @@ -376,9 +376,13 @@ private: Protected by m_CSIncomingData. */ AString m_IncomingData; - /** Protects data going out through m_Link against multi-threaded sending. */ + /** Protects m_OutgoingData against multithreaded access. */ cCriticalSection m_CSOutgoingData; + /** Buffer for storing outgoing data from any thread; will get sent in Tick() (to prevent deadlocks). + Protected by m_CSOutgoingData. */ + AString m_OutgoingData; + Vector3d m_ConfirmPosition; cPlayer * m_Player; -- cgit v1.2.3 From de7f5fc794a77cb1914b5f77019c8ac5a242cc77 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Tue, 27 Jan 2015 13:57:35 +0100 Subject: Fixed server failure to open any port not aborting the server. --- src/Root.cpp | 64 +++++++++++++++++++++++++++++++++--------------------------- 1 file changed, 35 insertions(+), 29 deletions(-) (limited to 'src') diff --git a/src/Root.cpp b/src/Root.cpp index eaacf3608..27d87c717 100644 --- a/src/Root.cpp +++ b/src/Root.cpp @@ -181,43 +181,49 @@ void cRoot::Start(void) IniFile.WriteFile("settings.ini"); LOGD("Finalising startup..."); - m_Server->Start(); - - m_WebAdmin->Start(); - - #if !defined(ANDROID_NDK) - LOGD("Starting InputThread..."); - try + if (m_Server->Start()) { - m_InputThread = std::thread(InputThread, std::ref(*this)); - m_InputThread.detach(); - } - catch (std::system_error & a_Exception) - { - LOGERROR("cRoot::Start (std::thread) error %i: could not construct input thread; %s", a_Exception.code().value(), a_Exception.what()); - } - #endif + m_WebAdmin->Start(); - LOG("Startup complete, took %ldms!", static_cast(std::chrono::duration_cast(std::chrono::steady_clock::now() - BeginTime).count())); - #ifdef _WIN32 - EnableMenuItem(hmenu, SC_CLOSE, MF_ENABLED); // Re-enable close button - #endif + #if !defined(ANDROID_NDK) + LOGD("Starting InputThread..."); + try + { + m_InputThread = std::thread(InputThread, std::ref(*this)); + m_InputThread.detach(); + } + catch (std::system_error & a_Exception) + { + LOGERROR("cRoot::Start (std::thread) error %i: could not construct input thread; %s", a_Exception.code().value(), a_Exception.what()); + } + #endif - while (!m_bStop && !m_bRestart && !m_TerminateEventRaised) // These are modified by external threads - { - std::this_thread::sleep_for(std::chrono::seconds(1)); - } + LOG("Startup complete, took %ldms!", static_cast(std::chrono::duration_cast(std::chrono::steady_clock::now() - BeginTime).count())); + #ifdef _WIN32 + EnableMenuItem(hmenu, SC_CLOSE, MF_ENABLED); // Re-enable close button + #endif - if (m_TerminateEventRaised) + while (!m_bStop && !m_bRestart && !m_TerminateEventRaised) // These are modified by external threads + { + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + + if (m_TerminateEventRaised) + { + m_bStop = true; + } + + // Stop the server: + m_WebAdmin->Stop(); + + LOG("Shutting down server..."); + m_Server->Shutdown(); + } // if (m_Server->Start()) + else { m_bStop = true; } - // Stop the server: - m_WebAdmin->Stop(); - - LOG("Shutting down server..."); - m_Server->Shutdown(); delete m_MojangAPI; m_MojangAPI = nullptr; LOGD("Shutting down deadlock detector..."); -- cgit v1.2.3 From 79dee9bb70a093e163fe64b1a538973147ca1123 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Tue, 27 Jan 2015 13:58:46 +0100 Subject: Fixed listening ports not closed on cServerHandle::Close. --- src/OSSupport/ServerHandleImpl.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/OSSupport/ServerHandleImpl.cpp b/src/OSSupport/ServerHandleImpl.cpp index 5244f3fd2..5fc5662e1 100644 --- a/src/OSSupport/ServerHandleImpl.cpp +++ b/src/OSSupport/ServerHandleImpl.cpp @@ -83,6 +83,9 @@ void cServerHandleImpl::Close(void) // Remove the ptr to self, so that the object may be freed: m_SelfPtr.reset(); + + // Remove self from cNetworkSingleton: + cNetworkSingleton::Get().RemoveServer(this); } -- cgit v1.2.3 From 076c87f797df654dd30e6c57509ba68ae2ca3db3 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sat, 24 Jan 2015 12:58:27 +0100 Subject: Fixed missing LibEvent reference in OSSupport. --- src/OSSupport/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/OSSupport/CMakeLists.txt b/src/OSSupport/CMakeLists.txt index 9424b63da..c8e96cbec 100644 --- a/src/OSSupport/CMakeLists.txt +++ b/src/OSSupport/CMakeLists.txt @@ -52,6 +52,6 @@ if(NOT MSVC) target_link_libraries(OSSupport rt) endif() - target_link_libraries(OSSupport pthread) + target_link_libraries(OSSupport pthread event_core event_extra) endif() endif() -- cgit v1.2.3 From 64a16a730955cd928f201abf138896b31f64d0d5 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Thu, 29 Jan 2015 11:10:32 +0100 Subject: CheckBasicStyle: Fixed a forgotten file close. --- src/CheckBasicStyle.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/CheckBasicStyle.lua b/src/CheckBasicStyle.lua index 0c7b05d6d..648a5711b 100644 --- a/src/CheckBasicStyle.lua +++ b/src/CheckBasicStyle.lua @@ -167,6 +167,7 @@ local function ProcessFile(a_FileName) os.exit(1) end local all = f:read("*all") + f:close() -- Check that the last line is empty - otherwise processing won't work properly: local lastChar = string.byte(all, string.len(all)) -- cgit v1.2.3 From e7424adbf440b70edea14f6aa96029842a9a9a9e Mon Sep 17 00:00:00 2001 From: tycho Date: Sat, 31 Jan 2015 15:44:14 +0000 Subject: Switched LuaState to use sizeof... --- src/Bindings/LuaState.h | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) (limited to 'src') diff --git a/src/Bindings/LuaState.h b/src/Bindings/LuaState.h index 7ac4120e1..97e6b47e1 100644 --- a/src/Bindings/LuaState.h +++ b/src/Bindings/LuaState.h @@ -346,20 +346,6 @@ protected: /** Number of arguments currently pushed (for the Push / Call chain) */ int m_NumCurrentFunctionArgs; - - /** Variadic template terminator: Counting zero args returns zero. */ - int CountArgs(void) - { - return 0; - } - - /** Variadic template: Counting args means add one to the count of the rest. */ - template - int CountArgs(T, Args... args) - { - return 1 + CountArgs(args...); - } - /** Variadic template terminator: If there's nothing more to push / pop, just call the function. Note that there are no return values either, because those are prefixed by a cRet value, so the arg list is never empty. */ bool PushCallPop(void) @@ -380,7 +366,7 @@ protected: bool PushCallPop(cLuaState::cRet, Args &&... args) { // Calculate the number of return values (number of args left): - int NumReturns = CountArgs(args...); + int NumReturns = sizeof...(args); // Call the function: if (!CallFunction(NumReturns)) -- cgit v1.2.3 From 360c632e36acfe8b271c3212feef9b6f93623ba1 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Wed, 28 Jan 2015 15:14:05 +0100 Subject: 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 (limited to 'src') 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); +} + + + + -- cgit v1.2.3 From e098728fa8986aea8997a183839c8fc91c4775f8 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Wed, 28 Jan 2015 15:14:35 +0100 Subject: cTCPLink: Fixed missing addresses on link connection. --- src/OSSupport/TCPLinkImpl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') 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: -- cgit v1.2.3 From 17498a97a289119debdb651ab898ddea99e86ff9 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Thu, 29 Jan 2015 11:09:56 +0100 Subject: cNetwork: Exported lookup functions to Lua API. Also added an example in the NetworkTest plugin. --- 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 ++++++++++++++++++++++++++++++- 5 files changed, 219 insertions(+), 3 deletions(-) create mode 100644 src/Bindings/LuaNameLookup.cpp create mode 100644 src/Bindings/LuaNameLookup.h (limited to 'src') 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); -- cgit v1.2.3 From 014b96adb33fa902072d9f35661bc4f5e7c323e8 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Fri, 30 Jan 2015 21:24:02 +0100 Subject: Exported cServerHandle and cNetwork:Listen to Lua. Also added an example to the NetworkTest plugin. --- 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 + 11 files changed, 613 insertions(+), 14 deletions(-) create mode 100644 src/Bindings/LuaServerHandle.cpp create mode 100644 src/Bindings/LuaServerHandle.h (limited to 'src') 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(); + } + + 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); } - // Close the link: - Link->Close(); + // 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; -- cgit v1.2.3 From 0e769f12ac39e1ef587c1eb7af549c553cd8a330 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Fri, 30 Jan 2015 23:08:09 +0100 Subject: LuaServerHandle: Removed debugging output. --- src/Bindings/LuaServerHandle.cpp | 4 ---- 1 file changed, 4 deletions(-) (limited to 'src') 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) -- cgit v1.2.3 From adf0020cd41f6a947ef883c582dde74d67255b1f Mon Sep 17 00:00:00 2001 From: Mattes D Date: Fri, 6 Feb 2015 18:44:05 +0100 Subject: APIDump: Added cNetwork documentation. --- src/Bindings/ManualBindings_Network.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') 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 ( -- cgit v1.2.3 From 9b9ce6fa3b13cb00e380664a2b51e51ffb209b7d Mon Sep 17 00:00:00 2001 From: Howaner Date: Fri, 6 Feb 2015 21:40:20 +0100 Subject: Added IsOnGround() to cEntity --- src/Entities/Entity.h | 3 +++ src/Protocol/Protocol18x.cpp | 12 ++++++------ 2 files changed, 9 insertions(+), 6 deletions(-) (limited to 'src') 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()); } -- cgit v1.2.3 From 7813cd20222451f3f1f02b3264bb2d689006f95a Mon Sep 17 00:00:00 2001 From: Howaner Date: Fri, 6 Feb 2015 21:42:32 +0100 Subject: cPlayer should override IsOnGround() --- src/Entities/Entity.h | 4 ++-- src/Entities/Player.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src') 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; } -- cgit v1.2.3 From ca591c15a043f66d52d835b316473bdf8ef658d8 Mon Sep 17 00:00:00 2001 From: Howaner Date: Fri, 6 Feb 2015 21:45:29 +0100 Subject: Spacing --- src/Entities/Player.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') 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; } -- cgit v1.2.3 From d32831d7e8549227176655da494f32aa6b77ac15 Mon Sep 17 00:00:00 2001 From: Howaner Date: Sat, 7 Feb 2015 11:03:38 +0100 Subject: Set reuse flag to sockets Should fix #1726 --- src/OSSupport/ServerHandleImpl.cpp | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) (limited to 'src') 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 } -- cgit v1.2.3 From be528a9f527e631181b590346d77eaaf64c914cd Mon Sep 17 00:00:00 2001 From: Howaner Date: Sat, 7 Feb 2015 18:39:24 +0100 Subject: Use evutil_make_listen_socket_reuseable --- src/OSSupport/ServerHandleImpl.cpp | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) (limited to 'src') 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)) { -- cgit v1.2.3 From a939e2ded97730b7847593dab2b9abb849bd6555 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sun, 8 Feb 2015 12:24:15 +0100 Subject: 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(-) (limited to 'src') 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 -- cgit v1.2.3 From 81d7329ad31657e9e587dd7231be503ae1b157a9 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sun, 8 Feb 2015 14:41:24 +0100 Subject: ServerHandle: Fixed socket reuse. Fixes CID 104670, CID 104670 and CID 103724. --- src/OSSupport/ServerHandleImpl.cpp | 40 ++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) (limited to 'src') 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) { -- cgit v1.2.3 From 1ce91646949467f72226472116088a5aaaa59664 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sun, 8 Feb 2015 14:49:46 +0100 Subject: 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(-) (limited to 'src') 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); -- cgit v1.2.3 From 16636ff6e2bff3658e0843eee9dfad440771b62f Mon Sep 17 00:00:00 2001 From: Mattes D Date: Thu, 12 Feb 2015 20:05:55 +0100 Subject: LuaAPI: Added client TLS support for TCP links. --- 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 +- 9 files changed, 335 insertions(+), 7 deletions(-) (limited to 'src') 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; } } -- cgit v1.2.3 From 505dce1fc38def5d28f8f71fa060a2bc76dd40b4 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Thu, 12 Feb 2015 20:22:39 +0100 Subject: Fixed Linux compilation. std::make_unique is not available in C++11. --- src/Bindings/LuaTCPLink.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') 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: -- cgit v1.2.3 From bae8b2e1faa918ad483f9f2c88621e7b7498ca3a Mon Sep 17 00:00:00 2001 From: Mattes D Date: Thu, 12 Feb 2015 20:23:04 +0100 Subject: PolarSSL++: Fixed debugging output. --- src/PolarSSL++/SslContext.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') 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); //*/ -- cgit v1.2.3 From b8bf795dd1701a32075950a6ea98a16eacb9edc9 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Fri, 13 Feb 2015 18:31:54 +0100 Subject: Exported cTCPLink:Close and :Shutdown() to Lua API. --- src/Bindings/ManualBindings_Network.cpp | 96 ++++++++++++++++++++++++++++----- 1 file changed, 83 insertions(+), 13 deletions(-) (limited to 'src') 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); -- cgit v1.2.3 From 557adf3be944b8a91c768ee85241b7c8bc57c0a6 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Fri, 13 Feb 2015 23:18:22 +0100 Subject: Exported TLS server start on cTCPLink to Lua API. --- src/Bindings/LuaTCPLink.cpp | 117 ++++++++++++++++++++++++++++++-- src/Bindings/LuaTCPLink.h | 31 ++++++++- src/Bindings/ManualBindings_Network.cpp | 47 +++++++++++++ 3 files changed, 188 insertions(+), 7 deletions(-) (limited to 'src') 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"); -- cgit v1.2.3 From 1f3d11de32e36dfc983393266f105b969d8dfef8 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sat, 14 Feb 2015 09:17:26 +0100 Subject: Fixed Linux compilation. --- src/Bindings/LuaTCPLink.cpp | 2 +- src/Bindings/LuaTCPLink.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') 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); -- cgit v1.2.3 From 1ca0a4915ece26d5700808320496227650819a9b Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sat, 14 Feb 2015 13:31:31 +0100 Subject: SslContext: Turned debug messages off. --- src/PolarSSL++/SslContext.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') 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); //*/ -- cgit v1.2.3 From d336a3ea9e581372e225ee64113fe7fd7e080d45 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sat, 14 Feb 2015 13:55:54 +0100 Subject: Fixed TCP link shutdown. The shutdown is postponed until there's no more outgoing data in the LibEvent buffers. --- src/OSSupport/TCPLinkImpl.cpp | 65 +++++++++++++++++++++++++++++++++++++------ src/OSSupport/TCPLinkImpl.h | 12 ++++++++ 2 files changed, 68 insertions(+), 9 deletions(-) (limited to 'src') 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); }; -- cgit v1.2.3 From 612637ab2e3c6e63124053cce770bf406d06c742 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Wed, 18 Feb 2015 09:34:33 +0100 Subject: Network: Fixed two-socket servers. --- src/OSSupport/ServerHandleImpl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') 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.", -- cgit v1.2.3 From 70d54054e332c05d94e69b1eeca45a8773115c14 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Wed, 18 Feb 2015 22:41:22 +0100 Subject: 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(-) (limited to 'src') 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); -- cgit v1.2.3 From 1a60785ca248bb081bf9fc699273292540a1de5f Mon Sep 17 00:00:00 2001 From: Howaner Date: Wed, 18 Feb 2015 23:33:27 +0100 Subject: 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(-) (limited to 'src') 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(); } -- cgit v1.2.3 From 5d4dd103a12a44c8482d524c38841221da1d8fb2 Mon Sep 17 00:00:00 2001 From: Matyas Dolak Date: Fri, 20 Feb 2015 09:51:18 +0100 Subject: 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(+) (limited to 'src') 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; } -- cgit v1.2.3 From 9c5162041e6e0699283862b87e2e424bf8e3b8b8 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Fri, 20 Feb 2015 14:28:05 +0100 Subject: cNetwork: Added UDP API. --- 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 +++++ 11 files changed, 1400 insertions(+), 32 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 (limited to 'src') 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); +}; + + + + -- cgit v1.2.3 From e30ee8063d9e7b3471c9bbb197418d792d8fb468 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Fri, 20 Feb 2015 16:05:53 +0100 Subject: UDPEndpointImpl: Fixed clang warnings. --- src/OSSupport/UDPEndpointImpl.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') 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); } } } -- cgit v1.2.3 From 22d3a6a47f89ae1bb36f4f5f93c1694658b92bed Mon Sep 17 00:00:00 2001 From: Mattes D Date: Fri, 20 Feb 2015 22:55:19 +0100 Subject: Fixed monster spawn randomness. Fixes #1699. --- src/MobSpawner.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') 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++) { -- cgit v1.2.3 From b9e4fe0a3b2a6a4d688d1a0807f15944361fb585 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sat, 21 Feb 2015 09:41:14 +0100 Subject: Added cCryptoHash namespace to Lua API. --- src/Bindings/ManualBindings.cpp | 109 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 107 insertions(+), 2 deletions(-) (limited to 'src') 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); -- cgit v1.2.3 From b165ab63633cd8c45d505a2fd2165807ea2b50ec Mon Sep 17 00:00:00 2001 From: Freddie Wang Date: Wed, 4 Feb 2015 23:10:46 -0800 Subject: Fix door placement check --- src/Items/ItemDoor.h | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'src') 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; } } ; - - - - -- cgit v1.2.3 From 1bcc4abd68ad7cff943b1aa785f9d7bcb62c6b5d Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sat, 21 Feb 2015 14:23:37 +0100 Subject: 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(-) (limited to 'src') 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; } -- cgit v1.2.3 From 13f81a051d948fd5acb278c5d8679efa5fb3297c Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Sun, 22 Feb 2015 17:34:20 +0100 Subject: Exported CompressString and UncompressString to Lua --- src/Bindings/ManualBindings.cpp | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) (limited to 'src') 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); -- cgit v1.2.3 From b474b9fb5d95f3c511ce0f01f4a6c3e205f13629 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sun, 22 Feb 2015 19:05:43 +0100 Subject: 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(-) (limited to 'src') 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; -- cgit v1.2.3 From 54410bfe4dac8793d6b46df725c400f7ce8d365e Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Mon, 23 Feb 2015 12:53:02 +0100 Subject: Exported all compression functions in a new class. --- src/Bindings/ManualBindings.cpp | 145 +++++++++++++++++++++++++++++++++++----- 1 file changed, 129 insertions(+), 16 deletions(-) (limited to 'src') 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,58 @@ 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); + 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; + } + + // 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; +} + + - CompressString(ToCompress, Length, res, Factor); - LuaState.Push(res); + + +static int tolua_UncompressStringZLIB(lua_State * tolua_S) +{ + cLuaState S(tolua_S); + if ( + !lua_isstring(tolua_S, 1) || + !lua_isnumber(tolua_S, 2) + ) + { + cLuaState::LogStackTrace(tolua_S); + return 0; + } + + // 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; } @@ -128,16 +170,79 @@ static int tolua_CompressString(lua_State * tolua_S) -static int tolua_UncompressString(lua_State * tolua_S) +static int tolua_CompressStringGZIP(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); + 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; +} - UncompressString(ToUncompress, Length, res, UncompressedSize); - LuaState.Push(res); + + + + +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); -- cgit v1.2.3 From 8c8ec1094d8ba6df410f1fb0d0fba216b98a8f9a Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Mon, 23 Feb 2015 15:29:07 +0100 Subject: Replaced lua_isXYZ with cLuaState::CheckParamXYZ --- src/Bindings/ManualBindings.cpp | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) (limited to 'src') 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); -- cgit v1.2.3 From d39d2ca5e9592760246b11bdb0336e067ed8ee8a Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Mon, 23 Feb 2015 15:40:31 +0100 Subject: Added forgotten indent --- src/Bindings/ManualBindings.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') 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) ) ) { -- cgit v1.2.3 From 9e1db16ba497fd527b95fc00c69229de3957876d Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Mon, 23 Feb 2015 16:09:35 +0100 Subject: Fixed operators --- src/Bindings/ManualBindings.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src') 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) ) { -- cgit v1.2.3 From c286b186c4905b0a36a6bae7bc2af6d672cd82e2 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Tue, 24 Feb 2015 10:04:43 +0100 Subject: 1.8 Protocol: Fixed a possible race condition. Fixes #1759. --- src/Protocol/Protocol18x.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'src') 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())); + } } -- cgit v1.2.3 From a56b6906d60f5b511aeb93cf765359634e4bcf6e Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Wed, 25 Feb 2015 17:26:48 +0100 Subject: Fixed flowing water turning into ice when snowing --- src/Chunk.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') 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); } -- cgit v1.2.3 From f1f23b09ae20fa4d6288a9139462e49ff77d98c4 Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Wed, 25 Feb 2015 19:00:52 +0100 Subject: 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(+) (limited to 'src') 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 -- cgit v1.2.3 From b3f07511306080545fb3e763e99ce31796d7de7d Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Wed, 25 Feb 2015 19:02:08 +0100 Subject: Weather: Snow starts forming when the top block is at the right height or higher --- src/Chunk.cpp | 123 +++++++++++++++++++++++++++------------------------------- 1 file changed, 57 insertions(+), 66 deletions(-) (limited to 'src') 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)) - { - case biTaiga: - case biFrozenOcean: - case biFrozenRiver: - case biIcePlains: - case biIceMountains: - case biTaigaHills: - { - // 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)) + + // 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) + { + 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++) + { + 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; - } - } - } - if (TopMeta < MaxSize) - { - FastSetBlock(X, Height, Z, E_BLOCK_SNOW, TopMeta + 1); + MaxSize = 0; + break; } - else if (TopMeta > MaxSize) + case E_BLOCK_SNOW: { - 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: + } + if (TopMeta < MaxSize) { - break; + FastSetBlock(X, Height, Z, E_BLOCK_SNOW, TopMeta + 1); + } + else if (TopMeta > MaxSize) + { + FastSetBlock(X, Height, Z, E_BLOCK_SNOW, TopMeta - 1); } - } // switch (biome) + } + 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); + } } -- cgit v1.2.3 From ba3eaf922377c4b19d66b205a47c0698ea02f5b4 Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Wed, 25 Feb 2015 19:12:53 +0100 Subject: Snow finisher uses GetSnowStartHeight instead of specific biomes --- src/Generating/FinishGen.cpp | 36 ++++++++++++++---------------------- 1 file changed, 14 insertions(+), 22 deletions(-) (limited to 'src') 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 } -- cgit v1.2.3 From ac2c88b4510c8504cf015bfa69fc844aa9293e34 Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Wed, 25 Feb 2015 19:22:44 +0100 Subject: Ice finisher uses GetSnowStartHeight instead of specific biomes --- src/Generating/FinishGen.cpp | 45 +++++++++++++++++++------------------------- 1 file changed, 19 insertions(+), 26 deletions(-) (limited to 'src') 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 } -- cgit v1.2.3 From 378528136c921cc520289c2753e1edb5f25d3c7c Mon Sep 17 00:00:00 2001 From: Raekye Date: Wed, 25 Feb 2015 20:56:45 -0500 Subject: use DoSetSpeed in AddSpeed* in Entity.cpp --- src/Entities/Entity.cpp | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) (limited to 'src') 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); } -- cgit v1.2.3 From 81e8577cfd9d1a8dd40ca1e9fc25c83f990b7f82 Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Thu, 26 Feb 2015 20:26:45 +0100 Subject: 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(-) (limited to 'src') 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 -- cgit v1.2.3 From e63b4f4913f93e2346bab32d079e7de289aac257 Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Sat, 28 Feb 2015 16:34:17 +0100 Subject: Added experience drops when mining ores --- src/Blocks/BlockOre.h | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) (limited to 'src') 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); + } + } } ; -- cgit v1.2.3 From 88fc70a06a9e2d15f9a672f792e0029a79be136f Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sat, 28 Feb 2015 17:16:21 +0100 Subject: 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(-) (limited to 'src') 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 } -- cgit v1.2.3 From 224df08d30b556c0d7214e451fd4322ca75f32ea Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Sat, 28 Feb 2015 17:27:28 +0100 Subject: GetSnowStartHeight returns an int --- src/BiomeDef.cpp | 2 +- src/BiomeDef.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') 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 -- cgit v1.2.3 From b65a6ef210a590c6d265cdd71efe58b81a4ae836 Mon Sep 17 00:00:00 2001 From: DevToaster Date: Sun, 1 Mar 2015 03:03:41 +1030 Subject: 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(-) (limited to 'src') 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; -- cgit v1.2.3 From e012c06281624e97fc79fa90e18e51a42fb002af Mon Sep 17 00:00:00 2001 From: DevToaster Date: Sun, 1 Mar 2015 03:09:36 +1030 Subject: Replaced short int with int for Player experience --- src/Entities/Player.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') 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; -- cgit v1.2.3 From f5a216cabc14e6b2381ea881a12ce4db24841c9e Mon Sep 17 00:00:00 2001 From: DevToaster Date: Sun, 1 Mar 2015 03:45:06 +1030 Subject: Changed C-styled casts to static_cast and removed unneeded casts --- src/Entities/Player.cpp | 68 ++++++++++++++++++++++++------------------------- 1 file changed, 34 insertions(+), 34 deletions(-) (limited to 'src') 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 { -- cgit v1.2.3 From 19d7ec51a0a4cb70d5f1e4a62fcf3e944e072a5d Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Sat, 28 Feb 2015 22:37:33 +0100 Subject: 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(+) (limited to 'src') 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 { -- cgit v1.2.3 From 0394acfc0ca25e0b3497a4494d98c0b21062ad47 Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Sat, 28 Feb 2015 22:40:13 +0100 Subject: 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(-) (limited to 'src') 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; }; -- cgit v1.2.3 From 780a9ae9d121ebbf52276dea6545006822ad1c62 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sun, 1 Mar 2015 12:13:39 +0100 Subject: 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(-) (limited to 'src') 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 -- cgit v1.2.3 From d2e1ed3a3be0da9244e12740c28f40cd19b9ccab Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sun, 1 Mar 2015 12:40:53 +0100 Subject: Steppy HeiGen: Fixed Linux compilation. --- src/Generating/HeiGen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') 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)); } -- cgit v1.2.3 From e63f9bdc1a2cdb32e5e49fe325d3bad7317b5418 Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Sun, 1 Mar 2015 20:06:44 +0100 Subject: Replaced cFinishGenPtr with std::make_shared --- src/Generating/ComposableGenerator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') 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) { -- cgit v1.2.3 From 3d3735a37864b3abf416ec5f9ae962f68392589d Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Sun, 1 Mar 2015 20:08:05 +0100 Subject: Replaced IntNoiseXX with IntNoiseXXInt --- src/Generating/FinishGen.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') 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; } -- cgit v1.2.3 From db2a406c13711ac751117793c9655255ec70731c Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Sun, 1 Mar 2015 20:09:44 +0100 Subject: Removed trailing whitespace --- src/Generating/FinishGen.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') 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) { -- cgit v1.2.3 From d4b505db025b05379ac9a5df923a60f0d37eed9a Mon Sep 17 00:00:00 2001 From: Mattes D Date: Tue, 3 Mar 2015 01:28:58 +0100 Subject: 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(-) (limited to 'src') 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; -- cgit v1.2.3 From f71b1fe799eb944b9488019da134a6cc34675605 Mon Sep 17 00:00:00 2001 From: joshi07 Date: Thu, 5 Mar 2015 11:52:42 +0100 Subject: Added OnTeleportEntity hook for plugins. Plugins may or may not allow teleport to the new position. Updated the HookNotify plugin with it. --- 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 +++++++++++++++---------- 7 files changed, 64 insertions(+), 12 deletions(-) (limited to 'src') 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(); -- cgit v1.2.3 From 7348bf38534bfdfa0c82a103474c4488bf21fe9e Mon Sep 17 00:00:00 2001 From: Alexander Harkness Date: Thu, 5 Mar 2015 19:29:57 +0000 Subject: Grass checks for sufficient light before spreading --- src/Blocks/BlockDirt.h | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'src') 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 -- cgit v1.2.3 From 134246fb152740c5f08abb583d399ed7d097faae Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Sun, 8 Mar 2015 13:29:49 +0100 Subject: Added grass/course dirt layer on MesaPlateauF(M) --- src/Generating/CompoGenBiomal.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'src') 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); + } } -- cgit v1.2.3 From 2bbfd0341f1faae711064f64372a74b8c98c5840 Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Sun, 8 Mar 2015 13:30:21 +0100 Subject: Added proper trees to Mesa biomes Mesa only has small apple trees. --- src/Generating/Trees.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'src') 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: -- cgit v1.2.3 From d19f2a472b2ba4d7ae32a2b5cf2507a446ea7806 Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Sun, 8 Mar 2015 15:22:01 +0100 Subject: Added parenthesis around the comparisons --- src/Generating/CompoGenBiomal.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') 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); } -- cgit v1.2.3 From ce6219530a2a3e6550967b21bad8ad9a2145c465 Mon Sep 17 00:00:00 2001 From: Howaner Date: Mon, 9 Mar 2015 22:32:12 +0100 Subject: Fixed client kick/crash if many block changes happend --- src/Chunk.cpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) (limited to 'src') 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) + { + // 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 { - (*itr)->SendBlockChanges(m_PosX, m_PosZ, m_PendingSendBlocks); + // 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(); } -- cgit v1.2.3