commit 49241b8a50f1079ecd4af87e026af4450153283c Author: rubenwardy Date: Fri Dec 30 22:08:38 2016 +0000 Initial Commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d450949 --- /dev/null +++ b/.gitignore @@ -0,0 +1,85 @@ + +# Created by https://www.gitignore.io/api/lua,linux,windows + +### Lua ### +# Compiled Lua sources +luac.out + +# luarocks build files +*.src.rock +*.zip +*.tar.gz + +# Object files +*.o +*.os +*.ko +*.obj +*.elf + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo +*.def +*.exp + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + + + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.gitignore.io/api/lua,linux,windows diff --git a/ChatCmdBuilder.lua b/ChatCmdBuilder.lua new file mode 100644 index 0000000..657695a --- /dev/null +++ b/ChatCmdBuilder.lua @@ -0,0 +1,231 @@ +perplayer_gamemode.ChatCmdBuilder = {} +local ChatCmdBuilder = perplayer_gamemode.ChatCmdBuilder + +function ChatCmdBuilder.new(name, func, def) + def = def or {} + local cmd = ChatCmdBuilder.build(func) + cmd.def = def + def.func = cmd.run + minetest.register_chatcommand(name, def) + return cmd +end + +local STATE_READY = 1 +local STATE_PARAM = 2 +local STATE_PARAM_TYPE = 3 +local bad_chars = {} +bad_chars["("] = true +bad_chars[")"] = true +bad_chars["."] = true +bad_chars["%"] = true +bad_chars["+"] = true +bad_chars["-"] = true +bad_chars["*"] = true +bad_chars["?"] = true +bad_chars["["] = true +bad_chars["^"] = true +bad_chars["$"] = true +local function escape(char) + if bad_chars[char] then + return "%" .. char + else + return char + end +end + +function ChatCmdBuilder.build(func) + local cmd = { + _subs = {} + } + function cmd:sub(route, func, def) + print("Parsing " .. route) + + def = def or {} + if string.trim then + route = string.trim(route) + end + + local sub = { + pattern = "^", + params = {}, + func = func + } + + -- End of param reached: add it to the pattern + local param = "" + local param_type = "" + local should_be_eos = false + local function finishParam() + if param ~= "" and param_type ~= "" then + print(" - Found param " .. param .. " type " .. param_type) + + if param_type == "pos" then + sub.pattern = sub.pattern .. "%(? *([%d.]+) *, *([%d.]+) *, *([%d.]+) *%)?" + elseif param_type == "text" then + sub.pattern = sub.pattern .. "(*+)" + should_be_eos = true + elseif param_type == "number" then + sub.pattern = sub.pattern .. "([%d.]+)" + elseif param_type == "int" then + sub.pattern = sub.pattern .. "([%d]+)" + else + if param_type ~= "word" then + print("Unrecognised param_type=" .. param_type .. ", using 'word' type instead") + param_type = "word" + end + sub.pattern = sub.pattern .. "([^ ]+)" + end + + table.insert(sub.params, param_type) + + param = "" + param_type = "" + end + end + + -- Iterate through the route to find params + local state = STATE_READY + for i = 1, #route do + local c = route:sub(i, i) + if should_be_eos then + error("Should be end of string. Nothing is allowed after a param of type text.") + end + + if state == STATE_READY then + if c == ":" then + print(" - Found :, entering param") + state = STATE_PARAM + param_type = "word" + else + sub.pattern = sub.pattern .. escape(c) + end + elseif state == STATE_PARAM then + if c == ":" then + print(" - Found :, entering param type") + state = STATE_PARAM_TYPE + param_type = "" + elseif c:match("%W") then + print(" - Found nonalphanum, leaving param") + state = STATE_READY + finishParam() + sub.pattern = sub.pattern .. escape(c) + else + param = param .. c + end + elseif state == STATE_PARAM_TYPE then + if c:match("%W") then + print(" - Found nonalphanum, leaving param type") + state = STATE_READY + finishParam() + sub.pattern = sub.pattern .. escape(c) + else + param_type = param_type .. c + end + end + end + print(" - End of route") + finishParam() + print("Pattern: " .. sub.pattern) + + table.insert(self._subs, sub) + end + + if func then + func(cmd) + end + + cmd.run = function(name, param) + print("Running <" .. name .. "> CMD " .. param) + for i = 1, #cmd._subs do + local sub = cmd._subs[i] + local res = { string.match(param, sub.pattern) } + if res then + local pointer = 1 + local params = { name } + for j = 1, #sub.params do + local param = sub.params[j] + if param == "pos" then + local pos = { + x = tonumber(res[pointer]), + y = tonumber(res[pointer + 1]), + z = tonumber(res[pointer + 2]) + } + table.insert(params, pos) + pointer = pointer + 3 + elseif param == "number" or param == "int" then + table.insert(params, tonumber(res[pointer])) + pointer = pointer + 1 + else + table.insert(params, res[pointer]) + pointer = pointer + 1 + end + end + return sub.func(unpack(params)) + end + end + print("No matches") + end + + return cmd +end + +local function run_tests() + if not (ChatCmdBuilder.build(function(cmd) + cmd:sub("bar :one and :two:word", function(name, one, two) + if name == "singleplayer" and one == "abc" and two == "def" then + return true + end + end) + end))("singleplayer", "bar abc and def") then + error("Test 1 failed") + end + + local move = ChatCmdBuilder.build(function(cmd) + cmd:sub("move :target to :pos:pos", function(name, target, pos) + if name == "singleplayer" and target == "player1" and + pos.x == 0 and pos.y == 1 and pos.z == 2 then + return true + end + end) + end) + if not move("singleplayer", "move player1 to 0,1,2") then + error("Test 2 failed") + end + if not move("singleplayer", "move player1 to (0,1,2)") then + error("Test 3 failed") + end + if not move("singleplayer", "move player1 to 0, 1,2") then + error("Test 4 failed") + end + if not move("singleplayer", "move player1 to 0 ,1, 2") then + error("Test 5 failed") + end + if not move("singleplayer", "move player1 to 0, 1, 2") then + error("Test 6 failed") + end + if not move("singleplayer", "move player1 to 0 ,1 ,2") then + error("Test 7 failed") + end + if not move("singleplayer", "move player1 to ( 0 ,1 ,2)") then + error("Test 7 failed") + end + if move("singleplayer", "move player1 to abc,def,sdosd") then + error("Test 8 failed") + end + if move("singleplayer", "move player1 to abc def sdosd") then + error("Test 8 failed") + end + + if not (ChatCmdBuilder.build(function(cmd) + cmd:sub("does :one:int plus :two:int equal :three:int", function(name, one, two, three) + if name == "singleplayer" and one + two == three then + return true + end + end) + end))("singleplayer", "does 1 plus 2 equal 3") then + error("Test 9 failed") + end +end +if not minetest then + run_tests() +end diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..be631d5 --- /dev/null +++ b/LICENSE @@ -0,0 +1,7 @@ +Copyright 2017 rubenwardy + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..0b3ae1e --- /dev/null +++ b/README.md @@ -0,0 +1,10 @@ +# Per Player Gamemode + +Created by rubenwardy. Licensed under MIT. + +Enable or disable game mode per player + +* `/gamemode ` - requires `gamemode` privilege +* `/gamemode playername ` - requires `gamemode_super` privilege + +Where `` is either `creative` or `survival` diff --git a/depends.txt b/depends.txt new file mode 100644 index 0000000..9015762 --- /dev/null +++ b/depends.txt @@ -0,0 +1,2 @@ +sfinv +creative diff --git a/init.lua b/init.lua new file mode 100644 index 0000000..16a16e8 --- /dev/null +++ b/init.lua @@ -0,0 +1,82 @@ +perplayer_gamemode = { users = {} } + +dofile(minetest.get_modpath("perplayer_gamemode") .. "/ChatCmdBuilder.lua") + +function perplayer_gamemode.is_in_creative(name) + if perplayer_gamemode.users[name] == nil then + return minetest.setting_getbool("creative_mode") + else + return perplayer_gamemode.users[name] + end +end + +function perplayer_gamemode.set_creative(name, v) + perplayer_gamemode.users[name] = v + + local player = minetest.get_player_by_name(name) + if player then + local context = sfinv.contexts[name] + context.page = sfinv.get_homepage_name(player) + sfinv.set_player_inventory_formspec(player) + end +end + +if creative.is_in_creative then + creative.is_in_creative = perplayer_gamemode.is_in_creative +else + for name, def in pairs(sfinv.pages) do + if #name > 9 and name:sub(1, 9) == "creative:" then + def.is_in_nav = function(self, player, context) + return perplayer_gamemode.is_in_creative(player:get_player_name()) + end + end + end + + local old_homepage_name = sfinv.get_homepage_name + function sfinv.get_homepage_name(player) + if perplayer_gamemode.is_in_creative(player:get_player_name()) then + return "creative:all" + else + return "sfinv:crafting" + end + end +end + +minetest.register_privilege("gamemode", "Can set own creative mode") +minetest.register_privilege("gamemode_super", "Can set anyone's creative mode") + +local function is_creative(str) + return minetest.is_yes(str) or str == "creative" +end + +perplayer_gamemode.ChatCmdBuilder.new("gamemode", function(cmd) + cmd:sub(":value", function(name, value) + if minetest.check_player_privs(name, { gamemode = true }) then + local v = is_creative(value) + perplayer_gamemode.set_creative(name, v) + if v then + return true, "Turned creative mode on" + else + return true, "Turned creative mode off" + end + else + return false, "Missing privs: gamemode" + end + end) + + cmd:sub(":username :value", function(name, username, value) + if minetest.check_player_privs(name, { gamemode_super = true }) then + local v = is_creative(value) + perplayer_gamemode.set_creative(username, v) + if v then + return true, "Turned creative mode on for " .. username + else + return true, "Turned creative mode off for " .. username + end + else + return false, "Missing privs: gamemode_super" + end + end) +end, { + description = "Set game mode (creative or survival)" +})