From 91c8808ad94e6f49754770a85d239cb49dbf8735 Mon Sep 17 00:00:00 2001 From: WillConker Date: Wed, 3 Jul 2024 15:59:30 +0100 Subject: [PATCH] Initial commit: insecure server-client communication protocol --- api.lua | 131 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ init.lua | 6 +++ mod.conf | 1 + ping.lua | 20 +++++++++ 4 files changed, 158 insertions(+) create mode 100644 api.lua create mode 100644 init.lua create mode 100644 mod.conf create mode 100644 ping.lua diff --git a/api.lua b/api.lua new file mode 100644 index 0000000..ffff12f --- /dev/null +++ b/api.lua @@ -0,0 +1,131 @@ +local channel +vl_client = { + message_callbacks = {}, + connect_callbacks = {}, + activate_callbacks = {}, + capabilities = {}, + server_capabilities = {}, + is_connected = false, + is_active = nil, -- importantly not false +} + +-- SEND MESSAGE +-- format: (string) {opname, message} + +-- Send a message via the modchannel to the server +-- data: table to be sent to server, must be json serialisable +local function send_data(data) + -- TODO: Should channel ever be nil? + if not channel then return end + + local string_data = minetest.write_json(data) + --minetest.debug("Sending data to server:", string_data) + channel:send_all(string_data) + return true +end + +-- opname: the name of the category of this message +-- message: any json-serialisable object (not necessarily table) +function vl_client.send_message(opname, message) + send_data({opname, message}) +end + +-- ON MESSAGE RECEIVED +-- format of received messages: (string) {playername, opname, message} + +minetest.register_on_modchannel_message(function(channel_name, sender, raw_data) + if channel_name ~= "vl_client" then return end + + local data = minetest.parse_json(raw_data) + if data == nil then + minetest.debug("ERROR vl_client: received non-json data:", raw_data) + end + local receiver, opname, message = unpack(data) + --minetest.debug("Client: message received from server with opname " .. opname) + if minetest.localplayer:get_name() ~= receiver then return end + + --minetest.debug("Message received on client, sender:", sender, "opname: ", opname, "message:", dump(message)) + + local callbacks = vl_client.message_callbacks[opname] + if callbacks == nil then return end + + for _, callback in pairs(callbacks) do + callback(message) + end +end) + +-- opname: the name of the category of message to call function on +-- func: the callback function; arguments: +-- playername: the name of the player whose client sent the message +-- message: the message sent from the server - must not be modified by callback +function vl_client.register_on_message(opname, func) + if vl_client.message_callbacks[opname] == nil then + vl_client.message_callbacks[opname] = {} + end + table.insert(vl_client.message_callbacks[opname], func) +end + + + +-- CONNECT TO SERVER + +local function connect() + channel = minetest.mod_channel_join("vl_client") + vl_client.is_connected = true + minetest.debug("Client connected to modchannel") + + for _, callback in pairs(vl_client.connect_callbacks) do + callback() + end +end + +minetest.register_on_mods_loaded(connect) + +-- Register a function to be called when we connect +-- Args: none +function vl_client.register_on_connect(func) + table.insert(vl_client.connect_callbacks, func) +end + +-- ACTIVATE +-- Activation protocol: client sends capabilities, then server replies with capabilities + +minetest.register_on_modchannel_signal(function(channel_name, signal) + if channel_name ~= "vl_client" then return end + if signal == 1 then -- join failed, retry + minetest.after(1, join) + end + if signal == 0 then + vl_client.send_message("_capabilities", vl_client.capabilities) + end +end) + + +-- receiving a _capabilities message means the server supports active csm (but not necessarily any features) +vl_client.register_on_message("_capabilities", function (message) + vl_client.is_active = true + vl_client.server_capabilities = message + minetest.debug("Client activated") + + --minetest.debug("Server has capabilities:", dump(message)) + + for _, callback in pairs(vl_client.activate_callbacks) do + callback() + end +end) + +-- Register a function to be called when we activate +-- Args: none +function vl_client.register_on_activate(func) + table.insert(vl_client.activate_callbacks, func) +end + + +-- CAPABILITIES + +-- Register a capability of the client, of which the server will be informed +-- Capabilities can have a value to give details about that capability +-- The value can be any json-serialisable object (not necessarily table) +function vl_client.register_capability(capability, value) + vl_client.capabilities[capability] = value +end diff --git a/init.lua b/init.lua new file mode 100644 index 0000000..53759e0 --- /dev/null +++ b/init.lua @@ -0,0 +1,6 @@ +local modpath = minetest.get_modpath(minetest.get_current_modname()) + +dofile(modpath .. "/api.lua") +minetest.debug(modpath .. "/api.lua", modpath .. "/ping.lua") + +dofile(modpath .. "/ping.lua") diff --git a/mod.conf b/mod.conf new file mode 100644 index 0000000..8c1c51c --- /dev/null +++ b/mod.conf @@ -0,0 +1 @@ +name=vl_client diff --git a/ping.lua b/ping.lua new file mode 100644 index 0000000..93fced7 --- /dev/null +++ b/ping.lua @@ -0,0 +1,20 @@ +vl_client.register_capability("ping", 1) + +minetest.register_chatcommand("ping", { + params = "", + description = "pings the server from the client", + func = function(param) + minetest.debug("Pinging...") + vl_client.send_message("ping", {type="request", content=param}) + return true + end, +}) + +vl_client.register_on_message("ping", function(message) + if message.type == "request" then + minetest.debug("Client received ping with content:", message.content) + vl_client.send_message("ping", {type="response", content=message.content}) + else + minetest.debug("Client received server response to ping with content:", message.content) + end +end)