- initial beta version
master
Leslie Krause 2020-09-30 19:15:50 -04:00
commit 370d353f5b
4 changed files with 447 additions and 0 deletions

90
README.txt Normal file
View File

@ -0,0 +1,90 @@
Debugging Console Mod v1.1
by Leslie Krause
Debugging Console provides an in-game HUD for developers and administrators to monitor
debug output more conveniently than via the terminal or minetest.chat_send_all().
Repository
----------------------
Browse source code...
https://bitbucket.org/sorcerykid/console
Download archive...
https://bitbucket.org/sorcerykid/console/get/master.zip
https://bitbucket.org/sorcerykid/console/get/master.tar.gz
Compatability
----------------------
Minetest 0.4.14+ required
Installation
----------------------
1) Unzip the archive into the mods directory of your game.
2) Rename the console-master directory to "console".
3) Add "console" 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
Multimedia License (textures, sounds, and models)
----------------------------------------------------------
Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0)
/textures/debug.png
by Flat Icons
obtained from https://www.flaticon.com/free-icon/debug_1485257
licensed to sorcerykid
You are free to:
Share — copy and redistribute the material in any medium or format.
Adapt — remix, transform, and build upon the material for any purpose, even commercially.
The licensor cannot revoke these freedoms as long as you follow the license terms.
Under the following terms:
Attribution — You must give appropriate credit, provide a link to the license, and
indicate if changes were made. You may do so in any reasonable manner, but not in any way
that suggests the licensor endorses you or your use.
No additional restrictions — You may not apply legal terms or technological measures that
legally restrict others from doing anything the license permits.
Notices:
You do not have to comply with the license for elements of the material in the public
domain or where your use is permitted by an applicable exception or limitation.
No warranties are given. The license may not give you all of the permissions necessary
for your intended use. For example, other rights such as publicity, privacy, or moral
rights may limit how you use the material.
For more details:
http://creativecommons.org/licenses/by-sa/3.0/

2
depends.txt Normal file
View File

@ -0,0 +1,2 @@
timekeeper
config

355
init.lua Normal file
View File

