diff --git a/luprex/core/Makefile b/luprex/core/Makefile index 2b136f48..eba9b281 100644 --- a/luprex/core/Makefile +++ b/luprex/core/Makefile @@ -1,3 +1,10 @@ +OS=unspecified + +ifeq ($(OS),mingw) + EXE=main.exe +else + EXE=main +endif CXX=g++ -std=c++17 -Wall -g -Iinc -Icpp @@ -6,7 +13,6 @@ CPP_FILES=\ cpp/spookyv2.cpp\ cpp/debugcollector.cpp\ cpp/drivenengine.cpp\ - cpp/driver-mingw.cpp\ cpp/util.cpp\ cpp/luastack.cpp\ cpp/traceback.cpp\ @@ -33,14 +39,16 @@ CPP_FILES=\ cpp/lpxclient.cpp\ cpp/drivertests.cpp\ cpp/printbuffer.cpp\ - cpp/main.cpp + cpp/main.cpp \ + cpp/driver-$(OS).cpp\ OBJ_FILES=$(patsubst cpp/%.cpp,obj/%.o,$(CPP_FILES)) obj/%.o: cpp/%.cpp $(CXX) -c -MMD $< -o $@ -main.exe: $(OBJ_FILES) - $(CXX) -o main.exe $(OBJ_FILES) -lws2_32 -Llib lib/liblua-dbg.a +$(EXE): $(OBJ_FILES) + $(CXX) -o $< $(OBJ_FILES) -lws2_32 -Llib lib/liblua-dbg.a + -include $(OBJ_FILES:%.o=%.d) diff --git a/luprex/core/build.bat b/luprex/core/build.bat index 8b2f4e26..a333ebb4 100644 --- a/luprex/core/build.bat +++ b/luprex/core/build.bat @@ -1,2 +1,2 @@ clear -make main.exe +make OS=mingw main.exe diff --git a/luprex/core/build.sh b/luprex/core/build.sh new file mode 100755 index 00000000..91b87d1b --- /dev/null +++ b/luprex/core/build.sh @@ -0,0 +1,2 @@ +#!/bin/sh +make OS=linux main diff --git a/luprex/core/cpp/driver-linux.cpp b/luprex/core/cpp/driver-linux.cpp new file mode 100644 index 00000000..88fe23ec --- /dev/null +++ b/luprex/core/cpp/driver-linux.cpp @@ -0,0 +1,365 @@ + +#include "driver.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + + +class Driver { +public: + static const int MAX_CHAN = DrivenEngine::MAX_CHAN; + static const int CONSOLE_MAX = 512; + DrivenEngine *driven_; + SOCKET socket_[MAX_CHAN]; + bool connected_[MAX_CHAN]; + bool short_sleep_; + std::map listen_sockets_; + std::unique_ptr chbuf; + int64_t basetime_; + + int64_t get_now() { + FILETIME ft_now; + GetSystemTimeAsFileTime(&ft_now); + return (LONGLONG)ft_now.dwLowDateTime + ((LONGLONG)(ft_now.dwHighDateTime) << 32LL); + } + + static PADDRINFOA find_good_addr(PADDRINFOA addrinfo) { + for (PADDRINFOA addr = addrinfo; addr != nullptr; addr = addr->ai_next) { + if (addr->ai_family == AF_INET) { + return addr; + } + } + return nullptr; + } + + void set_nonblocking(SOCKET sock) { + u_long mode = 1; // 1 to enable non-blocking socket + int status = ioctlsocket(sock, FIONBIO, &mode); + assert(status == 0); + } + + SOCKET open_connection(const std::string &target, std::string &err) { + PADDRINFOA addrs = nullptr; + PADDRINFOA goodaddr = nullptr; + SOCKET sock = INVALID_SOCKET; + std::string host, port; + + err = ""; + util::split_host_port(target, host, port); + int status = getaddrinfo(host.c_str(), port.c_str(), nullptr, &addrs); + while (status == WSATRY_AGAIN) { + status = getaddrinfo(host.c_str(), port.c_str(), nullptr, &addrs); + goto error; + } + if (status == WSAHOST_NOT_FOUND) { + err = "host not found"; + goto error; + } + if (status != 0) { + err = "DNS resolution malfunction"; + goto error; + } + goodaddr = find_good_addr(addrs); + if (goodaddr == nullptr) { + err = "host not an internet host"; + goto error; + } + sock = socket(goodaddr->ai_family, SOCK_STREAM, IPPROTO_TCP); + assert(sock != INVALID_SOCKET); + set_nonblocking(sock); + status = connect(sock, goodaddr->ai_addr, goodaddr->ai_addrlen); + if (status != 0) { + int errcode = WSAGetLastError(); + if (errcode != WSAEWOULDBLOCK) { + std::ostringstream oss; + oss << "connect error " << errcode; + err = oss.str(); + goto error; + } + } + freeaddrinfo(addrs); + return sock; + + error: + if (sock != INVALID_SOCKET) closesocket(sock); + if (addrs != nullptr) freeaddrinfo(addrs); + return SOCKET_ERROR; + } + + SOCKET listen_on_port(int port, std::string &err) { + int status; + err = ""; + SOCKET sock = socket(AF_INET, SOCK_STREAM, 0); + assert(sock != INVALID_SOCKET); + + struct sockaddr_in server; + server.sin_family = AF_INET; + server.sin_addr.s_addr = INADDR_ANY; + server.sin_port = htons(port); + + status = bind(sock, (struct sockaddr *)&server, sizeof(server)); + assert(status == 0); + status = listen(sock, 10); + assert(status == 0); + set_nonblocking(sock); + return sock; + } + + void init(DrivenEngine *de) { + driven_ = de; + for (int i = 0; i < MAX_CHAN; i++) { + socket_[i] = INVALID_SOCKET; + connected_[i] = false; + } + short_sleep_ = false; + chbuf.reset(new char[65536]); + } + + void handle_listen_ports() { + std::set listenports; + driven_->drv_get_listen_ports(listenports); + for (int port : listenports) { + if (listen_sockets_.find(port) == listen_sockets_.end()) { + std::string err; + SOCKET sock = listen_on_port(port, err); + if (sock != INVALID_SOCKET) { + listen_sockets_[port] = sock; + } + } + } + } + + void handle_lua_source() { + if (driven_->drv_get_rescan_lua_source()) { + driven_->drv_set_lua_source(util::read_lua_source("lua")); + short_sleep_ = true; + } + } + + void handle_new_closed_sockets() { + std::set chans; + driven_->drv_get_new_closed(chans); + for (int chid : chans) { + if (socket_[chid] != INVALID_SOCKET) { + assert(closesocket(socket_[chid]) == 0); + socket_[chid] = INVALID_SOCKET; + connected_[chid] = false; + } + } + } + + void handle_new_outgoing_sockets() { + std::set chans; + driven_->drv_get_new_outgoing(chans); + for (int chid : chans) { + assert(socket_[chid] == INVALID_SOCKET); + std::string err; + SOCKET sock = open_connection(driven_->drv_get_target(chid), err); + if (sock == INVALID_SOCKET) { + driven_->drv_notify_close(chid, err); + short_sleep_ = true; + } else { + socket_[chid] = sock; + connected_[chid] = false; + Sleep(1000); + } + } + } + + void handle_console_output() { + int nbytes; const char *bytes; + while (true) { + driven_->drv_peek_outgoing(0, &nbytes, &bytes); + if (nbytes == 0) break; + HANDLE hstdout = GetStdHandle(STD_OUTPUT_HANDLE); + assert(hstdout != INVALID_HANDLE_VALUE); + DWORD nwrote; + if (nbytes > 10000) nbytes = 10000; + assert(WriteConsoleA(hstdout, bytes, nbytes, &nwrote, nullptr)); + assert(nwrote > 0); + driven_->drv_sent_outgoing(0, nwrote); + } + } + + // We're feeding raw console characters to the DrivenEngine layer. + // The DrivenEngine channel is expected to be in readline mode, + // which will handle echoing and line processing. + void handle_console_input() { + HANDLE hstdin = GetStdHandle(STD_INPUT_HANDLE); + assert(hstdin != INVALID_HANDLE_VALUE); + INPUT_RECORD inrecords[512]; + DWORD nread, nevents; + char ascii[512]; + int nascii = 0; + if (GetNumberOfConsoleInputEvents(hstdin, &nevents)) { + if (nevents > 512) nevents = 512; + ReadConsoleInputA(hstdin, inrecords, nevents, &nread); + for (int i = 0; i < int(nread); i++) { + const INPUT_RECORD &inr = inrecords[i]; + if (inr.EventType != KEY_EVENT) continue; + const KEY_EVENT_RECORD &key = inr.Event.KeyEvent; + if (!key.bKeyDown) continue; + char c = key.uChar.AsciiChar; + ascii[nascii++] = c; + } + if (nascii > 0) { + driven_->drv_recv_incoming(0, nascii, ascii); + short_sleep_ = true; + } + } + } + + void handle_clock() { + int now = get_now() - basetime_; + driven_->drv_set_clock(double(now) / 10000000.0); + } + + void close_socket(int chid, const std::string err) { + assert(socket_[chid] != INVALID_SOCKET); + assert(closesocket(socket_[chid]) == 0); + driven_->drv_notify_close(chid, err); + socket_[chid] = INVALID_SOCKET; + connected_[chid] = false; + short_sleep_ = true; + } + + bool calc_select_sets(fd_set &rfds, fd_set &wfds, fd_set &efds) const { + FD_ZERO(&rfds); + FD_ZERO(&wfds); + FD_ZERO(&efds); + bool any = false; + for (const auto &p : listen_sockets_) { + FD_SET(p.second, &rfds); + FD_SET(p.second, &efds); + any = true; + } + for (int chid = 1; chid < MAX_CHAN; chid++) { + SOCKET sock = socket_[chid]; + if (sock == INVALID_SOCKET) continue; + any = true; + FD_SET(sock, &rfds); + FD_SET(sock, &efds); + if (!driven_->drv_outgoing_empty(chid)) { + FD_SET(sock, &wfds); + } + } + return any; + } + + void accept_connections(int port, SOCKET sock) { + while (true) { + SOCKET chsock = accept(sock, nullptr, nullptr); + if (chsock != INVALID_SOCKET) { + int chid = driven_->drv_notify_accept(port); + socket_[chid] = chsock; + connected_[chid] = true; + short_sleep_ = true; + continue; + } + int errcode = WSAGetLastError(); + if (errcode == WSAEWOULDBLOCK) { + return; + } + if (errcode == WSAECONNRESET) { + // The remote disconnected before we had a chance to accept. + // Just pretend it never happened. + continue; + } + // If a listening port fails in a non-transient way, + // we don't really have any good way of handling + // that. + assert(false); + } + } + + void handle_socket_input_output(int mstimeout) { + fd_set rfds, wfds, efds; + int nbytes; const char *bytes; + bool any = calc_select_sets(rfds, wfds, efds); + if (!any) { + if (mstimeout>0) Sleep(mstimeout); + return; + } + TIMEVAL timeout; + timeout.tv_sec = mstimeout / 1000; + timeout.tv_usec = (mstimeout - (timeout.tv_sec*1000)) * 1000; + int status = select(1, &rfds, &wfds, &efds, &timeout); + assert(status != SOCKET_ERROR); + + for (auto &p : listen_sockets_) { + if (FD_ISSET(p.second, &rfds) || FD_ISSET(p.second, &efds)) { + accept_connections(p.first, p.second); + } + } + + for (int chid = 1; chid < MAX_CHAN; chid++) { + SOCKET sock = socket_[chid]; + if (sock == INVALID_SOCKET) continue; + if (FD_ISSET(sock, &wfds)) { + connected_[chid] = true; + driven_->drv_peek_outgoing(chid, &nbytes, &bytes); + if (nbytes > 0) { + int wbytes = send(sock, bytes, nbytes, 0); + if (wbytes == SOCKET_ERROR) { + close_socket(chid, "send failure"); + continue; + } else { + driven_->drv_sent_outgoing(chid, wbytes); + } + } + } + if (FD_ISSET(sock, &rfds) || FD_ISSET(sock, &efds)) { + int nrecv = recv(sock, chbuf.get(), 65536, 0); + if ((nrecv == SOCKET_ERROR) || (nrecv == 0)) { + close_socket(chid, "recv failure"); + continue; + } else { + driven_->drv_recv_incoming(chid, nrecv, chbuf.get()); + short_sleep_ = true; + } + } + } + } + + void drive(DrivenEngine *de, int argc, char *argv[]) { + WSADATA whocares; + assert(WSAStartup(MAKEWORD(2,2), &whocares) == 0); + HANDLE hconsole = GetStdHandle(STD_INPUT_HANDLE); + assert(hconsole != INVALID_HANDLE_VALUE); + init(de); + DrivenEngine::set(de); + basetime_ = get_now(); + driven_->drv_set_lua_source(util::read_lua_source("lua")); + driven_->drv_invoke_event_init(argc, argv); + handle_listen_ports(); + while (!de->drv_get_stop_driver()) { + short_sleep_ = false; + handle_lua_source(); + handle_new_closed_sockets(); + handle_new_outgoing_sockets(); + handle_console_output(); + handle_console_input(); + handle_console_output(); + int mstimeout = short_sleep_ ? 0 : 100; + handle_socket_input_output(mstimeout); + handle_clock(); + de->drv_invoke_event_update(); + } + DrivenEngine::set(nullptr); + } +}; + + +void driver_drive(DrivenEngine *de, int argc, char *argv[]) { + Driver driver; + driver.drive(de, argc, argv); +} + diff --git a/luprex/core/cpp/luasnap.hpp b/luprex/core/cpp/luasnap.hpp index 07d494eb..10592f59 100644 --- a/luprex/core/cpp/luasnap.hpp +++ b/luprex/core/cpp/luasnap.hpp @@ -14,7 +14,7 @@ #ifndef LUASNAP_HPP #define LUASNAP_HPP -#include "StreamBuffer.hpp" +#include "streambuffer.hpp" #include "luastack.hpp" class LuaSnap { diff --git a/luprex/core/cpp/spookyv2.cpp b/luprex/core/cpp/spookyv2.cpp index f8f368b7..fbb9d11f 100644 --- a/luprex/core/cpp/spookyv2.cpp +++ b/luprex/core/cpp/spookyv2.cpp @@ -14,7 +14,7 @@ #define ALLOW_UNALIGNED_READS 1 -#define INLINE __forceinline +#define INLINE inline // number of uint64_t's in internal state static const size_t sc_numVars = 12; diff --git a/luprex/eris-master/build.sh b/luprex/eris-master/build.sh new file mode 100755 index 00000000..a8361a46 --- /dev/null +++ b/luprex/eris-master/build.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +cd src + +make "CFLAGS=-g -DLUA_USE_APICHECK -DLUA_COMPAT_ALL" clean win-gcc-static +cp liblua.a ../../core/lib/liblua-dbg.a + +make "CFLAGS=-O2 -DLUA_COMPAT_ALL" clean win-gcc-static +cp liblua.a ../../core/lib/liblua-opt.a + +cp lua.h ../../core/inc/ +cp luaconf.h ../../core/inc/ +cp lauxlib.h ../../core/inc/ +cp lualib.h ../../core/inc/ +cp eris.h ../../core/inc/ + +cd .. +