1
0
Fork 0

Exported cServerHandle and cNetwork:Listen to Lua.

Also added an example to the NetworkTest plugin.
This commit is contained in:
Mattes D 2015-01-30 21:24:02 +01:00
parent 17498a97a2
commit 014b96adb3
15 changed files with 1145 additions and 15 deletions

View File

@ -37,6 +37,32 @@ g_PluginInfo =
}, -- ParameterCombinations
}, -- client
close =
{
HelpString = "Close a listening socket",
Handler = HandleConsoleNetClose,
ParameterCombinations =
{
{
Params = "[Port]",
Help = "Closes a socket listening on the specified port [1024]",
},
}, -- ParameterCombinations
}, -- close
listen =
{
HelpString = "Creates a new listening socket on the specified port with the specified service attached to it",
Handler = HandleConsoleNetListen,
ParameterCombinations =
{
{
Params = "[Port] [Service]",
Help = "Starts listening on the specified port [1024] providing the specified service [echo]",
},
}, -- ParameterCombinations
}, -- listen
lookup =
{
HelpString = "Looks up the IP addresses corresponding to the given hostname (google.com by default)",
@ -57,6 +83,7 @@ g_PluginInfo =
},
},
}, -- lookup
}, -- Subcommands
}, -- net
},

View File

@ -7,11 +7,109 @@
function Initialize()
--- Map of all servers currently open
-- g_Servers[PortNum] = cServerHandle
local g_Servers = {}
--- List of fortune messages for the fortune server
-- A random message is chosen for each incoming connection
-- The contents are loaded from the splashes.txt file on plugin startup
local g_Fortunes =
{
"Empty splashes.txt",
}
--- Map of all services that can be run as servers
-- g_Services[ServiceName] = function() -> callbacks
local g_Services =
{
-- Echo service: each connection echoes back what has been sent to it
echo = function (a_Port)
return
{
-- A new connection has come, give it new link callbacks:
OnIncomingConnection = function (a_RemoteIP, a_RemotePort)
return
{
OnError = function (a_Link, a_ErrorCode, a_ErrorMsg)
LOG("EchoServer(" .. a_Port .. ": Connection to " .. a_Link:GetRemoteIP() .. ":" .. a_Link:GetRemotePort() .. " failed: " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")")
end,
OnReceivedData = function (a_Link, a_Data)
-- Echo the received data back to the link:
a_Link:Send(a_Data)
end,
OnRemoteClosed = function (a_Link)
end
} -- Link callbacks
end, -- OnIncomingConnection()
-- Send a welcome message to newly accepted connections:
OnAccepted = function (a_Link)
a_Link:Send("Hello, " .. a_Link:GetRemoteIP() .. ", welcome to the echo server @ MCServer-Lua\r\n")
end, -- OnAccepted()
-- There was an error listening on the port:
OnError = function (a_ErrorCode, a_ErrorMsg)
LOGINFO("EchoServer(" .. a_Port .. ": Cannot listen: " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")")
end, -- OnError()
} -- Listen callbacks
end, -- echo
fortune = function (a_Port)
return
{
-- A new connection has come, give it new link callbacks:
OnIncomingConnection = function (a_RemoteIP, a_RemotePort)
return
{
OnError = function (a_Link, a_ErrorCode, a_ErrorMsg)
LOG("FortuneServer(" .. a_Port .. ": Connection to " .. a_Link:GetRemoteIP() .. ":" .. a_Link:GetRemotePort() .. " failed: " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")")
end,
OnReceivedData = function (a_Link, a_Data)
-- Ignore any received data
end,
OnRemoteClosed = function (a_Link)
end
} -- Link callbacks
end, -- OnIncomingConnection()
-- Send a welcome message to newly accepted connections:
OnAccepted = function (a_Link)
a_Link:Send("Hello, " .. a_Link:GetRemoteIP() .. ", welcome to the fortune server @ MCServer-Lua\r\n\r\nYour fortune:\r\n")
a_Link:Send(g_Fortunes[math.random(#g_Fortunes)] .. "\r\n")
end, -- OnAccepted()
-- There was an error listening on the port:
OnError = function (a_ErrorCode, a_ErrorMsg)
LOGINFO("FortuneServer(" .. a_Port .. ": Cannot listen: " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")")
end, -- OnError()
} -- Listen callbacks
end, -- fortune
-- TODO: Other services (fortune, daytime, ...)
}
function Initialize(a_Plugin)
-- Load the splashes.txt file into g_Fortunes:
for line in io.lines(a_Plugin:GetLocalFolder() .. "/splashes.txt") do
table.insert(g_Fortunes, line)
end
-- Use the InfoReg shared library to process the Info.lua file:
dofile(cPluginManager:GetPluginsPath() .. "/InfoReg.lua")
RegisterPluginInfoCommands()
RegisterPluginInfoConsoleCommands()
-- Seed the random generator:
math.randomseed(os.time())
return true
end
@ -62,6 +160,26 @@ end
function HandleConsoleNetClose(a_Split)
-- Get the port to close:
local Port = tonumber(a_Split[3] or 1024)
if not(Port) then
return true, "Bad port number: \"" .. Port .. "\"."
end
-- Close the server, if there is one:
if not(g_Servers[Port]) then
return true, "There is no server currently listening on port " .. Port .. "."
end
g_Servers[Port]:Close()
g_Servers[Port] = nil
return true, "Port " .. Port .. " closed."
end
function HandleConsoleNetLookup(a_Split)
-- Get the name to look up:
local Addr = a_Split[3] or "google.com"
@ -103,3 +221,31 @@ end
function HandleConsoleNetListen(a_Split)
-- Get the params:
local Port = tonumber(a_Split[3] or 1024)
if not(Port) then
return true, "Invalid port: \"" .. Port .. "\"."
end
local Service = string.lower(a_Split[4] or "echo")
-- Create the callbacks specific for the service:
if (g_Services[Service] == nil) then
return true, "No such service: " .. Service
end
local Callbacks = g_Services[Service](Port)
-- Start the server:
local srv = cNetwork:Listen(Port, Callbacks)
if not(srv:IsListening()) then
-- The error message has already been printed in the Callbacks.OnError()
return true
end
g_Servers[Port] = srv
return true, Service .. " server started on port " .. Port
end

View File

@ -0,0 +1,357 @@
As seen on TV!
Awesome!
100% pure!
May contain nuts!
Better than Prey!
More polygons!
Sexy!
Limited edition!
Flashing letters!
Made by Notch!
It's here!
Best in class!
It's finished!
Kind of dragon free!
Excitement!
More than 500 sold!
One of a kind!
Heaps of hits on YouTube!
Indev!
Spiders everywhere!
Check it out!
Holy cow, man!
It's a game!
Made in Sweden!
Uses LWJGL!
Reticulating splines!
Minecraft!
Yaaay!
Singleplayer!
Keyboard compatible!
Undocumented!
Ingots!
Exploding creepers!
That's no moon!
l33t!
Create!
Survive!
Dungeon!
Exclusive!
The bee's knees!
Down with O.P.P.!
Closed source!
Classy!
Wow!
Not on steam!
Oh man!
Awesome community!
Pixels!
Teetsuuuuoooo!
Kaaneeeedaaaa!
Now with difficulty!
Enhanced!
90% bug free!
Pretty!
12 herbs and spices!
Fat free!
Absolutely no memes!
Free dental!
Ask your doctor!
Minors welcome!
Cloud computing!
Legal in Finland!
Hard to label!
Technically good!
Bringing home the bacon!
Indie!
GOTY!
Ceci n'est pas une title screen!
Euclidian!
Now in 3D!
Inspirational!
Herregud!
Complex cellular automata!
Yes, sir!
Played by cowboys!
OpenGL 2.1 (if supported)!
Thousands of colors!
Try it!
Age of Wonders is better!
Try the mushroom stew!
Sensational!
Hot tamale, hot hot tamale!
Play him off, keyboard cat!
Guaranteed!
Macroscopic!
Bring it on!
Random splash!
Call your mother!
Monster infighting!
Loved by millions!
Ultimate edition!
Freaky!
You've got a brand new key!
Water proof!
Uninflammable!
Whoa, dude!
All inclusive!
Tell your friends!
NP is not in P!
Notch <3 ez!
Music by C418!
Livestreamed!
Haunted!
Polynomial!
Terrestrial!
All is full of love!
Full of stars!
Scientific!
Cooler than Spock!
Collaborate and listen!
Never dig down!
Take frequent breaks!
Not linear!
Han shot first!
Nice to meet you!
Buckets of lava!
Ride the pig!
Larger than Earth!
sqrt(-1) love you!
Phobos anomaly!
Punching wood!
Falling off cliffs!
0% sugar!
150% hyperbole!
Synecdoche!
Let's danec!
Seecret Friday update!
Reference implementation!
Lewd with two dudes with food!
Kiss the sky!
20 GOTO 10!
Verlet intregration!
Peter Griffin!
Do not distribute!
Cogito ergo sum!
4815162342 lines of code!
A skeleton popped out!
The Work of Notch!
The sum of its parts!
BTAF used to be good!
I miss ADOM!
umop-apisdn!
OICU812!
Bring me Ray Cokes!
Finger-licking!
Thematic!
Pneumatic!
Sublime!
Octagonal!
Une baguette!
Gargamel plays it!
Rita is the new top dog!
SWM forever!
Representing Edsbyn!
Matt Damon!
Supercalifragilisticexpialidocious!
Consummate V's!
Cow Tools!
Double buffered!
Fan fiction!
Flaxkikare!
Jason! Jason! Jason!
Hotter than the sun!
Internet enabled!
Autonomous!
Engage!
Fantasy!
DRR! DRR! DRR!
Kick it root down!
Regional resources!
Woo, facepunch!
Woo, somethingawful!
Woo, /v/!
Woo, tigsource!
Woo, minecraftforum!
Woo, worldofminecraft!
Woo, reddit!
Woo, 2pp!
Google anlyticsed!
Now supports åäö!
Give us Gordon!
Tip your waiter!
Very fun!
12345 is a bad password!
Vote for net neutrality!
Lives in a pineapple under the sea!
MAP11 has two names!
Omnipotent!
Gasp!
...!
Bees, bees, bees, bees!
Jag känner en bot!
This text is hard to read if you play the game at the default resolution, but at 1080p it's fine!
Haha, LOL!
Hampsterdance!
Switches and ores!
Menger sponge!
idspispopd!
Eple (original edit)!
So fresh, so clean!
Slow acting portals!
Try the Nether!
Don't look directly at the bugs!
Oh, ok, Pigmen!
Finally with ladders!
Scary!
Play Minecraft, Watch Topgear, Get Pig!
Twittered about!
Jump up, jump up, and get down!
Joel is neat!
A riddle, wrapped in a mystery!
Huge tracts of land!
Welcome to your Doom!
Stay a while, stay forever!
Stay a while and listen!
Treatment for your rash!
"Autological" is!
Information wants to be free!
"Almost never" is an interesting concept!
Lots of truthiness!
The creeper is a spy!
Turing complete!
It's groundbreaking!
Let our battle's begin!
The sky is the limit!
Jeb has amazing hair!
Ryan also has amazing hair!
Casual gaming!
Undefeated!
Kinda like Lemmings!
Follow the train, CJ!
Leveraging synergy!
This message will never appear on the splash screen, isn't that weird?
DungeonQuest is unfair!
110813!
90210!
Check out the far lands!
Tyrion would love it!
Also try VVVVVV!
Also try Super Meat Boy!
Also try Terraria!
Also try Mount And Blade!
Also try Project Zomboid!
Also try World of Goo!
Also try Limbo!
Also try Pixeljunk Shooter!
Also try Braid!
That's super!
Bread is pain!
Read more books!
Khaaaaaaaaan!
Less addictive than TV Tropes!
More addictive than lemonade!
Bigger than a bread box!
Millions of peaches!
Fnord!
This is my true form!
Totally forgot about Dre!
Don't bother with the clones!
Pumpkinhead!
Hobo humping slobo babe!
Made by Jeb!
Has an ending!
Finally complete!
Feature packed!
Boots with the fur!
Stop, hammertime!
Testificates!
Conventional!
Homeomorphic to a 3-sphere!
Doesn't avoid double negatives!
Place ALL the blocks!
Does barrel rolls!
Meeting expectations!
PC gaming since 1873!
Ghoughpteighbteau tchoghs!
Déjà vu!
Déjà vu!
Got your nose!
Haley loves Elan!
Afraid of the big, black bat!
Doesn't use the U-word!
Child's play!
See you next Friday or so!
From the streets of Södermalm!
150 bpm for 400000 minutes!
Technologic!
Funk soul brother!
Pumpa kungen!
日本ハロー!
한국 안녕하세요!
Helo Cymru!
Cześć Polsko!
你好中国!
Привет Россия!
Γεια σου Ελλάδα!
My life for Aiur!
Lennart lennart = new Lennart();
I see your vocabulary has improved!
Who put it there?
You can't explain that!
if not ok then return end
§1C§2o§3l§4o§5r§6m§7a§8t§9i§ac
§kFUNKY LOL
SOPA means LOSER in Swedish!
Big Pointy Teeth!
Bekarton guards the gate!
Mmmph, mmph!
Don't feed avocados to parrots!
Swords for everyone!
Plz reply to my tweet!
.party()!
Take her pillow!
Put that cookie down!
Pretty scary!
I have a suggestion.
Now with extra hugs!
Now Java 6!
Woah.
HURNERJSGER?
What's up, Doc?
Now contains 32 random daily cats!
That's Numberwang!
pls rt
Do you want to join my server?
Put a little fence around it!
Throw a blanket over it!
One day, somewhere in the future, my work will be quoted!
Now with additional stuff!
Extra things!
Yay, puppies for everyone!
So sweet, like a nice bon bon!
Popping tags!
Very influential in its circle!
Now With Multiplayer!
Rise from your grave!
Warning! A huge battleship "STEVE" is approaching fast!
Blue warrior shot the food!
Run, coward! I hunger!
Flavor with no seasoning!
Strange, but not a stranger!
Tougher than diamonds, rich like cream!
Getting ready to show!
Getting ready to know!
Getting ready to drop!
Getting ready to shock!
Getting ready to freak!
Getting ready to speak!
It swings, it jives!
Cruising streets for gold!
Take an eggbeater and beat it against a skillet!
Make me a table, a funky table!
Take the elevator to the mezzanine!
Stop being reasonable, this is the Internet!
/give @a hugs 64
This is good for Realms.
Any computer is a laptop if you're brave enough!

View File

@ -9,6 +9,7 @@ SET (SRCS
DeprecatedBindings.cpp
LuaChunkStay.cpp
LuaNameLookup.cpp
LuaServerHandle.cpp
LuaState.cpp
LuaTCPLink.cpp
LuaWindow.cpp
@ -27,6 +28,7 @@ SET (HDRS
LuaChunkStay.h
LuaFunctions.h
LuaNameLookup.h
LuaServerHandle.h
LuaState.h
LuaTCPLink.h
LuaWindow.h

View File

@ -25,7 +25,7 @@ public:
cLuaNameLookup(const AString & a_Query, cPluginLua & a_Plugin, int a_CallbacksTableStackPos);
protected:
/** The plugin for which the link is created. */
/** The plugin for which the query is created. */
cPluginLua & m_Plugin;
/** The Lua table that holds the callbacks to be invoked. */

View File

@ -0,0 +1,213 @@
// LuaServerHandle.cpp
// Implements the cLuaServerHandle class wrapping the cServerHandle functionality for Lua API
#include "Globals.h"
#include "LuaServerHandle.h"
#include "LuaTCPLink.h"
#include "tolua++/include/tolua++.h"
cLuaServerHandle::cLuaServerHandle(UInt16 a_Port, cPluginLua & a_Plugin, int a_CallbacksTableStackPos):
m_Plugin(a_Plugin),
m_Callbacks(a_Plugin.GetLuaState(), a_CallbacksTableStackPos),
m_Port(a_Port)
{
LOGD("Creating LuaServerHandle at %p.", this);
}
cLuaServerHandle::~cLuaServerHandle()
{
// If the server handle is still open, close it explicitly:
LOGD("Deleting LuaServerHandle at %p.", this);
Close();
}
void cLuaServerHandle::SetServerHandle(cServerHandlePtr a_ServerHandle, cLuaServerHandlePtr a_Self)
{
ASSERT(m_ServerHandle == nullptr); // The handle can be set only once
m_ServerHandle = a_ServerHandle;
m_Self = a_Self;
}
void cLuaServerHandle::Close(void)
{
LOGD("Closing LuaServerHandle at %p.", this);
// Safely grab a copy of the server handle:
cServerHandlePtr ServerHandle = m_ServerHandle;
if (ServerHandle == nullptr)
{
return;
}
// Close the underlying server handle:
ServerHandle->Close();
// Close all connections at this server:
cLuaTCPLinkPtrs Connections;
{
cCSLock Lock(m_CSConnections);
std::swap(Connections, m_Connections);
}
for (auto & conn: Connections)
{
conn->Close();
}
Connections.clear();
// Allow the internal server handle to be deallocated:
m_ServerHandle.reset();
}
bool cLuaServerHandle::IsListening(void)
{
// Safely grab a copy of the server handle:
cServerHandlePtr ServerHandle = m_ServerHandle;
if (ServerHandle == nullptr)
{
return false;
}
// Query the underlying server handle:
return ServerHandle->IsListening();
}
void cLuaServerHandle::RemoveLink(cLuaTCPLink * a_Link)
{
cCSLock Lock(m_CSConnections);
for (auto itr = m_Connections.begin(), end = m_Connections.end(); itr != end; ++itr)
{
if (itr->get() == a_Link)
{
m_Connections.erase(itr);
break;
}
} // for itr - m_Connections[]
}
void cLuaServerHandle::Release(void)
{
// Close the server, if it isn't closed yet:
Close();
// Allow self to deallocate:
m_Self.reset();
}
cTCPLink::cCallbacksPtr cLuaServerHandle::OnIncomingConnection(const AString & a_RemoteIPAddress, UInt16 a_RemotePort)
{
// If not valid anymore, drop the connection:
if (!m_Callbacks.IsValid())
{
return nullptr;
}
// Ask the plugin for link callbacks:
cPluginLua::cOperation Op(m_Plugin);
cLuaState::cRef LinkCallbacks;
if (
!Op().Call(cLuaState::cTableRef(m_Callbacks, "OnIncomingConnection"), a_RemoteIPAddress, a_RemotePort, m_Port, cLuaState::Return, LinkCallbacks) ||
!LinkCallbacks.IsValid()
)
{
LOGINFO("cNetwork server (port %d) OnIncomingConnection callback failed in plugin %s. Dropping connection.",
m_Port, m_Plugin.GetName().c_str()
);
return nullptr;
}
// Create the link wrapper to use with the callbacks:
auto res = std::make_shared<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;
}
}

View 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;
};

View File

@ -656,6 +656,18 @@ void cLuaState::Push(cItems * a_Items)
void cLuaState::Push(cLuaServerHandle * a_ServerHandle)
{
ASSERT(IsValid());
tolua_pushusertype(m_LuaState, a_ServerHandle, "cServerHandle");
m_NumCurrentFunctionArgs += 1;
}
void cLuaState::Push(cLuaTCPLink * a_TCPLink)
{
ASSERT(IsValid());
@ -970,6 +982,15 @@ void cLuaState::GetStackValue(int a_StackPos, pWorld & a_ReturnedVal)
void cLuaState::GetStackValue(int a_StackPos, cRef & a_Ref)
{
a_Ref.RefStack(*this, a_StackPos);
}
bool cLuaState::CallFunction(int a_NumResults)
{
ASSERT (m_NumCurrentFunctionArgs >= 0); // A function must be pushed to stack first
@ -1539,6 +1560,18 @@ cLuaState::cRef::cRef(cLuaState & a_LuaState, int a_StackPos) :
cLuaState::cRef::cRef(cRef && a_FromRef):
m_LuaState(a_FromRef.m_LuaState),
m_Ref(a_FromRef.m_Ref)
{
a_FromRef.m_LuaState = nullptr;
a_FromRef.m_Ref = LUA_REFNIL;
}
cLuaState::cRef::~cRef()
{
if (m_LuaState != nullptr)

View File

@ -60,6 +60,7 @@ class cHopperEntity;
class cBlockEntity;
class cBoundingBox;
class cLuaTCPLink;
class cLuaServerHandle;
typedef cBoundingBox * pBoundingBox;
typedef cWorld * pWorld;
@ -84,6 +85,10 @@ public:
/** Creates a reference in the specified LuaState for object at the specified StackPos */
cRef(cLuaState & a_LuaState, int a_StackPos);
/** Moves the reference from the specified instance into a newly created instance.
The old instance is then "!IsValid()". */
cRef(cRef && a_FromRef);
~cRef();
@ -203,6 +208,7 @@ public:
void Push(cHopperEntity * a_Hopper);
void Push(cItem * a_Item);
void Push(cItems * a_Items);
void Push(cLuaServerHandle * a_ServerHandle);
void Push(cLuaTCPLink * a_TCPLink);
void Push(cMonster * a_Monster);
void Push(cPickup * a_Pickup);
@ -242,6 +248,9 @@ public:
/** Retrieve value at a_StackPos, if it is a valid cWorld class. If not, a_Value is unchanged */
void GetStackValue(int a_StackPos, pWorld & a_Value);
/** Store the value at a_StackPos as a reference. */
void GetStackValue(int a_StackPos, cRef & a_Ref);
/** Call the specified Lua function.
Returns true if call succeeded, false if there was an error.

View File

@ -5,6 +5,7 @@
#include "Globals.h"
#include "LuaTCPLink.h"
#include "LuaServerHandle.h"
@ -14,6 +15,47 @@ cLuaTCPLink::cLuaTCPLink(cPluginLua & a_Plugin, int a_CallbacksTableStackPos):
m_Plugin(a_Plugin),
m_Callbacks(a_Plugin.GetLuaState(), a_CallbacksTableStackPos)
{
// Warn if the callbacks aren't valid:
if (!m_Callbacks.IsValid())
{
LOGWARNING("cTCPLink in plugin %s: callbacks could not be retrieved", m_Plugin.GetName().c_str());
cPluginLua::cOperation Op(m_Plugin);
Op().LogStackTrace();
}
}
cLuaTCPLink::cLuaTCPLink(cPluginLua & a_Plugin, cLuaState::cRef && a_CallbacksTableRef, cLuaServerHandleWPtr a_ServerHandle):
m_Plugin(a_Plugin),
m_Callbacks(std::move(a_CallbacksTableRef)),
m_Server(std::move(a_ServerHandle))
{
// Warn if the callbacks aren't valid:
if (!m_Callbacks.IsValid())
{
LOGWARNING("cTCPLink in plugin %s: callbacks could not be retrieved", m_Plugin.GetName().c_str());
cPluginLua::cOperation Op(m_Plugin);
Op().LogStackTrace();
}
}
cLuaTCPLink::~cLuaTCPLink()
{
// If the link is still open, close it:
cTCPLinkPtr Link = m_Link;
if (Link != nullptr)
{
Link->Close();
}
Terminated();
}
@ -107,15 +149,14 @@ UInt16 cLuaTCPLink::GetRemotePort(void) const
void cLuaTCPLink::Shutdown(void)
{
// Safely grab a copy of the link:
// Safely grab a copy of the link and shut it down:
cTCPLinkPtr Link = m_Link;
if (Link == nullptr)
if (Link != nullptr)
{
return;
Link->Shutdown();
}
// Shutdown:
Link->Shutdown();
Terminated();
}
@ -124,17 +165,48 @@ void cLuaTCPLink::Shutdown(void)
void cLuaTCPLink::Close(void)
{
// Safely grab a copy of the link:
// If the link is still open, close it:
cTCPLinkPtr Link = m_Link;
if (Link == nullptr)
if (Link != nullptr)
{
return;
Link->Close();
}
// Close the link:
Link->Close();
Terminated();
}
void cLuaTCPLink::Terminated(void)
{
// Disable the callbacks:
if (m_Callbacks.IsValid())
{
m_Callbacks.UnRef();
}
// If the managing server is still alive, let it know we're terminating:
auto Server = m_Server.lock();
if (Server != nullptr)
{
Server->RemoveLink(this);
}
// If the link is still open, close it:
cTCPLinkPtr Link = m_Link;
if (Link != nullptr)
{
Link->Close();
m_Link.reset();
}
}
void cLuaTCPLink::OnConnected(cTCPLink & a_Link)
{
// Check if we're still valid:
@ -171,6 +243,8 @@ void cLuaTCPLink::OnError(int a_ErrorCode, const AString & a_ErrorMsg)
m_Plugin.GetName().c_str(), a_ErrorCode, a_ErrorMsg.c_str()
);
}
Terminated();
}
@ -221,7 +295,8 @@ void cLuaTCPLink::OnRemoteClosed(void)
{
LOGINFO("cTCPLink OnRemoteClosed() callback failed in plugin %s.", m_Plugin.GetName().c_str());
}
m_Link.reset();
Terminated();
}

View File

@ -16,6 +16,14 @@
// fwd:
class cLuaServerHandle;
typedef WeakPtr<cLuaServerHandle> cLuaServerHandleWPtr;
class cLuaTCPLink:
public cNetwork::cConnectCallbacks,
public cTCPLink::cCallbacks
@ -24,6 +32,11 @@ public:
/** Creates a new instance of the link, attached to the specified plugin and wrapping the callbacks that are in a table at the specified stack pos. */
cLuaTCPLink(cPluginLua & a_Plugin, int a_CallbacksTableStackPos);
/** Creates a new instance of the link, attached to the specified plugin and wrapping the callbacks that are in the specified referenced table. */
cLuaTCPLink(cPluginLua & a_Plugin, cLuaState::cRef && a_CallbacksTableRef, cLuaServerHandleWPtr a_Server);
~cLuaTCPLink();
/** Sends the data contained in the string to the remote peer.
Returns true if successful, false on immediate failure (queueing the data failed or link not available). */
bool Send(const AString & a_Data);
@ -60,6 +73,14 @@ protected:
May be nullptr. */
cTCPLinkPtr m_Link;
/** The server that is responsible for this link, if any. */
cLuaServerHandleWPtr m_Server;
/** Common code called when the link is considered as terminated.
Releases m_Link, m_Callbacks and this from m_Server, each when applicable. */
void Terminated(void);
// cNetwork::cConnectCallbacks overrides:
virtual void OnConnected(cTCPLink & a_Link) override;
virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override;

View File

@ -10,6 +10,7 @@
#include "LuaState.h"
#include "LuaTCPLink.h"
#include "LuaNameLookup.h"
#include "LuaServerHandle.h"
@ -72,6 +73,7 @@ static int tolua_cNetwork_Connect(lua_State * L)
/** Binds cNetwork::HostnameToIP */
static int tolua_cNetwork_HostnameToIP(lua_State * L)
{
// Function signature:
@ -112,6 +114,7 @@ static int tolua_cNetwork_HostnameToIP(lua_State * L)
/** Binds cNetwork::IPToHostname */
static int tolua_cNetwork_IPToHostname(lua_State * L)
{
// Function signature:
@ -152,9 +155,66 @@ static int tolua_cNetwork_IPToHostname(lua_State * L)
/** Binds cNetwork::Listen */
static int tolua_cNetwork_Listen(lua_State * L)
{
// Function signature:
// cNetwork:Listen(Port, Callbacks) -> bool
cLuaState S(L);
if (
!S.CheckParamUserTable(1, "cNetwork") ||
!S.CheckParamNumber(2) ||
!S.CheckParamTable(3) ||
!S.CheckParamEnd(4)
)
{
return 0;
}
// Get the plugin instance:
cPluginLua * Plugin = GetLuaPlugin(L);
if (Plugin == nullptr)
{
// An error message has been already printed in GetLuaPlugin()
S.Push(false);
return 1;
}
// Read the params:
int Port;
S.GetStackValues(2, Port);
if ((Port < 0) || (Port > 65535))
{
LOGWARNING("cNetwork:Listen() called with invalid port (%d), failing the request.", Port);
S.Push(false);
return 1;
}
UInt16 Port16 = static_cast<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;
}
////////////////////////////////////////////////////////////////////////////////
// cTCPLink bindings (routed through cLuaTCPLink):
/** Binds cLuaTCPLink::Send */
static int tolua_cTCPLink_Send(lua_State * L)
{
// Function signature:
@ -193,6 +253,7 @@ static int tolua_cTCPLink_Send(lua_State * L)
/** Binds cLuaTCPLink::GetLocalIP */
static int tolua_cTCPLink_GetLocalIP(lua_State * L)
{
// Function signature:
@ -226,6 +287,7 @@ static int tolua_cTCPLink_GetLocalIP(lua_State * L)
/** Binds cLuaTCPLink::GetLocalPort */
static int tolua_cTCPLink_GetLocalPort(lua_State * L)
{
// Function signature:
@ -259,6 +321,7 @@ static int tolua_cTCPLink_GetLocalPort(lua_State * L)
/** Binds cLuaTCPLink::GetRemoteIP */
static int tolua_cTCPLink_GetRemoteIP(lua_State * L)
{
// Function signature:
@ -292,6 +355,7 @@ static int tolua_cTCPLink_GetRemoteIP(lua_State * L)
/** Binds cLuaTCPLink::GetRemotePort */
static int tolua_cTCPLink_GetRemotePort(lua_State * L)
{
// Function signature:
@ -325,6 +389,90 @@ static int tolua_cTCPLink_GetRemotePort(lua_State * L)
////////////////////////////////////////////////////////////////////////////////
// cServerHandle bindings (routed through cLuaServerHandle):
/** Called when Lua destroys the object instance.
Close the server and let it deallocate on its own (it's in a SharedPtr). */
static int tolua_collect_cServerHandle(lua_State * L)
{
cLuaServerHandle * Srv = static_cast<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;
}
////////////////////////////////////////////////////////////////////////////////
// Register the bindings:
@ -335,15 +483,15 @@ void ManualBindings::BindNetwork(lua_State * tolua_S)
tolua_cclass(tolua_S, "cNetwork", "cNetwork", "", nullptr);
tolua_usertype(tolua_S, "cTCPLink");
tolua_cclass(tolua_S, "cTCPLink", "cTCPLink", "", nullptr);
tolua_usertype(tolua_S, "cServerHandle");
tolua_cclass(tolua_S, "cServerHandle", "cServerHandle", "", tolua_collect_cServerHandle);
// Fill in the functions (alpha-sorted):
tolua_beginmodule(tolua_S, "cNetwork");
tolua_function(tolua_S, "Connect", tolua_cNetwork_Connect);
tolua_function(tolua_S, "HostnameToIP", tolua_cNetwork_HostnameToIP);
tolua_function(tolua_S, "IPToHostname", tolua_cNetwork_IPToHostname);
/*
tolua_function(tolua_S, "Listen", tolua_cNetwork_Listen);
*/
tolua_endmodule(tolua_S);
tolua_beginmodule(tolua_S, "cTCPLink");
@ -353,6 +501,11 @@ void ManualBindings::BindNetwork(lua_State * tolua_S)
tolua_function(tolua_S, "GetRemoteIP", tolua_cTCPLink_GetRemoteIP);
tolua_function(tolua_S, "GetRemotePort", tolua_cTCPLink_GetRemotePort);
tolua_endmodule(tolua_S);
tolua_beginmodule(tolua_S, "cServerHandle");
tolua_function(tolua_S, "Close", tolua_cServerHandle_Close);
tolua_function(tolua_S, "IsListening", tolua_cServerHandle_IsListening);
tolua_endmodule(tolua_S);
}

View File

@ -381,6 +381,7 @@ void inline LOG(const char * a_Format, ...)
// Unified shared ptr from before C++11. Also no silly undercores.
#define SharedPtr std::shared_ptr
#define WeakPtr std::weak_ptr

View File

@ -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;

View File

@ -119,6 +119,7 @@ void DoTest(void)
LOG("Server terminating.");
Server->Close();
ASSERT(!Server->IsListening());
Server.reset();
LOGD("Server has been closed.");
}