A lua interface for csockets in a spirit of luasocket
parent
0b4d32d0e6
commit
d9c50d677f
@ -0,0 +1,67 @@
|
||||
local _ENV = mkmodule('plugins.luasocket')
|
||||
local _funcs={}
|
||||
for k,v in pairs(_ENV) do
|
||||
if type(v)=="function" then
|
||||
_funcs[k]=v
|
||||
_ENV[k]=nil
|
||||
end
|
||||
end
|
||||
|
||||
local socket=defclass(socket)
|
||||
socket.ATTRS={
|
||||
server_id=-1,
|
||||
client_id=-1,
|
||||
}
|
||||
|
||||
function socket:close( )
|
||||
if self.client_id==-1 then
|
||||
_funcs.lua_server_close(self.server_id)
|
||||
else
|
||||
_funcs.lua_client_close(self.server_id,self.client_id)
|
||||
end
|
||||
end
|
||||
function socket:setTimeout( sec,msec )
|
||||
msec=msec or 0
|
||||
_funcs.lua_socket_set_timeout(self.server_id,self.client_id,sec,msec)
|
||||
end
|
||||
|
||||
local client=defclass(client,socket)
|
||||
function client:receive( pattern )
|
||||
local pattern=pattern or "*l"
|
||||
local bytes=-1
|
||||
if type(pattern)== number then
|
||||
bytes=pattern
|
||||
end
|
||||
local ret=_funcs.lua_client_receive(self.server_id,self.client_id,bytes,pattern,false)
|
||||
if ret=="" then
|
||||
return
|
||||
else
|
||||
return ret
|
||||
end
|
||||
end
|
||||
function client:send( data )
|
||||
_funcs.lua_client_send(self.server_id,self.client_id,data)
|
||||
end
|
||||
|
||||
|
||||
local server=defclass(server,socket)
|
||||
function server:accept()
|
||||
local id=_funcs.lua_server_accept(self.server_id,false)
|
||||
if id~=nil then
|
||||
return client{server_id=self.server_id,client_id=id}
|
||||
else
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
tcp={}
|
||||
function tcp:bind( address,port )
|
||||
local id=_funcs.lua_socket_bind(address,port)
|
||||
return server{server_id=id}
|
||||
end
|
||||
function tcp:connect( address,port )
|
||||
local id=_funcs.lua_socket_connect(address,port)
|
||||
return client{client_id=id}
|
||||
end
|
||||
--TODO garbage collect stuff
|
||||
return _ENV
|
@ -0,0 +1,352 @@
|
||||
#include "Core.h"
|
||||
#include "Console.h"
|
||||
#include "Export.h"
|
||||
#include "PluginManager.h"
|
||||
#include "DataDefs.h"
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <PassiveSocket.h>
|
||||
#include <ActiveSocket.h>
|
||||
#include "MiscUtils.h"
|
||||
#include "LuaTools.h"
|
||||
#include "DataFuncs.h"
|
||||
#include <stdexcept> //todo convert errors to lua-errors and co. Then remove this
|
||||
|
||||
using namespace DFHack;
|
||||
using namespace df::enums;
|
||||
struct server
|
||||
{
|
||||
CPassiveSocket *socket;
|
||||
std::map<int,CActiveSocket*> clients;
|
||||
int last_client_id;
|
||||
void close();
|
||||
};
|
||||
std::map<int,server> servers;
|
||||
typedef std::map<int,CActiveSocket*> clients_map;
|
||||
clients_map clients; //free clients, i.e. non-server spawned clients
|
||||
DFHACK_PLUGIN("luasocket");
|
||||
|
||||
// The error messages are taken from the clsocket source code
|
||||
const char * translate_socket_error(CSimpleSocket::CSocketError err) {
|
||||
switch (err) {
|
||||
case CSimpleSocket::SocketError:
|
||||
return "Generic socket error translates to error below.";
|
||||
case CSimpleSocket::SocketSuccess:
|
||||
return "No socket error.";
|
||||
case CSimpleSocket::SocketInvalidSocket:
|
||||
return "Invalid socket handle.";
|
||||
case CSimpleSocket::SocketInvalidAddress:
|
||||
return "Invalid destination address specified.";
|
||||
case CSimpleSocket::SocketInvalidPort:
|
||||
return "Invalid destination port specified.";
|
||||
case CSimpleSocket::SocketConnectionRefused:
|
||||
return "No server is listening at remote address.";
|
||||
case CSimpleSocket::SocketTimedout:
|
||||
return "Timed out while attempting operation.";
|
||||
case CSimpleSocket::SocketEwouldblock:
|
||||
return "Operation would block if socket were blocking.";
|
||||
case CSimpleSocket::SocketNotconnected:
|
||||
return "Currently not connected.";
|
||||
case CSimpleSocket::SocketEinprogress:
|
||||
return "Socket is non-blocking and the connection cannot be completed immediately";
|
||||
case CSimpleSocket::SocketInterrupted:
|
||||
return "Call was interrupted by a signal that was caught before a valid connection arrived.";
|
||||
case CSimpleSocket::SocketConnectionAborted:
|
||||
return "The connection has been aborted.";
|
||||
case CSimpleSocket::SocketProtocolError:
|
||||
return "Invalid protocol for operation.";
|
||||
case CSimpleSocket::SocketFirewallError:
|
||||
return "Firewall rules forbid connection.";
|
||||
case CSimpleSocket::SocketInvalidSocketBuffer:
|
||||
return "The receive buffer point outside the process's address space.";
|
||||
case CSimpleSocket::SocketConnectionReset:
|
||||
return "Connection was forcibly closed by the remote host.";
|
||||
case CSimpleSocket::SocketAddressInUse:
|
||||
return "Address already in use.";
|
||||
case CSimpleSocket::SocketInvalidPointer:
|
||||
return "Pointer type supplied as argument is invalid.";
|
||||
case CSimpleSocket::SocketEunknown:
|
||||
return "Unknown error please report to mark@carrierlabs.com";
|
||||
default:
|
||||
return "No such CSimpleSocket error";
|
||||
}
|
||||
}
|
||||
void server::close()
|
||||
{
|
||||
for(auto it=clients.begin();it!=clients.end();it++)
|
||||
{
|
||||
CActiveSocket* sock=it->second;
|
||||
sock->Close();
|
||||
delete sock;
|
||||
}
|
||||
clients.clear();
|
||||
socket->Close();
|
||||
delete socket;
|
||||
}
|
||||
std::pair<CActiveSocket*,clients_map*> get_client(int server_id,int client_id)
|
||||
{
|
||||
std::map<int,CActiveSocket*>* target=&clients;
|
||||
if(server_id>0)
|
||||
{
|
||||
if(servers.count(server_id)==0)
|
||||
{
|
||||
throw std::runtime_error("Server with this id does not exist");
|
||||
}
|
||||
server &cur_server=servers[server_id];
|
||||
target=&cur_server.clients;
|
||||
}
|
||||
|
||||
if(target->count(client_id)==0)
|
||||
{
|
||||
throw std::runtime_error("Client does with this id not exist");
|
||||
}
|
||||
CActiveSocket *sock=(*target)[client_id];
|
||||
return std::make_pair(sock,target);
|
||||
}
|
||||
void handle_error(CSimpleSocket::CSocketError err,bool skip_timeout=true)
|
||||
{
|
||||
if(err==CSimpleSocket::SocketSuccess)
|
||||
return;
|
||||
if(err==CSimpleSocket::SocketTimedout && skip_timeout)
|
||||
return;
|
||||
throw std::runtime_error(translate_socket_error(err));
|
||||
}
|
||||
static int lua_socket_bind(std::string ip,int port)
|
||||
{
|
||||
static int server_id=0;
|
||||
CPassiveSocket* sock=new CPassiveSocket;
|
||||
if(!sock->Initialize())
|
||||
{
|
||||
CSimpleSocket::CSocketError err=sock->GetSocketError();
|
||||
delete sock;
|
||||
handle_error(err,false);
|
||||
}
|
||||
sock->SetBlocking();
|
||||
if(!sock->Listen((uint8_t*)ip.c_str(),port))
|
||||
{
|
||||
handle_error(sock->GetSocketError(),false);
|
||||
}
|
||||
server_id++;
|
||||
server& cur_server=servers[server_id];
|
||||
cur_server.socket=sock;
|
||||
cur_server.last_client_id=0;
|
||||
return server_id;
|
||||
}
|
||||
static int lua_server_accept(int id,bool fail_on_timeout)
|
||||
{
|
||||
if(servers.count(id)==0)
|
||||
{
|
||||
throw std::runtime_error("Server not bound");
|
||||
}
|
||||
server &cur_server=servers[id];
|
||||
CActiveSocket* sock=cur_server.socket->Accept();
|
||||
if(!sock)
|
||||
{
|
||||
handle_error(sock->GetSocketError(),!fail_on_timeout);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
cur_server.last_client_id++;
|
||||
cur_server.clients[cur_server.last_client_id]=sock;
|
||||
return cur_server.last_client_id;
|
||||
}
|
||||
}
|
||||
static void lua_client_close(int server_id,int client_id)
|
||||
{
|
||||
auto info=get_client(server_id,client_id);
|
||||
|
||||
CActiveSocket *sock=info.first;
|
||||
std::map<int,CActiveSocket*>* target=info.second;
|
||||
|
||||
target->erase(client_id);
|
||||
CSimpleSocket::CSocketError err=CSimpleSocket::SocketSuccess;
|
||||
if(!sock->Close())
|
||||
err=sock->GetSocketError();
|
||||
delete sock;
|
||||
if(err!=CSimpleSocket::SocketSuccess)
|
||||
{
|
||||
throw std::runtime_error(translate_socket_error(err));
|
||||
}
|
||||
}
|
||||
static void lua_server_close(int server_id)
|
||||
{
|
||||
if(servers.count(server_id)==0)
|
||||
{
|
||||
throw std::runtime_error("Server with this id does not exist");
|
||||
}
|
||||
server &cur_server=servers[server_id];
|
||||
try{
|
||||
cur_server.close();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
servers.erase(server_id);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
static std::string lua_client_receive(int server_id,int client_id,int bytes,std::string pattern,bool fail_on_timeout)
|
||||
{
|
||||
auto info=get_client(server_id,client_id);
|
||||
CActiveSocket *sock=info.first;
|
||||
if(bytes>0)
|
||||
{
|
||||
if(sock->Receive(bytes)<=0)
|
||||
{
|
||||
throw std::runtime_error(translate_socket_error(sock->GetSocketError()));
|
||||
}
|
||||
return std::string((char*)sock->GetData(),bytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string ret;
|
||||
if(pattern=="*a") //??
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
int received=sock->Receive(1);
|
||||
if(received<0)
|
||||
{
|
||||
handle_error(sock->GetSocketError(),!fail_on_timeout);
|
||||
return "";//maybe return partial string?
|
||||
}
|
||||
else if(received==0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
ret+=(char)*sock->GetData();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
else if (pattern=="" || pattern=="*l")
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
|
||||
if(sock->Receive(1)<=0)
|
||||
{
|
||||
handle_error(sock->GetSocketError(),!fail_on_timeout);
|
||||
return "";//maybe return partial string?
|
||||
}
|
||||
char rec=(char)*sock->GetData();
|
||||
if(rec=='\n')
|
||||
break;
|
||||
ret+=rec;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Unsupported receive pattern");
|
||||
}
|
||||
}
|
||||
}
|
||||
static void lua_client_send(int server_id,int client_id,std::string data)
|
||||
{
|
||||
if(data.size()==0)
|
||||
return;
|
||||
std::map<int,CActiveSocket*>* target=&clients;
|
||||
if(server_id>0)
|
||||
{
|
||||
if(servers.count(server_id)==0)
|
||||
{
|
||||
throw std::runtime_error("Server with this id does not exist");
|
||||
}
|
||||
server &cur_server=servers[server_id];
|
||||
target=&cur_server.clients;
|
||||
}
|
||||
|
||||
if(target->count(client_id)==0)
|
||||
{
|
||||
throw std::runtime_error("Client does with this id not exist");
|
||||
}
|
||||
CActiveSocket *sock=(*target)[client_id];
|
||||
if(sock->Send((const uint8_t*)data.c_str(),data.size())!=data.size())
|
||||
{
|
||||
throw std::runtime_error(translate_socket_error(sock->GetSocketError()));
|
||||
}
|
||||
}
|
||||
static int lua_socket_connect(std::string ip,int port)
|
||||
{
|
||||
static int last_client_id=0;
|
||||
CActiveSocket* sock=new CActiveSocket;
|
||||
if(!sock->Initialize())
|
||||
{
|
||||
CSimpleSocket::CSocketError err=sock->GetSocketError();
|
||||
delete sock;
|
||||
throw std::runtime_error(translate_socket_error(err));
|
||||
}
|
||||
if(!sock->Open((const uint8_t*)ip.c_str(),port))
|
||||
{
|
||||
CSimpleSocket::CSocketError err=sock->GetSocketError();
|
||||
delete sock;
|
||||
throw std::runtime_error(translate_socket_error(err));
|
||||
}
|
||||
last_client_id++;
|
||||
clients[last_client_id]=sock;
|
||||
return last_client_id;
|
||||
}
|
||||
static void lua_socket_set_timeout(int server_id,int client_id,int32_t sec,int32_t msec)
|
||||
{
|
||||
std::map<int,CActiveSocket*>* target=&clients;
|
||||
if(server_id>0)
|
||||
{
|
||||
if(servers.count(server_id)==0)
|
||||
{
|
||||
throw std::runtime_error("Server with this id does not exist");
|
||||
}
|
||||
server &cur_server=servers[server_id];
|
||||
if(client_id==-1)
|
||||
{
|
||||
cur_server.socket->SetConnectTimeout(sec,msec);
|
||||
cur_server.socket->SetReceiveTimeout(sec,msec);
|
||||
cur_server.socket->SetSendTimeout(sec,msec);
|
||||
return;
|
||||
}
|
||||
target=&cur_server.clients;
|
||||
}
|
||||
|
||||
if(target->count(client_id)==0)
|
||||
{
|
||||
throw std::runtime_error("Client does with this id not exist");
|
||||
}
|
||||
CActiveSocket *sock=(*target)[client_id];
|
||||
sock->SetConnectTimeout(sec,msec);
|
||||
sock->SetReceiveTimeout(sec,msec);
|
||||
sock->SetSendTimeout(sec,msec);
|
||||
}
|
||||
DFHACK_PLUGIN_LUA_FUNCTIONS {
|
||||
DFHACK_LUA_FUNCTION(lua_socket_bind), //spawn a server
|
||||
DFHACK_LUA_FUNCTION(lua_socket_connect),//spawn a client (i.e. connection)
|
||||
DFHACK_LUA_FUNCTION(lua_socket_set_timeout),
|
||||
DFHACK_LUA_FUNCTION(lua_server_accept),
|
||||
DFHACK_LUA_FUNCTION(lua_server_close),
|
||||
DFHACK_LUA_FUNCTION(lua_client_close),
|
||||
DFHACK_LUA_FUNCTION(lua_client_send),
|
||||
DFHACK_LUA_FUNCTION(lua_client_receive),
|
||||
DFHACK_LUA_END
|
||||
};
|
||||
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
|
||||
{
|
||||
|
||||
return CR_OK;
|
||||
}
|
||||
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
|
||||
{
|
||||
for(auto it=clients.begin();it!=clients.end();it++)
|
||||
{
|
||||
CActiveSocket* sock=it->second;
|
||||
sock->Close();
|
||||
delete sock;
|
||||
}
|
||||
clients.clear();
|
||||
for(auto it=servers.begin();it!=servers.end();it++)
|
||||
{
|
||||
it->second.close();
|
||||
}
|
||||
servers.clear();
|
||||
return CR_OK;
|
||||
}
|
Loading…
Reference in New Issue