merging master

master
Nathan Salapat 2022-05-16 21:51:41 -05:00
commit 40d5f5ce5c
4 changed files with 262 additions and 97 deletions

View File

@ -303,6 +303,10 @@ minetest.register_node('lobby:button_1', {
lobby.corpses[map_id] = {} lobby.corpses[map_id] = {}
for i=1,player_count do for i=1,player_count do
minetest.chat_send_player(map_players[i], 'Hold tight, loading the level.') minetest.chat_send_player(map_players[i], 'Hold tight, loading the level.')
local player = minetest.get_player_by_name(map_players[i])
local player_attributes = player:get_meta()
player_attributes:set_string('voting', 'false')
player_attributes:set_string('mode', 'player')
lobby.voted[map_players[i]] = true lobby.voted[map_players[i]] = true
if i == traitor then if i == traitor then
minetest.chat_send_player(map_players[i], 'You\'re the imposter. Try to kill all the other players.') minetest.chat_send_player(map_players[i], 'You\'re the imposter. Try to kill all the other players.')
@ -313,11 +317,8 @@ minetest.register_node('lobby:button_1', {
minetest.after(10, function() minetest.after(10, function()
player_inv:add_item('main', 'lobby:shank') player_inv:add_item('main', 'lobby:shank')
end) end)
player_attributes:set_string('mode', 'traitor')
end end
local player = minetest.get_player_by_name(map_players[i])
local player_attributes = player:get_meta()
player_attributes:set_string('voting', 'false')
player_attributes:set_string('mode', 'player')
local privs = minetest.get_player_privs(map_players[i]) local privs = minetest.get_player_privs(map_players[i])
local player_inv = player:get_inventory() local player_inv = player:get_inventory()
player_inv:set_list('main', {}) player_inv:set_list('main', {})

View File

@ -33,10 +33,12 @@ This is set anytime a player goes to a level they have build access on.
player_attributes:set_string('mode', 'player') player_attributes:set_string('mode', 'player')
This is set when a player is playing a level with other people, as it's meant to be played. This is set when a player is playing a level with other people, as it's meant to be played.
player_attributes:set_string('mode', 'traitor')
This is set when a player is playing a level with other people, and is the traitor.
player_attributes:set_string('mode', 'solo') player_attributes:set_string('mode', 'solo')
This is set when a player plays a level solo, usually to earn XP, but could also be to explore levels. This is set when a player plays a level solo, usually to earn XP, but could also be to explore levels.
player_attributes:set_string('mode', 'ghost') player_attributes:set_string('mode', 'ghost')
This is set when a player, playing with others, dies on a level. This is set when a player, playing with others, dies on a level.
]] ]]

View File

