#include "Core.h" #include "Console.h" #include "Export.h" #include "PluginManager.h" #include "DataDefs.h" #include #include #include #include #include #include #include "MiscUtils.h" #include "LuaTools.h" #include "DataFuncs.h" #include //todo convert errors to lua-errors and co. Then remove this using namespace DFHack; using namespace df::enums; struct server { CPassiveSocket *socket; std::map clients; int last_client_id; void close(); }; std::map servers; typedef std::map clients_map; clients_map clients; //free clients, i.e. non-server spawned clients DFHACK_PLUGIN("luasocket"); 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 get_client(int server_id,int client_id) { std::map* 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; if (err == CSimpleSocket::SocketEwouldblock && skip_timeout) return; throw std::runtime_error(CSimpleSocket::DescribeError(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(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* 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(CSimpleSocket::DescribeError(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(sock->DescribeError()); } 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* 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(size_t(sock->Send((const uint8_t*)data.c_str(),data.size()))!=data.size()) { throw std::runtime_error(sock->DescribeError()); } } 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(CSimpleSocket::DescribeError(err)); } if(!sock->Open(ip.c_str(),port)) { CSimpleSocket::CSocketError err=sock->GetSocketError(); delete sock; throw std::runtime_error(CSimpleSocket::DescribeError(err)); } sock->SetNonblocking(); last_client_id++; clients[last_client_id]=sock; return last_client_id; } CSimpleSocket* get_socket(int server_id, int client_id) { std::map* 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) { return cur_server.socket; } 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 sock; } static void lua_socket_set_timeout(int server_id,int client_id,int32_t sec,int32_t msec) { CSimpleSocket *sock = get_socket(server_id, client_id); sock->SetConnectTimeout(sec,msec); if (!sock->SetReceiveTimeout(sec, msec) || !sock->SetSendTimeout(sec, msec)) { CSimpleSocket::CSocketError err = sock->GetSocketError(); throw std::runtime_error(CSimpleSocket::DescribeError(err)); } } static bool lua_socket_select(int server_id, int client_id, int32_t sec, int32_t msec) { CSimpleSocket *sock = get_socket(server_id, client_id); return sock->Select(sec, msec); } static void lua_socket_set_blocking(int server_id, int client_id, bool value) { CSimpleSocket *sock = get_socket(server_id, client_id); bool ok; if (value) { ok = sock->SetBlocking(); } else { ok = sock->SetNonblocking(); } if (!ok) { CSimpleSocket::CSocketError err = sock->GetSocketError(); throw std::runtime_error(CSimpleSocket::DescribeError(err)); } } static bool lua_socket_is_blocking(int server_id, int client_id) { CSimpleSocket *sock = get_socket(server_id, client_id); return !sock->IsNonblocking(); } 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_select), DFHACK_LUA_FUNCTION(lua_socket_set_blocking), DFHACK_LUA_FUNCTION(lua_socket_is_blocking), 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 &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; }