-- Player Indices hashes = {} -- Keeps track of players who have joined this round function GetRequiredVersion() return 10058 end function OnScriptLoad(process) end function OnScriptUnload() table.save(players, "players.data") table.save(indices, "indices.data") end function OnNewGame(map) end function OnGameEnd(mode) end function OnServerChat(player, chattype, message) return 1 end function OnServerCommand(admin, command) local cmd, args = cmdsplit(command) -- Players Commands -- if cmd == "sv_players" then -- Modified version of sv_players which prints players' indices as well as their IDs and names hprintf("ID Index Name") for i = 0,15 do local hash = gethash(i) if hash then hprintf(resolveplayer(i) .. " " .. players[hash].index .. " " .. getname(i)) end end return 0 elseif cmd == "sv_players_recent" then -- Prints indices and names of all players who have joined the server during this game (minus those who are still currently in it) hprintf("Index Name") for k,v in pairs(hashes) do local found for i = 0,15 do if gethash(i) == k then found = true break end end if not found then hprintf(players[k].index .. " " .. v) end end return 0 elseif cmd == "sv_players_search" then -- Prints a list of players' indices and names matching the name search specified local search = args[1] local matches = {} -- Loop through players table for k,v in pairs(players) do -- Find matches and organize by priority; more common aliases have higher priority for name,v in pairs(v.alias) do local val = string.find(string.lower(name), string.lower(search)) if val then table.insert(matches, {["priority"] = val + v, ["hash"] = k}) end end end -- Sort matches table by priority value table.sort(matches, function(a,b) return matches[a].priority > matches[b].priority end) if #matches > 0 then hprintf("Players matching \"" .. search .. "\":") hprintf("Index Name") -- Print up to 20 results local len = math.min(#matches, 20) for i = 1,len do hprintf(players[matches[i].hash].index .. " " .. players[matches[i].hash].name) end end return 0 -- Ban Commands elseif cmd == "sv_ban" then -- Modified version of sv_ban allowing for Ban on Sight (banning a player via their Index while they aren't in the server puts them on the BOS list). Also added a "name" parameter for those players who should be banned on sight who haven't yet joined the server with this script running (this parameter will be ignored in any other case). local id = args[1] local duration = args[2] or "-1" local name = args[3] if not id then hprintf("Syntax: sv_ban ") return 0 end for i = 0,15 do if getname(i) == id then return 1 end end if string.len(id) < 3 then return 1 end if indices[string.upper(id)] then local hash = indices[string.upper(id)] local player = hashtoplayer(hash) if player then if duration == "-1" then svcmd("sv_ban " .. resolveplayer(player)) else svcmd("sv_ban " .. resolveplayer(player) .. " " .. duration) end else players[hash].bos = duration hprintf((hashes[hash] or players[hash].name) .. " will be banned on sight.") end return 0 end if players[id] then players[id].bos = duration hprintf((hashes[id] or players[id].name) .. " will be banned on sight.") else name = name or "???" players[id] = {} players[id].bos = duration players[id].name = name end elseif cmd == "sv_unban" then -- Modified version of sv_unban which allows the ability to unban from a player's Ban ID, Hash, or Index and takes the player off of the Ban on Sight list (even if they haven't been banned yet). local id = args[1] if not id then hprintf("Syntax: sv_unban ") return 0 end local hash local dir = getprofilepath() local banned = io.string(dir .. "\\banned.txt") local lines = string.split(banned, "\n") for k,v in ipairs(lines) do local split = string.split(v, ",") local ban_id = split[1] local ban_hash = split[3] if tonumber(id) == tonumber(ban_id) then players[ban_hash].bos = nil return 1 end end if indices[string.upper(id)] then hash = indices[string.upper(id)] elseif players[id] then hash = id end if hash then players[hash].bos = nil hprintf((hashes[id] or players[hash].name) .. " has been taken off of the Ban on Sight list.") for k,v in ipairs(lines) do local split = string.split(v, ",") local ban_id = split[1] local ban_hash = split[3] if hash == ban_hash then svcmd("sv_unban " .. ban_id) return 0 end end else hprintf("Invalid Player.") end elseif cmd == "sv_bos_list" then -- Lists all players on the Ban on Sight list hprintf("Players to be banned on sight (-1 duration specifies an indefinite ban):") for k,v in pairs(players) do if players[k].bos then hprintf(players[k].index .. " " .. (hashes[k] or players[k].name) .. " " .. players[k].bos) end end return 0 -- Alias elseif cmd == "sv_alias" then -- Modified version of sv_alias which allows the ability to view a player's aliases by their Player ID, Hash or Index. This does not include aliases previously saved by Phasor; the new version of Phasor will hopefully have a way to access the alias database via scripts. Once that happens, I'll be able to add the aliases Phasor has stored into this command as well. local id = args[1] if not id then hprintf("Syntax: sv_alias ") return 0 end local hash if string.len(id) < 3 then local player = rresolveplayer(tonumber(id)) hash = gethash(player) elseif indices[string.upper(id)] then hash = indices[string.upper(id)] elseif players[id] then hash = id end if hash then local aliases = {} for k,v in pairs(players[hash].alias) do table.insert(aliases, k) end table.sort(aliases, function(a,b) return players[hash].alias[a] > players[hash].alias[b] end) for k,v in ipairs(aliases) do aliases[k] = "[" .. players[hash].alias[v] .. "]: " .. aliases[k] end local str = formatlist(aliases, " ", 4) hprintf("Aliases for player " .. players[hash].index .. " sorted by most common alias:") hprintf(str) return 0 else hprintf("Invalid Player.") return 0 end -- Admin Commands elseif cmd == "sv_admin_add" then -- Modified version of sv_admin_add which allows the ability to add admins with their Player ID, Hash or Index. sv_admin_del does not need Index support because you can pull up the list of admins with sv_admin_list. local id = args[1] local name = args[2] local level = args[3] if not id then hprintf("Syntax: sv_admin_add ") return 0 end local hash if string.len(id) < 3 then local player = rresolveplayer(tonumber(id)) hash = gethash(player) elseif indices[string.upper(id)] then hash = indices[string.upper(id)] elseif players[id] then return 1 else players[id] = {} players[id].name = name or "???" hash = id end if hash then if name and level then svcmd("sv_admin_add " .. hash .. " " .. name .. " " .. level) elseif name and not level then svcmd("sv_admin_add " .. hash .. " " .. name) elseif not name and not level then svcmd("sv_admin_add " .. hash) end end return 0 elseif cmd == "sv_commands" then registertimer(1, "DelayPrint") end return 1 end function DelayPrint(id, count) hprintf("sv_players_recent sv_players_search sv_bos_list") return 0 end function OnTeamDecision(team) return team end function OnPlayerJoin(player, team) local hash = gethash(player) updatePlayer(hash) hashes[hash] = getname(player) end function OnPlayerLeave(player, team) end function OnPlayerKill(killer, victim, mode) end function OnKillMultiplier(player, multiplier) end function OnPlayerSpawn(player, m_objId) end function OnPlayerSpawnEnd(player, m_objId) end function OnTeamChange(relevant, player, cur_team, dest_team) return 1 end function OnObjectCreation(m_objId, player, tagName) end function OnObjectInteraction(player, m_objId, tagType, tagName) return 1 end function OnWeaponAssignment(player, m_objId, slot, tagName) return 0 end function OnWeaponReload(player, m_weapId) return 1 end function OnDamageLookup(receiver, causer, tagData, tagName) end function OnVehicleEntry(relevant, player, m_vehicleId, tagName, seat) return 1 end function OnVehicleEject(player, forced) return 1 end function OnClientUpdate(player, m_objId) end math.inf = 1 / 0 function table.save(t, filename) local dir = getprofilepath() local file = io.open(dir .. "\\data\\" .. filename, "w") local spaces = 0 local function tab() local str = "" for i = 1,spaces do str = str .. " " end return str end local function format(t) spaces = spaces + 4 local str = "{ " for k,v in pairs(t) do -- Key datatypes if type(k) == "string" then k = string.format("%q", k) end -- Value datatypes if type(v) == "string" then v = string.format("%q", v) elseif v == math.inf then v = "1 / 0" end if type(v) == "table" then if table.len(v) > 0 then str = str .. "\n" .. tab() .. "[" .. k .. "] = " .. format(v) .. "," else str = str .. "\n" .. tab() .. "[" .. k .. "] = {}," end else str = str .. "\n" .. tab() .. "[" .. k .. "] = " .. tostring(v) .. "," end end spaces = spaces - 4 return string.sub(str, 1, string.len(str) - 1) .. "\n" .. tab() .. "}" end file:write("return " .. format(t)) file:close() end function table.load(filename) local dir = getprofilepath() local file = loadfile(dir .. "\\data\\" .. filename) if file then return file() end return {} end function table.len(t) local count = 0 for k,v in pairs(t) do count = count + 1 end return count end function table.max(t) local key local max = -math.inf for k,v in pairs(t) do v = tonumber(v) if v then if v > max then key = k max = v end end end return key, max end players = table.load("players.data") indices = table.load("indices.data") function hashtoplayer(hash) for i = 0,15 do if gethash(i) then return i end end end function updatePlayer(hash, name) local player = hashtoplayer(hash) local name = name or getname(player) players[hash] = players[hash] or {} -- Name players[hash].alias = players[hash].alias or {} players[hash].alias[name] = (players[hash].alias[name] or 0) + 1 players[hash].name = players[hash].name or table.max(players[hash].alias) -- Index players[hash].index = players[hash].index or newIndex(hash) -- Check if the player should be banned on sight if players[hash].bos then if players[hash].bos == "-1" then svcmd("sv_ban " .. resolveplayer(player)) else svcmd("sv_ban " .. resolveplayer(player) .. " " .. players[hash].bos) end players[hash].bos = nil end end function newIndex(hash) indices.unique = (indices.unique or 0) + 1 local index repeat index = convertbase((indices.next or 0), 36) while string.len(index) < 3 do index = "0" .. index end indices.next = (indices.next or 0) + 1 until validIndex(index) indices[index] = hash return index end function validIndex(entry) local invalid = {"red", "blue", "all", "me"} for _,v in ipairs(invalid) do if string.lower(entry) == v then return false end end return true end function convertbase(input, base) if not base or base == 10 then return tostring(input) end local digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" local answer = {} repeat local digit = (input % base) + 1 input = math.floor(input / base) table.insert(answer, 1, string.sub(digits, digit, digit)) until input == 0 return table.concat(answer, "") end function string.split(str, ...) for k,v in ipairs(arg) do if v == "" then local subs = {} for i = 1,string.len(str) do table.insert(subs, string.sub(str, i, i)) end return subs end end local subs = {} local sub = "" for i = 1,string.len(str) do local bool local char = string.sub(str, i, i) for k,v in ipairs(arg) do local delim = string.sub(str, i - (string.len(v) - 1), i) if v == delim then bool = true sub = string.sub(sub, 1, string.len(sub) - (string.len(v) - 1)) if sub ~= "" then table.insert(subs, sub) end sub = "" break end end if not bool then sub = sub .. char end if i == string.len(str) then table.insert(subs, sub) end end return subs end function cmdsplit(str) local subs = {} local sub = "" local ignore_quote, inquote, endquote for i = 1,string.len(str) do local bool local char = string.sub(str, i, i) if char == " " then if (inquote and endquote) or (not inquote and not endquote) then bool = true end elseif char == "\\" then ignore_quote = true elseif char == "\"" then if not ignore_quote then if not inquote then inquote = true else endquote = true end end end if char ~= "\\" then ignore_quote = false end if bool then if inquote and endquote then sub = string.sub(sub, 2, string.len(sub) - 1) end if sub ~= "" then table.insert(subs, sub) end sub = "" inquote = false endquote = false else sub = sub .. char end if i == string.len(str) then if string.sub(sub, 1, 1) == "\"" and string.sub(sub, string.len(sub), string.len(sub)) == "\"" then sub = string.sub(sub, 2, string.len(sub) - 1) end table.insert(subs, sub) end end local cmd = subs[1] local args = subs table.remove(args, 1) return cmd, args end function formatlist(t, delim, length) local str = "" for k,v in ipairs(t) do if k % length == 0 then str = str .. v .. "\n" else str = str .. v .. delim end end return str end function io.string(filename) local str local file = io.open(filename, "a+") if file then str = file:read("*all") end file:close() return str end