@ -13,6 +13,10 @@ local function _info(msg)
return minetest.colorize('#ffff80', msg) return minetest.colorize('#ffff80', msg)
end end
local function _stdout(msg)
return minetest.colorize('#80ddee', msg)
end
local function _lsitem(item) local function _lsitem(item)
return TAB .. '- ' .. item return TAB .. '- ' .. item
end end
@ -47,63 +51,157 @@ local function list_corpses(ctab)
end end
tdc = { --[[ -------------------- tdc namespace -------------------- ]]--
fix_corpses = function(plr_name, map) tdc = {}
local pmap = map or '*'
local count = 0
for mapid, cplist in pairs(lobby.corpses) do function tdc.fix_corpses(td_user, map)
if pmap == '*' then local pmap = map or '*'
local count = 0
for mapid, cplist in pairs(lobby.corpses) do
if pmap == '*' then
lobby.corpse_removal(mapid)
lobby.corpses[mapid] = {}
count = count + 1
else
if mapid:find(pmap) then
lobby.corpse_removal(mapid) lobby.corpse_removal(mapid)
lobby.corpses[mapid] = {} lobby.corpses[mapid] = {}
count = count + 1 count = count + 1
else minetest.chat_send_player(td_user,
if mapid:find(pmap) then _info('Corpses in ' .. _keyw(mapid) .. ' fixed.'))
lobby.corpse_removal(mapid) break
lobby.corpses[mapid] = {}
count = count + 1
minetest.chat_send_player(plr_name,
_info('Corpses in ' .. _keyw(mapid) .. ' fixed.'))
break
end
end end
end end
if count == 0 then end
minetest.chat_send_player(plr_name, _info('No maps found')) if count == 0 then
elseif pmap == '*' then minetest.chat_send_player(td_user, _info('No maps found'))
minetest.chat_send_player(plr_name, elseif pmap == '*' then
_info('Corpses in ' .. tostring(count) .. ' map(s) fixed.')) minetest.chat_send_player(td_user,
end _info('Corpses in ' .. tostring(count) .. ' map(s) fixed.'))
end, end
end
fix_map = function(plr_name, map) function tdc.fix_map(td_user, map)
local pmap = map or '*' local pmap = map or '*'
local count = 0 local count = 0
for mapid, count in pairs(lobby.map) do for mapid, count in pairs(lobby.map) do
if pmap == '*' then if pmap == '*' then
lobby.map[mapid] = 0
lobby.update_maps(mapid)
count = count + 1
else
if mapid:find(pmap) then
lobby.map[mapid] = 0 lobby.map[mapid] = 0
lobby.update_maps(mapid) lobby.update_maps(mapid)
count = count + 1 count = count + 1
else minetest.chat_send_player(td_user,
if mapid:find(pmap) then _info('Map status of ' .. _keyw(mapid) .. ' fixed.'))
lobby.map[mapid] = 0 break
lobby.update_maps(mapid)
count = count + 1
minetest.chat_send_player(plr_name,
_info('Map status of ' .. _keyw(mapid) .. ' fixed.'))
break
end
end end
end end
if count == 0 then end
minetest.chat_send_player(plr_name, _info('No maps found')) if count == 0 then
elseif pmap == '*' then minetest.chat_send_player(td_user, _info('No maps found'))
minetest.chat_send_player(plr_name, elseif pmap == '*' then
_info('Status data for ' .. tostring(count) .. ' map(s) fixed.')) minetest.chat_send_player(td_user,
_info('Status data for ' .. tostring(count) .. ' map(s) fixed.'))
end
end
-- Return active players whose player name matches `expr`
function tdc.active_players_matching(expr)
local players = {}
for _, player in pairs(minetest.get_connected_players()) do
local pl_name = player:get_player_name()
if pl_name:find(expr) then
table.insert(players, player)
end end
end, end
} return players
end
-- Return active map ids matching `expr`
function tdc.active_mapids_matching(expr)
local mapids = {}
for mapid, count in pairs(lobby.map) do
if mapid:find(expr) and count ~= nil and count > 0 then
mapids[mapid] = true
end
end
-- The lobby itself never gets into lobby.map, so we insert it manually,
-- since we want to treat it just like a map.
local mapid_lobby = 'lobby'
if mapid_lobby:find(expr) then mapids[mapid_lobby] = true end
return mapids
end
-- Return players currently visiting a map whose id is in the list `mapids`
function tdc.players_visiting(mapids)
local players = {}
for pl_name, mapid in pairs(lobby.game) do
local mid = mapid
if mapid:find("_solo$") then
mid = mapid:sub(1, -6)
elseif mapid:find("_ghost$") then
mid = mapid:sub(1, -7)
end
if mapids[mid] then
table.insert(players, minetest.get_player_by_name(pl_name))
end
end
return players
end
-- For each player in the `players` list, create an entry in the `index` table,
-- having `player:get_player_name()` as keyword.
function tdc.index_by_name(players, index)
if players == nil or index == nil then return end
for _, player in ipairs(players) do
local pl_name = player:get_player_name()
index[pl_name] = player
end
end
-- Return a list of currently active players where either the player name
-- or the map id they're currently visiting matches `expr`.
function tdc.list_players_matching(expr)
local players = tdc.active_players_matching(expr)
local mapids = tdc.active_mapids_matching(expr)
local visitors = tdc.players_visiting(mapids)
local matches = {}
-- index all matching players by their name
tdc.index_by_name(players, matches)
tdc.index_by_name(visitors, matches)
return matches
end
-- Return a "footprint" representation of the player privileges `privs`
function tdc.privs_footprint(privs)
local s, t, b, c, w, p, m
if privs.server then s = 's' else s = '-' end
if privs.traitor_dev then t = 't' else t = '-' end
if privs.builder then b = 'b' else b = '-' end
if privs.creative then c = 'c' else c = '-' end
if privs.worldeditor then w = 'w' else w = '-' end
if privs.pro_player then p = 'p' else p = '-' end
if privs.multihome then m = 'm' else m = '-' end
return table.concat({s, t, b, c, w, p, m})
end
-- tdc.actions: enumerates the possible /td commands -- tdc.actions: enumerates the possible /td commands
-- tdc.actions = { cmd_1 = def_1, cmd_2 = def_2, ... } -- tdc.actions = { cmd_1 = def_1, cmd_2 = def_2, ... }
@ -131,20 +229,20 @@ tdc.actions = {
table.insert(msgtab, 'Type "' .. _keyw('/td help <cmd>') .. '" to get help for a specific command.') table.insert(msgtab, 'Type "' .. _keyw('/td help <cmd>') .. '" to get help for a specific command.')
return table.concat(msgtab, '\n') return table.concat(msgtab, '\n')
end, end,
exec = function(plr_name, params) exec = function(td_user, params)
if params then if params then
local par1 = params:match('[%w_]+') local par1 = params:match('[%w_]+')
if par1 and tdc.actions[par1] then if par1 and tdc.actions[par1] then
minetest.chat_send_player(plr_name, tdc.actions[par1].help()) minetest.chat_send_player(td_user, tdc.actions[par1].help())
elseif par1 then elseif par1 then
-- TODO: find par1 as cmd prefix in tdc.actions -- TODO: find par1 as cmd prefix in tdc.actions
local msg = _info('Unknown command "' .. par1 .. '"\n') .. list_actions() local msg = _info('Unknown command "' .. par1 .. '"\n') .. list_actions()
minetest.chat_send_player(plr_name, msg) minetest.chat_send_player(td_user, msg)
else else
minetest.chat_send_player(plr_name, tdc.actions['help'].help()) minetest.chat_send_player(td_user, tdc.actions['help'].help())
end end
else else
minetest.chat_send_player(plr_name, tdc.actions['help'].help()) minetest.chat_send_player(td_user, tdc.actions['help'].help())
end end
end, end,
}, },
@ -160,7 +258,7 @@ tdc.actions = {
} }
return table.concat(msgtab, '\n') return table.concat(msgtab, '\n')
end, end,
exec = function(plr_name, params) exec = function(td_user, params)
local map = params:match('[%w_]+') or '*' local map = params:match('[%w_]+') or '*'
local hdr, msg, ccount, clist local hdr, msg, ccount, clist
if params == '' then if params == '' then
@ -191,7 +289,7 @@ tdc.actions = {
else else
msg = _info('No corpses yet.') msg = _info('No corpses yet.')
end end
minetest.chat_send_player(plr_name, msg) minetest.chat_send_player(td_user, msg)
minetest.log('action', minetest.strip_colors(msg)) minetest.log('action', minetest.strip_colors(msg))
end, end,
}, },
@ -205,7 +303,7 @@ tdc.actions = {
} }
return table.concat(msgtab, '\n') return table.concat(msgtab, '\n')
end, end,
exec = function(plr_name, params) exec = function(td_user, params)
local msgtab = {_info('Active maps:')} local msgtab = {_info('Active maps:')}
local plr_count = 0 local plr_count = 0
local msg local msg
@ -233,7 +331,7 @@ tdc.actions = {
table.insert(msgtab, _lsitem('lobby: ' .. tostring(clobby) .. ' player(s)')) table.insert(msgtab, _lsitem('lobby: ' .. tostring(clobby) .. ' player(s)'))
end end
msg = table.concat(msgtab, '\n') msg = table.concat(msgtab, '\n')
minetest.chat_send_player(plr_name, msg) minetest.chat_send_player(td_user, msg)
minetest.log('action', minetest.strip_colors(msg)) minetest.log('action', minetest.strip_colors(msg))
end, end,
}, },
@ -247,7 +345,7 @@ tdc.actions = {
} }
return table.concat(msgtab, '\n') return table.concat(msgtab, '\n')
end, end,
exec = function(plr_name, params) exec = function(td_user, params)
local msg = '' local msg = ''
for mapid, traitor in pairs(lobby.traitors) do for mapid, traitor in pairs(lobby.traitors) do
-- table value could be nil if entry is to be GC'd -- table value could be nil if entry is to be GC'd
@ -260,54 +358,88 @@ tdc.actions = {
else else
msg = _info('Active traitors:\n') .. msg msg = _info('Active traitors:\n') .. msg
end end
minetest.chat_send_player(plr_name, msg) minetest.chat_send_player(td_user, msg)
minetest.log('action', minetest.strip_colors(msg)) minetest.log('action', minetest.strip_colors(msg))
end, end,
}, },
-- CMD: /td player <id> -- CMD: /td players <id>
player = { players = {
info = 'Show player attributes', info = 'Show player attributes',
help = function() help = function()
local msgtab = { local msgtab = {
_info('Usage: /td player <id>'), _info('Usage: /td players <expr>'),
'List some player metadata.', 'Show metadata of one or more players.',
'Params:', 'Params:',
TAB .. '<id> player name (substring suffices).', TAB .. '<expr> Part of a player name or map ID used as search expression.',
'\nNote: <id> is restricted to alphanumeric chars and "_", for the sake of security.' TAB .. ' Any matching player name will be included in the result list.',
TAB .. ' If <expr> is part of a map ID, lists all players currently',
TAB .. ' visiting that map.',
'\nNote: <expr> is restricted to alphanumeric chars and "_", for the sake of security.',
'\nThe metadata of matching players is listed in a table with the following columns:',
TAB .. 'Player displays the player\'s login name',
TAB .. 'Map shows the map id the player is currently visiting',
TAB .. 'Privs shows the state of some more widely used traitor privileges, which is',
TAB .. ' a short string where each letter symbolizes a certain privilege',
TAB .. ' (' ..
_stdout('s') .. 'erver, ' ..
_stdout('t') .. 'raitor_dev, ' ..
_stdout('b') .. 'uilder, ' ..
_stdout('c') .. 'reative, ' ..
_stdout('w') .. 'orldeditor, ' ..
_stdout('p') .. 'ro_player, ' ..
_stdout('m') .. 'ultihome)',
TAB .. ' If a player does not have a certain privilege, the privilege\'s letter',
TAB .. ' is replaced by a \'-\' instead.',
TAB .. 'Mode displays the player mode',
TAB .. 'Tone shows the player\'s current tone color',
TAB .. 'Spawn prints the player\'s current spawn position (if any)',
} }
return table.concat(msgtab, '\n') return table.concat(msgtab, '\n')
end, end,
exec = function(plr_name, params) exec = function(td_user, params)
if not params or not params:find("[%w_]+") then if not params or not params:find("[%w_]+") then
minetest.chat_send_player(plr_name, 'Missing argument, type "/td help player" for help.') minetest.chat_send_player(td_user, 'Missing argument, type "/td help players" for help.')
else else
local p1, p2, plid = params:find('([%w_]+)') local p1, p2, expr = params:find('([%w_]+)')
local matches = tdc.list_players_matching(expr)
local count = 0 local count = 0
local mtab = {
_info('Player Map Privs Mode Tone Spawn'),
'--------------------------------------------------------------------------------------',
}
-- sort list by player name
local sorted = {}
for name in pairs(matches) do table.insert(sorted, name) end
table.sort(sorted)
for _, player in pairs(minetest.get_connected_players()) do for _, name in pairs(sorted) do
local pname = player:get_player_name() count = count + 1
local player = matches[name]
local attr = player:get_meta()
local pl_name = _keyw(name)
local padding = 20 - name:len()
local pl_padding = string.rep(' ', padding)
local pl_map = lobby.game[name] or '<nil>'
local pl_privs = tdc.privs_footprint(minetest.get_player_privs(name))
local pl_mode = attr:get_string('mode') or '<nil>'
local pl_tone = attr:get_int('tone')
local pl_spawn = attr:get_string('spawn_pos') or '<nil>'
if pname:find(plid) then table.insert(mtab, string.format('%s%s %-20s %-7s %-7s %5d %-20s',
count = count + 1 pl_name, pl_padding, pl_map, pl_privs, pl_mode, pl_tone, pl_spawn)
local attr = player:get_meta() )
local mtab = {
_info('Attributes of ') .. _keyw(pname) .. ':',
TAB .. 'ghost: ' .. (attr:get_string('ghost') or 'false'),
TAB .. 'spawn: ' .. (attr:get_string('spawn_pos') or '<nil>'),
TAB .. 'voting: ' .. (attr:get_string('voting') or 'false')
}
minetest.chat_send_player(plr_name, table.concat(mtab, '\n'))
end
end end
if count == 0 then if count == 0 then
minetest.chat_send_player(plr_name, _info('No matching player')) minetest.chat_send_player(td_user, _info('No match'))
else
minetest.chat_send_player(td_user, table.concat(mtab, '\n'))
end end
end end
end, end,
}, },
-- CMD: /td fix (corpses|map) <map> -- CMD: /td fix (corpses|map) <map>
--[[ --[[
fix corpses: call lobby.corpse_removal(<map>), then reset its poslist fix corpses: call lobby.corpse_removal(<map>), then reset its poslist
fix maps: set lobby.map[<map>] = 0, then call lobby.update_maps(<map>) fix maps: set lobby.map[<map>] = 0, then call lobby.update_maps(<map>)
--]] --]]
fix = { fix = {
@ -326,31 +458,63 @@ tdc.actions = {
} }
return table.concat(msgtab, '\n') return table.concat(msgtab, '\n')
end, end,
exec = function(plr_name, params) exec = function(td_user, params)
local helpcmd = 'type "/td help fix" for help.' local helpcmd = 'type "/td help fix" for help.'
if not params then if not params then
minetest.chat_send_player(plr_name, _info('Missing arguments, ' .. helpcmd)) minetest.chat_send_player(td_user, _info('Missing arguments, ' .. helpcmd))
else else
local p1, p2, fix, map local p1, p2, fix, map
p1, p2, fix, map = params:find('(%w+)%s+([%w_*]+)') p1, p2, fix, map = params:find('(%w+)%s+([%w_*]+)')
if not fix or not map then if not fix or not map then
minetest.chat_send_player(plr_name, _info('Missing arguments, ' .. helpcmd)) minetest.chat_send_player(td_user, _info('Missing arguments, ' .. helpcmd))
else else
if string.find('corpses', fix) then if string.find('corpses', fix) then
tdc.fix_corpses(plr_name, map) tdc.fix_corpses(td_user, map)
elseif string.find('map', fix) then elseif string.find('map', fix) then
tdc.fix_map(plr_name, map) tdc.fix_map(td_user, map)
else else
minetest.chat_send_player(plr_name, _info('Unknown fix action, ' .. helpcmd)) minetest.chat_send_player(td_user, _info('Unknown fix action, ' .. helpcmd))
end end
end end
end end
end, end,
} },
--CMD: /td mode(mode)
mode = {
info = 'Switch your play mode',
help = function()
local msgtab = {
_info('Usage /td mode <mode>'),
'Changes your playing mode.',
'Params:',
TAB .. '<mode> One of the following:',
TAB .. 'builder, ghost, player, solo, traitor'
}
return table.concat(msgtab, '\n')
end,
exec = function(td_user, params)
local helpcmd = 'type "/td help mode" for help.'
if not params then
minetest.chat_send_player(td_user, _info('Missing arguments, ' .. helpcmd))
else
local modes = 'builder, ghost, player, solo, traitor'
if string.find(modes, params) then
local player = minetest.get_player_by_name(td_user)
local player_attributes = player:get_meta()
player_attributes:set_string('mode', params)
minetest.chat_send_player(td_user, _info('Switched you to '..params..' mode.'))
else
minetest.chat_send_player(td_user, _info('Missing or bad arguments, ' .. helpcmd))
end
end
end,
}
} }
function tdc.exec(plr_name, params) function tdc.exec(td_user, params)
local cmd = nil local cmd = nil
if params and params ~= '' then if params and params ~= '' then
local par1 = params:match('[%w_]+') local par1 = params:match('[%w_]+')
@ -369,14 +533,14 @@ function tdc.exec(plr_name, params)
end end
end end
if cname then if cname then
minetest.log('action', plr_name .. ' runs "/td ' .. cname .. parN .. '"') minetest.log('action', td_user .. ' runs "/td ' .. cname .. parN .. '"')
cmd.exec(plr_name, parN) cmd.exec(td_user, parN)
else else
minetest.chat_send_player(plr_name, minetest.chat_send_player(td_user,
_info('Unknown command "' .. par1 .. '", type "/td help" for possible commands.')) _info('Unknown command "' .. par1 .. '", type "/td help" for possible commands.'))
end end
else else
minetest.chat_send_player(plr_name, list_actions()) minetest.chat_send_player(td_user, list_actions())
end end
return true return true
end end
@ -387,4 +551,3 @@ minetest.register_chatcommand('td', {
description = 'Traitor debugging commands. Type "/td help" for a list of possible commands.', description = 'Traitor debugging commands. Type "/td help" for a list of possible commands.',
func = tdc.exec func = tdc.exec
}) })

@ -1 +0,0 @@
Subproject commit b6fbd71be1ceaa590b3ac4f84219aaeeb5c464c6