mirror of
https://github.com/IntQuant/noita_entangled_worlds.git
synced 2025-10-19 07:03:16 +00:00
Cleanup
This commit is contained in:
parent
9fe394a558
commit
028ce97172
5 changed files with 0 additions and 520 deletions
Binary file not shown.
|
@ -1,493 +0,0 @@
|
|||
-- pollnet bindings for luajit+ffi
|
||||
|
||||
-- Change this as necessary to point to where [lib?]pollnet.dll|.so|.dylib
|
||||
-- is actually located.
|
||||
local LIBDIR = "mods/quant.ew/files/lib/"
|
||||
local API_VERSION = "1.0.0"
|
||||
|
||||
local ffi = require("ffi")
|
||||
ffi.cdef[[
|
||||
typedef struct pollnet_ctx pollnet_ctx;
|
||||
typedef uint64_t sockethandle_t;
|
||||
typedef uint32_t socketstatus_t;
|
||||
|
||||
const char* pollnet_version();
|
||||
bool pollnet_handle_is_valid(sockethandle_t handle);
|
||||
sockethandle_t pollnet_invalid_handle();
|
||||
|
||||
pollnet_ctx* pollnet_init();
|
||||
pollnet_ctx* pollnet_get_or_init_static();
|
||||
void pollnet_shutdown(pollnet_ctx* ctx);
|
||||
sockethandle_t pollnet_open_tcp(pollnet_ctx* ctx, const char* addr);
|
||||
sockethandle_t pollnet_listen_tcp(pollnet_ctx* ctx, const char* addr);
|
||||
sockethandle_t pollnet_open_ws(pollnet_ctx* ctx, const char* url);
|
||||
sockethandle_t pollnet_simple_http_get(pollnet_ctx* ctx, const char* url, const char* headers, bool ret_body_only);
|
||||
sockethandle_t pollnet_simple_http_post(pollnet_ctx* ctx, const char* url, const char* headers, const char* data, uint32_t datasize, bool ret_body_only);
|
||||
void pollnet_close(pollnet_ctx* ctx, sockethandle_t handle);
|
||||
void pollnet_close_all(pollnet_ctx* ctx);
|
||||
void pollnet_send(pollnet_ctx* ctx, sockethandle_t handle, const char* msg);
|
||||
void pollnet_send_binary(pollnet_ctx* ctx, sockethandle_t handle, const unsigned char* msg, uint32_t msgsize);
|
||||
socketstatus_t pollnet_update(pollnet_ctx* ctx, sockethandle_t handle);
|
||||
socketstatus_t pollnet_update_blocking(pollnet_ctx* ctx, sockethandle_t handle);
|
||||
uint32_t pollnet_get_data_size(pollnet_ctx* ctx, sockethandle_t handle);
|
||||
uint32_t pollnet_get_data(pollnet_ctx* ctx, sockethandle_t handle, char* dest, uint32_t dest_size);
|
||||
const uint8_t* pollnet_unsafe_get_data_ptr(pollnet_ctx* ctx, sockethandle_t handle);
|
||||
void pollnet_clear_data(pollnet_ctx* ctx, sockethandle_t handle);
|
||||
uint32_t pollnet_get_error(pollnet_ctx* ctx, sockethandle_t handle, char* dest, uint32_t dest_size);
|
||||
sockethandle_t pollnet_get_connected_client_handle(pollnet_ctx* ctx, sockethandle_t handle);
|
||||
sockethandle_t pollnet_listen_ws(pollnet_ctx* ctx, const char* addr);
|
||||
sockethandle_t pollnet_serve_static_http(pollnet_ctx* ctx, const char* addr, const char* serve_dir);
|
||||
sockethandle_t pollnet_serve_dynamic_http(pollnet_ctx* ctx, const char* addr, bool keep_alive);
|
||||
sockethandle_t pollnet_serve_http(pollnet_ctx* ctx, const char* addr);
|
||||
void pollnet_add_virtual_file(pollnet_ctx* ctx, sockethandle_t handle, const char* filename, const char* filedata, uint32_t filesize);
|
||||
void pollnet_remove_virtual_file(pollnet_ctx* ctx, sockethandle_t handle, const char* filename);
|
||||
uint32_t pollnet_get_nanoid(char* dest, uint32_t dest_size);
|
||||
void pollnet_sleep_ms(uint32_t milliseconds);
|
||||
]]
|
||||
|
||||
local pollnet
|
||||
if jit.os == 'Windows' then
|
||||
pollnet = ffi.load(LIBDIR .. "pollnet.dll")
|
||||
elseif jit.os == 'OSX' or jit.os == 'Darwin' then
|
||||
pollnet = ffi.load(LIBDIR .. "libpollnet.dylib")
|
||||
else
|
||||
pollnet = ffi.load(LIBDIR .. "libpollnet.so")
|
||||
end
|
||||
local POLLNET_VERSION = ffi.string(pollnet.pollnet_version())
|
||||
|
||||
do
|
||||
local function split_version(v)
|
||||
local major, minor, patch = v:match("(%d+)%.(%d+)%.(%d+)")
|
||||
return tonumber(major), tonumber(minor), tonumber(patch)
|
||||
end
|
||||
|
||||
local maj_req, min_req, pat_req = split_version(API_VERSION)
|
||||
local maj_dll, min_dll, pat_dll = split_version(POLLNET_VERSION)
|
||||
if maj_dll ~= maj_req then
|
||||
error("Incompatible Pollnet binary: expected " .. API_VERSION
|
||||
.. " got " .. POLLNET_VERSION)
|
||||
end
|
||||
if (min_dll < min_req) or (min_dll == min_req and pat_dll < pat_req) then
|
||||
error("Incompatible Pollnet binary: expected " .. API_VERSION
|
||||
.. " got " .. POLLNET_VERSION)
|
||||
end
|
||||
end
|
||||
|
||||
local POLLNET_RESULT_CODES = {
|
||||
[0] = "invalid_handle",
|
||||
[1] = "error",
|
||||
[2] = "closed",
|
||||
[3] = "opening",
|
||||
[4] = "nodata",
|
||||
[5] = "hasdata",
|
||||
[6] = "newclient"
|
||||
}
|
||||
|
||||
local _ctx
|
||||
|
||||
local function init_ctx()
|
||||
if _ctx then return end
|
||||
_ctx = ffi.gc(pollnet.pollnet_init(), pollnet.pollnet_shutdown)
|
||||
assert(_ctx ~= nil)
|
||||
end
|
||||
|
||||
local function init_ctx_hack_static()
|
||||
if _ctx then return end
|
||||
_ctx = pollnet.pollnet_get_or_init_static()
|
||||
assert(_ctx ~= nil)
|
||||
pollnet.pollnet_close_all(_ctx)
|
||||
end
|
||||
|
||||
local function shutdown_ctx()
|
||||
if not _ctx then return end
|
||||
pollnet.pollnet_shutdown(ffi.gc(_ctx, nil))
|
||||
_ctx = nil
|
||||
end
|
||||
|
||||
local socket_mt = {}
|
||||
local function Socket()
|
||||
return setmetatable({}, {__index = socket_mt})
|
||||
end
|
||||
|
||||
function socket_mt:_from_handle(handle)
|
||||
init_ctx()
|
||||
if self._socket then self:close() end
|
||||
self._socket = handle
|
||||
self._status = "unpolled"
|
||||
return self
|
||||
end
|
||||
|
||||
function socket_mt:_open(opener, ...)
|
||||
init_ctx()
|
||||
if self._socket then self:close() end
|
||||
self._socket = opener(_ctx, ...)
|
||||
self._status = "unpolled"
|
||||
return self
|
||||
end
|
||||
|
||||
local function format_headers(headers)
|
||||
if type(headers) == 'string' then return headers end
|
||||
if type(headers) ~= 'table' then
|
||||
error("HTTP headers must be table|string, got: " .. tostring(headers))
|
||||
end
|
||||
local keys = {}
|
||||
for name, _ in pairs(headers) do
|
||||
table.insert(keys, name)
|
||||
end
|
||||
table.sort(keys)
|
||||
local frags = {}
|
||||
for idx, name in ipairs(keys) do
|
||||
local val = headers[name]
|
||||
if type(val) == 'string' then
|
||||
table.insert(frags, ("%s:%s"):format(name, val))
|
||||
else -- assume table representing a duplicated header
|
||||
for _, subval in ipairs(val) do
|
||||
table.insert(frags, ("%s:%s"):format(name, subval))
|
||||
end
|
||||
end
|
||||
end
|
||||
return table.concat(frags, "\n")
|
||||
end
|
||||
|
||||
local function parse_headers(headers_str)
|
||||
local headers = {}
|
||||
for line in headers_str:gmatch("[^\n]+") do
|
||||
local key, val = line:match("^([^:]*):(.*)$")
|
||||
if key then headers[key:lower()] = val end
|
||||
end
|
||||
return headers
|
||||
end
|
||||
|
||||
local function parse_query(s)
|
||||
local queries = {[1]=s}
|
||||
for k, v in s:gmatch("([^=&]+)=([^&]+)") do
|
||||
queries[k] = v
|
||||
end
|
||||
return queries
|
||||
end
|
||||
|
||||
local function parse_method(s)
|
||||
local method, path, query = s:match("^(%w+) ([^?]+)%??(.*)$")
|
||||
local queries = parse_query(query)
|
||||
return method, path, queries
|
||||
end
|
||||
|
||||
function socket_mt:http_get(url, headers, ret_body_only)
|
||||
headers = format_headers(headers or "")
|
||||
ret_body_only = not not ret_body_only
|
||||
return self:_open(
|
||||
pollnet.pollnet_simple_http_get,
|
||||
url,
|
||||
headers,
|
||||
ret_body_only
|
||||
)
|
||||
end
|
||||
|
||||
function socket_mt:http_post(url, headers, body, ret_body_only)
|
||||
body = body or ""
|
||||
headers = format_headers(headers or {
|
||||
["content-type"] = "application/x-www-form-urlencoded"
|
||||
})
|
||||
ret_body_only = not not ret_body_only
|
||||
return self:_open(
|
||||
pollnet.pollnet_simple_http_post,
|
||||
url,
|
||||
headers,
|
||||
body,
|
||||
#body,
|
||||
ret_body_only
|
||||
)
|
||||
end
|
||||
|
||||
function socket_mt:open_ws(url)
|
||||
return self:_open(pollnet.pollnet_open_ws, url)
|
||||
end
|
||||
|
||||
function socket_mt:open_tcp(addr)
|
||||
return self:_open(pollnet.pollnet_open_tcp, addr)
|
||||
end
|
||||
|
||||
function socket_mt:serve_http(addr, dir)
|
||||
self.is_http_server = true
|
||||
if dir and dir ~= "" then
|
||||
return self:_open(pollnet.pollnet_serve_static_http, addr, dir)
|
||||
else
|
||||
return self:_open(pollnet.pollnet_serve_http, addr)
|
||||
end
|
||||
end
|
||||
|
||||
function socket_mt:add_virtual_file(filename, filedata)
|
||||
assert(filedata and type(filedata) == 'string', "filedata must be provided as string!")
|
||||
if filename:sub(1,1) ~= "/" then
|
||||
-- url paths start from root at "/"
|
||||
filename = "/" .. filename
|
||||
end
|
||||
local dsize = #filedata
|
||||
pollnet.pollnet_add_virtual_file(_ctx, self._socket, filename, filedata, dsize)
|
||||
end
|
||||
|
||||
function socket_mt:remove_virtual_file(filename)
|
||||
pollnet.pollnet_remove_virtual_file(_ctx, self._socket, filename)
|
||||
end
|
||||
|
||||
function socket_mt:listen_ws(addr, callback)
|
||||
if callback then self:on_connection(callback) end
|
||||
return self:_open(pollnet.pollnet_listen_ws, addr)
|
||||
end
|
||||
|
||||
function socket_mt:listen_tcp(addr, callback)
|
||||
if callback then self:on_connection(callback) end
|
||||
return self:_open(pollnet.pollnet_listen_tcp, addr)
|
||||
end
|
||||
|
||||
function socket_mt:serve_dynamic_http(addr, keep_alive, callback)
|
||||
if callback then self:on_connection(callback) end
|
||||
return self:_open(pollnet.pollnet_serve_dynamic_http, addr, keep_alive or false)
|
||||
end
|
||||
|
||||
function socket_mt:on_connection(f)
|
||||
self._on_connection = f
|
||||
return self
|
||||
end
|
||||
|
||||
function socket_mt:_get_message()
|
||||
local msg_size = pollnet.pollnet_get_data_size(_ctx, self._socket)
|
||||
if msg_size > 0 then
|
||||
-- Note: unsafe_get_data_ptr requires careful consideration to use safely!
|
||||
-- Here we are OK because ffi.string copies the data to a new Lua string,
|
||||
-- so we only hang on to the pointer long enough for the copy.
|
||||
local raw_pointer = pollnet.pollnet_unsafe_get_data_ptr(_ctx, self._socket)
|
||||
if raw_pointer == nil then
|
||||
error("Impossible situation: msg_size > 0 but null data pointer")
|
||||
end
|
||||
return ffi.string(raw_pointer, msg_size)
|
||||
else
|
||||
return ""
|
||||
end
|
||||
end
|
||||
|
||||
function socket_mt:poll()
|
||||
self._last_message = nil
|
||||
if not self._socket then
|
||||
self._status = "invalid"
|
||||
return false, "invalid"
|
||||
end
|
||||
local res = POLLNET_RESULT_CODES[pollnet.pollnet_update(_ctx, self._socket)] or "error"
|
||||
self._status = res
|
||||
if res == "hasdata" then
|
||||
self._status = "open"
|
||||
self._last_message = self:_get_message()
|
||||
return true, self._last_message
|
||||
elseif res == "nodata" then
|
||||
self._status = "open"
|
||||
return true
|
||||
elseif res == "opening" then
|
||||
self._status = "opening"
|
||||
return true
|
||||
elseif res == "error" then
|
||||
self._status = "error"
|
||||
self._last_message = self:_get_message()
|
||||
return false, self._last_message
|
||||
elseif res == "closed" then
|
||||
self._status = "closed"
|
||||
self._last_message = "closed"
|
||||
return false, "closed"
|
||||
elseif res == "newclient" then
|
||||
self._status = "open"
|
||||
local client_addr = self:_get_message()
|
||||
local client_handle = pollnet.pollnet_get_connected_client_handle(_ctx, self._socket)
|
||||
assert(client_handle > 0)
|
||||
local client_sock = Socket():_from_handle(client_handle)
|
||||
client_sock.parent = self
|
||||
client_sock.remote_addr = client_addr
|
||||
if self._on_connection then
|
||||
self._on_connection(client_sock, client_addr)
|
||||
else
|
||||
print("Incoming connection but no :on_connection handler! Just closing it!")
|
||||
client_sock:close()
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
function socket_mt:await()
|
||||
local yield_count = 0
|
||||
while true do
|
||||
if self.timeout and (yield_count > self.timeout) then
|
||||
return false, "timeout"
|
||||
end
|
||||
local happy, msg = self:poll()
|
||||
if not happy then
|
||||
self:close()
|
||||
return false, "error: " .. tostring(msg)
|
||||
end
|
||||
if msg then return msg end
|
||||
yield_count = yield_count + 1
|
||||
coroutine.yield()
|
||||
end
|
||||
end
|
||||
|
||||
function socket_mt:await_n(count)
|
||||
local parts = {}
|
||||
for idx = 1, count do
|
||||
local part, err = self:await()
|
||||
if not part then return false, err end
|
||||
parts[idx] = part
|
||||
end
|
||||
return parts
|
||||
end
|
||||
|
||||
function socket_mt:last_message()
|
||||
return self._last_message
|
||||
end
|
||||
|
||||
function socket_mt:status()
|
||||
return self._status
|
||||
end
|
||||
|
||||
function socket_mt:send(msg)
|
||||
assert(self._socket)
|
||||
assert(type(msg) == 'string', "Argument to send must be a string")
|
||||
pollnet.pollnet_send(_ctx, self._socket, msg)
|
||||
end
|
||||
|
||||
function socket_mt:send_binary(msg)
|
||||
assert(self._socket)
|
||||
assert(type(msg) == 'string', "Argument to send must be a string")
|
||||
pollnet.pollnet_send_binary(_ctx, self._socket, msg, #msg)
|
||||
end
|
||||
|
||||
function socket_mt:close()
|
||||
if not self._socket then return end
|
||||
pollnet.pollnet_close(_ctx, self._socket)
|
||||
self._socket = nil
|
||||
end
|
||||
|
||||
local function get_nanoid()
|
||||
local _id_scratch = ffi.new("int8_t[?]", 128)
|
||||
local msg_size = pollnet.pollnet_get_nanoid(_id_scratch, 128)
|
||||
return ffi.string(_id_scratch, msg_size)
|
||||
end
|
||||
|
||||
local function sleep_ms(ms)
|
||||
pollnet.pollnet_sleep_ms(ms)
|
||||
end
|
||||
|
||||
local reactor_mt = {}
|
||||
local function Reactor()
|
||||
local ret = setmetatable({}, {__index = reactor_mt})
|
||||
ret:init()
|
||||
return ret
|
||||
end
|
||||
|
||||
function reactor_mt:init()
|
||||
self.threads = {}
|
||||
end
|
||||
|
||||
function reactor_mt:run(thread_body)
|
||||
local thread = coroutine.create(function()
|
||||
thread_body(self)
|
||||
end)
|
||||
self.threads[thread] = true
|
||||
end
|
||||
|
||||
function reactor_mt:run_server(server_sock, client_body)
|
||||
server_sock:on_connection(function(client_sock, addr)
|
||||
self:run(function()
|
||||
client_body(client_sock, addr)
|
||||
end)
|
||||
end)
|
||||
self:run(function()
|
||||
while true do server_sock:await() end
|
||||
end)
|
||||
end
|
||||
|
||||
function reactor_mt:log(...)
|
||||
print(...)
|
||||
end
|
||||
|
||||
function reactor_mt:update()
|
||||
local live_count = 0
|
||||
local cur_threads = self.threads
|
||||
self.threads = {}
|
||||
for thread, _ in pairs(cur_threads) do
|
||||
if coroutine.status(thread) == "dead" then
|
||||
cur_threads[thread] = nil
|
||||
else
|
||||
live_count = live_count + 1
|
||||
local happy, err = coroutine.resume(thread)
|
||||
if not happy then self:log("Error", err) end
|
||||
end
|
||||
end
|
||||
for thread, _ in pairs(self.threads) do
|
||||
live_count = live_count + 1
|
||||
cur_threads[thread] = true
|
||||
end
|
||||
self.threads = cur_threads
|
||||
return live_count
|
||||
end
|
||||
|
||||
local function invoke_handler(handler, req, expose_errors)
|
||||
local happy, res = pcall(handler, req)
|
||||
if happy then
|
||||
return res
|
||||
else
|
||||
return {
|
||||
status = "500",
|
||||
body = (expose_errors and tostring(res)) or "Internal Error"
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
local function wrap_req_handler(handler, expose_errors)
|
||||
return function(req_sock, addr)
|
||||
while true do
|
||||
local raw_req = req_sock:await_n(3)
|
||||
if not raw_req then break end
|
||||
local method, path, query = parse_method(raw_req[1])
|
||||
local headers = parse_headers(raw_req[2])
|
||||
local reply = invoke_handler(handler, {
|
||||
addr = addr,
|
||||
method = method,
|
||||
path = path,
|
||||
query = query,
|
||||
headers = headers,
|
||||
body = raw_req[3],
|
||||
raw = raw_req
|
||||
}, expose_errors)
|
||||
req_sock:send(reply.status or "404")
|
||||
req_sock:send(format_headers(reply.headers or {}))
|
||||
req_sock:send_binary(reply.body or "")
|
||||
end
|
||||
req_sock:close()
|
||||
end
|
||||
end
|
||||
|
||||
local exports = {
|
||||
VERSION = POLLNET_VERSION,
|
||||
init = init_ctx,
|
||||
init_hack_static = init_ctx_hack_static,
|
||||
shutdown = shutdown_ctx,
|
||||
Socket = Socket,
|
||||
Reactor = Reactor,
|
||||
pollnet = pollnet,
|
||||
nanoid = get_nanoid,
|
||||
sleep_ms = sleep_ms,
|
||||
format_headers = format_headers,
|
||||
parse_headers = parse_headers,
|
||||
parse_method = parse_method,
|
||||
wrap_req_handler = wrap_req_handler
|
||||
}
|
||||
|
||||
local fnames = {
|
||||
"open_ws", "listen_ws", "open_tcp", "listen_tcp",
|
||||
"serve_http", "serve_dynamic_http", "http_get", "http_post"
|
||||
}
|
||||
for _, name in ipairs(fnames) do
|
||||
exports[name] = function(...)
|
||||
local sock = Socket()
|
||||
return sock[name](sock, ...)
|
||||
end
|
||||
end
|
||||
|
||||
return exports
|
Loading…
Add table
Add a link
Reference in a new issue