Merge branch 'master' into Inventory
This commit is contained in:
@@ -8,9 +8,14 @@ SET (SRCS
|
||||
Bindings.cpp
|
||||
DeprecatedBindings.cpp
|
||||
LuaChunkStay.cpp
|
||||
LuaNameLookup.cpp
|
||||
LuaServerHandle.cpp
|
||||
LuaState.cpp
|
||||
LuaTCPLink.cpp
|
||||
LuaUDPEndpoint.cpp
|
||||
LuaWindow.cpp
|
||||
ManualBindings.cpp
|
||||
ManualBindings_Network.cpp
|
||||
ManualBindings_RankManager.cpp
|
||||
Plugin.cpp
|
||||
PluginLua.cpp
|
||||
@@ -23,7 +28,11 @@ SET (HDRS
|
||||
DeprecatedBindings.h
|
||||
LuaChunkStay.h
|
||||
LuaFunctions.h
|
||||
LuaNameLookup.h
|
||||
LuaServerHandle.h
|
||||
LuaState.h
|
||||
LuaTCPLink.h
|
||||
LuaUDPEndpoint.h
|
||||
LuaWindow.h
|
||||
ManualBindings.h
|
||||
Plugin.h
|
||||
|
||||
88
src/Bindings/LuaNameLookup.cpp
Normal file
88
src/Bindings/LuaNameLookup.cpp
Normal file
@@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
46
src/Bindings/LuaNameLookup.h
Normal file
46
src/Bindings/LuaNameLookup.h
Normal file
@@ -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 query 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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
209
src/Bindings/LuaServerHandle.cpp
Normal file
209
src/Bindings/LuaServerHandle.cpp
Normal file
@@ -0,0 +1,209 @@
|
||||
|
||||
// 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)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cLuaServerHandle::~cLuaServerHandle()
|
||||
{
|
||||
// If the server handle is still open, close it explicitly:
|
||||
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)
|
||||
{
|
||||
// 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<cLuaTCPLink>(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<cLuaTCPLink *>(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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
89
src/Bindings/LuaServerHandle.h
Normal file
89
src/Bindings/LuaServerHandle.h
Normal file
@@ -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<cLuaTCPLink> cLuaTCPLinkPtr;
|
||||
typedef std::vector<cLuaTCPLinkPtr> cLuaTCPLinkPtrs;
|
||||
class cLuaServerHandle;
|
||||
typedef SharedPtr<cLuaServerHandle> 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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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());
|
||||
@@ -656,6 +668,42 @@ 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());
|
||||
|
||||
tolua_pushusertype(m_LuaState, a_TCPLink, "cTCPLink");
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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());
|
||||
@@ -958,6 +1006,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
|
||||
@@ -1527,6 +1584,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)
|
||||
|
||||
@@ -59,6 +59,9 @@ class cTNTEntity;
|
||||
class cHopperEntity;
|
||||
class cBlockEntity;
|
||||
class cBoundingBox;
|
||||
class cLuaTCPLink;
|
||||
class cLuaServerHandle;
|
||||
class cLuaUDPEndpoint;
|
||||
|
||||
typedef cBoundingBox * pBoundingBox;
|
||||
typedef cWorld * pWorld;
|
||||
@@ -83,6 +86,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();
|
||||
|
||||
@@ -178,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);
|
||||
@@ -202,6 +211,9 @@ 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(cLuaUDPEndpoint * a_UDPEndpoint);
|
||||
void Push(cMonster * a_Monster);
|
||||
void Push(cPickup * a_Pickup);
|
||||
void Push(cPlayer * a_Player);
|
||||
@@ -240,6 +252,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.
|
||||
|
||||
610
src/Bindings/LuaTCPLink.cpp
Normal file
610
src/Bindings/LuaTCPLink.cpp
Normal file
@@ -0,0 +1,610 @@
|
||||
|
||||
// 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"
|
||||
#include "LuaServerHandle.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
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 and shut it down:
|
||||
cTCPLinkPtr Link = m_Link;
|
||||
if (Link != nullptr)
|
||||
{
|
||||
if (m_SslContext != nullptr)
|
||||
{
|
||||
m_SslContext->NotifyClose();
|
||||
m_SslContext->ResetSelf();
|
||||
m_SslContext.reset();
|
||||
}
|
||||
Link->Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cLuaTCPLink::Close(void)
|
||||
{
|
||||
// If the link is still open, close it:
|
||||
cTCPLinkPtr Link = m_Link;
|
||||
if (Link != nullptr)
|
||||
{
|
||||
if (m_SslContext != nullptr)
|
||||
{
|
||||
m_SslContext->NotifyClose();
|
||||
m_SslContext->ResetSelf();
|
||||
m_SslContext.reset();
|
||||
}
|
||||
Link->Close();
|
||||
}
|
||||
|
||||
Terminated();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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:
|
||||
if (m_Callbacks.IsValid())
|
||||
{
|
||||
m_Callbacks.UnRef();
|
||||
}
|
||||
|
||||
// If the managing server is still alive, let it know we're terminating:
|
||||
auto Server = m_Server.lock();
|
||||
if (Server != nullptr)
|
||||
{
|
||||
Server->RemoveLink(this);
|
||||
}
|
||||
|
||||
// If the link is still open, close it:
|
||||
{
|
||||
cTCPLinkPtr Link = m_Link;
|
||||
if (Link != nullptr)
|
||||
{
|
||||
Link->Close();
|
||||
m_Link.reset();
|
||||
}
|
||||
}
|
||||
|
||||
// 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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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()
|
||||
);
|
||||
}
|
||||
|
||||
Terminated();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 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)))
|
||||
{
|
||||
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());
|
||||
}
|
||||
|
||||
Terminated();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
181
src/Bindings/LuaTCPLink.h
Normal file
181
src/Bindings/LuaTCPLink.h
Normal file
@@ -0,0 +1,181 @@
|
||||
|
||||
// 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 "PluginLua.h"
|
||||
#include "../PolarSSL++/SslContext.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// fwd:
|
||||
class cLuaServerHandle;
|
||||
typedef WeakPtr<cLuaServerHandle> cLuaServerHandleWPtr;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cLuaTCPLink:
|
||||
public cNetwork::cConnectCallbacks,
|
||||
public cTCPLink::cCallbacks
|
||||
{
|
||||
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);
|
||||
|
||||
/** 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);
|
||||
|
||||
/** 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;
|
||||
|
||||
/** 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;
|
||||
|
||||
/** 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;
|
||||
|
||||
// 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
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
221
src/Bindings/LuaUDPEndpoint.cpp
Normal file
221
src/Bindings/LuaUDPEndpoint.cpp
Normal file
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
86
src/Bindings/LuaUDPEndpoint.h
Normal file
86
src/Bindings/LuaUDPEndpoint.h
Normal file
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
@@ -165,6 +309,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 +336,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 +354,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 +372,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;
|
||||
}
|
||||
@@ -256,7 +432,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);
|
||||
@@ -2171,11 +2347,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 +2366,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,6 +3653,14 @@ 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);
|
||||
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, "StringSplitAndTrim", tolua_StringSplitAndTrim);
|
||||
@@ -3397,6 +3671,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);
|
||||
@@ -3553,9 +3828,23 @@ 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);
|
||||
|
||||
tolua_endmodule(tolua_S);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
942
src/Bindings/ManualBindings_Network.cpp
Normal file
942
src/Bindings/ManualBindings_Network.cpp
Normal file
@@ -0,0 +1,942 @@
|
||||
|
||||
// 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"
|
||||
#include "LuaNameLookup.h"
|
||||
#include "LuaServerHandle.h"
|
||||
#include "LuaUDPEndpoint.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<cLuaTCPLink>(*Plugin, 4);
|
||||
|
||||
// Try to connect:
|
||||
bool res = cNetwork::Connect(Host, static_cast<UInt16>(Port), Link, Link);
|
||||
S.Push(res);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** 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::HostnameToIP */
|
||||
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<cLuaNameLookup>(Host, *Plugin, 3));
|
||||
S.Push(res);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Binds cNetwork::IPToHostname */
|
||||
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<cLuaNameLookup>(Host, *Plugin, 3));
|
||||
S.Push(res);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Binds cNetwork::Listen */
|
||||
static int tolua_cNetwork_Listen(lua_State * L)
|
||||
{
|
||||
// Function signature:
|
||||
// cNetwork:Listen(Port, Callbacks) -> cServerHandle
|
||||
|
||||
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<UInt16>(Port);
|
||||
|
||||
// Create the LuaTCPLink glue class:
|
||||
auto Srv = std::make_shared<cLuaServerHandle>(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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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::Close */
|
||||
static int tolua_cTCPLink_Close(lua_State * L)
|
||||
{
|
||||
// Function signature:
|
||||
// LinkInstance:Close()
|
||||
|
||||
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:Close(): invalid link object. Stack trace:");
|
||||
S.LogStackTrace();
|
||||
return 0;
|
||||
}
|
||||
Link = *static_cast<cLuaTCPLink **>(lua_touserdata(L, 1));
|
||||
|
||||
// CLose the link:
|
||||
Link->Close();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Binds cLuaTCPLink::GetLocalIP */
|
||||
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<cLuaTCPLink **>(lua_touserdata(L, 1));
|
||||
|
||||
// Get the IP:
|
||||
S.Push(Link->GetLocalIP());
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Binds cLuaTCPLink::GetLocalPort */
|
||||
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<cLuaTCPLink **>(lua_touserdata(L, 1));
|
||||
|
||||
// Get the Port:
|
||||
S.Push(Link->GetLocalPort());
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Binds cLuaTCPLink::GetRemoteIP */
|
||||
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<cLuaTCPLink **>(lua_touserdata(L, 1));
|
||||
|
||||
// Get the IP:
|
||||
S.Push(Link->GetRemoteIP());
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Binds cLuaTCPLink::GetRemotePort */
|
||||
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<cLuaTCPLink **>(lua_touserdata(L, 1));
|
||||
|
||||
// Get the Port:
|
||||
S.Push(Link->GetRemotePort());
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// cUDPEndpoint bindings (routed through cLuaUDPEndpoint):
|
||||
|
||||
/** Called when Lua destroys the object instance.
|
||||
Close the endpoint and let it deallocate on its own (it's in a SharedPtr). */
|
||||
static int tolua_collect_cUDPEndpoint(lua_State * L)
|
||||
{
|
||||
LOGD("Lua: Collecting cUDPEndpoint");
|
||||
cLuaUDPEndpoint * Endpoint = static_cast<cLuaUDPEndpoint *>(tolua_tousertype(L, 1, nullptr));
|
||||
Endpoint->Release();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Binds cLuaUDPEndpoint::Close */
|
||||
static int tolua_cUDPEndpoint_Close(lua_State * L)
|
||||
{
|
||||
// Function signature:
|
||||
// EndpointInstance:Close()
|
||||
|
||||
cLuaState S(L);
|
||||
if (
|
||||
!S.CheckParamUserType(1, "cUDPEndpoint") ||
|
||||
!S.CheckParamEnd(2)
|
||||
)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Get the endpoint:
|
||||
if (lua_isnil(L, 1))
|
||||
{
|
||||
LOGWARNING("cUDPEndpoint:Close(): invalid endpoint object. Stack trace:");
|
||||
S.LogStackTrace();
|
||||
return 0;
|
||||
}
|
||||
auto Endpoint = *static_cast<cLuaUDPEndpoint **>(lua_touserdata(L, 1));
|
||||
|
||||
// Close it:
|
||||
Endpoint->Close();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Binds cLuaUDPEndpoint::EnableBroadcasts */
|
||||
static int tolua_cUDPEndpoint_EnableBroadcasts(lua_State * L)
|
||||
{
|
||||
// Function signature:
|
||||
// EndpointInstance:EnableBroadcasts()
|
||||
|
||||
cLuaState S(L);
|
||||
if (
|
||||
!S.CheckParamUserType(1, "cUDPEndpoint") ||
|
||||
!S.CheckParamEnd(2)
|
||||
)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Get the endpoint:
|
||||
if (lua_isnil(L, 1))
|
||||
{
|
||||
LOGWARNING("cUDPEndpoint:EnableBroadcasts(): invalid endpoint object. Stack trace:");
|
||||
S.LogStackTrace();
|
||||
return 0;
|
||||
}
|
||||
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(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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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);
|
||||
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, "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");
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
127
src/BiomeDef.cpp
127
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 0;
|
||||
}
|
||||
|
||||
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 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
139
src/Chunk.cpp
139
src/Chunk.cpp
@@ -776,10 +776,22 @@ void cChunk::BroadcastPendingBlockChanges(void)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (cClientHandleList::iterator itr = m_LoadedByClient.begin(), end = m_LoadedByClient.end(); itr != end; ++itr)
|
||||
|
||||
if (m_PendingSendBlocks.size() >= 10240)
|
||||
{
|
||||
(*itr)->SendBlockChanges(m_PosX, m_PosZ, m_PendingSendBlocks);
|
||||
// Resend the full chunk
|
||||
for (cClientHandleList::iterator itr = m_LoadedByClient.begin(), end = m_LoadedByClient.end(); itr != end; ++itr)
|
||||
{
|
||||
m_World->ForceSendChunkTo(m_PosX, m_PosZ, cChunkSender::E_CHUNK_PRIORITY_MEDIUM, (*itr));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Only send block changes
|
||||
for (cClientHandleList::iterator itr = m_LoadedByClient.begin(), end = m_LoadedByClient.end(); itr != end; ++itr)
|
||||
{
|
||||
(*itr)->SendBlockChanges(m_PosX, m_PosZ, m_PendingSendBlocks);
|
||||
}
|
||||
}
|
||||
m_PendingSendBlocks.clear();
|
||||
}
|
||||
@@ -874,80 +886,71 @@ void cChunk::ApplyWeatherToTop()
|
||||
|
||||
int X = m_World->GetTickRandomNumber(15);
|
||||
int Z = m_World->GetTickRandomNumber(15);
|
||||
switch (GetBiomeAt(X, Z))
|
||||
|
||||
// TODO: Check light levels, don't snow over when the BlockLight is higher than (7?)
|
||||
int Height = GetHeight(X, Z);
|
||||
|
||||
if (GetSnowStartHeight(GetBiomeAt(X, Z)) > Height)
|
||||
{
|
||||
case biTaiga:
|
||||
case biFrozenOcean:
|
||||
case biFrozenRiver:
|
||||
case biIcePlains:
|
||||
case biIceMountains:
|
||||
case biTaigaHills:
|
||||
return;
|
||||
}
|
||||
|
||||
BLOCKTYPE TopBlock = GetBlock(X, Height, Z);
|
||||
NIBBLETYPE TopMeta = GetMeta (X, Height, Z);
|
||||
if (m_World->IsDeepSnowEnabled() && (TopBlock == E_BLOCK_SNOW))
|
||||
{
|
||||
int MaxSize = 7;
|
||||
BLOCKTYPE BlockType[4];
|
||||
NIBBLETYPE BlockMeta[4];
|
||||
UnboundedRelGetBlock(X - 1, Height, Z, BlockType[0], BlockMeta[0]);
|
||||
UnboundedRelGetBlock(X + 1, Height, Z, BlockType[1], BlockMeta[1]);
|
||||
UnboundedRelGetBlock(X, Height, Z - 1, BlockType[2], BlockMeta[2]);
|
||||
UnboundedRelGetBlock(X, Height, Z + 1, BlockType[3], BlockMeta[3]);
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
// TODO: Check light levels, don't snow over when the BlockLight is higher than (7?)
|
||||
int Height = GetHeight(X, Z);
|
||||
BLOCKTYPE TopBlock = GetBlock(X, Height, Z);
|
||||
NIBBLETYPE TopMeta = GetMeta (X, Height, Z);
|
||||
if (m_World->IsDeepSnowEnabled() && (TopBlock == E_BLOCK_SNOW))
|
||||
switch (BlockType[i])
|
||||
{
|
||||
int MaxSize = 7;
|
||||
BLOCKTYPE BlockType[4];
|
||||
NIBBLETYPE BlockMeta[4];
|
||||
UnboundedRelGetBlock(X - 1, Height, Z, BlockType[0], BlockMeta[0]);
|
||||
UnboundedRelGetBlock(X + 1, Height, Z, BlockType[1], BlockMeta[1]);
|
||||
UnboundedRelGetBlock(X, Height, Z - 1, BlockType[2], BlockMeta[2]);
|
||||
UnboundedRelGetBlock(X, Height, Z + 1, BlockType[3], BlockMeta[3]);
|
||||
for (int i = 0; i < 4; i++)
|
||||
case E_BLOCK_AIR:
|
||||
{
|
||||
switch (BlockType[i])
|
||||
{
|
||||
case E_BLOCK_AIR:
|
||||
{
|
||||
MaxSize = 0;
|
||||
break;
|
||||
}
|
||||
case E_BLOCK_SNOW:
|
||||
{
|
||||
MaxSize = std::min(BlockMeta[i] + 1, MaxSize);
|
||||
break;
|
||||
}
|
||||
}
|
||||
MaxSize = 0;
|
||||
break;
|
||||
}
|
||||
if (TopMeta < MaxSize)
|
||||
case E_BLOCK_SNOW:
|
||||
{
|
||||
FastSetBlock(X, Height, Z, E_BLOCK_SNOW, TopMeta + 1);
|
||||
}
|
||||
else if (TopMeta > MaxSize)
|
||||
{
|
||||
FastSetBlock(X, Height, Z, E_BLOCK_SNOW, TopMeta - 1);
|
||||
MaxSize = std::min(BlockMeta[i] + 1, MaxSize);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (cBlockInfo::IsSnowable(TopBlock) && (Height + 1 < cChunkDef::Height))
|
||||
{
|
||||
SetBlock(X, Height + 1, Z, E_BLOCK_SNOW, 0);
|
||||
}
|
||||
else if ((TopBlock == E_BLOCK_WATER) || (TopBlock == E_BLOCK_STATIONARY_WATER))
|
||||
{
|
||||
SetBlock(X, Height, Z, E_BLOCK_ICE, 0);
|
||||
}
|
||||
else if (
|
||||
(m_World->IsDeepSnowEnabled()) &&
|
||||
(
|
||||
(TopBlock == E_BLOCK_RED_ROSE) ||
|
||||
(TopBlock == E_BLOCK_YELLOW_FLOWER) ||
|
||||
(TopBlock == E_BLOCK_RED_MUSHROOM) ||
|
||||
(TopBlock == E_BLOCK_BROWN_MUSHROOM)
|
||||
)
|
||||
)
|
||||
{
|
||||
SetBlock(X, Height, Z, E_BLOCK_SNOW, 0);
|
||||
}
|
||||
break;
|
||||
} // case (snowy biomes)
|
||||
default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
} // switch (biome)
|
||||
if (TopMeta < MaxSize)
|
||||
{
|
||||
FastSetBlock(X, Height, Z, E_BLOCK_SNOW, TopMeta + 1);
|
||||
}
|
||||
else if (TopMeta > MaxSize)
|
||||
{
|
||||
FastSetBlock(X, Height, Z, E_BLOCK_SNOW, TopMeta - 1);
|
||||
}
|
||||
}
|
||||
else if (cBlockInfo::IsSnowable(TopBlock) && (Height + 1 < cChunkDef::Height))
|
||||
{
|
||||
SetBlock(X, Height + 1, Z, E_BLOCK_SNOW, 0);
|
||||
}
|
||||
else if (IsBlockWater(TopBlock) && (TopMeta == 0))
|
||||
{
|
||||
SetBlock(X, Height, Z, E_BLOCK_ICE, 0);
|
||||
}
|
||||
else if (
|
||||
(m_World->IsDeepSnowEnabled()) &&
|
||||
(
|
||||
(TopBlock == E_BLOCK_RED_ROSE) ||
|
||||
(TopBlock == E_BLOCK_YELLOW_FLOWER) ||
|
||||
(TopBlock == E_BLOCK_RED_MUSHROOM) ||
|
||||
(TopBlock == E_BLOCK_BROWN_MUSHROOM)
|
||||
)
|
||||
)
|
||||
{
|
||||
SetBlock(X, Height, Z, E_BLOCK_SNOW, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1895,9 +1895,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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1913,10 +1917,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 +1926,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 +1935,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 +1944,7 @@ void cEntity::AddSpeedY(double a_AddSpeedY)
|
||||
|
||||
void cEntity::AddSpeedZ(double a_AddSpeedZ)
|
||||
{
|
||||
m_Speed.z += a_AddSpeedZ;
|
||||
WrapSpeed();
|
||||
AddSpeed(0, 0, a_AddSpeedZ);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -443,6 +443,9 @@ public:
|
||||
|
||||
/** Set the invulnerable ticks from the entity */
|
||||
void SetInvulnerableTicks(int a_InvulnerableTicks) { m_InvulnerableTicks = a_InvulnerableTicks; }
|
||||
|
||||
/** Returns whether the entity is on ground or not */
|
||||
virtual bool IsOnGround(void) const { return m_bOnGround; }
|
||||
|
||||
// tolua_end
|
||||
|
||||
|
||||
@@ -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<int>(World->GetSpawnX()), static_cast<int>(World->GetSpawnY()), static_cast<int>(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<float>(GetPosY());
|
||||
m_LastGroundHeight = static_cast<float>(GetPosY());
|
||||
m_Stance = GetPosY() + 1.62;
|
||||
|
||||
if (m_GameMode == gmNotSet)
|
||||
@@ -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();
|
||||
@@ -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<float>(GetPosY());
|
||||
}
|
||||
|
||||
if (m_TicksUntilNextSave == 0)
|
||||
@@ -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 static_cast<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 static_cast<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 static_cast<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 static_cast<int>((1.5 * a_Level * a_Level) - (29.5 * a_Level) + 360);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
short cPlayer::GetXpLevel()
|
||||
int cPlayer::GetXpLevel()
|
||||
{
|
||||
return CalcLevelFromXp(m_CurrentXp);
|
||||
}
|
||||
@@ -351,20 +351,20 @@ 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);
|
||||
return static_cast<float>(m_CurrentXp - currentLevel_XpBase) /
|
||||
static_cast<float>(XpForLevel(1+currentLevel) - currentLevel_XpBase);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cPlayer::SetCurrentExperience(short int a_CurrentXp)
|
||||
bool cPlayer::SetCurrentExperience(int a_CurrentXp)
|
||||
{
|
||||
if (!(a_CurrentXp >= 0) || (a_CurrentXp > (std::numeric_limits<short>().max() - m_LifetimeTotalXp)))
|
||||
if (!(a_CurrentXp >= 0) || (a_CurrentXp > (std::numeric_limits<int>().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,20 @@ 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<short>().max() - m_CurrentXp))
|
||||
if (a_Xp_delta > (std::numeric_limits<int>().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<short>(m_CurrentXp, 0);
|
||||
m_CurrentXp = std::max(m_CurrentXp, 0);
|
||||
|
||||
|
||||
// Update total for score calculation
|
||||
if (a_Xp_delta > 0)
|
||||
@@ -466,7 +467,7 @@ void cPlayer::SetTouchGround(bool a_bTouchGround)
|
||||
{
|
||||
if (GetPosY() > m_LastJumpHeight)
|
||||
{
|
||||
m_LastJumpHeight = (float)GetPosY();
|
||||
m_LastJumpHeight = static_cast<float>(GetPosY());
|
||||
}
|
||||
cWorld * World = GetWorld();
|
||||
if ((GetPosY() >= 0) && (GetPosY() < cChunkDef::Height))
|
||||
@@ -483,13 +484,13 @@ void cPlayer::SetTouchGround(bool a_bTouchGround)
|
||||
(BlockType == E_BLOCK_VINES)
|
||||
)
|
||||
{
|
||||
m_LastGroundHeight = (float)GetPosY();
|
||||
m_LastGroundHeight = static_cast<float>(GetPosY());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float Dist = (float)(m_LastGroundHeight - floor(GetPosY()));
|
||||
float Dist = static_cast<float>(m_LastGroundHeight - floor(GetPosY()));
|
||||
|
||||
if (Dist >= 2.0) // At least two blocks - TODO: Use m_LastJumpHeight instead of m_LastGroundHeight above
|
||||
{
|
||||
@@ -497,12 +498,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<int>(Dist - 3.f);
|
||||
if (m_LastJumpHeight > m_LastGroundHeight)
|
||||
{
|
||||
Damage++;
|
||||
}
|
||||
m_LastJumpHeight = (float)GetPosY();
|
||||
m_LastJumpHeight = static_cast<float>(GetPosY());
|
||||
|
||||
if (Damage > 0)
|
||||
{
|
||||
@@ -510,10 +511,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<int>(GetPosY()) - 1, POSZ_TOINT, Damage /* Used as particle effect speed modifier */);
|
||||
}
|
||||
|
||||
m_LastGroundHeight = (float)GetPosY();
|
||||
m_LastGroundHeight = static_cast<float>(GetPosY());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -551,7 +552,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<double>(m_FoodLevel));
|
||||
}
|
||||
|
||||
|
||||
@@ -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 = (float)a_PosY;
|
||||
m_LastJumpHeight = (float)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<float>(a_PosY);
|
||||
m_LastJumpHeight = static_cast<float>(a_PosY);
|
||||
m_bIsTeleporting = true;
|
||||
|
||||
m_World->BroadcastTeleportEntity(*this, GetClientHandle());
|
||||
m_ClientHandle->SendPlayerMoveLook();
|
||||
m_World->BroadcastTeleportEntity(*this, GetClientHandle());
|
||||
m_ClientHandle->SendPlayerMoveLook();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1714,9 +1719,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<float>(JSON_PlayerRotation[(unsigned)0].asDouble()));
|
||||
SetPitch (static_cast<float>(JSON_PlayerRotation[(unsigned)1].asDouble()));
|
||||
SetRoll (static_cast<float>(JSON_PlayerRotation[(unsigned)2].asDouble()));
|
||||
}
|
||||
|
||||
m_Health = root.get("health", 0).asInt();
|
||||
@@ -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 = (short) root.get("xpTotal", 0).asInt();
|
||||
m_CurrentXp = (short) 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();
|
||||
|
||||
@@ -1812,18 +1817,18 @@ bool cPlayer::SaveToDisk()
|
||||
root["world"] = m_World->GetName();
|
||||
if (m_GameMode == m_World->GetGameMode())
|
||||
{
|
||||
root["gamemode"] = (int) eGameMode_NotSet;
|
||||
root["gamemode"] = static_cast<int>(eGameMode_NotSet);
|
||||
}
|
||||
else
|
||||
{
|
||||
root["gamemode"] = (int) m_GameMode;
|
||||
root["gamemode"] = static_cast<int>(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<int>(eGameMode_NotSet);
|
||||
}
|
||||
|
||||
Json::StyledWriter writer;
|
||||
@@ -1839,7 +1844,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<int>(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 +1899,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<float>(0.75 + (static_cast<float>((GetUniqueID() * 23) % 32)) / 64));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2042,17 +2047,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<double>(Value));
|
||||
}
|
||||
else if (IsSwimming())
|
||||
{
|
||||
m_Stats.AddValue(statDistSwum, Value);
|
||||
AddFoodExhaustion(0.00015 * (double)Value);
|
||||
AddFoodExhaustion(0.00015 * static_cast<double>(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<double>(Value));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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; }
|
||||
@@ -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;
|
||||
|
||||
@@ -1036,7 +1036,7 @@ protected:
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// cBioGenGrown:
|
||||
// cBioGenProtGrown:
|
||||
|
||||
class cBioGenProtGrown:
|
||||
public cBiomeGen
|
||||
|
||||
@@ -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<int>(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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -616,6 +616,11 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
|
||||
int MaxDensity = a_IniFile.GetValueSetI("Generator", "VillageMaxDensity", 80);
|
||||
m_FinishGens.push_back(std::make_shared<cVillageGen>(Seed, GridSize, MaxOffset, MaxDepth, MaxSize, MinDensity, MaxDensity, m_BiomeGen, m_CompositedHeightCache));
|
||||
}
|
||||
else if (NoCaseCompare(*itr, "Vines") == 0)
|
||||
{
|
||||
int Level = a_IniFile.GetValueSetI("Generator", "VinesLevel", 40);
|
||||
m_FinishGens.push_back(std::make_shared<cFinishGenVines>(Seed, Level));
|
||||
}
|
||||
else if (NoCaseCompare(*itr, "WaterLakes") == 0)
|
||||
{
|
||||
int Probability = a_IniFile.GetValueSetI("Generator", "WaterLakesProbability", 25);
|
||||
|
||||
@@ -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.IntNoise2DInt(xx, zz) % 101) < 50)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int Height = a_ChunkDesc.GetHeight(x, z);
|
||||
for (int y = Height; y > m_Level; y--)
|
||||
{
|
||||
if (a_ChunkDesc.GetBlockType(x, y, z) != E_BLOCK_AIR)
|
||||
{
|
||||
// Can't place vines in non-air blocks
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((m_Noise.IntNoise3DInt(xx, y, zz) % 101) < 50)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<NIBBLETYPE> 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:
|
||||
|
||||
@@ -470,30 +564,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
|
||||
}
|
||||
|
||||
@@ -511,34 +597,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
|
||||
}
|
||||
|
||||
|
||||
@@ -118,6 +118,29 @@ protected:
|
||||
|
||||
|
||||
|
||||
class cFinishGenVines :
|
||||
public cFinishGen
|
||||
{
|
||||
public:
|
||||
cFinishGenVines(int a_Seed, int a_Level) :
|
||||
m_Noise(a_Seed),
|
||||
m_Level(a_Level)
|
||||
{
|
||||
}
|
||||
|
||||
bool IsJungleVariant(EMCSBiome a_Biome);
|
||||
|
||||
protected:
|
||||
cNoise m_Noise;
|
||||
int m_Level;
|
||||
|
||||
virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cFinishGenSoulsandRims :
|
||||
public cFinishGen
|
||||
{
|
||||
|
||||
@@ -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<cProtIntGenWeightAvg<16, 1, 0>>(
|
||||
std::make_shared<cProtIntGenSmooth> (a_Seed + 1,
|
||||
std::make_shared<cProtIntGenZoom> (a_Seed + 2,
|
||||
std::make_shared<cProtIntGenSmooth> (a_Seed + 3,
|
||||
std::make_shared<cProtIntGenZoom> (a_Seed + 4,
|
||||
std::make_shared<cProtIntGenAddRnd> (a_Seed + 5, 1,
|
||||
std::make_shared<cProtIntGenSmooth> (a_Seed + 6,
|
||||
std::make_shared<cProtIntGenZoom> (a_Seed + 7,
|
||||
std::make_shared<cProtIntGenRndBetween> (a_Seed + 8, 60,
|
||||
std::make_shared<cProtIntGenAddRnd> (a_Seed + 9, 1,
|
||||
std::make_shared<cProtIntGenSmooth> (a_Seed + 1,
|
||||
std::make_shared<cProtIntGenZoom> (a_Seed + 2,
|
||||
std::make_shared<cProtIntGenRndBetween> (a_Seed + 3, 60,
|
||||
std::make_shared<cProtIntGenSmooth> (a_Seed + 4,
|
||||
std::make_shared<cProtIntGenZoom> (a_Seed + 5,
|
||||
std::make_shared<cProtIntGenRndBetween> (a_Seed + 6, 60,
|
||||
std::make_shared<cProtIntGenRndChoice> (a_Seed + 7, 10, 50, 50,
|
||||
std::make_shared<cProtIntGenSmooth> (a_Seed + 8,
|
||||
std::make_shared<cProtIntGenZoom> (a_Seed + 9,
|
||||
std::make_shared<cProtIntGenRndChoice> (a_Seed + 1, 10, 50, 50,
|
||||
std::make_shared<cProtIntGenAddRnd> (a_Seed + 2, 2,
|
||||
std::make_shared<cProtIntGenZoom> (a_Seed + 3,
|
||||
std::make_shared<cProtIntGenZoom> (a_Seed + 4,
|
||||
std::make_shared<cProtIntGenChoice> (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 (size_t i = 0; i < ARRAYCOUNT(heights); i++)
|
||||
{
|
||||
a_HeightMap[i] = static_cast<HEIGHTTYPE>(std::max(std::min(60 + heights[i], cChunkDef::Height - 60), 40));
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
int m_Seed;
|
||||
|
||||
SharedPtr<cProtIntGen> 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<cHeiGenSteppy>(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
|
||||
|
||||
@@ -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 <int WeightCenter, int WeightCardinal, int WeightDiagonal>
|
||||
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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -379,8 +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
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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))
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -106,7 +119,3 @@ public:
|
||||
return true;
|
||||
}
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -110,7 +110,8 @@ eMonsterType cMobSpawner::ChooseMobType(EMCSBiome a_Biome)
|
||||
if (allowedMobsSize > 0)
|
||||
{
|
||||
std::set<eMonsterType>::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++)
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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<sockaddr_in *>(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<sockaddr_in6 *>(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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -127,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<cUDPEndpoint> cUDPEndpointPtr;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cNetwork
|
||||
{
|
||||
public:
|
||||
@@ -180,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;
|
||||
@@ -239,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);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -125,6 +125,7 @@ bool cServerHandleImpl::Listen(UInt16 a_Port)
|
||||
bool NeedsTwoSockets = false;
|
||||
int err;
|
||||
evutil_socket_t MainSock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
|
||||
|
||||
if (!IsValidSocket(MainSock))
|
||||
{
|
||||
// Failed to create IPv6 socket, create an IPv4 one instead:
|
||||
@@ -138,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));
|
||||
@@ -164,6 +175,16 @@ bool cServerHandleImpl::Listen(UInt16 a_Port)
|
||||
setsockopt(MainSock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<const char *>(&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));
|
||||
@@ -193,6 +214,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 +223,7 @@ 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);
|
||||
|
||||
if (!IsValidSocket(SecondSock))
|
||||
{
|
||||
err = EVUTIL_SOCKET_ERROR();
|
||||
@@ -208,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(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.",
|
||||
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)
|
||||
{
|
||||
@@ -233,7 +266,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
|
||||
}
|
||||
|
||||
@@ -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<cTCPLinkImpl *>(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);
|
||||
@@ -221,6 +254,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 +263,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:
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
|
||||
|
||||
608
src/OSSupport/UDPEndpointImpl.cpp
Normal file
608
src/OSSupport/UDPEndpointImpl.cpp
Normal file
@@ -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<Byte>((a_SrcAddr.sin_addr.s_addr >> 0) & 0xff);
|
||||
a_DstAddr.sin6_addr.s6_addr[13] = static_cast<Byte>((a_SrcAddr.sin_addr.s_addr >> 8) & 0xff);
|
||||
a_DstAddr.sin6_addr.s6_addr[14] = static_cast<Byte>((a_SrcAddr.sin_addr.s_addr >> 16) & 0xff);
|
||||
a_DstAddr.sin6_addr.s6_addr[15] = static_cast<Byte>((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<socklen_t>(m_Data.size()), 0, reinterpret_cast<const sockaddr *>(&m_AddrIPv6), static_cast<socklen_t>(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<socklen_t>(m_Data.size()), 0, reinterpret_cast<const sockaddr *>(&m_AddrIPv4), static_cast<socklen_t>(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<socklen_t>(m_Data.size()), 0, reinterpret_cast<const sockaddr *>(&m_AddrIPv6), static_cast<socklen_t>(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<socklen_t>(m_Data.size()), 0, reinterpret_cast<const sockaddr *>(&m_AddrIPv4), static_cast<socklen_t>(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<int>(sizeof(sa));
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
if (evutil_parse_sockaddr_port(a_Host.c_str(), reinterpret_cast<sockaddr *>(&sa), &salen) != 0)
|
||||
{
|
||||
// a_Host is a hostname, we need to do a lookup first:
|
||||
auto queue = std::make_shared<cUDPSendAfterLookup>(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<sockaddr_in *>(&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<int>(sendto(m_SecondarySock, a_Payload.data(), static_cast<socklen_t>(a_Payload.size()), 0, reinterpret_cast<const sockaddr *>(&sa), static_cast<socklen_t>(salen)));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Need to convert IPv4 to IPv6 address before sending:
|
||||
sockaddr_in6 IPv6;
|
||||
ConvertIPv4ToMappedIPv6(*reinterpret_cast<sockaddr_in *>(&sa), IPv6);
|
||||
NumSent = static_cast<int>(sendto(m_MainSock, a_Payload.data(), static_cast<socklen_t>(a_Payload.size()), 0, reinterpret_cast<const sockaddr *>(&IPv6), static_cast<socklen_t>(sizeof(IPv6))));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NumSent = static_cast<int>(sendto(m_MainSock, a_Payload.data(), static_cast<socklen_t>(a_Payload.size()), 0, reinterpret_cast<const sockaddr *>(&sa), static_cast<socklen_t>(salen)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case AF_INET6:
|
||||
{
|
||||
reinterpret_cast<sockaddr_in6 *>(&sa)->sin6_port = htons(a_Port);
|
||||
NumSent = static_cast<int>(sendto(m_MainSock, a_Payload.data(), static_cast<socklen_t>(a_Payload.size()), 0, reinterpret_cast<const sockaddr *>(&sa), static_cast<socklen_t>(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<const char *>(&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<const char *>(&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<const sockaddr *>(&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<const char *>(&Zero), sizeof(Zero));
|
||||
err = EVUTIL_SOCKET_ERROR();
|
||||
NeedsTwoSockets = ((res == SOCKET_ERROR) && (err == WSAENOPROTOOPT));
|
||||
#else
|
||||
setsockopt(m_MainSock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<const char *>(&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<const sockaddr *>(&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<socklen_t>(sizeof(name));
|
||||
getsockname(m_MainSock, reinterpret_cast<sockaddr *>(&name), &namelen);
|
||||
switch (name.ss_family)
|
||||
{
|
||||
case AF_INET:
|
||||
{
|
||||
sockaddr_in * sin = reinterpret_cast<sockaddr_in *>(&name);
|
||||
m_Port = ntohs(sin->sin_port);
|
||||
break;
|
||||
}
|
||||
case AF_INET6:
|
||||
{
|
||||
sockaddr_in6 * sin6 = reinterpret_cast<sockaddr_in6 *>(&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<const sockaddr *>(&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<cUDPEndpointImpl *>(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];
|
||||
socklen_t buflen = static_cast<socklen_t>(sizeof(buf));
|
||||
sockaddr_storage sa;
|
||||
socklen_t salen = static_cast<socklen_t>(sizeof(sa));
|
||||
auto len = recvfrom(a_Socket, buf, buflen, 0, reinterpret_cast<sockaddr *>(&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<sockaddr_in *>(&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<sockaddr_in6 *>(&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, static_cast<size_t>(len), RemoteHost, RemotePort);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// cNetwork API:
|
||||
|
||||
cUDPEndpointPtr cNetwork::CreateUDPEndpoint(UInt16 a_Port, cUDPEndpoint::cCallbacks & a_Callbacks)
|
||||
{
|
||||
return std::make_shared<cUDPEndpointImpl>(a_Port, a_Callbacks);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
81
src/OSSupport/UDPEndpointImpl.h
Normal file
81
src/OSSupport/UDPEndpointImpl.h
Normal file
@@ -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 <event2/event.h>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// fwd:
|
||||
class cUDPEndpointImpl;
|
||||
typedef SharedPtr<cUDPEndpointImpl> 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);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<cCtrDrbgContext> &
|
||||
// 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(2);
|
||||
|
||||
ssl_set_verify(&m_Ssl, &SSLVerifyCert, this);
|
||||
//*/
|
||||
|
||||
|
||||
@@ -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<Byte>((it->GetType() << 4) | static_cast<Byte>(it->GetRot() & 0xf)));
|
||||
Pkt.WriteByte(static_cast<Byte>(it->GetPixelX()));
|
||||
Pkt.WriteByte(static_cast<Byte>(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<UInt32>(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);
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
@@ -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<UInt32>(ClientHandle->GetPing()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -947,7 +951,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 +980,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 +1309,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());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1007,21 +1007,28 @@ cBlockEntity * cWSSAnvil::LoadFlowerPotFromNBT(const cParsedNBT & a_NBT, int a_T
|
||||
}
|
||||
|
||||
std::unique_ptr<cFlowerPotEntity> 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();
|
||||
}
|
||||
|
||||
@@ -3136,8 +3143,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
|
||||
|
||||
Reference in New Issue
Block a user