diff --git a/examples/udp.lua b/examples/udp.lua new file mode 100644 index 000000000..c33239f76 --- /dev/null +++ b/examples/udp.lua @@ -0,0 +1,82 @@ +meta = { + name = "UDP echo example", + description = "Opens UDP server on a random port and echoes sent messages, closes the server on 'gg'", + author = "Dregu", + unsafe = true +} + +options = { + host = "127.0.0.1", + port = 0 +} + +function init() + deinit() + + server = { + -- Save messages to queue instead of printing directly, cause that doesn't work from the server thread + queue = {}, + + -- Open server on an ephemeral random port, push messages to queue and echo back to sender + socket = udp_listen(options.host, options.port, function(msg) + msg = msg:gsub("%s*$", "") + table.insert(server.queue, msg) + if msg == "gg" then + return "bye!\n" + else + return "echo: " .. msg .. "\n" + end + end) + } + + -- If port was opened successfully, start checking the message queue + if server.socket:open() then + print(F "Listening on {server.socket.port}, please send some UDP datagrams or 'gg' to close") + server.inter = set_global_interval(function() + for _, msg in pairs(server.queue) do + print(F "Received: {msg}") + if msg == "gg" then + server.socket:close() + clear_callback() + print("Server is now closed, have a nice day") + end + end + server.queue = {} + end, 1) + else + print(F "Failed to open server: {server.socket:error()}") + end +end + +function deinit() + if server then + server.socket:close() + if server.inter then clear_callback(server.inter) end + server = nil + end +end + +set_callback(init, ON.LOAD) +set_callback(init, ON.SCRIPT_ENABLE) +set_callback(deinit, ON.SCRIPT_DISABLE) + +register_option_callback("x", nil, function(ctx) + options.host = ctx:win_input_text("Host", options.host) + options.port = ctx:win_input_int("Port", options.port) + if options.port < 0 then options.port = 0 end + if options.port > 65535 then options.port = 65535 end + if ctx:win_button("Start server") then init() end + ctx:win_inline() + if ctx:win_button("Stop server") then deinit() end + if server then + ctx:win_text(server.socket:error()) + end + if server and server.socket:open() then + ctx:win_text(F "Listening on {server.socket.host}:{server.socket.port}\nTry sending something with udp_send:") + if ctx:win_button("Send 'Hello World!'") then udp_send(server.socket.host, server.socket.port, "Hello World!") end + ctx:win_inline() + if ctx:win_button("Send 'gg'") then udp_send(server.socket.host, server.socket.port, "gg") end + else + ctx:win_text("Stopped") + end +end) diff --git a/src/game_api/script/usertypes/socket_lua.cpp b/src/game_api/script/usertypes/socket_lua.cpp index 7d2ed3d35..7f4760da4 100644 --- a/src/game_api/script/usertypes/socket_lua.cpp +++ b/src/game_api/script/usertypes/socket_lua.cpp @@ -13,10 +13,10 @@ namespace NSocket void register_usertypes(sol::state& lua) { lua.new_usertype( - "UdpServer", sol::no_constructor, "close", &UdpServer::close, "open", &UdpServer::open, "error", &UdpServer::error); + "UdpServer", sol::no_constructor, "close", &UdpServer::close, "open", &UdpServer::open, "error", &UdpServer::error, "host", sol::readonly(&UdpServer::host), "port", sol::readonly(&UdpServer::port)); - /// Start an UDP server on specified address and run callback when data arrives. Return a string from the callback to reply. Requires unsafe mode. - /// The server will be closed lazily by garbage collection once the handle is released, or immediately by calling close(). + /// Start an UDP server on specified address and run callback when data arrives. Set port to 0 to use a random ephemeral port. Return a string from the callback to reply. + /// The server will be closed lazily by garbage collection once the handle is released, or immediately by calling close(). Requires unsafe mode. lua["udp_listen"] = [](std::string host, in_port_t port, sol::function cb) { return std::unique_ptr{new UdpServer(std::move(host), std::move(port), make_safe_cb(std::move(cb)))}; diff --git a/src/game_api/socket.cpp b/src/game_api/socket.cpp index 9bf07027e..5408ba40b 100644 --- a/src/game_api/socket.cpp +++ b/src/game_api/socket.cpp @@ -78,6 +78,8 @@ UdpServer::UdpServer(std::string host_, in_port_t port_, std::function { if (sock.bind(sockpp::inet_address(host, port))) { + const auto addr = (sockpp::inet_address)sock.address(); + port = addr.port(); is_opened = true; sock.set_non_blocking(); kill_thr.test_and_set(); diff --git a/src/game_api/socket.hpp b/src/game_api/socket.hpp index 90f4c4202..97167ae38 100644 --- a/src/game_api/socket.hpp +++ b/src/game_api/socket.hpp @@ -19,7 +19,7 @@ class UdpServer /// Closes the server. void close(); - /// Returns true if the port was opened successfully and hasn't been closed yet. + /// Returns true if the port was opened successfully and the server hasn't been closed yet. bool open(); /// Returns a string explaining the last error, at least if open() returned false.