123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322 |
- xban.gui = xban.gui or {}
- xban.gui.states = xban.gui.states or {}
- -- Localize for performance.
- local vector_round = vector.round
- local FORMNAME = "xban2:main"
- local MAXLISTSIZE = 1000
- local strfind, format = string.find, string.format
- local ESC = minetest.formspec_escape
- -- Get records of all registered players.
- local function make_list(filter)
- filter = filter and filter:split(",") or {}
- local list, dropped = { }, false
- -- Trim whitespace from filters.
- for k, v in ipairs(filter) do
- filter[k] = v:trim()
- end
- -- If a filter is chosen, only return filtered items.
- if #filter > 0 then
- for _, data in ipairs(xban.db) do
- for name, _ in pairs(data.names) do
- if not name:find("[%.%:]") then -- No IP addresses.
- for _, fname in ipairs(filter) do
- -- Plaintext search.
- if fname ~= "" then -- Don't search empty filters.
- -- Match real name or alias.
- if strfind(name, fname, 1, true) or strfind(rename.gpn(name), fname, 1, true) then
- if #list > MAXLISTSIZE then
- dropped = true
- goto done
- end
- list[#list+1] = name -- Insert real name.
- end
- end
- end
- end
- end
- end
- else
- for _, data in ipairs(xban.db) do
- for name, _ in pairs(data.names) do
- if not name:find("[%.%:]") then -- No IP addresses.
- if #list > MAXLISTSIZE then
- dropped = true
- goto done
- end
- list[#list+1] = name
- end
- end
- end
- end
- ::done::
- -- If filter has more than one entry, remove duplicates in the list.
- if #filter > 1 then
- local klist = {}
- for k, v in ipairs(list) do
- klist[v] = true
- end
- list = {}
- for k, v in pairs(klist) do
- list[#list+1] = k
- end
- end
- table.sort(list)
- return list, dropped
- end
- local states = xban.gui.states
- local function get_state(name)
- local state = states[name]
- if not state then
- state = { index=1, filter="" }
- states[name] = state
- state.list, state.dropped = make_list()
- end
- return state
- end
- local function get_record_simple(name)
- local e = xban.find_entry(name)
- if not e then
- return nil, {("No entry found for <%s>."):format(rename.gpn(name))}, false
- elseif (not e.record) or (#e.record == 0) then
- return e, {("Player <%s> has no ban records."):format(rename.gpn(name))}, false
- end
- local strings = {}
- -- Assemble ban record strings.
- for _, rec in ipairs(e.record) do
- local msg = (os.date("%Y-%m-%d %H:%M:%S", rec.time).." | "
- ..(rec.reason or "No reason given."))
- table.insert(strings, msg)
- end
- return e, strings, true
- end
- local function make_fs(name)
- local state = get_state(name)
- local list, filter = state.list, state.filter
- local pli, ei = state.player_index or 1, state.entry_index or 0
- if pli > #list then
- pli = #list
- end
- local fs = {
- "size[16,12]",
- default.gui_bg,
- default.gui_bg_img,
- default.gui_slots,
- "label[0,0.02;Filter]",
- "field[1.5,0.33;12.8,1;filter;;"..ESC(filter).."]",
- "button[14,0;2,1;search;Search]",
- "field_close_on_enter[filter;false]",
- }
- local fsn = #fs
- -- Translate internal player names to display names.
- local nlist = {}
- for k, v in ipairs(list) do
- local dn = rename.gpn(v)
- local rn = rename.grn(v)
- local ts = ac.get_total_suspicion(rn)
- if dn ~= rn then
- if ts > 0 then
- nlist[k] = ESC(dn .. " [" .. rn .. "] (" .. ts .. ")")
- else
- nlist[k] = ESC(dn .. " [" .. rn .. "]")
- end
- else
- if ts > 0 then
- nlist[k] = ESC(rn .. " (" .. ts .. ")")
- else
- nlist[k] = ESC(rn)
- end
- end
- end
- fsn=fsn+1 fs[fsn] = format("textlist[0,1.8;4,8;player;%s;%d;0]",
- table.concat(nlist, ","), pli)
- local record_name = list[pli]
- if record_name then
- local e, strings, gotten = get_record_simple(record_name)
- for i, r in ipairs(strings) do
- strings[i] = ESC(r)
- end
- -- Element field name changes based on whether we got a real set of ban records.
- fsn=fsn+1 fs[fsn] = format(
- "textlist[4.2,1.8;11.6,6;" .. (gotten and "entry" or "err") .. ";%s;%d;0]",
- table.concat(strings, ","), ei)
- local rec = e.record[ei]
- if #e.record > 0 then
- -- Ensure a valid record is selected.
- if not rec then
- rec = e.record[1]
- state.entry_index = 1
- ei = 1
- end
- fsn=fsn+1 fs[fsn] = format("label[0,10.3;%s]",
- ESC("Source: "..(rec.source or "<none>")
- .."\nDate: "..os.date("%c", rec.time)
- .."\n"..(rec.expires and os.date("Expires: %c", rec.expires) or "")
- .."\n"..(e.banned and "Status: Banned!" or "Player is not banned.")),
- pli) -- End format.
- else
- -- No ban records?
- fsn=fsn+1 fs[fsn] = format("label[0,10.3;%s]",
- ESC("Player <" .. rename.gpn(record_name) .. "> has no ban records.")
- ) -- End format.
- end
- -- Obtain all alternate names/IPs for this record.
- local names = {}
- local ips = {}
- for k, v in pairs(e.names) do
- if not k:find("[%.%:]") then
- names[#names+1] = rename.gpn(k)
- else
- ips[#ips+1] = k -- Is an IP address.
- end
- end
-
- local infomsg = {}
- infomsg[#infomsg+1] = "Other names (" .. #names .. "): {"..table.concat(names, ", ").."}"
- if #ips <= 5 then
- infomsg[#infomsg+1] = "IPs used (" .. #ips .. "): ["..table.concat(ips, " | ").."]"
- else
- infomsg[#infomsg+1] = "IPs used (" .. #ips .. "): DYNAMIC"
- end
- -- last_pos and last_seen are per name, not per record-entry.
- if type(e.last_pos) == "table" and e.last_pos[record_name] then
- infomsg[#infomsg+1] = "User was last seen at " ..
- rc.pos_to_namestr(vector_round(e.last_pos[record_name])) .. "."
- -- We can also add a button to allow the formspec user to jump to this
- -- location.
- fsn=fsn+1 fs[fsn] = "button[13,10.3;3,1;jump;Jump To Last Pos]"
- end
- if type(e.last_seen) == "table" and e.last_seen[record_name] then
- infomsg[#infomsg+1] = "Last login: " ..
- os.date("!%Y/%m/%d, %H:%M:%S UTC", e.last_seen[record_name]) .. "."
- end
- if sheriff.is_cheater(record_name) then
- infomsg[#infomsg+1] = "Player is a registered cheater/hacker."
- elseif sheriff.is_suspected_cheater(record_name) then
- infomsg[#infomsg+1] = "Player is a suspected cheater!"
- end
- for k, v in ipairs(infomsg) do
- infomsg[k] = ESC(v)
- end
- fsn=fsn+1 fs[fsn] = "textlist[4.2,8.0;11.6,1.8;info;"..table.concat(infomsg, ",")..";0]"
- else
- local e = "No entry matches the query."
- fsn=fsn+1 fs[fsn] = "textlist[4.2,1.8;11.6,6;err;"..ESC(e)..";0]"
- fsn=fsn+1 fs[fsn] = "textlist[4.2,8.0;11.6,1.8;info;;0]"
- fsn=fsn+1 fs[fsn] = "label[0,10.3;"..ESC(e).."]"
- end
- return table.concat(fs)
- end
- function xban.gui.on_receive_fields(player, formname, fields)
- if formname ~= FORMNAME then return end
- local name = player:get_player_name()
- if not minetest.check_player_privs(name, { ban=true }) then
- minetest.log("warning", "[xban2] Received fields from unauthorized user: " .. name)
- return true
- end
- local state = get_state(name)
- if fields.player then
- local t = minetest.explode_textlist_event(fields.player)
- if (t.type == "CHG") or (t.type == "DCL") then
- state.player_index = t.index
- minetest.show_formspec(name, FORMNAME, make_fs(name))
- end
- return true
- end
- if fields.entry then
- local t = minetest.explode_textlist_event(fields.entry)
- if (t.type == "CHG") or (t.type == "DCL") then
- state.entry_index = t.index
- minetest.show_formspec(name, FORMNAME, make_fs(name))
- end
- return true
- end
- if fields.key_enter_field == "filter" or fields.search then
- local filter = fields.filter or ""
- state.filter = filter
- state.list = make_list(filter)
- minetest.show_formspec(name, FORMNAME, make_fs(name))
- end
- if fields.jump then
- local list = state.list
- local pli = state.player_index or 1
- if pli > #list then
- pli = #list
- end
- local record_name = list[pli]
- if record_name then
- local e, strings, gotten = get_record_simple(record_name)
- if type(e.last_pos) == "table" and e.last_pos[record_name] then
- local pos = vector_round(table.copy(e.last_pos[record_name]))
- minetest.chat_send_player(name,
- "# Server: Teleporting to <" .. rename.gpn(record_name) ..
- ">'s last known exit position at " .. rc.pos_to_namestr(pos) .. ".")
- player:set_pos(pos)
- rc.notify_realm_update(name, pos)
- end
- end
- end
- return true
- end
- function xban.gui.chatcommand(name, params)
- minetest.show_formspec(name, FORMNAME, make_fs(name))
- end
- if not xban.gui.registered then
- minetest.register_on_player_receive_fields(function(...)
- return xban.gui.on_receive_fields(...)
- end)
- minetest.register_chatcommand("xban_gui", {
- description = "Show XBan GUI.",
- params = "",
- privs = { ban=true, },
- func = function(...)
- return xban.gui.chatcommand(...)
- end,
- })
- xban.gui.registered = true
- end
|