- initial beta version
This commit is contained in:
Leslie Krause 2020-02-28 11:08:31 -05:00
commit 37fd0976ab
3 changed files with 346 additions and 0 deletions

105
README.txt Normal file
View File

@ -0,0 +1,105 @@
Configuration Panel Mod v1.0
By Leslie Krause
Configuration Panel is an API extension for Minetest that allows seamless loading of mod
configuration at runtime. It provides much greater flexibility than Minetest's builtin
'Settings' interface, since the mod configuration itself is read as a native Lua script.
Hence it's possible to include simple data structures without the need for serialization,
in addition to temporary variables, mathematical expressions, and conditional branching.
While some might be opposed to using Lua for configuration purposes, arguing that it is
anti-pattern, that's not actually true. In fact, Lua itself was originally intended to
double as a configuration language, like JSON, so it is very befitting of its purpose.
"An important use of Lua is as a configuration language...."
from Programming in Lua: Extending your Application (https://www.lua.org/pil/25.html)
There is only one function call necessary to load your mod's configuration:
minetest.load_config( base_config, options )
Automatically loads the mod configuration at server-startup according to the options
and returns a table of key-value pairs.
* 'base_config' is the default mod configuration (optional)
* 'options' are the additional options that effect loading behavior (optional)
By default, the configuration is first loaded from the 'config.lua' script within the mod
directory. If not found, it will instead be loaded from a script residing within the
'config' subdirectory of the current world. That script must be named the same as the mod
but with a '.lua' extension, of course.
This would be the typical order of loading on Linux distros of Minetest:
/usr/local/share/minetest/games/minetest_game/mods/sample_mod/config.lua
/home/minetest/.minetest/worlds/sample_world/config/sample_mod.lua
By specifying the option 'can_override = true', both scripts will be loaded, allowing for
the world configuration to override the game configuration. So for example
/usr/local/share/minetest/games/minetest_game/mods/sample_mod/config.lua
> allow_fly = false
> allow_walk = false
> allow_swim = false
/home/minetest/.minetest/worlds/sample_world/config/sample_mod.lua
> allow_swim = true
Hence, 'allow_fly' and 'allow_walk' will be false, whereas 'allow_swim' will be true. By
default, however, all three would remain false since the world configuration is ignored
whenever the game configuration is found.
A chat command is also available for editing the configuration directly in-game. Simply
type '/config' followed by the mod name to configure. The interface is self-explanatory.
Repository
----------------------
Browse source code:
https://bitbucket.org/sorcerykid/config
Download archive:
https://bitbucket.org/sorcerykid/config/get/master.zip
https://bitbucket.org/sorcerykid/config/get/master.tar.gz
Dependencies
----------------------
Default Mod (required)
https://github.com/minetest-game-mods/default
ActiveFormspecs Mod v2.6 (required)
https://bitbucket.org/sorcerykid/formspecs
Installation
----------------------
1) Unzip the archive into the mods directory of your game.
2) Rename the config-master directory to "config".
3) Add "config" as a dependency to any mods using the API.
Source Code License
----------------------
The MIT License (MIT)
Copyright (c) 2020, Leslie Krause (leslie@searstower.org)
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.
For more details:
https://opensource.org/licenses/MIT

2
depends.txt Normal file
View File

@ -0,0 +1,2 @@
default
formspecs

239
init.lua Normal file
View File

