Build 01
- initial beta version
This commit is contained in:
commit
370d353f5b
90
README.txt
Normal file
90
README.txt
Normal 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
2
depends.txt
Normal file
@ -0,0 +1,2 @@
|
||||
timekeeper
|
||||
config
|
355
init.lua
Normal file
355
init.lua
Normal 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
BIN
textures/debug.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
Loading…
x
Reference in New Issue
Block a user