From 05a06cf2913eac587ac9844a33b64a4d1cdff761 Mon Sep 17 00:00:00 2001 From: luk3yx Date: Tue, 8 Jan 2019 11:08:42 +1300 Subject: [PATCH] Fork and convert to server-side mod. --- LICENSE.md | 2 +- README.md | 21 +-- depends.txt | 1 + init.lua | 398 ++++++++++++++++++++++++++++++++-------------------- 4 files changed, 262 insertions(+), 160 deletions(-) create mode 100644 depends.txt diff --git a/LICENSE.md b/LICENSE.md index 2433aff..76b584c 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,7 +1,7 @@ # The MIT License (MIT) -*Copyright © 2018 by luk3yx* +*Copyright © 2019 by luk3yx* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 26c4a7f..ea0496c 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,20 @@ -# advmarkers +# advmarkers (non-CSM) -A marker/waypoint CSM for Minetest, designed for use with the [marker] mod. +# Do not release yet, still WIP -To display markers, the server currently needs the [marker] mod installed, -otherwise "command not found" errors will be displayed, as CSMs cannot currently -create HUDs on their own. +A marker/waypoint mod for Minetest. + +Unlike the [CSM], this mod does not require the [markers] mod to be installed. ## How to use `advmarkers` introduces the following chatcommands: - - `.mrkr`: Opens a formspec allowing you to display or delete markers. - - `.add_mrkr`: Adds markers. You can use `.add_mrkr x,y,z Marker name` to add markers. Markers are (currently) cross-server, and adding a marker with (exactly) the same name as another will overwrite the original marker. If you replace `x,y,z` with `here`, the marker will be set to your current position, and replacing it with `there` will set the marker to the last `.coords` position. - - `.mrkr_export`: Exports your markers to an advmarkers string. Remember to not modify the text before copying it. You can use `.mrkr_export old` if you want an export string compatible with older versions of advmarkers (it should start with `M` instead of `J`). The old format will probably not work nicely with the planned server-side mod, however. - - `.mrkr_import`: Imports your markers from an advmarkers string (`.mrkr_import `). Any markers with the same name will not be overwritten, and if they do not have the same co-ordinates, `_` will be appended to the imported one. - - `.mrkrthere`: Sets a marker at the last `.coords` position. + - `/mrkr`: Opens a formspec allowing you to display or delete markers. If you give this command a parameter (`h`/`here`, `t`/`there` or co-ordinates), it will set your HUD position to those co-ordinates. + - `/add_mrkr`: Adds markers. You can use `.add_mrkr x,y,z Marker name` to add markers. Markers are (currently) cross-server, and adding a marker with (exactly) the same name as another will overwrite the original marker. If you replace `x,y,z` with `here`, the marker will be set to your current position, and replacing it with `there` will set the marker to the last `.coords` position. + - `/mrkr_export`: Exports your markers to an advmarkers string. Remember to not modify the text before copying it. You can use `.mrkr_export old` if you want an export string compatible with older versions of advmarkers (it should start with `M` instead of `J`). The old format will probably not work nicely with the planned server-side mod, however. + - `/mrkr_import`: Imports your markers from an advmarkers string (`.mrkr_import `). Any markers with the same name will not be overwritten, and if they do not have the same co-ordinates, `_` will be appended to the imported one. + - `/mrkrthere`: Alias for `/mrkr there`. If you die, a marker is automatically added at your death position, and will update the last `.coords` position. @@ -27,6 +27,7 @@ advmarkers temporarily stores this position, and you can set a temporary marker at the `.coords` position with `.mrkrthere`, or add a permanent marker with `.add_mrkr there Marker name`. +[CSM]: https://gitlab.com/luk3yx/minetest-advmarkers-csm [marker]: https://github.com/Billy-S/kingdoms_game/blob/master/mods/marker [GitHub]: https://github.com/luk3yx/minetest-chat_channels [GitLab]: https://gitlab.com/luk3yx/minetest-chat_channels diff --git a/depends.txt b/depends.txt new file mode 100644 index 0000000..0520ee9 --- /dev/null +++ b/depends.txt @@ -0,0 +1 @@ +cloaking? diff --git a/init.lua b/init.lua index 77377aa..561561b 100644 --- a/init.lua +++ b/init.lua @@ -1,14 +1,15 @@ -- --- Minetest advmarkers CSM +-- Minetest advmarkers mod -- --- Needs the https://github.com/Billy-S/kingdoms_game/tree/master/mods/marker --- mod to be able to display HUD elements +-- The advmarkers CSM ported to a server-side mod -- advmarkers = {} -- Get the mod storage local storage = minetest.get_mod_storage() +local hud = {} +advmarkers.last_coords = {} -- Convert positions to/from strings local function pos_to_string(pos) @@ -29,53 +30,121 @@ local function string_to_pos(pos) end end +-- Get player name or object +local get_player_by_name = minetest.get_player_by_name +local get_connected_players = minetest.get_connected_players +if minetest.get_modpath('cloaking') then + get_player_by_name = cloaking.get_player_by_name + get_connected_players = cloaking.get_connected_players +end + +local function get_player(player, t) + local name + if type(player) == 'string' then + name = player + if t ~= 0 then + player = get_player_by_name(name) + end + else + name = player:get_player_name() + end + if t == 0 then + return name + elseif t == 1 then + return player + end + return name, player +end + -- Set the HUD position --- TODO: Make this entirely client-side or allow the command to be changed. -function advmarkers.set_hud_pos(pos) +function advmarkers.set_hud_pos(player, pos, title) + local name, player = get_player(player) pos = string_to_pos(pos) - if not pos then return end - minetest.run_server_chatcommand('mrkr', tostring(pos.x) .. ' ' .. - tostring(pos.y) .. ' ' .. tostring(pos.z)) + if not player or not pos then return end + if not title then + title = pos.x .. ', ' .. pos.y .. ', ' .. pos.z + end + if hud[player] then + player:hud_change(hud[player], 'name', title) + player:hud_change(hud[player], 'world_pos', pos) + else + hud[player] = player:hud_add({ + hud_elem_type = 'waypoint', + name = title, + text = 'm', + number = 0xbf360c, + world_pos = pos + }) + end + minetest.chat_send_player(name, 'Marker set to ' .. title) + return true +end + +-- Get and save player storage +local function get_storage(name) + name = get_player(name, 0) + return minetest.deserialize(storage:get_string(name)) or {} +end + +local function save_storage(name, data) + name = get_player(name, 0) + if type(data) == 'table' then + data = minetest.serialize(data) + end + if type(data) ~= 'string' then return end + if #data > 0 then + storage:set_string(name, data) + else + storage:set_string(name, '') + end return true end -- Add a marker -function advmarkers.set_marker(pos, name) +function advmarkers.set_marker(player, pos, name) pos = pos_to_string(pos) if not pos then return end - storage:set_string('marker-' .. tostring(name), pos) - return true + local data = get_storage(player) + data['marker-' .. tostring(name)] = pos + return save_storage(player, data) end -- Delete a marker -function advmarkers.delete_marker(name) - storage:set_string('marker-' .. tostring(name), '') +function advmarkers.delete_marker(player, name) + local data = get_storage(player) + data['marker-' .. tostring(name)] = nil + return save_storage(player, data) end -- Get a marker -function advmarkers.get_marker(name) - return string_to_pos(storage:get_string('marker-' .. tostring(name))) +function advmarkers.get_marker(player, name) + local data = get_storage(player) + return string_to_pos(data['marker-' .. tostring(name)]) end -- Rename a marker and re-interpret the position. -function advmarkers.rename_marker(oldname, newname) +function advmarkers.rename_marker(player, oldname, newname) + player = get_player(player, 0) oldname, newname = tostring(oldname), tostring(newname) - local pos = advmarkers.get_marker(oldname) - if not pos or not advmarkers.set_marker(pos, newname) then return end + local pos = advmarkers.get_marker(player, oldname) + if not pos or not advmarkers.set_marker(player, pos, newname) then + return + end if oldname ~= newname then - advmarkers.delete_marker(oldname) + advmarkers.delete_marker(player, oldname) end return true end -- Display a marker -function advmarkers.display_marker(name) - return advmarkers.set_hud_pos(advmarkers.get_marker(name)) +function advmarkers.display_marker(player, name) + return advmarkers.set_hud_pos(player, advmarkers.get_marker(player, name), + name) end -- Export markers -function advmarkers.export(raw) - local s = storage:to_table().fields +function advmarkers.export(player, raw) + local s = get_storage(player) if raw == 'M' then s = minetest.compress(minetest.serialize(s)) s = 'M' .. minetest.encode_base64(s) @@ -86,48 +155,47 @@ function advmarkers.export(raw) return s end --- Import markers -function advmarkers.import(s) +-- Import markers - Note that this won't import strings made by older versions +-- of the CSM. +function advmarkers.import(player, s) if type(s) ~= 'table' then - local ver = s:sub(1, 1) - if ver ~= 'M' and ver ~= 'J' then return end + if s:sub(1, 1) ~= 'J' then return end s = minetest.decode_base64(s:sub(2)) local success, msg = pcall(minetest.decompress, s) if not success then return end - if ver == 'M' then - s = minetest.deserialize(msg) - else - s = minetest.parse_json(msg) - end + s = minetest.parse_json(msg) end -- Iterate over markers to preserve existing ones and check for errors. if type(s) == 'table' then + local data = get_storage(player) for name, pos in pairs(s) do if type(name) == 'string' and type(pos) == 'string' and name:sub(1, 7) == 'marker-' and minetest.string_to_pos(pos) and - storage:get_string(name) ~= pos then + data[name] ~= pos then -- Prevent collisions local c = 0 - while #storage:get_string(name) > 0 and c < 50 do + while data[name] and c < 50 do name = name .. '_' c = c + 1 end -- Sanity check if c < 50 then - storage:set_string(name, pos) + data[name] = pos end end end - return true + return save_storage(player, data) end end -- Get the markers formspec local formspec_list = {} -local selected_name = false -function advmarkers.display_formspec() +local selected_name = {} +function advmarkers.display_formspec(player) + player = get_player(player, 0) + if not get_player_by_name(player) then return end local formspec = 'size[5.25,8]' .. 'label[0,0;Marker list]' .. 'button_exit[0,7.5;1.3125,0.5;display;Display]' .. @@ -139,29 +207,29 @@ function advmarkers.display_formspec() -- Iterate over all the markers local id = 0 local selected = 1 - formspec_list = {} - for name, pos in pairs(storage:to_table().fields) do + formspec_list[player] = {} + for name, pos in pairs(get_storage(player)) do if name:sub(1, 7) == 'marker-' then id = id + 1 if id > 1 then formspec = formspec .. ',' end name = name:sub(8) - if not selected_name then - selected_name = name + if not selected_name[player] then + selected_name[player] = name end - if name == selected_name then + if name == selected_name[player] then selected = id end - formspec_list[#formspec_list + 1] = name + formspec_list[player][#formspec_list[player] + 1] = name formspec = formspec .. '##' .. minetest.formspec_escape(name) end end -- Close the text list and display the selected marker position formspec = formspec .. ';' .. tostring(selected) .. ']' - if selected_name then - local pos = advmarkers.get_marker(selected_name) + if selected_name[player] then + local pos = advmarkers.get_marker(player, selected_name[player]) if pos then pos = minetest.formspec_escape(tostring(pos.x) .. ', ' .. tostring(pos.y) .. ', ' .. tostring(pos.z)) @@ -171,45 +239,70 @@ function advmarkers.display_formspec() else -- Draw over the buttons formspec = formspec .. 'button_exit[0,7.5;5.25,0.5;quit;Close dialog]' .. - 'label[0,6.75;No markers. Add one with ".add_mrkr".]' + 'label[0,6.75;No markers. Add one with "/add_mrkr".]' end -- Display the formspec - return minetest.show_formspec('advmarkers-csm', formspec) + return minetest.show_formspec(player, 'advmarkers-ssm', formspec) +end + +-- Get marker position +function advmarkers.get_chatcommand_pos(player, pos) + local pname = get_player(player, 0) + + -- Validate the position + if pos == 'h' or pos == 'here' then + pos = get_player(player, 1):get_pos() + elseif pos == 't' or pos == 'there' then + if not advmarkers.last_coords[pname] then + return false, 'No-one has used ".coords" and you have not died!' + end + pos = advmarkers.last_coords[pname] + else + pos = string_to_pos(pos) + if not pos then + return false, 'Invalid position!' + end + end + return pos end -- Open the markers GUI minetest.register_chatcommand('mrkr', { params = '', description = 'Open the advmarkers GUI', - func = advmarkers.display_formspec + func = function(pname, param) + if #param:gsub(' ', '') > 0 then + local pos, err = advmarkers.get_chatcommand_pos(pname, param) + if not pos then + return false, err + end + if not advmarkers.set_hud_pos(pname, pos) then + return false, 'Error setting the marker!' + end + else + advmarkers.display_formspec(pname) + end + end }) -- Add a marker minetest.register_chatcommand('add_mrkr', { params = ' ', description = 'Adds a marker.', - func = function(param) + func = function(pname, param) + -- Get the parameters local s, e = param:find(' ') if not s or not e then - return false, 'Invalid syntax! See .help add_mrkr for more info.' + return false, 'Invalid syntax! See /help add_mrkr for more info.' end local pos = param:sub(1, s - 1) local name = param:sub(e + 1) - -- Validate the position - if pos == 'here' then - pos = minetest.localplayer:get_pos() - elseif pos == 'there' then - if not advmarkers.last_coords then - return false, 'No-one has used ".coords" and you have not died!' - end - pos = advmarkers.last_coords - else - pos = string_to_pos(pos) - if not pos then - return false, 'Invalid position!' - end + -- Get the position + local pos, err = advmarkers.get_chatcommand_pos(pname, pos) + if not pos then + return false, err end -- Validate the name @@ -218,34 +311,35 @@ minetest.register_chatcommand('add_mrkr', { end -- Set the marker - return advmarkers.set_marker(pos, name), 'Done!' + return advmarkers.set_marker(pname, pos, name), 'Done!' end }) -- Set the HUD -minetest.register_on_formspec_input(function(formname, fields) +minetest.register_on_player_receive_fields(function(player, formname, fields) + local pname, player = get_player(player) if formname == 'advmarkers-ignore' then return true - elseif formname ~= 'advmarkers-csm' then + elseif formname ~= 'advmarkers-ssm' then return end local name = false if fields.marker then local event = minetest.explode_textlist_event(fields.marker) if event.index then - name = formspec_list[event.index] + name = formspec_list[pname][event.index] end else - name = selected_name + name = selected_name[pname] end if name then if fields.display then - if not advmarkers.display_marker(name) then - minetest.display_chat_message('Error displaying marker!') + if not advmarkers.display_marker(player, name) then + minetest.chat_send_player(pname, 'Error displaying marker!') end elseif fields.rename then - minetest.show_formspec('advmarkers-csm', 'size[6,3]' .. + minetest.show_formspec(pname, 'advmarkers-ssm', 'size[6,3]' .. 'label[0.35,0.2;Rename marker]' .. 'field[0.3,1.3;6,1;new_name;New name;' .. minetest.formspec_escape(name) .. ']' .. @@ -253,98 +347,81 @@ minetest.register_on_formspec_input(function(formname, fields) 'button[3,2;3,1;rename_confirm;Rename]') elseif fields.rename_confirm then if fields.new_name and #fields.new_name > 0 then - if advmarkers.rename_marker(name, fields.new_name) then - selected_name = fields.new_name + if advmarkers.rename_marker(pname, name, fields.new_name) then + selected_name[pname] = fields.new_name else - minetest.display_chat_message('Error renaming marker!') + minetest.chat_send_player(pname, 'Error renaming marker!') end - advmarkers.display_formspec() + advmarkers.display_formspec(pname) else - minetest.display_chat_message( + minetest.chat_send_player(pname, 'Please enter a new name for the marker.' ) end elseif fields.teleport then - minetest.show_formspec('advmarkers-csm', 'size[6,2.2]' .. + minetest.show_formspec(pname, 'advmarkers-ssm', 'size[6,2.2]' .. 'label[0.35,0.25;' .. minetest.formspec_escape( 'Teleport to a marker\n - ' .. name ) .. ']' .. - 'button[0,1.25;2,1;cancel;Cancel]' .. - 'button_exit[2,1.25;1,1;teleport_tpj;/tpj]' .. - 'button_exit[3,1.25;1,1;teleport_tpc;/tpc]' .. - 'button_exit[4,1.25;2,1;teleport_confirm;/teleport]') - elseif fields.teleport_tpj then - -- Teleport with /tpj - local pos = advmarkers.get_marker(name) - if pos and minetest.localplayer then - local cpos = minetest.localplayer:get_pos() - for _, dir in ipairs({'x', 'y', 'z'}) do - local distance = pos[dir] - cpos[dir] - minetest.run_server_chatcommand('tpj', dir .. ' ' .. - tostring(distance)) - end - else - minetest.display_chat_message('Error teleporting to marker!') - end - elseif fields.teleport_confirm or fields.teleport_tpc then + 'button[0,1.25;3,1;cancel;Cancel]' .. + 'button_exit[3,1.25;3,1;teleport_confirm;Teleport]') + elseif fields.teleport_confirm then -- Teleport with /teleport - local pos = advmarkers.get_marker(name) - local cmd - if fields.teleport_confirm then - cmd = 'teleport' + local pos = advmarkers.get_marker(pname, name) + if not pos then + minetest.chat_send_player(pname, 'Error teleporting to marker!') + elseif minetest.check_player_privs(pname, 'teleport') then + player:set_pos(pos) + minetest.chat_send_player(pname, 'Teleported to marker "' .. + name .. '".') else - cmd = 'tpc' - end - if pos and minetest.localplayer then - minetest.run_server_chatcommand(cmd, - pos.x .. ',' .. pos.y .. ',' .. pos.z) - else - minetest.display_chat_message('Error teleporting to marker!') + minetest.chat_send_player(pname, 'Insufficient privileges!') end elseif fields.delete then - minetest.show_formspec('advmarkers-csm', 'size[6,2]' .. + minetest.show_formspec(pname, 'advmarkers-ssm', 'size[6,2]' .. 'label[0.35,0.25;Are you sure you want to delete this marker?]' .. 'button[0,1;3,1;cancel;Cancel]' .. 'button[3,1;3,1;delete_confirm;Delete]') elseif fields.delete_confirm then - advmarkers.delete_marker(name) - selected_name = false - advmarkers.display_formspec() + advmarkers.delete_marker(pname, name) + selected_name[pname] = nil + advmarkers.display_formspec(pname) elseif fields.cancel then - advmarkers.display_formspec() - elseif name ~= selected_name then - selected_name = name - advmarkers.display_formspec() + advmarkers.display_formspec(pname) + elseif name ~= selected_name[pname] then + selected_name[pname] = name + if not fields.quit then + advmarkers.display_formspec(pname) + end end elseif fields.display or fields.delete then - minetest.display_chat_message('Please select a marker.') + minetest.chat_send_player(pname, 'Please select a marker.') end return true end) -- Auto-add markers on death. -minetest.register_on_death(function() - if minetest.localplayer then - local name = os.date('Death on %Y-%m-%d %H:%M:%S') - local pos = minetest.localplayer:get_pos() - advmarkers.last_coords = pos - advmarkers.set_marker(pos, name) - minetest.display_chat_message('Added marker "' .. name .. '".') - end +minetest.register_on_dieplayer(function(player) + local name = os.date('Death on %Y-%m-%d %H:%M:%S') + local pos = player:get_pos() + advmarkers.last_coords[player] = pos + advmarkers.set_marker(player, pos, name) + minetest.chat_send_player(player:get_player_name(), + 'Added marker "' .. name .. '".') end) -- Allow string exporting minetest.register_chatcommand('mrkr_export', { - params = '[old]', + params = '', description = 'Exports an advmarkers string containing all your markers.', - func = function(param) + func = function(name, param) local export if param == 'old' then - export = advmarkers.export('M') + export = advmarkers.export(name, 'M') else - export = advmarkers.export() + export = advmarkers.export(name) end - minetest.show_formspec('advmarkers-ignore', + minetest.show_formspec(name, 'advmarkers-ignore', 'field[_;Your marker export string;' .. minetest.formspec_escape(export) .. ']') end @@ -355,8 +432,8 @@ minetest.register_chatcommand('mrkr_import', { params = '', description = 'Imports an advmarkers string. This will not overwrite ' .. 'existing markers that have the same name.', - func = function(param) - if advmarkers.import(param) then + func = function(name, param) + if advmarkers.import(name, param) then return true, 'Markers imported!' else return false, 'Invalid advmarkers string!' @@ -366,30 +443,53 @@ minetest.register_chatcommand('mrkr_import', { -- Chat channels .coords integration. -- You do not need to have chat channels installed for this to work. -if not minetest.registered_on_receiving_chat_message then - minetest.registered_on_receiving_chat_message = - minetest.registered_on_receiving_chat_messages +local function get_coords(msg) + local s, e = msg:find('Current Position: %-?[0-9]+, %-?[0-9]+, %-?[0-9]+%.') + local pos = false + if s and e then + pos = string_to_pos(msg:sub(s + 18, e - 1)) + end + return pos end -table.insert(minetest.registered_on_receiving_chat_message, 1, function(msg) - local s, e = msg:find('Current Position: %-?[0-9]+, %-?[0-9]+, %-?[0-9]+%.') - if s and e then - local pos = string_to_pos(msg:sub(s + 18, e - 1)) - if pos then - advmarkers.last_coords = pos +-- Get global co-ords +table.insert(minetest.registered_on_chat_messages, 1, function(name, msg) + local pos = get_coords(msg) + if pos then + advmarkers.last_coords = {} + for _, player in ipairs(get_connected_players()) do + advmarkers.last_coords[player:get_player_name()] = pos end end end) --- Add '.mrkrthere' -minetest.register_chatcommand('mrkrthere', { - params = '', - description = 'Adds a (temporary) marker at the last ".coords" position.', - func = function(param) - if not advmarkers.last_coords then - return false, 'No-one has used ".coords" and you have not died!' - elseif not advmarkers.set_hud_pos(advmarkers.last_coords) then - return false, 'Error setting the marker!' +-- Override chat_send_player to get PMed co-ords etc +local old_chat_send_player = minetest.chat_send_player +function minetest.chat_send_player(name, msg, ...) + if type(name) == 'string' and type(msg) == 'string' and + get_player_by_name(name) then + local pos = get_coords(msg) + if pos then + advmarkers.last_coords[name] = pos end end + return old_chat_send_player(name, msg, ...) +end + +-- Clean up variables if a player leaves +minetest.register_on_leaveplayer(function(player) + local name = get_player(player, 0) + hud[name] = nil + formspec_list[name] = nil + selected_name[name] = nil + advmarkers.last_coords[name] = nil +end) + +-- Add '/mrkrthere' +minetest.register_chatcommand('mrkrthere', { + params = '', + description = 'Alias for "/mrkr there".', + func = function(name, param) + return minetest.registered_chatcommands['mrkr'].func(name, 'there') + end })