summaryrefslogtreecommitdiffstats
path: root/src/Bindings
diff options
context:
space:
mode:
Diffstat (limited to 'src/Bindings')
-rw-r--r--src/Bindings/CMakeLists.txt2
-rw-r--r--src/Bindings/LuaState.cpp24
-rw-r--r--src/Bindings/LuaState.h4
-rw-r--r--src/Bindings/LuaTCPLink.cpp318
-rw-r--r--src/Bindings/LuaTCPLink.h84
-rw-r--r--src/Bindings/LuaUDPEndpoint.cpp221
-rw-r--r--src/Bindings/LuaUDPEndpoint.h86
-rw-r--r--src/Bindings/LuaWindow.cpp18
-rw-r--r--src/Bindings/LuaWindow.h1
-rw-r--r--src/Bindings/ManualBindings.cpp331
-rw-r--r--src/Bindings/ManualBindings_Network.cpp550
-rw-r--r--src/Bindings/Plugin.h5
-rw-r--r--src/Bindings/PluginLua.cpp29
-rw-r--r--src/Bindings/PluginLua.h5
-rw-r--r--src/Bindings/PluginManager.cpp24
-rw-r--r--src/Bindings/PluginManager.h4
16 files changed, 1628 insertions, 78 deletions
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 73b114599..25c77a652 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());
@@ -680,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 f68b022ea..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;
@@ -184,6 +185,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);
@@ -210,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/LuaTCPLink.cpp b/src/Bindings/LuaTCPLink.cpp
index 6b8395806..d88c41120 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)
@@ -153,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();
}
@@ -169,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();
}
@@ -179,6 +196,110 @@ 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.reset(new cLinkSslContext(*this));
+ m_SslContext->Initialize(true);
+
+ // Create the peer cert, if required:
+ if (!a_OwnCertData.empty() && !a_OwnPrivKeyData.empty())
+ {
+ auto OwnCert = std::make_shared<cX509Cert>();
+ 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<cCryptoKey>();
+ 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);
+ }
+ 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<cX509Cert>();
+ 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<cCryptoKey>();
+ 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();
+ return "";
+}
+
+
+
+
+
void cLuaTCPLink::Terminated(void)
{
// Disable the callbacks:
@@ -195,11 +316,36 @@ 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();
+}
+
+
+
+
+
+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());
}
}
@@ -269,6 +415,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 +455,156 @@ void cLuaTCPLink::OnRemoteClosed(void)
+
+////////////////////////////////////////////////////////////////////////////////
+// cLuaTCPLink::cLinkSslContext:
+
+cLuaTCPLink::cLinkSslContext::cLinkSslContext(cLuaTCPLink & a_Link):
+ m_Link(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:
+ TryFinishHandshaking();
+
+ // Flush any cleartext data that can be "received":
+ FlushBuffers();
+}
+
+
+
+
+
+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())
+ {
+ return;
+ }
+
+ char Buffer[1024];
+ int NumBytes;
+ while ((NumBytes = ReadPlain(Buffer, sizeof(Buffer))) > 0)
+ {
+ m_Link.ReceivedCleartextData(Buffer, static_cast<size_t>(NumBytes));
+ if (m_Self.expired())
+ {
+ // The callback closed the SSL context, bail out
+ return;
+ }
+ }
+}
+
+
+
+
+
+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())
+ {
+ 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)
+{
+ // 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())
+ {
+ 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)
+{
+ // 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())
+ {
+ 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<int>(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<int>(a_NumBytes);
+}
+
+
+
+
diff --git a/src/Bindings/LuaTCPLink.h b/src/Bindings/LuaTCPLink.h
index f2af911ec..c8ae776fe 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,82 @@ 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
+ );
+
+ /** 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<cLinkSslContext> cLinkSslContextPtr;
+ typedef WeakPtr<cLinkSslContext> cLinkSslContextWPtr;
+
+ /** 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;
+
+ /** 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);
+
+ /** 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 +152,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. */
+ cLinkSslContextPtr 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/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<cLuaUDPEndpoint> 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/LuaWindow.cpp b/src/Bindings/LuaWindow.cpp
index 35730878d..d4014059b 100644
--- a/src/Bindings/LuaWindow.cpp
+++ b/src/Bindings/LuaWindow.cpp
@@ -167,6 +167,24 @@ void cLuaWindow::Destroy(void)
+void cLuaWindow::DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer& a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply)
+{
+ cSlotAreas Areas;
+ for (auto Area : m_SlotAreas)
+ {
+ if (Area != a_ClickedArea)
+ {
+ Areas.push_back(Area);
+ }
+ }
+
+ super::DistributeStackToAreas(a_ItemStack, a_Player, Areas, a_ShouldApply, false);
+}
+
+
+
+
+
void cLuaWindow::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum)
{
if (a_ItemGrid != &m_Contents)
diff --git a/src/Bindings/LuaWindow.h b/src/Bindings/LuaWindow.h
index dab99a2e2..76530d99d 100644
--- a/src/Bindings/LuaWindow.h
+++ b/src/Bindings/LuaWindow.h
@@ -84,6 +84,7 @@ protected:
// cWindow overrides:
virtual bool ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse) override;
virtual void Destroy(void) override;
+ virtual void DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) override;
// cItemGrid::cListener overrides:
virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) override;
diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp
index 24e3f0052..40ac12b41 100644
--- a/src/Bindings/ManualBindings.cpp
+++ b/src/Bindings/ManualBindings.cpp
@@ -3,8 +3,11 @@
#include "ManualBindings.h"
#undef TOLUA_TEMPLATE_BIND
+#include <sstream>
+#include <iomanip>
#include "tolua++/include/tolua++.h"
#include "polarssl/md5.h"
+#include "polarssl/sha1.h"
#include "PluginLua.h"
#include "PluginManager.h"
#include "LuaWindow.h"
@@ -28,6 +31,7 @@
#include "../LineBlockTracer.h"
#include "../WorldStorage/SchematicFileSerializer.h"
#include "../CompositeChat.h"
+#include "../StringCompression.h"
@@ -107,6 +111,146 @@ static int tolua_Clamp(lua_State * tolua_S)
+static int tolua_CompressStringZLIB(lua_State * tolua_S)
+{
+ cLuaState S(tolua_S);
+ if (
+ !S.CheckParamString(1) ||
+ (
+ !S.CheckParamNumber(2) &&
+ !S.CheckParamEnd(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;
+}
+
+
+
+
+
+static int tolua_UncompressStringZLIB(lua_State * tolua_S)
+{
+ cLuaState S(tolua_S);
+ if (
+ !S.CheckParamString(1) ||
+ !S.CheckParamNumber(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;
+}
+
+
+
+
+
+static int tolua_CompressStringGZIP(lua_State * tolua_S)
+{
+ cLuaState S(tolua_S);
+ if (
+ !S.CheckParamString(1) ||
+ !S.CheckParamEnd(2)
+ )
+ {
+ 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 (
+ !S.CheckParamString(1) ||
+ !S.CheckParamEnd(2)
+ )
+ {
+ cLuaState::LogStackTrace(tolua_S);
+ return 0;
+ }
+
+ // Get the params:
+ AString ToUncompress;
+ S.GetStackValues(1, ToUncompress);
+
+ // Compress the string:
+ AString res;
+ UncompressStringGZIP(ToUncompress.data(), ToUncompress.size(), res);
+ S.Push(res);
+ return 1;
+}
+
+
+
+
+
+static int tolua_InflateString(lua_State * tolua_S)
+{
+ cLuaState S(tolua_S);
+ if (
+ !S.CheckParamString(1) ||
+ !S.CheckParamEnd(2)
+ )
+ {
+ 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;
+}
+
+
+
+
+
static int tolua_StringSplit(lua_State * tolua_S)
{
cLuaState LuaState(tolua_S);
@@ -122,6 +266,24 @@ static int tolua_StringSplit(lua_State * tolua_S)
+static int tolua_StringSplitWithQuotes(lua_State * tolua_S)
+{
+ cLuaState S(tolua_S);
+
+ AString str;
+ AString delim;
+
+ S.GetStackValues(1, str, delim);
+
+ AStringVector Split = StringSplitWithQuotes(str, delim);
+ S.Push(Split);
+ return 1;
+}
+
+
+
+
+
static int tolua_StringSplitAndTrim(lua_State * tolua_S)
{
cLuaState LuaState(tolua_S);
@@ -165,6 +327,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 +354,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 +372,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 +390,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;
}
@@ -2171,11 +2365,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;
@@ -2185,6 +2384,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::setfill('0');
+ for (size_t i = 0; i < ARRAYCOUNT(md5Output); i++)
+ {
+ Output << std::setw(2) << static_cast<unsigned short>(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::setfill('0');
+ for (size_t i = 0; i < ARRAYCOUNT(sha1Output); i++)
+ {
+ Output << std::setw(2) << static_cast<unsigned short>(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);
@@ -3387,16 +3671,26 @@ static int tolua_cCompositeChat_UnderlineUrls(lua_State * tolua_S)
void ManualBindings::Bind(lua_State * tolua_S)
{
tolua_beginmodule(tolua_S, nullptr);
- tolua_function(tolua_S, "Clamp", tolua_Clamp);
- tolua_function(tolua_S, "StringSplit", tolua_StringSplit);
- tolua_function(tolua_S, "StringSplitAndTrim", tolua_StringSplitAndTrim);
- tolua_function(tolua_S, "LOG", tolua_LOG);
- tolua_function(tolua_S, "LOGINFO", tolua_LOGINFO);
- tolua_function(tolua_S, "LOGWARN", tolua_LOGWARN);
- tolua_function(tolua_S, "LOGWARNING", tolua_LOGWARN);
- tolua_function(tolua_S, "LOGERROR", tolua_LOGERROR);
- tolua_function(tolua_S, "Base64Encode", tolua_Base64Encode);
- tolua_function(tolua_S, "Base64Decode", tolua_Base64Decode);
+
+ // 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, "StringSplit", tolua_StringSplit);
+ tolua_function(tolua_S, "StringSplitWithQuotes", tolua_StringSplitWithQuotes);
+ tolua_function(tolua_S, "StringSplitAndTrim", tolua_StringSplitAndTrim);
+ tolua_function(tolua_S, "LOG", tolua_LOG);
+ tolua_function(tolua_S, "LOGINFO", tolua_LOGINFO);
+ tolua_function(tolua_S, "LOGWARN", tolua_LOGWARN);
+ tolua_function(tolua_S, "LOGWARNING", tolua_LOGWARN);
+ 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);
@@ -3553,7 +3847,20 @@ 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);
+
+ 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);
diff --git a/src/Bindings/ManualBindings_Network.cpp b/src/Bindings/ManualBindings_Network.cpp
index 902f687c8..628cda7f0 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,85 @@ 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<cLuaUDPEndpoint>(*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::EnumLocalIPAddresses */
+static int tolua_cNetwork_EnumLocalIPAddresses(lua_State * L)
+{
+ // Function signature:
+ // cNetwork:EnumLocalIPAddresses() -> {string, ...}
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserTable(1, "cNetwork") ||
+ !S.CheckParamEnd(2)
+ )
+ {
+ return 0;
+ }
+
+ // Push the enumerated addresses:
+ S.Push(cNetwork::EnumLocalIPAddresses());
+ return 1;
+}
+
+
+
+
+
/** Binds cNetwork::HostnameToIP */
static int tolua_cNetwork_HostnameToIP(lua_State * L)
{
@@ -159,7 +239,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 (
@@ -212,19 +292,102 @@ 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<cLuaServerHandle *>(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<cLuaServerHandle **>(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<cLuaServerHandle **>(lua_touserdata(L, 1));
+
+ // Close it:
+ S.Push(Srv->IsListening());
+ return 1;
+}
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
// 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 +397,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<cLuaTCPLink **>(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,15 +548,180 @@ 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<cLuaTCPLink **>(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<cLuaTCPLink **>(lua_touserdata(L, 1));
+
+ // Shutdown the link:
+ Link->Shutdown();
+ return 0;
+}
+
+
+
+
+
+/** 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<cLuaTCPLink **>(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;
+}
+
+
+
+
+
+/** 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<cLuaTCPLink **>(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):
+// 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<cLuaServerHandle *>(tolua_tousertype(L, 1, nullptr));
- Srv->Release();
+ LOGD("Lua: Collecting cUDPEndpoint");
+ cLuaUDPEndpoint * Endpoint = static_cast<cLuaUDPEndpoint *>(tolua_tousertype(L, 1, nullptr));
+ Endpoint->Release();
return 0;
}
@@ -405,33 +729,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<cLuaServerHandle **>(lua_touserdata(L, 1));
+ auto Endpoint = *static_cast<cLuaUDPEndpoint **>(lua_touserdata(L, 1));
// Close it:
- Srv->Close();
+ Endpoint->Close();
return 0;
}
@@ -439,33 +762,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<cLuaServerHandle **>(lua_touserdata(L, 1));
+ auto Endpoint = *static_cast<cLuaUDPEndpoint **>(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<cLuaUDPEndpoint **>(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<cLuaUDPEndpoint **>(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<cLuaUDPEndpoint **>(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<UInt16>(RemotePort)));
return 1;
}
@@ -485,27 +922,44 @@ 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_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, "Connect", tolua_cNetwork_Connect);
+ tolua_function(tolua_S, "CreateUDPEndpoint", tolua_cNetwork_CreateUDPEndpoint);
+ tolua_function(tolua_S, "EnumLocalIPAddresses", tolua_cNetwork_EnumLocalIPAddresses);
+ 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");
+ 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_function(tolua_S, "StartTLSServer", tolua_cTCPLink_StartTLSServer);
+ tolua_endmodule(tolua_S);
+
+ 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/Bindings/Plugin.h b/src/Bindings/Plugin.h
index 6210dbed4..3f9fa7655 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;
@@ -110,12 +111,12 @@ public:
Command permissions have already been checked.
Returns true if command handled successfully
*/
- virtual bool HandleCommand(const AStringVector & a_Split, cPlayer & a_Player) = 0;
+ virtual bool HandleCommand(const AStringVector & a_Split, cPlayer & a_Player, const AString & a_FullCommand) = 0;
/** Handles the console command split into a_Split.
Returns true if command handled successfully. Output is to be sent to the a_Output callback.
*/
- virtual bool HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output) = 0;
+ virtual bool HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output, const AString & a_FullCommand) = 0;
/// All bound commands are to be removed, do any language-dependent cleanup here
virtual void ClearCommands(void) {}
diff --git a/src/Bindings/PluginLua.cpp b/src/Bindings/PluginLua.cpp
index 500913e76..263d1f005 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);
@@ -1445,7 +1465,7 @@ bool cPluginLua::OnWorldTick(cWorld & a_World, std::chrono::milliseconds a_Dt, s
-bool cPluginLua::HandleCommand(const AStringVector & a_Split, cPlayer & a_Player)
+bool cPluginLua::HandleCommand(const AStringVector & a_Split, cPlayer & a_Player, const AString & a_FullCommand)
{
ASSERT(!a_Split.empty());
CommandMap::iterator cmd = m_Commands.find(a_Split[0]);
@@ -1457,7 +1477,7 @@ bool cPluginLua::HandleCommand(const AStringVector & a_Split, cPlayer & a_Player
cCSLock Lock(m_CriticalSection);
bool res = false;
- m_LuaState.Call(cmd->second, a_Split, &a_Player, cLuaState::Return, res);
+ m_LuaState.Call(cmd->second, a_Split, &a_Player, a_FullCommand, cLuaState::Return, res);
return res;
}
@@ -1465,7 +1485,7 @@ bool cPluginLua::HandleCommand(const AStringVector & a_Split, cPlayer & a_Player
-bool cPluginLua::HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output)
+bool cPluginLua::HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output, const AString & a_FullCommand)
{
ASSERT(!a_Split.empty());
CommandMap::iterator cmd = m_ConsoleCommands.find(a_Split[0]);
@@ -1480,7 +1500,7 @@ bool cPluginLua::HandleConsoleCommand(const AStringVector & a_Split, cCommandOut
cCSLock Lock(m_CriticalSection);
bool res = false;
AString str;
- m_LuaState.Call(cmd->second, a_Split, cLuaState::Return, res, str);
+ m_LuaState.Call(cmd->second, a_Split, a_FullCommand, cLuaState::Return, res, str);
if (res && !str.empty())
{
a_Output.Out(str);
@@ -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..4f3529fc9 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;
@@ -130,9 +131,9 @@ public:
virtual bool OnWorldStarted (cWorld & a_World) override;
virtual bool OnWorldTick (cWorld & a_World, std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_LastTickDurationMSec) override;
- virtual bool HandleCommand(const AStringVector & a_Split, cPlayer & a_Player) override;
+ virtual bool HandleCommand(const AStringVector & a_Split, cPlayer & a_Player, const AString & a_FullCommand) override;
- virtual bool HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output) override;
+ virtual bool HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output, const AString & a_FullCommand) override;
virtual void ClearCommands(void) override;
diff --git a/src/Bindings/PluginManager.cpp b/src/Bindings/PluginManager.cpp
index 9d86c64a2..8935f7dd3 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);
@@ -1447,7 +1465,7 @@ cPluginManager::CommandResult cPluginManager::HandleCommand(cPlayer & a_Player,
ASSERT(cmd->second.m_Plugin != nullptr);
- if (!cmd->second.m_Plugin->HandleCommand(Split, a_Player))
+ if (!cmd->second.m_Plugin->HandleCommand(Split, a_Player, a_Command))
{
return crError;
}
@@ -1750,7 +1768,7 @@ bool cPluginManager::IsConsoleCommandBound(const AString & a_Command)
-bool cPluginManager::ExecuteConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output)
+bool cPluginManager::ExecuteConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output, const AString & a_Command)
{
if (a_Split.empty())
{
@@ -1777,7 +1795,7 @@ bool cPluginManager::ExecuteConsoleCommand(const AStringVector & a_Split, cComma
return false;
}
- return cmd->second.m_Plugin->HandleConsoleCommand(a_Split, a_Output);
+ return cmd->second.m_Plugin->HandleConsoleCommand(a_Split, a_Output, a_Command);
}
diff --git a/src/Bindings/PluginManager.h b/src/Bindings/PluginManager.h
index 97e91c1df..4efcbb6f3 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);
@@ -282,7 +284,7 @@ public:
bool IsConsoleCommandBound(const AString & a_Command); // tolua_export
/** Executes the command split into a_Split, as if it was given on the console. Returns true if executed. Output is sent to the a_Output callback */
- bool ExecuteConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output);
+ bool ExecuteConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output, const AString & a_Command);
/** Appends all commands beginning with a_Text (case-insensitive) into a_Results.
If a_Player is not nullptr, only commands for which the player has permissions are added.