@ -0,0 +1,355 @@
--------------------------------------------------------
-- Minetest :: Debug Console Mod (console)
--
-- See README.txt for licensing and release notes.
-- Copyright (c) 2020, Leslie E. Krause
--
-- ./games/minetest_game/mods/console/init.lua
--------------------------------------------------------
local config = minetest.load_config( {
default_size = "none",
max_output_lines = 17,
max_review_lines = 100,
has_line_numbers = false,
} )
local player_huds = { }
local buffer = { }
local output = ""
local pipes = { }
local log_file
local unsafe_funcs = {
["pairs"] = true,
["ipairs"] = true,
["next"] = true,
["tonumber"] = true,
["tostring"] = true,
["printf"] = true,
["assert"] = true,
["error"] = true,
}
----------------
local gsub = string.gsub
local join = table.join
local max = math.max
local sprintf = string.format
local insert = table.insert
function printf( str, ... )
if type( str ) == "table" then
str = join( str, " ", function ( i, v )
return tostring( v )
end, true )
elseif type( str ) ~= "string" then
str = tostring( str )
end
if #{ ... } > 0 then
str = sprintf( str, ... )
end
gsub( str .. "\n", "(.-)\n", function ( line )
insert( buffer, line )
end )
output = ""
for i = max( 1, #buffer - config.max_output_lines + 1 ), #buffer do
if config.has_line_numbers then
output = output .. sprintf( "%03d: %s\n", i, buffer[ i ], "\n" )
else
output = output .. buffer[ i ] .. "\n"
end
end
for name, data in pairs( player_huds ) do
if data.size ~= "none" then
data.player:hud_change( data.refs.body_text, "text", output )
end
end
end
----------------
local _ = nil
local function is_match( text, glob )
-- use underscore variable to preserve captures
_ = { string.match( text, glob ) }
return #_ > 0
end
local function parse_id( param )
if is_match( param, "^([a-zA-Z][a-zA-Z0-9_]+)$" ) then
return { method = _[ 1 ] }
elseif is_match( param, "^([a-zA-Z][a-zA-Z0-9_]+)%.([a-zA-Z][a-zA-Z0-9_]+)$" ) then
return { parent = _[ 1 ], method = _[ 2 ] }
end
return nil
end
local function resize_hud( name, size )
local data = player_huds[ name ]
local player = data.player
local refs = data.refs
if size == data.size then return end
if refs then
player:hud_remove( refs.head_bg )
player:hud_remove( refs.head_text )
player:hud_remove( refs.head_icon )
player:hud_remove( refs.body_bg )
player:hud_remove( refs.body_text )
end
if size == "none" then
refs = nil
else
refs = { }
refs.body_bg = player:hud_add( {
hud_elem_type = "image",
text = "default_cloud.png^[colorize:#000000DD",
position = { x = size == "full" and 0.0 or 0.6, y = 0.5 },
scale = { x = -100, y = -50 },
alignment = { x = 1, y = 0 },
} )
refs.body_text = player:hud_add( {
hud_elem_type = "text",
text = output,
number = 0xFFFFFF,
position = { x = size == "full" and 0.0 or 0.6, y = 0.25 },
alignment = { x = 1, y = 1 },
offset = { x = 8, y = 38 },
} )
refs.head_bg = player:hud_add( {
hud_elem_type = "image",
text = "default_cloud.png^[colorize:#222222CC",
position = { x = size == "full" and 0.0 or 0.6, y = 0.25 },
scale = { x = -100, y = 2 },
alignment = { x = 1, y = 1 },
} )
refs.head_text = player:hud_add( {
hud_elem_type = "text",
text = "Debug Console",
number = 0x999999,
position = { x = size == "full" and 0.0 or 0.6, y = 0.25 },
alignment = { x = 1, y = 1 },
offset = { x = 36, y = 8 },
} )
refs.head_icon = player:hud_add( {
hud_elem_type = "image",
text = "debug.png",
position = { x = size == "full" and 0.0 or 0.6, y = 0.25 },
scale = { x = 1, y = 1 },
alignment = { x = 1, y = 1 },
offset = { x = 6, y = 4 },
} )
end
data.refs = refs
data.size = size
end
----------------
minetest.register_privilege( "debug", {
description = "Manage and review the debugging console.",
give_to_singleplayer = true,
} )
minetest.register_on_joinplayer( function( player )
local pname = player:get_player_name( )
if minetest.check_player_privs( pname, "debug" ) then
player_huds[ pname ] = { player = player }
resize_hud( pname, config.default_size )
end
end )
minetest.register_on_leaveplayer( function( player )
local pname = player:get_player_name( )
if player_huds[ pname ] then
player_huds[ pname ] = nil
end
end )
----------------
minetest.register_chatcommand( "debug", {
description = "Open the debug history viewer",
privs = { server = true },
func = function( name, param )
local formspec = "size[11.0,7.8]"
.. minetest.gui_bg
.. minetest.gui_bg_img
formspec = formspec .. "textarea[0.3,0.5;11.0,7.5;buffer;Debug History;"
for i = max( 1, #buffer - config.max_review_lines + 1 ), #buffer do
formspec = formspec .. minetest.formspec_escape( buffer[ i ] ) .. "\n"
end
formspec = formspec .. "]"
.. "label[9.0,0.0;" .. os.date( "%X" ) .. "]"
.. "button_exit[0.0,7.1;2.0,1.0;clear;Clear]"
.. "button_exit[9.0,7.1;2.0,1.0;close;Close]"
minetest.create_form( nil, name, formspec, function( state, player, fields )
if fields.clear then
buffer = { }
output = ""
for name, data in pairs( player_huds ) do
if data.size ~= "none" then
data.player:hud_change( data.refs.body_text, "text", "" )
end
end
end
end )
end,
} )
minetest.register_chatcommand( "tail", {
description = "Continuously follow the output stream of a plain-text log file",
privs = { server = true },
func = function( name, param )
if param == "" then
if log_file then
log_file:close( )
log_file = nil
return true, "Log file closed."
end
else
log_file = io.open( param, "r" )
if not log_file then
return false, "Failed to open log file"
end
log_file:seek( "end", 0 )
return true, "Log file opened."
end
end
} )
minetest.register_chatcommand( "unpipe", {
description = "Unpipe a function from the debugging console",
privs = { server = true },
func = function( name, param )
if param == "" then
for k, v in pairs( pipes ) do
if v.class.parent then
_G[ v.class.parent ][ v.class.method ] = v.func
else
_G[ v.class.method ] = v.func
end
end
pipes = { }
return true, "Removed all function pipes."
elseif pipes[ param ] then
local v = pipes[ param ]
if v.class.parent then
_G[ v.class.parent ][ v.class.method ] = v.func
else
_G[ v.class.method ] = v.func
end
pipes[ param ] = nil
return true, "Removed function pipe."
else
return false, "Failed to remove function pipe."
end
end,
} )
minetest.register_chatcommand( "pipe", {
description = "Pipe a function to the debugging console",
privs = { server = true },
func = function( name, param )
if param == "" then
local list = { }
for k, v in pairs( pipes ) do
table.insert( list, k )
end
table.sort( list )
return true, "Piped Functions: " .. table.concat( list, ", " )
elseif not unsafe_funcs[ param ] then
local class = parse_id( param )
if class and not pipes[ param ] then
local func
if not class.parent then
func = _G[ class.method ]
elseif _G[ class.parent ] then
func = _G[ class.parent ][ class.method ]
end
if func then
pipes[ param ] = { func = func, class = class }
local new_func = function( ... )
local args = { "[" .. param .. "]", ... }
for i = 2, #args do
args[ i ] = tostring( args[ i ] )
end
printf( args )
return func( ... )
end
if class.parent then
_G[ class.parent ][ class.method ] = new_func
else
_G[ class.method ] = new_func
end
return true, "Function pipe created."
end
end
end
return false, "Failed to create function pipe."
end
} )
globaltimer.start( 1.0, "console:slurp_file", function ( )
if log_file then
local str = log_file:read( "*a" )
if str ~= "" then
printf( string.match( str, "(.-)\n?$" ) ) -- remove trailing newline
end
end
end )
globaltimer.start( 0.2, "console:resize_huds", function( )
for name, data in pairs( player_huds ) do
local controls = data.player:get_player_control( )
if controls.sneak and controls.aux1 then
if data.size == "half" then
resize_hud( name, "full" )
elseif data.size == "full" then
resize_hud( name, "none" )
else
resize_hud( name, "half" )
end
end
end
end )

BIN
textures/debug.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB