From 5106eb4eb1eb178bfa588f74fdbb879b2be2e8cb Mon Sep 17 00:00:00 2001 From: Apollo Date: Tue, 24 May 2022 17:22:24 -0400 Subject: [PATCH] Added Waypoints & Limits Limits: Players can now be governed by how many total homes they can place Default values are: Basic = 2 Advanced = 4 Super = 8 Unlimited = inf Where Basic is default for singleplayer modes (ie given by default) Waypoints: Players can now choose what homes become visable via a waypoint. THIS FEATURE REQUIRES MINETEST VERSION 5.0 OR HIGHER Removing a home that has been "marked" as a waypoint also will remove the waypoint Setting a home to a new location will update the waypoint to the new location The api for waypoints has been reworked to support golang-like function responses (success, errmsg, value) Waypoints currently use (0, 200, 0) RGB values 0x0c800 in HEX This mod is ready to prepare for release onto contentdb. (v1.0) --- README.md | 36 ++++++++-- init.lua | 174 +++++++++++++++++++++++++++++++++++++++++++---- settings.lua | 25 +++++++ settingtypes.txt | 9 +++ store_base.lua | 138 ++++++++++++++++++++++++++++++++++++- 5 files changed, 363 insertions(+), 19 deletions(-) create mode 100644 settings.lua create mode 100644 settingtypes.txt diff --git a/README.md b/README.md index be87400..eee47ae 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,44 @@ # home_point -A /sethome and /home mod for Minetest +A multi homes teleport feature. -## CMDs +## Commands -All commands require `home_point` priviledge +All commands require `home_point` privilege (And `/sh` needs one of the further privileges, See Limiting homes section below) * `/h (place_name)` Goes to a place called place_name unless not given then your player name is used. * `/sh (place_name)` Saves a place called place_name unless not given then your player name is used. * `/rh (place_name)` Removes a place called place_name unless not given then your player name is used. * `/lh` Lists all your homes, if you don't have any it will tell you how to make one. +* `/wh (place_name)` Places a waypoint at the designated home till you log-out/quit, if place_name not given then your player name is used. + +> `/wh` actually toggles a waypoint, and when `/sh` is used with the same home as a waypoint a new waypoint will be place at the new location. (Also when `/rh` is used on a home with a waypoint the waypoint will be removed) ## Notice -This mod uses mod storage... this means if the server crashes the mod will lose a few home points. +This mod uses mod storage... this means if the server crashes the mod could lose a few home points. + +## Limiting homes + +There are 4 default privileges which are defined in settings. + +All these do is limit the number of home_points players can set. + +> (For servers) This means you need to add `home_point` and at least `home_point_basic` to your default_privs in minetest.conf, +so new players can at least use home_point. (See [here](https://github.com/minetest/minetest/blob/master/builtin/settingtypes.txt#L1166) for info on default_privs in minetest.conf) + +### home_point_basic + +Defaults to max of 2 homes (Change with `home_point.home_point_basic` in settings) + +### home_point_advanced + +Defaults to max of 4 homes (Change with `home_point.home_point_advanced` in settings) + +### home_point_super + +Defaults to max of 8 homes (Change with `home_point.home_point_super` in settings) + +### home_point_unlimited + +Allows unlimited number of homes (Not defined in settings, as unlimited is assumed to be unlimited) diff --git a/init.lua b/init.lua index e92ff4b..82b677f 100644 --- a/init.lua +++ b/init.lua @@ -4,9 +4,10 @@ local modpath = minetest.get_modpath("home_point") home_point = {} home_point.storage = minetest.get_mod_storage() +home_point.temp = {} -- Used to track who has what waypoints set for what homes -- Actually it's our api so if someone else wanted to monkey with points they can -dofile(modpath.."/store_base.lua") +dofile(modpath..DIR_DELIM.."store_base.lua") -- Assistants function home_point.firstToUpper(str) @@ -24,7 +25,29 @@ function home_point.split(inputstr, sep) return t end -minetest.register_privilege("home_point", "Gives access to home point") +dofile(modpath..DIR_DELIM.."settings.lua") + +minetest.register_privilege("home_point", { + description = "Gives access to home point commands", + give_to_singleplayer = true -- This should mean in singleplayer you start off with the defaults +}) + +minetest.register_privilege("home_point_basic", { + description = "Gives access upto "..tostring(home_point.home_point_basic).." homes", + give_to_singleplayer = true -- This should mean in singleplayer you start off with the defaults +}) +minetest.register_privilege("home_point_advanced", { + description = "Gives access upto "..tostring(home_point.home_point_advanced).." homes", + give_to_singleplayer = false +}) +minetest.register_privilege("home_point_super", { + description = "Gives access upto "..tostring(home_point.home_point_super).." homes", + give_to_singleplayer = false +}) +minetest.register_privilege("home_point_unlimited", { + description= "Gives access to unlimited homes", + give_to_singleplayer = false +}) -- Set home minetest.register_chatcommand("sh", { @@ -37,13 +60,63 @@ minetest.register_chatcommand("sh", { if name ~= "singleplayer" then if minetest.get_player_by_name(name) == nil then return false, "You must be online to use this command" end end + -- Ensure we stop users from placing unlimited homes when they are not allowed to + local homes = home_point.count(name) + if not minetest.check_player_privs(name, {home_point_unlimited=true}) then + if minetest.check_player_privs(name, {home_point_super=true}) then + if homes+1 > home_point.home_point_super then + return false, "You can only have "..tostring(home_point.home_point_super).." homes, currently you have "..tostring(homes).."." + end + elseif minetest.check_player_privs(name, {home_point_advanced=true}) then + if homes+1 > home_point.home_point_basic then + return false, "You can only have "..tostring(home_point.home_point_advanced).." homes, currently you have "..tostring(homes).."." + end + elseif minetest.check_player_privs(name, {home_point_basic=true}) then + if homes+1 > home_point.home_point_basic then + return false, "You can only have "..tostring(home_point.home_point_basic).." homes, currently you have "..tostring(homes).."." + end + else + return false, "You appear to not have access to place any homes, You need home_point and one of these (home_point_basic, home_point_advanced, home_point_super, or home_point_unlimited)." + end + end + -- Setup the place/home local place = string.match(param, "^([%a%d_-]+)") or "" if place ~= "" then minetest.log("action", "[home_point] "..name.." saves a point as '"..place.."'") - return home_point.save(name, place), "Saved as "..place + local rc = home_point.save(name, place) + -- Update a waypoints position if we are showing that home + local is_way = home_point.waypoint_is(name, place) + if is_way.success == true and is_way.value ~= -1 then + local pl = home_point.place_waypoint(name, place) + if pl.success ~= true then + minetest.log("action", "[home_point] Err="..pl.errmsg.." Val="..minetest.serialize(pl.value)) + end + pl = home_point.place_waypoint(name, place) + if pl.success ~= true then + minetest.log("action", "[home_point] Err="..pl.errmsg.." Val="..minetest.serialize(pl.value)) + end + else + minetest.log("action", "[home_point] Err="..is_way.errmsg.." Val="..minetest.serialize(is_way.value)) + end + return rc, "Saved as "..place else minetest.log("action", "[home_point] "..name.." saves a point as '"..name.."'") - return home_point.save(name, name), "Saved as "..name + local rc = home_point.save(name, name) + -- Update a waypoints position if we are showing that home + local is_way = home_point.waypoint_is(name, name) + if is_way.success == true and is_way.value ~= -1 then + local pl = home_point.place_waypoint(name, name) + if pl.success ~= true then + minetest.log("action", "[home_point] Err="..pl.errmsg.." Val="..minetest.serialize(pl.value)) + end + pl = home_point.place_waypoint(name, name) + if pl.success ~= true then + minetest.log("action", "[home_point] Err="..pl.errmsg.." Val="..minetest.serialize(pl.value)) + end + else + minetest.log("action", "[home_point] Err="..is_way.errmsg.." Val="..minetest.serialize(is_way.value)) + end + return rc, "Saved as "..name end return false, "Uable to determine place_name" end, @@ -94,15 +167,41 @@ minetest.register_chatcommand("rh", { if minetest.get_player_by_name(name) == nil then return false, "You must be online to use this command" end end local place = string.match(param, "^([%a%d_-]+)") or "" - local target = nil + local resp = "" if place ~= "" then - home_point.remove(name, place) minetest.log("action", "[home_point] "..name.." removes home of '"..place.."'") - minetest.chat_send_player(name, ""..place.." removed") + --minetest.chat_send_player(name, ""..place.." removed") + resp = place.." removed" + -- Remove waypoints from deleted homes + local is_way = home_point.waypoint_is(name, place) + if is_way.success and is_way.value ~= -1 then + local place_way = home_point.place_waypoint(name, place) + if place_way.success == true then + minetest.log("action", "[home_point] "..name.." removed waypoint at '"..place.."'") + else + minetest.log("action", "[home_point] Err="..place_way.errmsg.." Val="..minetest.serialize(place_way.value)) + end + end + if home_point.remove(name, place) then + minetest.chat_send_player(name, reps) + end else - home_point.remove(name, name) minetest.log("action", "[home_point] "..name.." removes home of '"..name.."'") - minetest.chat_send_player(name, ""..name.." removed") + --minetest.chat_send_player(name, ""..name.." removed") + resp = name.." removed" + -- Remove waypoints from deleted homes + local is_way = home_point.waypoint_is(name, name) + if is_way.success and is_way.value ~= -1 then + local place_way = home_point.place_waypoint(name, name) + if place_way.success == true then + minetest.log("action", "[home_point] "..name.." removed waypoint at '"..name.."'") + else + minetest.log("action", "[home_point] Err="..place_way.errmsg.." Val="..minetest.serialize(place_way.value)) + end + end + if home_point.remove(name, name) then + minetest.chat_send_player(name, reps) + end end end, }) @@ -119,11 +218,20 @@ minetest.register_chatcommand("lh", { if minetest.get_player_by_name(name) == nil then return false, "You must be online to use this command" end end local list = home_point.list(name) - if list ~= nil then - local r = "Homes: " .. tostring(#list+1) .. "\n" + if list ~= nil and home_point.count(name) ~= 0 then + --minetest.log("action", "[home_point] "..type(list).." "..minetest.serialize(list).." "..tostring(#list)) + local r = "Homes: " .. tostring(home_point.count(name)) .. "\n" for k in pairs(list) do local pos = list[k].split(list[k], " ") - r = r .. " " .. k .. " (" .. pos[1] .. ", " .. pos[2] .. ", " .. pos[3] .. ")\n" + local is_way = home_point.waypoint_is(name, k) + if is_way.success == false then + minetest.log("action", "[home_point] Err="..is_way.errmsg.." Val="..minetest.serialize(is_way.value)) + end + if is_way.success == true and is_way.value ~= -1 then + r = r .. " " .. k .. " (" .. pos[1] .. ", " .. pos[2] .. ", " .. pos[3] .. ") *\n" + else + r = r .. " " .. k .. " (" .. pos[1] .. ", " .. pos[2] .. ", " .. pos[3] .. ")\n" + end end return true, r else @@ -132,4 +240,46 @@ minetest.register_chatcommand("lh", { end, }) +-- Toggle waypoint on home +minetest.register_chatcommand("wh", { + privs = { + home_point = true + }, + description = "Toggles a waypoint at a home point", + func = function (name, param) + -- Don't allow offline players + if name ~= "singleplayer" then + if minetest.get_player_by_name(name) == nil then return false, "You must be online to use this command" end + end + local place = string.match(param, "^([%a%d_-]+)") or "" + if home_point.count(name) ~= 0 then + if place ~= "" then + if home_point.get(name, place) ~= "" then + local rc = home_point.place_waypoint(name, place) + if rc.success == true then + minetest.log("action", "[home_point] "..name.." "..rc.errmsg.." at "..place.." '"..home_point.get(name, place).."'") + minetest.chat_send_player(name, rc.errmsg.." at "..place) + else + minetest.log("action", "[home_point] Err="..rc.errmsg.." Val="..minetest.serialize(rc.value)) + end + else + minetest.chat_send_player(name, "No such home point "..place) + end + else + if home_point.get(name, name) ~= "" then + local rc = home_point.place_waypoint(name, name) + if rc.success == true then + minetest.log("action", "[home_point] "..name.." "..rc.errmsg.." at "..name.." '"..home_point.get(name, name).."'") + minetest.chat_send_player(name, rc.errmsg.." at "..name) + else + minetest.log("action", "[home_point] Err="..rc.errmsg.." Val="..minetest.serialize(rc.value)) + end + end + end + else + minetest.chat_send_player(name, "You don't have any homes yet, use /sh to place a home.") + end + end +}) + minetest.log("action", "[home_point] Ready") diff --git a/settings.lua b/settings.lua new file mode 100644 index 0000000..37c13da --- /dev/null +++ b/settings.lua @@ -0,0 +1,25 @@ + +home_point.home_point_basic = minetest.settings:get("home_point.home_point_basic") +if home_point.home_point_basic == nil then + home_point.home_point_basic = 2 + minetest.settings:set("home_point.home_point_basic", 2) +else + home_point.home_point_basic = tonumber(home_point.home_point_basic) +end + +home_point.home_point_advanced = minetest.settings:get("home_point.home_point_advanced") +if home_point.home_point_advanced == nil then + home_point.home_point_advanced = 4 + minetest.settings:set("home_point.home_point_advanced", 4) +else + home_point.home_point_advanced = tonumber(home_point.home_point_advanced) +end + +home_point.home_point_super = minetest.settings:get("home_point.home_point_super") +if home_point.home_point_super == nil then + home_point.home_point_super = 8 + minetest.settings:set("home_point.home_point_super", 8) +else + home_point.home_point_super = tonumber(home_point.home_point_super) +end + diff --git a/settingtypes.txt b/settingtypes.txt new file mode 100644 index 0000000..f7e7a93 --- /dev/null +++ b/settingtypes.txt @@ -0,0 +1,9 @@ + +# Limits how many homes basic users can use +home_point.home_point_basic (Home Point Basic) int 2 + +# Limits how many homes advanced users can use +home_point.home_point_advanced (Home Point Advanced) int 4 + +# Limits how many homes super users can use +home_point.home_point_super (Home Point Super) int 8 diff --git a/store_base.lua b/store_base.lua index 0cf85f6..47941ef 100644 --- a/store_base.lua +++ b/store_base.lua @@ -6,11 +6,11 @@ function home_point.save(pname, place_name) -- If the player really is a player if p ~= nil then -- Get their position and convert it to string - local pos = p:get_pos() - pos = "".. math.floor(pos.x) .." ".. math.floor(pos.y+1) .." ".. math.floor(pos.z) + local pos = vector.round(p:get_pos()) + pos = "".. pos.x .." ".. pos.y+1 .." ".. pos.z -- Obtain the player's homes update/insert then update the mods storage local tmp = minetest.deserialize(home_point.storage:get_string(pname)) or {} - tmp[place_name] = pos + tmp[place_name] = pos home_point.storage:set_string(pname, minetest.serialize(tmp)) return true end @@ -46,14 +46,19 @@ function home_point.remove(pname, place_name) if tmp ~= nil then -- Make a new table and add all except selected place local new = {} + local found = false for k in pairs(tmp) do if k ~= place_name then new[k] = tmp[k] + else + found = true end end home_point.storage:set_string(pname, minetest.serialize(new)) + return found end end + return false end -- Returns list of home and position for a player @@ -62,4 +67,131 @@ function home_point.list(pname) if p ~= nil then return minetest.deserialize(home_point.storage:get_string(pname)) end + return {} end + +-- Returns the actual count/number of homes +function home_point.count(pname) + local home_count = 0 + local p = minetest.get_player_by_name(pname) or nil + if p ~= nil then + local list = home_point.list(pname) + if list ~= nil then + for k in pairs(list) do + home_count = home_count + 1 + end + end + end + return home_count +end + +-- Waypoints are temporary, as in when the user quits/logs out I need to clear them from the temp list + +-- Do we have a waypoint hud id for the given home point? +function home_point.waypoint_is(pname, home) + if home_point.get(pname, home) ~= "" then + local waypoints = home_point.temp[pname] or {} + --minetest.log("action", minetest.serialize(waypoints)) + for _, way in pairs(waypoints) do + local way_name = way[1] + local way_hid = way[2] + --minetest.log("action", "[home_point.waypoint_is] way_name='"..way_name.."' way_hid="..tostring(way_hid)) + if way_name == home then + return {success=true, errmsg="", value=way_hid} + end + end + return {success=true, errmsg="Home point '"..home.."' doesn't have a waypoint set.", value=-1} + else + return {success=false, errmsg="No such home point '"..home.."'.", value=nil} + end +end + +-- Adds the given hud id to the given home point +function home_point.waypoint_add(pname, home, hud_id) + if home_point.get(pname, home) ~= "" then + local waypoints = home_point.temp[pname] or {} + local tab = {} + table.insert(tab, home) + table.insert(tab, hud_id) + table.insert(waypoints, tab) + home_point.temp[pname] = waypoints + return {success=true, errmsg="", value=tab} + else + return {success=false, errmsg="No such home point '"..home.."'", value=nil} + end +end + +function home_point.waypoint_remove(pname, home) + if home_point.get(pname, home) ~= "" then + local waypoints = home_point.temp[pname] or {} + local new_waypoints = {} + for _, way in pairs(waypoints) do + local way_name = way[1] + local way_hid = way[2] + --minetest.log("action", "[home_point.waypoint_remove] way_name='"..way_name.."' way_hid="..tostring(way_hid)) + if way_name ~= home then + local tab = {} + table.insert(tab, way_name) + table.insert(tab, way_hid) + table.insert(new_waypoints, tab) + end + end + home_point.temp[pname] = new_waypoints + return {success=true, errmsg="", value=nil} + else + return {success=false, errmsg="No such home point '"..home.."'", value=nil} + end +end + +function home_point.place_waypoint(pname, home) + if home_point.get(pname, home) ~= "" then + -- Obtain the actual pos + local raw_pos = home_point.split(home_point.get(pname, home), " ") + local pos = {x=tonumber(raw_pos[1]), y=tonumber(raw_pos[2]), z=tonumber(raw_pos[3])} + local player = minetest.get_player_by_name(pname) + + local is_way = home_point.waypoint_is(pname, home) + if is_way.success == true then + if is_way.value ~= -1 then + -- Remove + local rm = home_point.waypoint_remove(pname, home) + if rm.success ~= true then + return {success=false, errmsg="home_point.waypoint_remove returned error", value=rm} + else + player:hud_remove(is_way.value) + return {success=true, errmsg="Removed waypoint", value=nil} + end + else + -- Add + local add = home_point.waypoint_add(pname, home, player:hud_add({ + hud_elem_type = "waypoint", + world_pos = vector.subtract(pos, {x=0, y=1, z=0}), + name = home, + number = 0x00c800 + })) + if add.success ~= true then + return {success=false, errmsg="home_point.waypoint_add returned error", value=add} + else + return {success=true, errmsg="Created waypoint", value=add.value} + end + end + else + return {success=false, errmsg="home_point.waypoint_is returned error", value=is_way} + end + else + return {success=false, errmsg="No such home point '"..home.."'", value=nil} + end +end + +minetest.register_on_leaveplayer(function(player) + local pname = player:get_player_name() + if home_point.temp[pname] ~= nil then + local new = {} + for name, tab in pairs(home_point.temp) do + if name ~= pname then + new[name] = tab + end + end + home_point.temp = new + end +end)