@ -0,0 +1,239 @@
--------------------------------------------------------
-- Minetest :: Configuration Panel Mod (config)
--
-- See README.txt for licensing and other information.
-- Copyright (c) 2016-2020, Leslie E. Krause
--
-- ./games/minetest_game/mods/config/init.lua
--------------------------------------------------------
local world_path = minetest.get_worldpath( )
local configured_mods = { }
---------------------
-- Private Methods --
---------------------
local function import( config, filename )
local func = loadfile( filename )
if func then
setfenv( func, config )
local status = pcall( func )
if not status then
error( "Syntax error in configuration file: " .. filename )
end
return true
end
return false
end
local function load_world_config( mod_name )
local file = io.open( world_path .. "/config/" .. mod_name .. ".lua", "r" )
if not file then return nil end
local data = file:read( "*all" )
file:close( )
return data
end
local function load_game_config( mod_name )
local file = io.open( minetest.get_modpath( mod_name ) .. "/config.lua", "r" )
if not file then return nil end
local data = file:read( "*all" )
file:close( )
return data
end
local function save_world_config( mod_name, data )
local file = io.open( world_path .. "/config/" .. mod_name .. ".lua", "w" )
if not file then return false end
file:write( data )
file:close( )
return true
end
local function save_game_config( mod_name, data )
local file = io.open( minetest.get_modpath( mod_name ) .. "/config.lua", "w" )
if not file then return false end
file:write( data )
file:close( )
return true
end
local function create_world_config( mod_name )
local file = io.open( world_path .. "/config/" .. mod_name .. ".lua", "w" )
if not file then return false end
file:close( )
return true
end
local function create_game_config( mod_name )
local file = io.open( minetest.get_modpath( mod_name ) .. "/config.lua", "w" )
if not file then return false end
file:close( )
return true
end
local function delete_world_config( mod_name )
return os.remove( world_path .. "/config/" .. mod_name .. ".lua" ) ~= nil
end
local function delete_game_config( mod_name )
return os.remove( minetest.get_modpath( mod_name ) .. "/config.lua" ) ~= nil
end
local function open_config_editor( player_name, mod_name )
local origin_idx
local content
local delete_config, create_config, load_config, save_config
local function reset_origin_map( idx )
origin_idx = idx
if origin_idx == 1 then
delete_config = delete_game_config
create_config = create_game_config
load_config = load_game_config
save_config = save_game_config
else
delete_config = delete_world_config
create_config = create_world_config
load_config = load_world_config
save_config = save_world_config
end
end
local function get_formspec( )
local formspec =
"size[10.0,8.5]" ..
default.gui_bg ..
default.gui_bg_img ..
string.format( "label[0.0,0.1;Configuration for mod '%s']", mod_name ) ..
"label[2.2,8.0;Origin:]" ..
string.format( "dropdown[3.1,7.9;2.6,1.0;origin;Game Config,World Config;%d;true]", origin_idx )
if not content then
formspec = formspec ..
"box[0.0,0.8;9.8.0,6.6;#222222]" ..
"label[4.0,3.8;File does not exist.]" ..
"button_exit[0.0,7.8;2.0,1.0;close;Close]" ..
"button[8.0,7.8;2.0,1.0;create;Create]"
else
formspec = formspec ..
"textarea[0.3,0.8;10.0,7.6;content;;" .. minetest.formspec_escape( content ) .. "]" ..
"button_exit[0.0,7.8;2.0,1.0;close;Close]" ..
"button[6.0,7.8;2.0,1.0;delete;Delete]" ..
"button[8.0,7.8;2.0,1.0;save;Save]"
end
return formspec
end
local function on_close( state, player, fields )
if fields.quit then return end -- short-circuit on form closure
if fields.save then
content = fields.content
save_config( mod_name, content )
elseif fields.load then
content = load_config( mod_name )
minetest.update_form( player_name, get_formspec ( ) )
elseif fields.delete then
if not delete_config( mod_name ) then
minetest.destroy_form( player_name )
minetest.chat_send_player( player_name, "Unable to delete configuration file." )
end
content = nil
minetest.update_form( player_name, get_formspec ( ) )
elseif fields.create then
if not create_config( mod_name ) then
minetest.destroy_form( player_name )
minetest.chat_send_player( player_name, "Unable to create configuration file." )
end
content = ""
minetest.update_form( player_name, get_formspec ( ) )
elseif fields.origin then
reset_origin_map( fields.origin )
content = load_config( mod_name )
minetest.update_form( player_name, get_formspec ( ) )
end
end
reset_origin_map( 1 )
content = load_config( mod_name )
minetest.create_form( nil, player_name, get_formspec( ), on_close )
end
--------------------
-- Public Methods --
--------------------
minetest.load_config = function ( base_config, options )
local name = minetest.get_current_modname( )
local path = minetest.get_modpath( name )
local config = { } or base_config
local status
if not options then options = { } end
config.core = {
MOD_NAME = name,
MOD_PATH = path,
WORLD_PATH = world_path,
sprintf = string.format,
date = os.date,
time = os.time,
}
if options.can_override then
status = import( config, path .. "/config.lua" )
status = import( config, world_path .. "/config/" .. name .. ".lua" ) or status
else
status = import( config, path .. "/config.lua" ) or import( config, world_path .. "/config/" .. name .. ".lua" )
end
if not status then
minetest.log( "warning", "Missing configuration file for mod \"" .. name .. "\"" )
end
configured_mods[ name ] = {
base_config = base_config,
can_refresh = options.can_refresh,
can_override = options.can_override,
}
config.core = nil
return config
end
------------------------------
-- Registered Chat Commands --
------------------------------
minetest.register_chatcommand( "config", {
description = "View and edit the configuration for a given mod.",
privs = { server = true },
func = function( player_name, param )
if not string.match( param, "^[a-zA-Z0-9_]+$" ) then
return false, "Invalid mod name."
elseif not configured_mods[ param ] then
return false, "Configuration not available."
end
open_config_editor( player_name, param )
end
} )