LUA 178
V2+ Commands Script 5.20 Beta By xdedeone on 1st March 2019 05:05:38 PM
  1.         --Creator: Wizard
  2.         --Script Name: Command Script
  3.         --Website: http://phasorscripts.wordpress.com/
  4.         --Xfire: th3w1zard3
  5.         --Check Change Log for information on the updates
  6.         --Thank you AElite for the awesome work you did in updating this script to the latest phasor, and the other awesome features you put in.
  7.         --Without you I never would have updated to the newer phasor, and this script never would have been made.
  8.  
  9.         -- I FOUND A MAGICAL COMMAND THAT HELPED ME TEST THIS AMAZING SCRIPT: luac -p -l commands.lua | grep 'ETTABUP.*_ENV' (or ETGLOBAL for 5.1)     
  10.  
  11. --
  12. --Do Not Modify unless you really know what you are doing
  13. --
  14.         -- Counts
  15. local cur_players = 0
  16. local rtv_counter = 0
  17. local votekick_counter = 0
  18. local scorelimit = 50
  19.  
  20.         -- Strings
  21. local script_version = "5.2 The 'Nimbus' Release"
  22.  
  23.         -- Timers
  24. local infammo_timer
  25. local lo3_timer
  26. local maintimer
  27. local rtvtimer
  28. local votekicktimer
  29.  
  30.         -- Script Variables
  31. local changelog
  32. local dont_call_onservercommand
  33. local executingPlayerId
  34. local Game
  35. local gameend
  36. local gametype
  37. local http
  38. local flameId
  39. local Map
  40. local output_environment = -1
  41. local Persistent
  42. local processid
  43. local profilepath
  44. local server
  45. local server_prefix
  46. local someoneHidden = false
  47. local socket
  48. local team_play
  49. local team_play_temp
  50. local time_passed_value
  51. local timelimit_set_value
  52. local votekickPlayerId
  53.  
  54. -- Local variables defined for decreased execution time (but mostly to organize what is using the global environment)
  55. local io, tostring, tonumber, next, type, os, select = io, tostring, tonumber, next, type, os, select
  56. local sub, gsub, find, lower, format, match = string.sub, string.gsub, string.find, string.lower, string.format, string.match
  57. local insert, remove, concat = table.insert, table.remove, table.concat
  58. local ceil, floor = math.ceil, math.floor
  59.  
  60.  
  61.         -- Table Management
  62. local TM = setmetatable({
  63.                 unused_tables = {},
  64.                 __gc = function(self) self.unused_tables[#self.unused_tables+1] = self end,
  65.                 __tostring = function(t)
  66.                         local tableStr = "{ "
  67.                         if next(t) then
  68.                                 for k,v in next,t do
  69.                                         if v == t then goto nextVal end
  70.                                         k = type(k) ~= "number" and '["' .. k .. '"]' or k
  71.                                         v = type(v) == "string" and '"' .. v .. '"' or tostring(v)
  72.                                         tableStr = tableStr .. (k .. " = ") .. v .. ", "
  73.                                         ::nextVal::
  74.                                 end
  75.  
  76.                                 tableStr = sub(tableStr, 1, #tableStr-2)
  77.                         else
  78.                                 tableStr = tableStr
  79.                         end
  80.                         return tableStr .. "}"
  81.                 end,
  82.                 deleteEntries = function(t)
  83.                         for key,_ in next,t do
  84.                                 t[key] = nil
  85.                         end
  86.                         return t
  87.                 end
  88.         },
  89.         {
  90.                 __call = function(TM, t)
  91.                         local mt = getmetatable(t)
  92.                         if mt then
  93.                                 mt.__index = TM
  94.                                 return t
  95.                         else
  96.                                 return setmetatable(t, TM)
  97.                         end
  98.                 end
  99.         }
  100. )
  101. TM.__index = TM
  102.  
  103.         -- Tables
  104. local access_table = {}
  105. --local addresses
  106. local admin_table = {}
  107. local ban_table = {}
  108. local ban_penalty = {}
  109. local bos_table = {}
  110. local Commands = {commandAliases = {}}
  111. Commands.__index = Commands -- class setup
  112. local commandAliases = Commands.commandAliases
  113. local connections = {}
  114. local cmdreply = setmetatable({index = 0}, {
  115.         __mode = "v", -- weak table
  116.         __len = function(self) return (rawget(self, "index") or 0) end,
  117.         --old method
  118.         --[[
  119.         __newindex = function(self, key, value)
  120.                 if not tonumber(key) then
  121.                         return
  122.                 end
  123.  
  124.                 local index = rawget(self, "index")
  125.                 rawset(self, "index", value and (index+1) or (index-1)) -- update the index.
  126.                 rawset(self, key, value or rawget(self, index))
  127.         end,]]
  128.         __call = function(self, bResetAll)
  129.                 if not bResetAll then
  130.                         local index = rawget(self, "index") + 1
  131.                         rawset(self, "index", index)
  132.                         return index
  133.                 end
  134.                 rawset(self, "header", nil)
  135.                 rawset(self, "align", nil)
  136.                 rawset(self, "delim", nil)
  137.                 rawset(self, "separator", nil)
  138.                 rawset(self, "index", 0)
  139.         end
  140. })
  141. local clients = {}
  142. local debug_players = {}
  143. -- REM: change back cobalthidden before release.
  144. local player_colors = {[0] = "white", "black", "red", "blue", "grey", "yellow", "green", "pink", "purple", "cyan", "cobalthidden", "orange", "teal", "sage", "brown", "tan", "maroon", "salmon", white = 0, black = 1, red = 2, blue = 3, grey = 4, yellow = 5, green = 6, pink = 7, purple = 8, cyan = 9, cobalthidden = 10, orange = 11, teal = 12, sage = 13, brown = 14, tan = 15, maroon = 16, salmon = 17}
  145. local team_colors = {[0] = "red", "blue", "white", "black", "pink", "yellow", "cobalthidden", "orange", "purple", "cyan", "maroon", "teal", "green", "sage", "brown", "salmon"}
  146. local player_scores = {[0] = 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
  147. local team_scores = {[0] = 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
  148. -- Default Scripted Game Variables
  149. local defaults = {
  150.         kickbans_file = "KicksAndBans",
  151.         commands_file = "commands",
  152.         admin_file = "admins",
  153.         banlist_file = "banned",
  154.         sharedhash_file = "sharedhashes",
  155.         ban_penalty = "5m 1d 10d",
  156.         remote_bansystem = false,
  157.         remotecontrol = false,
  158.         --tkban_type = "ip",
  159.         adminblocker = 0,
  160.         anticaps = false,
  161.         antispam = "players",
  162.         chatcommands = true,
  163.         chatids = true,
  164.         crouch_camo = false,
  165.         deathless = false,
  166.         debug_mode = false,
  167.         falldamage = true,
  168.         firstjoin_message = true,
  169.         hash_duplicates = true,
  170.         infinite_ammo = false,
  171.         killing_spree = true,
  172.         multiteam_vehicles = false,
  173.         noweapons = false,
  174.         pm_enabled = true,
  175.         respawn_time = -1,
  176.         rtv_enabled = true,
  177.         rtv_required = 50,
  178.         rtv_timeout = 120,
  179.         sa_message = true,
  180.         scrim_mode = false,
  181.         spam_max = 7,
  182.         spam_timeout = 60,
  183.         tbag_detection = true,
  184.         uniques_enabled = true,
  185.         votekick_enabled = true,
  186.         votekick_action = "kick",
  187.         votekick_required = 70,
  188.         votekick_timeout = 120,
  189.         wb_message = true
  190. }
  191.  
  192. local identities = {}
  193. identities.__index = identities
  194.  
  195. local leave_table = {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}
  196. local locations = {} -- map = {locname = {x, y, z}}
  197. local spawnWeapons = {}
  198. local tag_table = {}
  199. local objects
  200. local playerMT = {}
  201. local PlayerClass = {
  202.         [0]={drones = TM{}, weapons = {}},
  203.                 {drones = TM{}, weapons = {}},
  204.                 {drones = TM{}, weapons = {}},
  205.                 {drones = TM{}, weapons = {}},
  206.                 {drones = TM{}, weapons = {}},
  207.                 {drones = TM{}, weapons = {}},
  208.                 {drones = TM{}, weapons = {}},
  209.                 {drones = TM{}, weapons = {}},
  210.                 {drones = TM{}, weapons = {}},
  211.                 {drones = TM{}, weapons = {}},
  212.                 {drones = TM{}, weapons = {}},
  213.                 {drones = TM{}, weapons = {}},
  214.                 {drones = TM{}, weapons = {}},
  215.                 {drones = TM{}, weapons = {}},
  216.                 {drones = TM{}, weapons = {}},
  217.                 {drones = TM{}, weapons = {}}
  218. }
  219. PlayerClass.__index = PlayerClass
  220.  
  221. for i = 0,15 do
  222.         setmetatable(PlayerClass[i], PlayerClass)
  223. end
  224.  
  225. local randomNames = {
  226.         Butcher = 1,
  227.         Caboose = 2,
  228.         Crazy = 3,
  229.         Cupid = 4,
  230.         Darling = 5,
  231.         Dasher = 6,
  232.         Disco = 7,
  233.         Donut = 8,
  234.         Dopey = 9,
  235.         Ghost = 10,
  236.         Goat = 11,
  237.         Grumpy = 12,
  238.         Hambone = 13,
  239.         Hollywood = 14,
  240.         Howard = 15,
  241.         Jack = 16,
  242.         Killer = 17,
  243.         King = 18,
  244.         Mopey = 19,
  245.         Noodle = 20,
  246.         Penguin = 21,
  247.         Pirate = 22,
  248.         Prancer = 23,
  249.         Saucy = 24,
  250.         Shadow = 25,
  251.         Sleepy = 26,
  252.         Snake = 27,
  253.         Sneak = 28,
  254.         Stompy = 29,
  255.         Stumpy = 30,
  256.         ["The Bear"] = 31,
  257.         ["The Big L"] = 32,
  258.         Tooth = 33,
  259.         ["Walla Walla"] = 34,
  260.         Weasel = 35,
  261.         Wheezy = 36,
  262.         Whicker = 37,
  263.         Whisp = 38,
  264.         Wilshire = 39
  265. }
  266. local rcon_passwords = {}
  267. local sharedhashes = {
  268.         ["f443106bd82fd6f3c22ba2df7c5e4094"] = 1,
  269.         ["c702226e783ea7e091c0bb44c2d0ec64"] = 2,
  270.         ["d72b3f33bfb7266a8d0f13b37c62fddb"] = 3,
  271.         ["55d368354b5021e7dd5d3d1525a4ab82"] = 4,
  272.         ["3d5cd27b3fa487b040043273fa00f51b"] = 5,
  273.         ["b661a51d4ccf44f5da2869b0055563cb"] = 6,
  274.         ["740da6bafb23c2fbdc5140b5d320edb1"] = 7,
  275.         ["10440b462f6cbc3160c6280c2734f184"] = 8,
  276.         ["7503dad2a08026fc4b6cfb32a940cfe0"] = 9,
  277.         ["4486253cba68da6786359e7ff2c7b467"] = 10,
  278.         ["f1d7c0018e1648d7d48f257dc35e9660"] = 11,
  279.         ["40da66d41e9c79172a84eef745739521"] = 12,
  280.         ["2863ab7e0e7371f9a6b3f0440c06c560"] = 13,
  281.         ["34146dc35d583f2b34693a83469fac2a"] = 14,
  282.         ["b315d022891afedf2e6bc7e5aaf2d357"] = 15,
  283.         ["81f9c914b3402c2702a12dc1405247ee"] = 16,
  284.         ["63bf3d5a51b292cd0702135f6f566bd1"] = 17,
  285.         ["6891d0a75336a75f9d03bb5e51a53095"] = 18,
  286.         ["325a53c37324e4adb484d7a9c6741314"] = 19,
  287.         ["0e3c41078d06f7f502e4bb5bd886772a"] = 20,
  288.         ["fc65cda372eeb75fc1a2e7d19e91a86f"] = 21,
  289.         ["f35309a653ae6243dab90c203fa50000"] = 22,
  290.         ["50bbef5ebf4e0393016d129a545bd09d"] = 23,
  291.         ["a77ee0be91bd38a0635b65991bc4b686"] = 24,
  292.         ["3126fab3615a94119d5fe9eead1e88c1"] = 25,
  293.         ["d41d8cd98f00b204e9800998ecf8427e"] = 26
  294. }
  295. local threads = {} -- list of all live threads
  296. local unique_table = TM(setmetatable({total = 0}, {__len = function(self) return self.total end}))
  297. local valid_maps = {}
  298. local valid_gametypes = {}
  299.  
  300.         -- Send Debug Messages
  301. local function sendDebugMessage(message)
  302.         local playerId
  303.         for playerId = 0,15 do
  304.                 if debug_players[playerId] then
  305.                         sendconsoletext(playerId, message)
  306.                 end
  307.         end
  308. end
  309.  
  310.         -- Private Functions
  311.        
  312. -- Phasor doesn't automatically check limits for write functions, it just errors, so let's make our own check.
  313. local function writewithinlimit(address, value, writefunc, minlimit, maxlimit)
  314.         writefunc(address, value <= minlimit and minlimit or value >= maxlimit and maxlimit or value)
  315. end
  316.  
  317. local function writebyte(address, offset, value)
  318.         if value then
  319.                 address = address + offset
  320.         else
  321.                 value = offset
  322.         end
  323.         writewithinlimit(address, value, _G.writebyte, 0, 0xFF)
  324. end
  325.  
  326. local function writechar(address, offset, value)
  327.         if value then
  328.                 address = address + offset
  329.         else
  330.                 value = offset
  331.         end
  332.         writewithinlimit(address, value, _G.writechar, -0x80, 0x7F)
  333. end
  334.  
  335. local function writeword(address, offset, value)
  336.         if value then
  337.                 address = address + offset
  338.         else
  339.                 value = offset
  340.         end
  341.         writewithinlimit(address, value, _G.writeword, 0, 0xFFFF)
  342. end
  343.  
  344. local function writeshort(address, offset, value)
  345.         if value then
  346.                 address = address + offset
  347.         else
  348.                 value = offset
  349.         end
  350.         writewithinlimit(address, value, _G.writeshort, -0x8000, 0x7FFF)
  351. end
  352.  
  353. local function writedword(address, offset, value)
  354.         if value then
  355.                 address = address + offset
  356.         else
  357.                 value = offset
  358.         end
  359.         writewithinlimit(address, value, _G.writedword, 0, 0xFFFFFFFF)
  360. end
  361.  
  362. local function writeint(address, offset, value)
  363.         if value then
  364.                 address = address + offset
  365.         else
  366.                 value = offset
  367.         end
  368.         writewithinlimit(address, value, _G.writeint, -0x80000000, 0x7FFFFFFF)
  369. end
  370.  
  371. -- Phasor's read/writestring in previous versions are broken.
  372. local function readstring(address, length)
  373.         address = type(address) == "number" and address or error("bad argument #1 to 'readstring' expected number, got '" .. type(address) .. "'")
  374.         if length then
  375.                 length = type(length) == "number" and length or error("bad argument #2 to 'readstring' expected number, got '" .. type(length) .. "'")
  376.         else
  377.                 length = length or 256
  378.         end
  379.         local char_table = {}
  380.         local char = string.char
  381.         local word, dword, byte1, byte2, byte3, byte4
  382.         local i = 0
  383.         while i < length do
  384.                 -- quickens read calls (screw you oxide for using virtualprotect)
  385.                 if (length-i) >= 4 then
  386.                         dword = readdword(address + i)
  387.  
  388.                         byte1 = bit32.band(dword, 0xFF)
  389.                         if byte1 == 255 or byte1 == 0 then break end
  390.                         char_table[#char_table+1] = char(byte1)
  391.  
  392.                         byte2 = bit32.band(bit32.rshift(dword, 8), 0xFF)
  393.                         if byte2 == 255 or byte2 == 0 then break end
  394.                         char_table[#char_table+1] = char(byte2)
  395.  
  396.                         byte3 = bit32.band(bit32.rshift(dword, 16), 0xFF)
  397.                         if byte3 == 255 or byte3 == 0 then break end
  398.                         char_table[#char_table+1] = char(byte3)
  399.  
  400.                         byte4 = bit32.band(bit32.rshift(dword, 24), 0xFF)
  401.                         if byte4 == 255 or byte4 == 0 then break end
  402.                         char_table[#char_table+1] = char(byte4)
  403.  
  404.                         i = i + 4
  405.                 elseif (length-i) >= 2 then
  406.                         word = readword(address + i)
  407.  
  408.                         byte1 = bit32.band(word, 0xFF)
  409.                         if byte1 == 255 or byte1 == 0 then break end
  410.                         char_table[#char_table+1] = char(byte1)
  411.  
  412.                         byte2 = bit32.band(bit32.rshift(word, 8), 0xFF)
  413.                         if byte2 == 255 or byte2 == 0 then break end
  414.                         char_table[#char_table+1] = char(byte2)
  415.  
  416.                         i = i + 2
  417.                 else
  418.                         byte1 = readbyte(address + i)
  419.                         if byte1 == 0 or byte1 == 255 then break end
  420.                         char_table[#char_table+1] = char(byte1)
  421.                         i = i + 1
  422.                 end
  423.         end
  424.         return table.concat(char_table)
  425. end
  426.  
  427. local function readwidestring(address, length)
  428.         address = type(address) == "number" and address or error("bad argument #1 to 'readwidestring' expected number, got '" .. type(address) .. "'")
  429.         if length then
  430.                 length = type(length) == "number" and length or error("bad argument #2 to 'readwidestring' expected number, got '" .. type(length) .. "'")
  431.         else
  432.                 length = length or 51001
  433.         end
  434.         local char_table = {}
  435.         local char = string.char
  436.         local word, dword, byte1, byte2, byte3, byte4
  437.         local i = 0
  438.         while i < length do
  439.                 -- quickens read calls (screw you oxide for using virtualprotect)
  440.                 if (length-i) >= 4 then
  441.                         dword = readdword(address + i)
  442.  
  443.                         byte1 = bit32.band(dword, 0xFF)
  444.                         if byte1 == 255 or byte1 == 0 then break end
  445.                         char_table[#char_table+1] = char(byte1)
  446.  
  447.                         byte2 = bit32.band(bit32.rshift(dword, 8), 0xFF)
  448.                         if byte2 ~= 255 or byte2 ~= 0 then break end
  449.  
  450.                         byte3 = bit32.band(bit32.rshift(dword, 16), 0xFF)
  451.                         if byte3 == 255 or byte3 == 0 then break end
  452.                         char_table[#char_table+1] = char(byte3)
  453.  
  454.                         byte4 = bit32.band(bit32.rshift(dword, 24), 0xFF)
  455.                         if byte4 ~= 255 or byte4 ~= 0 then break end
  456.  
  457.                         i = i + 4
  458.                 else
  459.                         word = readword(address + i)
  460.  
  461.                         byte1 = bit32.band(word, 0xFF)
  462.                         if byte1 == 255 or byte1 == 0 then break end
  463.                         char_table[#char_table+1] = char(byte1)
  464.  
  465.                         byte2 = bit32.band(bit32.rshift(word, 8), 0xFF)
  466.                         if byte2 ~= 255 or byte2 ~= 0 then break end
  467.  
  468.                         i = i + 2
  469.                 end
  470.         end
  471.         return table.concat(char_table)
  472. end
  473.  
  474. local function writestring(address, offset, str)
  475.         address = type(address) == "number" and address or error("bad argument #1 to 'writestring' expected number, got '" .. type(address) .. "'")
  476.         if str then
  477.                 offset = type(offset) == "number" and offset or error("bad argument #2 to 'writestring' expected number, got '" .. type(offset) .. "'")
  478.                 str = type(str) == "string" and str or error("bad argument #3 to 'writestring' expected string, got '" .. type(str) .. "'")
  479.         else
  480.                 str = type(offset) == "string" and offset or error("bad argument #2 to 'writestring' expected string, got '" .. type(offset) .. "'")
  481.                 offset = nil
  482.         end
  483.         address = address + (offset or 0x0)
  484.         local byte_table = {}
  485.         local byte = string.byte
  486.         for char in string.gmatch(str, '.') do
  487.                 byte_table[#byte_table+1] = byte(char)
  488.         end
  489.         local length = #byte_table
  490.         local i = 0
  491.         while i < length do
  492.                 -- quickens write calls (screw you oxide for using virtualprotect)
  493.                 if (length-i) >= 4 then
  494.                         writedword(address + i, tonumber(string.format("%02X%02X%02X%02X", byte_table[i+4], byte_table[i+3], byte_table[i+2], byte_table[i+1]), 16))
  495.                         i = i + 4
  496.                 elseif (length-i) >= 2 then
  497.                         writeword(address + i, tonumber(string.format("%02X%02X", byte_table[i+2], byte_table[i+1]), 16))
  498.                         i = i + 2
  499.                 else
  500.                         writebyte(address + i, byte_table[i+1])
  501.                         i = i + 1
  502.                 end
  503.         end
  504. end
  505.  
  506. local function writewidestring(address, offset, str)
  507.         address = type(address) == "number" and address or error("bad argument #1 to 'writewidestring' expected number, got '" .. type(address) .. "'")
  508.         if str then
  509.                 offset = type(offset) == "number" and offset or error("bad argument #2 to 'writewidestring' expected number, got '" .. type(offset) .. "'")
  510.                 str = type(str) == "string" and str or error("bad argument #3 to 'writewidestring' expected string, got '" .. type(str) .. "'")
  511.         else
  512.                 str = type(offset) == "string" and offset or error("bad argument #2 to 'writewidestring' expected string, got '" .. type(offset) .. "'")
  513.                 offset = nil
  514.         end
  515.         address = address + (offset or 0x0)
  516.         local byte_table = {}
  517.         local byte = string.byte
  518.         for char in string.gmatch(str, '.') do
  519.                 byte_table[#byte_table+1] = byte(char)
  520.         end
  521.         local length = #byte_table
  522.         while i < length do
  523.                 -- quickens write calls (screw you oxide for using virtualprotect)
  524.                 if i % 4 == 0 and length - i >= 4 then
  525.                         writedword(address + i, tonumber((byte_table[i+2]*100) .. (byte_table[i+1]*100)))
  526.                         i = i + 2
  527.                 else
  528.                         writeword(address + i, byte_table[i+1])
  529.                         i = i + 1
  530.                 end
  531.         end
  532. end
  533.  
  534. -- crash prevention code.
  535. local function createobject(mapId, ...)
  536.         if mapId and mapId ~= 0 and mapId ~= -1 then
  537.                 return _G.createobject(mapId, ...)
  538.         end
  539. end
  540.  
  541. local function getteam(playerId)
  542.         if defaults.multiteam_vehicles or not team_play then
  543.                 return playerId
  544.         end
  545.         return _G.getteam(playerId)
  546. end
  547.  
  548. -- Get Unused Table if available.
  549. function TM.New()
  550.  
  551.         local unused_tables = TM.unused_tables
  552.         local length = #unused_tables
  553.         local t = unused_tables[length]
  554.         if t then
  555.                 -- use an unused table
  556.                 unused_tables[length] = nil
  557.                 t:deleteEntries()
  558.         else
  559.                 -- no tables available, make a new one
  560.                 t = setmetatable({}, TM)
  561.         end
  562.  
  563.         return t
  564. end
  565.  
  566. local function validate_ipv4(ip)
  567.         if not ip then return nil end
  568.         ip = gsub(gsub(ip, "[%s]*", ""), "x+", "*")
  569.         local a,b,c,slash,d,finish = match(ip, "^([^%.]+)%.([^%.]*)%.?([^%./]*)%.?(/?)([^%.]*)()")
  570.         a = a == "" and "*" or match(a or "", "[%d%*]+")
  571.         b = b == "" and "*" or match(b or "", "[%d%*]+")
  572.         c = c == "" and "*" or match(c or "", "[%d%*]+")
  573.         slash = slash ~= ""
  574.         d = d or ""
  575.         --print("IP VALIDATION I:",a,b,c,d,slash,finish)
  576.         if slash then
  577.                 if d:find("/") or not match(d, "[%d%*]+") then return false end -- can't have two slashes
  578.                 d = "0/"..d -- Alternate form 194.1.4/24
  579.         else
  580.                 d = d == "" and "*" or match(d, "[%d%*/]+")
  581.         end
  582.  
  583.         if not a or not b or not c then
  584.                 return false -- bad ip
  585.         end
  586.  
  587.         local found,a2,b2,c2,d2 = match(ip, "(%-)(%d+)%.(%d*)%.?(%d*)%.?(%d*)%c*$", finish)
  588.         --print("IP VALIDATION II:",found,a2,b2,c2,d2)
  589.         if not found then
  590.                 if a2 and a ~= "" then return false end -- this should just never happen lol
  591.                 return format("%s.%s.%s.%s",a,b,c,d)
  592.         elseif slash then
  593.                 return false -- can't have a slash, and an iplimit.. lol
  594.         end
  595.         a2 = a2 == "" and "*" or match(a2, "[%d%*]+")
  596.         b2 = b2 == "" and "*" or match(b2, "[%d%*]+")
  597.         c2 = c2 == "" and "*" or match(c2, "[%d%*]+")
  598.         d2 = d2 == "" and "*" or match(d2, "[%d%*]+")
  599.  
  600.         if not a2 or not b2 or not c2 then
  601.                 return false -- bad ip
  602.         end
  603.  
  604.         if c2:find("/") and d2:find("/") then return false end
  605.         return format("%s.%s.%s.%s-%s.%s.%s.%s", a,b,c,d,a2,b2,c2,d2)
  606. end
  607.  
  608. local function check_ip(ip)
  609.         local i = 1
  610.         local octets = 1
  611.         local octet = ""
  612.         local err = ""
  613.         local errors = 0
  614.         local blocks = {}--TM.New()
  615.         local c, block
  616.         while (i <= #ip + 1) do
  617.                 c = sub(ip, i, i)
  618.                 if c == "." or #ip+1 == i then
  619.                         octets = octets + 1
  620.                         block = tonumber(octet)
  621.                         if not block or block > 255 or block < 0 or octets > 5 then
  622.                                 errors = errors + 1
  623.                                 err = err .. " \f3>" .. (block or "?") .. "<\f8"
  624.                         else
  625.                                 blocks[#blocks+1] = octet
  626.                                 err = err .. "\f0 " .. block .. "\f8"
  627.                         end
  628.                         octet = ""
  629.                 else
  630.                         octet = octet .. c
  631.                 end
  632.                 i = i + 1
  633.         end
  634.  
  635.         local result = {}--TM.New()
  636.         result[1] = err
  637.  
  638.         if errors > 0 then
  639.                 return result
  640.         end
  641.  
  642.         result[2] = blocks
  643.         return result
  644. end
  645.  
  646. local function ip2long(ip_addr)
  647.         local blocks = check_ip(ip_addr)[2] or error("Invalid IP-Address. IP_ADDR: " .. tostring(ip_addr) .. "\r\nError: " .. check_ip(ip_addr)[1])
  648.         local a = bit32.lshift(blocks[1], 24)
  649.         local b = #blocks >= 2 and bit32.lshift(blocks[2], 16)
  650.         local c = #blocks >= 3 and bit32.lshift(blocks[3], 8)
  651.         if not b or not c then return nil end
  652.         return bit32.bor(bit32.bor(a, b, c), blocks[4])
  653. end
  654.  
  655. local function convertSign(num, maxSize)
  656.         return num - bit32.band(num, maxSize)*2
  657. end
  658.  
  659. local function long2ip(addr)
  660.         addr = tonumber(addr) or error("Invalid 32-bit Long: " .. addr)
  661.         local a = bit32.rshift(bit32.band(addr, bit32.lshift(0xFF, 24)), 24)
  662.         local b = bit32.rshift(bit32.band(addr, bit32.lshift(0xFF, 16)), 16)
  663.         local c = bit32.rshift(bit32.band(addr, bit32.lshift(0xFF, 8)), 8)
  664.         local d = bit32.band(addr, 0xFF)
  665.         return format("%i.%i.%i.%i", convertSign(a, 255), convertSign(b, 255), convertSign(c, 255), convertSign(d, 255))
  666. end
  667.  
  668. local function netMatch(network, ip)
  669.         network = validate_ipv4(network)
  670.         if not network then return end
  671.        
  672.     -- if no ip to check, then just validate the network.
  673.         if not ip then return network end
  674.        
  675.         ip = validate_ipv4(ip)
  676.  
  677.         local orig_network = network
  678.         if ip == network then
  679.                 --print("used network " .. network .. " for ip " .. ip)
  680.                 return network
  681.         end
  682.         network = gsub(network, ' ', '')
  683.         if find(network, '*') then
  684.                 if find(network, '/') then
  685.                         network = tokenizestring(network, '/')[1]
  686.                 end
  687.                 local nCount
  688.                 network, nCount = gsub(network, '*', '*')
  689.                 network = gsub(network, '*', '0')
  690.                 if nCount == 1 then
  691.                         network = network .. '/24'
  692.                 elseif nCount == 2 then
  693.                         network = network .. '/16'
  694.                 elseif nCount == 3 then
  695.                         network = network .. '/8'
  696.                 elseif nCount > 3 then
  697.                         return network -- if *.*.*.*, then all, so matched
  698.                 end
  699.         end
  700.  
  701.         if find(ip, '*') then
  702.                 if find(ip, '/') then
  703.                         ip = tokenizestring(ip, '/')[1]
  704.                 end
  705.                 local nCount
  706.                 ip, nCount = gsub(ip, '*', '*')
  707.                 ip = gsub(ip, '*', '0')
  708.                 if nCount == 1 then
  709.                         ip = ip .. '/24'
  710.                 elseif nCount == 2 then
  711.                         ip = ip .. '/16'
  712.                 elseif nCount == 3 then
  713.                         ip = ip .. '/8'
  714.                 elseif nCount > 3 then
  715.                         return ip -- if *.*.*.*, then all, so matched
  716.                 end
  717.         end
  718.  
  719.         --print("from original network " .. orig_network .. ", used network " .. network .. " for " .. ip)
  720.  
  721.         local d = find(network, '-')
  722.         if not d then
  723.                 local ip_arr = tokenizestring(network, "/")
  724.                 local network_long = ip2long(ip_arr[1])
  725.                 local mask = bit32.lshift(0xFFFFFFFF, (32 - (ip_arr[2] or 32)))
  726.                 local ip_arr2 = tokenizestring(ip, "/")
  727.                 local ip_long = ip2long(ip_arr2[1])
  728.                 local mask2 = bit32.lshift(0xFFFFFFFF, (32 - (ip_arr2[2] or 32)))
  729.                 return bit32.band(network_long, mask, mask2) == bit32.band(ip_long, mask, mask2)
  730.         else
  731.                 local from = ip2long(sub(network, 1, d-1))
  732.                 local to = ip2long(sub(network, d+1))
  733.                 ip = ip2long(ip)
  734.                 return ip >= from and ip <= to
  735.         end
  736. end
  737.  
  738. local function iptoplayer(ip)
  739.         for playerId = 0,15 do
  740.                 if getplayer(playerId) and netMatch(getip(playerId) .. "/24", ip) then
  741.                         return playerId
  742.                 end
  743.         end
  744. end
  745.  
  746. local function getobject(objectId)
  747.         return objectId and _G.getobject(objectId) or nil
  748. end
  749.  
  750. -- hides a phasor bug where player 1 gets output from privatesay in their console...
  751. local function privatesay(playerId, message, appendServer)
  752.         _G.privatesay(playerId, message, appendServer)
  753.         if getplayer(0) then
  754.                 sendconsoletext(0, " \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n ")
  755.         end
  756. end
  757.  
  758. local function privateSayAdmins(message)
  759.         for playerId = 0,15 do
  760.                 if getplayer(playerId) and PlayerClass[playerId].admin_entry then
  761.                         sendconsoletext(playerId, message)
  762.                         privatesay(playerId, message)
  763.                 end
  764.         end
  765. end
  766.  
  767. local function getname(playerId)
  768.         return playerId and _G.getname(playerId) or "the Server"
  769. end
  770.  
  771. local function getMorePowerfulAdmin(adminEntry1, adminEntry2)
  772.         local access1 = #access_table[adminEntry1.level]
  773.         local access2 = #access_table[adminEntry2.level]
  774.         return access1 > access2 and adminEntry1 or access1 < access2 and adminEntry2 or access1 > 0
  775. end
  776.  
  777. local function lookupAdminEntry(playerId)
  778.         local ip = getip(playerId)
  779.         local retIndex = gethash(playerId)
  780.         local highestEntry = admin_table[retIndex]
  781.         local morePowerfulAdmin
  782.         for index,thisAdmin in next,admin_table do
  783.                 if thisAdmin.type == "ip" and netMatch(index, ip) then
  784.                         morePowerfulAdmin = highestEntry and getMorePowerfulAdmin(thisAdmin, highestEntry) or thisAdmin
  785.                         if type(morePowerfulAdmin) == "table" then
  786.                                 highestEntry = morePowerfulAdmin
  787.                                 retIndex = index
  788.                         end
  789.                 end
  790.         end
  791.         return highestEntry, highestEntry and retIndex
  792. end
  793.  
  794. -- Table Methods and Metamethods.
  795.  
  796. function getOrCreateIdentities(hash, ip, name, playerId) -- arguments: hash, ip, name, playerId
  797.         local length, probably_unique_name, ip_addresses, names, hash_identity, ip_identity, name_identity
  798.        
  799.         hash_identity = identities[hash]
  800.         ip_identity = identities[ip]
  801.         name_identity = identities[name]
  802.        
  803.         -- check the ip range to see if it matches an already existing entry.
  804.         if not ip_identity then
  805.                 for ip_key,v in next,identities do
  806.                         if validate_ipv4(ip_key) and netMatch(ip, ip_key) then
  807.                                 ip_identity = identities[ip_key]
  808.                                 break
  809.                         end
  810.                 end
  811.         end
  812.        
  813.         if hash_identity then
  814.                 if not ip_identity then
  815.                         ip_addresses = hash_identity.ip_addresses
  816.                         length = #ip_addresses+1
  817.                         ip_addresses[length]    = ip
  818.                         ip_addresses[ip]                = length
  819.                         for i = 1,#ip_addresses do
  820.                                 ip_identity = identities[ip_addresses[i]]
  821.                                 if ip_identity then
  822.                                         identities[ip] = ip_identity
  823.                                         break
  824.                                 end
  825.                         end
  826.                 end
  827.                 if not name_identity then
  828.                         names = hash_identity.names
  829.                         length = #names+1
  830.                         names[length]   = name
  831.                         names[name]             = length
  832.                         for i = 1,#names do
  833.                                 name_identity = identities[names[i]]
  834.                                 if name_identity then
  835.                                         identities[name] = name_identity
  836.                                         break
  837.                                 end
  838.                         end
  839.                 end
  840.                 hash_identity.playerId = playerId
  841.         end
  842.         if ip_identity then
  843.                 if not hash_identity and not sharedhashes[hash] then
  844.                         hash_identity = identities[ip]
  845.                         identities[hash] = hash_identity
  846.                         identities[ip].hash = hash
  847.                 end
  848.                 if not name_identity and not randomNames[name] then
  849.                         names = ip_identity.names
  850.                         length = #names+1
  851.                         names[length]   = name
  852.                         names[name]             = length
  853.                         for i = 1,#names do
  854.                                 name_identity = identities[names[i]]
  855.                                 if name_identity then
  856.                                         identities[name] = name_identity
  857.                                         break
  858.                                 end
  859.                         end
  860.                 end
  861.                 ip_identity.playerId = playerId
  862.         end
  863.         if name_identity then
  864.  
  865.                 if not hash_identity and not sharedhashes[hash] then
  866.                         hash = name_identity.hash
  867.                         hash_identity = identities[hash]
  868.                         identities[hash] = hash_identity
  869.                 end
  870.                 if not ip_identity then
  871.                         ip_addresses = hash_identity.ip_addresses
  872.                         length = #ip_addresses+1
  873.                         ip_addresses[length]    = ip
  874.                         ip_addresses[ip]                = length
  875.                         for i = 1,#ip_addresses do
  876.                                 ip_identity = identities[ip_addresses[i]]
  877.                                 if ip_identity then
  878.                                         identities[ip] = ip_identity
  879.                                         break
  880.                                 end
  881.                         end
  882.                 end
  883.                 name_identity.playerId = playerId
  884.         end
  885.        
  886.         ::CreateIdentity::
  887.         --Create a new identity for this new player.
  888.         if not ip_identity then
  889.                 identity                                = {playerId = playerId}
  890.                 --identity.index                = identifier
  891.                 identity.hash                   = sharedhashes[hash] and {} or hash
  892.                 identity.ip_addresses   = {ip, [ip] = 1}
  893.                 identity.names                  = randomNames[name] and {} or {name, [name] = 1}
  894.                
  895.                 identities[hash]        = identity
  896.                 identities[ip]          = identity
  897.                 identities[name]        = identity
  898.                
  899.                 identity.disarmed                       = false
  900.                 identity.tempadmin                      = false
  901.                 identity.spamcounter            = 0
  902.                 identity.rcon_fails                     = 0
  903.                 identity.rconfail_timer         = -1
  904.                 identity.muted                          = false
  905.                 identity.playerId                       = playerId
  906.                
  907.                 -- Calculate unique identity id by count.
  908.                 identities[#identities+1] = identity
  909.                
  910.                 --registered_names[name] = identity -- automatically register this name to this Hash/IP
  911.        
  912.                 return identity, identity, identity
  913.         end
  914.         return hash_identity, ip_identity, name_identity
  915. end
  916.  
  917. -- Not currently using this, if I remember correctly.
  918. --[[function PlayerClass:__newindex(key, value)
  919.         if rawget(self, key) ~= nil then
  920.                 rawset(self, key, value)
  921.                 return
  922.         end
  923.        
  924.         local hash_identity = identities[rawget(self, "hash")]
  925.         if hash_identity then rawset(hash_identity, key, value) end
  926.        
  927.         local ip_identity = identities[rawget(self, "ip")]
  928.         if ip_identity then rawset(ip_identity, key, value) end
  929.        
  930.         local name_identity = identities[rawget(self, "name")]
  931.         if name_identity then rawset(name_identity, key, value) end
  932. end--]]
  933.  
  934. function PlayerClass:__index(key)
  935.         --hprintf(TM.__tostring(PlayerClass[2]))
  936.         local identity = rawget(self, "hash_identity") or rawget(identities, rawget(self, "hash"))
  937.         if identity then
  938.                 return rawget(identity, key)
  939.         end
  940.        
  941.         local fallback_identity = rawget(self, "ip_identity") or rawget(identities, rawget(self, "ip"))
  942.         if fallback_identity then
  943.                 return rawget(fallback_identity, key)
  944.         end
  945.        
  946.         identity = rawget(self, "name_identity") or rawget(identities, rawget(self, "name"))
  947.         if identity then
  948.                 return rawget(identity, key)
  949.         end
  950.        
  951.         -- This will be used if all player identifiers are not unique
  952.         return rawget(fallback_identity, key)
  953. end
  954.  
  955. function PlayerClass:Initialize(playerId)
  956.         local player = rawget(PlayerClass, playerId)
  957.         local hash, ip, name = gethash(playerId), getip(playerId), getname(playerId)
  958.        
  959.         rawset(player, "admin_entry", lookupAdminEntry(playerId) or false)
  960.        
  961.         player.drones:deleteEntries()
  962.        
  963.         rawset(player, "hash", hash)
  964.         rawset(player, "ip", ip)
  965.         rawset(player, "name", name)
  966.        
  967.         -- Update our identities.
  968.         local hash_identity, ip_identity, name_identity = getOrCreateIdentities(hash, ip, name, playerId)
  969.  
  970.         rawset(player, "hash_identity", hash_identity)
  971.         rawset(player, "ip_identity", ip_identity)
  972.         rawset(player, "name_identity", name_identity)
  973.        
  974.         rawset(player, "afk", false)
  975.         rawset(player, "bulletmode", false)
  976.         rawset(player, "colorspawn", false)
  977.         rawset(player, "crouch", false)
  978.         rawset(player, "dmgmultiplier", 1)
  979.         rawset(player, "godmode", false)
  980.         rawset(player, "ghostmode", false)
  981.         rawset(player, "invisible", false)
  982.         rawset(player, "hidden", false)
  983.         rawset(player, "objspawnid", false)
  984.         rawset(player, "player_struct", getplayer(playerId) or false)
  985.         rawset(player, "playerId", playerId)
  986.         rawset(player, "playerIndex", resolveplayer(playerId) or -1)
  987.         rawset(player, "tbagname", "")
  988.         rawset(player, "x", 0)
  989.         rawset(player, "y", 0)
  990.         rawset(player, "z", 0)
  991.         return player
  992. end
  993.  
  994. local function GetGameAddresses(game)
  995.         if _SERVERAPP == "Sapp" then
  996.                 addresses = { -- check these against the module to confirm they're all the same (these should take preference)
  997.                         stats_globals = read_dword(sig_scan("33C0BF??????00F3AB881D") + 0x3), -- Confirmed. (Thanks Giraffe)
  998.                         ctf_globals = read_dword(sig_scan("C6000083C0303D??????00")+8), -- Confirmed.
  999.                         slayer_globals = read_dword(sig_scan("5733C0B910000000BFE8E05B00F3ABB910000000") + 19), -- Confirmed,
  1000.                         oddball_globals = read_dword(sig_scan("BF??????00F3ABB951000000")+0x1), -- Confirmed.
  1001.                         koth_globals = read_dword(sig_scan("BF??????00F3ABB96B000000")+0x1), -- Confirmed.
  1002.                         race_globals = read_dword(sig_scan("BF??????00F3ABB952000000")+0x1), -- Confirmed.
  1003.                         race_locs = 0x5F5078,
  1004.                         gametype_base = read_dword(sig_scan("B9360000008BF3BF78545F00")+0x8), -- Confirmed.
  1005.                         network_struct = read_dword(sig_scan("F3ABA1????????BA????????C740??????????E8????????668B0D") + 3),
  1006.                         player_header_pointer = read_dword(sig_scan("DDD8A1??????008944244835") + 0x3), -- Confirmed (Thanks Giraffe)
  1007.                         object_header_pointer = read_dword(sig_scan("8B0D????????8B513425FFFF00008D") + 2), -- Confirmed. (Thanks 002)
  1008.                         collideable_objects_pointer = 0x6C69F4,
  1009.                         map_header_base = 0x6E2C84,
  1010.                         banlist_header = read_dword(sig_scan("A3??????00A1??????0033DB3BC3")+1), -- Confirmed.
  1011.                         game_globals = nil, -- Don't care.
  1012.                         gameinfo_header = read_dword(sig_scan("A1????????8B480C894D00") + 0x1), -- Confirmed. (Thanks Wizard)
  1013.                         mapcycle_header = 0x598A8C,
  1014.                         network_server_globals = 0x61FB44,
  1015.                         hash_table_base = 0x5AFB14, -- Untested.
  1016.                        
  1017.                         -- Strings (Thanks to Giraffe for all the sigs in this section!)
  1018.                         broadcast_version_address = read_dword(sig_scan("751768??????0068??????00BA") + 0x3), -- Confirmed.
  1019.                         version_info_address = nil, -- Don't care.
  1020.                         broadcast_game_address = read_dword(sig_scan("CCCCBA??????002BD08A08") + 0x3), -- Confirmed (halor = PC, halom = CE)
  1021.                         server_ip_argument = read_dword(sig_scan("BA??????008BC72BD78A08880C024084C975F68B442404") + 0x1), -- Confirmed.
  1022.                         server_port_address = read_dword(sig_scan("668B0D??????000BF2C605") + 0x3), -- Confirmed.
  1023.                         server_path_address = read_dword(sig_scan("0000BE??????005657C605") + 0x3), -- Confirmed.
  1024.                         computer_name_address = read_dword(sig_scan("68??????0068??????0068000401006A00") + 0x1), -- Confirmed
  1025.                         profile_path_address = read_dword(sig_scan("68??????008D54245468") + 0x1), -- Confirmed.
  1026.                         map_name_address =read_dword(sig_scan("66A3??????00890D??????00C3") + 0x2), -- Confirmed. (Full name)
  1027.                         hardware_info_address = read_dword(sig_scan("BE??????008BC68B4DF064890D000000005F5E5B8BE55DC36A0C") + 0x1), -- Confirmed.
  1028.                        
  1029.                         map_name_address2 = read_dword(sig_scan("B8??????00E8??????0032C983F813") + 0x1), -- Confirmed. (File name)
  1030.                         server_password_address = read_dword(sig_scan("F3ABA3??????00A3??????00A2??????00C705") + 0x3), -- Confirmed.
  1031.                         logfile_path_address = read_dword(sig_scan("740ABB????5C00E8????0300") + 0x3), -- Confirmed. (CE Only)
  1032.                         banlist_path_address = read_dword(sig_scan("68??????00E8??????0083C41068") + 0x1), -- Confirmed.
  1033.                         banlist_path_address2 = read_dword(sig_scan("CCCCC605??????0000E8??????0085C0") + 0x4), -- Confirmed.
  1034.                         --Patches
  1035.                         rcon_password_address = read_dword(sig_scan("7740BA??????008D9B000000008A01") + 0x3), -- Confirmed.
  1036.                         rcon_failed_address = read_dword(sig_scan("B8????????E8??000000A1????????55") + 1), -- Found by 002
  1037.                         kill_message_address = read_dword(sig_scan("8B42348A8C28D500000084C9") + 3), -- Found by sehe (Write to 0x03EB01B1)
  1038.                         color_patch1 = read_dword(sig_scan("741F8B482085C9750C")), -- Found by 002 (Write to 235 if not 0)
  1039.                         color_patch2 = read_dword(sig_scan("EB1F8B482085C9750C")), -- Found by 002 (Write to 235)
  1040.                         nonslayer_score_patch = sig_scan("8B??3883C404????74??57FFD0")+0x8, -- 0xEB = patched, 0x74 = normal
  1041.                         slayer_score_patch = sig_scan("74178B94242808000052518B8C24280800005157FFD083C4108B8424240800003BF8530F94C383FFFF") -- 0xEB = patched, 0x74 = normal
  1042.                 }
  1043.                 --[[while true do
  1044.                         cprint("nonslayer: " .. tostring(addresses.nonslayer_score_patch) .. " slayer: " .. tostring(addresses.slayer_score_patch))
  1045.                 end--]]
  1046.         elseif game == "PC" then
  1047.                 addresses = {
  1048.                         -- Structs/headers.
  1049.                         stats_header = 0x639720,
  1050.                         stats_globals = 0x639898,
  1051.                         ctf_globals = 0x639B98,
  1052.                         slayer_globals = 0x63A0E8,
  1053.                         oddball_globals = 0x639E58,
  1054.                         koth_globals = 0x639BD0,
  1055.                         race_globals = 0x639FA0,
  1056.                         race_locs = 0x670F40,
  1057.                         gametype_base = 0x671340,
  1058.                         network_struct = 0x745BA0,
  1059.                         camera_base = 0x69C2F8,
  1060.                         player_header_pointer = 0x75ECE4,
  1061.                         obj_header_pointer = 0x744C18,
  1062.                         collideable_objects_pointer = 0x744C34,
  1063.                         map_header_base = 0x630E74,
  1064.                         banlist_header = 0x641280,
  1065.                         game_globals = "???", -- (???) Why do I not have this for PC?
  1066.                         gameinfo_header = 0x671420,
  1067.                         mapcycle_header = 0x614B4C,
  1068.                         network_server_globals = 0x69B934,
  1069.                         sockaddr_pointer = 0x6A1F08,
  1070.                         flags_pointer = 0x6A590C,
  1071.                         hash_table_base = 0x6A2AE4,
  1072.  
  1073.                         -- String/Data Addresses.
  1074.                         init_file_address = 0x8EB38,
  1075.                         broadcast_version_address = 0x5DF840,
  1076.                         version_info_address = 0x5E02C0,
  1077.                         broadcast_game_address = 0x5E4768,
  1078.                         mapcycle_timeout = 0x614AC0,
  1079.                         public_value_address = 0x6164C0,
  1080.                         server_port_address = 0x625230,
  1081.                         timelimit_address = 0x626630,
  1082.                         server_path_address = 0x62C390,
  1083.                         computer_name_address = 0x62CD60,
  1084.                         profile_path_address = 0x635610,
  1085.                         map_name_address = 0x63BC78,
  1086.                         computer_specs_address = 0x662D04,
  1087.                         map_name_address2 = 0x698F21,
  1088.                         server_password_address = 0x69B93C,
  1089.                         banlist_path_address = 0x69B950,
  1090.                         rcon_password_address = 0x69BA5C,
  1091.  
  1092.                         -- Patches
  1093.                         nonslayer_score_patch = 0x47F382, -- 0xEB = patched, 0x74 = normal
  1094.                         slayer_score_patch = 0x47F5D5, -- 0xEB = patched, 0x74 = normal
  1095.                         ctf_msgs_patch = 0x481545, -- 0xEB = patched, 0x74 = normal
  1096.                         color_patch = 0x4828FE, -- 0xEB = patched, 0x74 = normal
  1097.                         devmode_patch1 = 0x4A4DBF, -- 0xEB = patched, 0x74 = normal
  1098.                         devmode_patch2 = 0x4A4E7F, -- 0xEB = patched, 0x74 = normal
  1099.                         servername_patch = 0x517D6B, -- 0x9090 = patched, 0x4B74 = normal
  1100.                         hash_duplicate_patch = 0x59C518, -- 0x9090 = patched, 0xFD3B = normal
  1101.                         hashcheck_patch = 0x59c280, -- 0xEB = patched, 0x74 = normal
  1102.                         versioncheck_patch = 0x5152E7 -- 0xEB = patched, 0x7D = normal
  1103.                 }
  1104.         elseif game == "CE" then
  1105.                 addresses = {
  1106.                         -- Structs/headers.
  1107.                         stats_header = 0x5BD740,
  1108.                         stats_globals = 0x5BD8B8,
  1109.                         ctf_globals = 0x5BDBB8,
  1110.                         slayer_globals = 0x5BE108,
  1111.                         oddball_globals = 0x5BDE78,
  1112.                         koth_globals = 0x5BDBF0,
  1113.                         race_globals = 0x5BDFC0,
  1114.                         race_locs = 0x5F5098,
  1115.                         gametype_base = 0x5F5498,
  1116.                         network_struct = 0x6C7980,
  1117.                         camera_base = 0x62075C,
  1118.                         player_globals = 0x6E1478, -- From OS.
  1119.                         player_header_pointer = 0x6E1480,
  1120.                         obj_header_pointer = 0x6C69F0,
  1121.                         collideable_objects_pointer = 0x6C6A14,
  1122.                         map_header_base = 0x6E2CA4,
  1123.                         banlist_header = 0x5C52A0,
  1124.                         game_globals = 0x61CFE0, -- (???)
  1125.                         gameinfo_header = 0x5F55BC,
  1126.                         mapcycle_header = 0x598A8C,
  1127.                         network_server_globals = 0x61FB64,
  1128.                         sockaddr_pointer = 0x626388,
  1129.                         hash_table_base = 0x5AFB34,
  1130.  
  1131.                         -- String/Data Addresses.
  1132.                         init_file_address = 0x8EB26,
  1133.                         broadcast_version_address = 0x564B34,
  1134.                         version_info_address = 0x565104,
  1135.                         broadcast_game_address = 0x569EAC,
  1136.                         mapcycle_timeout = 0x598A00,
  1137.                         public_value_address = 0x59A424,
  1138.                         server_port_address = 0x5A91A0,
  1139.                         timelimit_address = 0x5AA5B0,
  1140.                         server_path_address = 0x5B0670,
  1141.                         computer_name_address = 0x5B0D40,
  1142.                         profile_path_address = 0x5B9630,
  1143.                         map_name_address = 0x5BFC98,
  1144.                         computer_specs_address = 0x5E6E5C,
  1145.                         map_name_address2 = 0x61D151,
  1146.                         server_password_address = 0x61FB6C,
  1147.                         banlist_path_address = 0x61FB80,
  1148.                         rcon_password_address = 0x61FC8C,
  1149.  
  1150.                         -- Patches
  1151.                         nonslayer_score_patch = 0x45BCB0, -- 0xEB = patched, 0x74 = normal
  1152.                         slayer_score_patch = 0x45BE15, -- 0xEB = patched, 0x74 = normal
  1153.                         ctf_msgs_patch = 0x45DA95, -- 0xEB = patched, 0x74 = normal
  1154.                         color_patch = 0x45EB5E, -- 0xEB = patched, 0x74 = normal
  1155.                         devmode_patch1 = 0x47DF0C, -- 0xEB = patched, 0x74 = normal
  1156.                         devmode_patch2 = 0x47DFBC, -- 0xEB = patched, 0x74 = normal
  1157.                         servername_patch = 0x4CE0CD, -- 0x9090 = patched, 0x4B74 = normal
  1158.                         hash_duplicate_patch = 0x5302E8, -- 0x9090 = patched, 0xFD3B = normal
  1159.                         hashcheck_patch = 0x530130, -- 0xEB = patched, 0x74 = normal
  1160.                         versioncheck_patch = 0x4CB587, -- 0xEB = patched, 0x7D = normal
  1161.                 }
  1162.         end
  1163. end
  1164.  
  1165. -- This is my ingenius way to execute a command and bypass ALL of my code.
  1166. -- So I can do sv_kick without having to worry about creating an infinite loop.
  1167. local function halo_svcmd(command, retBool)
  1168.         dont_call_onservercommand = true
  1169.         --print("BEFORE HALO_SVCMD")
  1170.         local response = svcmd(command, retBool)
  1171.         --print("AFTER HALO_SVCMD")
  1172.         dont_call_onservercommand = false
  1173.         return retBool and response
  1174. end
  1175.  
  1176. local function halo_svcmdplayer(command, playerId, retBool)
  1177.         dont_call_onservercommand = true
  1178.         --print "BEFORE HALO_SVCMDPLAYER"
  1179.         local response = svcmdplayer(command, playerId, retBool)
  1180.         --print "AFTER HALO_SVCMDPLAYER"
  1181.         dont_call_onservercommand = false
  1182.         return retBool and response
  1183. end
  1184.  
  1185. local function svcmd(command, retBool)
  1186.         --print "BEFORE SVCMD"
  1187.         if OnServerCommandAttempt(nil, command) ~= false then
  1188.                 --print "AFTER SVCMD"
  1189.                 return _G.svcmd(command, retBool)
  1190.         end
  1191. end
  1192.  
  1193. local function svcmdplayer(command, playerId, retBool)
  1194.         --print("BEFORE SVCMDPLAYER)
  1195.         if OnServerCommandAttempt(playerId, command) ~= false and OnServerCommand(playerId, command) ~= false then
  1196.                 --print("AFTER SVCMDPLAYER)
  1197.                 return halo_svcmd(command, retBool) or ""
  1198.         end
  1199. end
  1200.  
  1201.  
  1202. math.randomseed(ceil(os.clock()) + os.time())
  1203. local getsuckyrand = math.random
  1204. getsuckyrand(1, 10) getsuckyrand(1, 10) getsuckyrand(1, 10)
  1205. --Low and High are INCLUSIVE.
  1206. local function rand(low, high)
  1207.  
  1208.         low = assert(tonumber(low), "bad argument #1 to 'rand' (number expected, got " .. type(low) .. ")")
  1209.         high = assert(tonumber(high), "bad argument #2 to 'rand' (number expected, got " .. type(high) .. ")")
  1210.  
  1211.         return getsuckyrand(low, high)
  1212. end
  1213. local getrandomnumber = rand
  1214.  
  1215. -- I am having weird issues with resolveplayer not working correctly, so I'm overriding it.
  1216. local function resolveplayer(playerId)
  1217.         return playerId and PlayerClass[playerId].playerIndex
  1218. end
  1219.  
  1220. -- This function will get the XYZ coordinates of an object, and return them as 3 separate variables.
  1221. -- It will also determine if the object has a parent (i.e player in a vehicle) and return those coords instead.
  1222. -- I don't trust Phasor's getobjectcoords, and this way I know exactly how it will work.
  1223. local function getobjectcoords(objectId)
  1224.         local m_object = getobject(objectId)
  1225.         local vehicleObjId = readdword(m_object + 0x11C)
  1226.         local m_vehicleObj = getobject(vehicleObjId)
  1227.  
  1228.         -- Replace m_object with vehicle object struct (if there is one)
  1229.         m_object = m_vehicleObj or m_object
  1230.  
  1231.         return readfloat(m_object + 0x5C),readfloat(m_object + 0x60),readfloat(m_object + 0x64)
  1232. end
  1233.  
  1234. -- This is the same as getplayerobjectid except it returns BOTH the player's object struct and playerObjId
  1235. -- Returns the object struct and the object ID, or nil.
  1236. local function getplayerobject(playerId)
  1237.         local playerObjId = playerObjId or getplayerobjectid(playerId)
  1238.         return getobject(playerObjId), playerObjId
  1239. end
  1240.  
  1241. -- Gets the object ID of the player's vehicle.
  1242. -- Accepts argument 'playerId' (memory ID)
  1243. -- Returns the vehicle's object ID, or nil
  1244. local function getplayervehicleid(playerId)
  1245.         local m_playerObj = getplayerobject(playerId)
  1246.         return m_playerObj and readdword(m_playerObj + 0x11C)
  1247. end
  1248.  
  1249. -- This is the same as getplayervehicleid except it returns BOTH the vehicle's object struct and vehicleObjId.
  1250. -- Returns the vehicle's object struct and vehicle's object ID, or nil.
  1251. local function getplayervehicle(playerId)
  1252.         local vehicleObjId = getplayervehicleid(playerId)
  1253.         return getobject(vehicleObjId), vehicleObjId
  1254. end
  1255.  
  1256. -- Gets the object ID of the player's weapon.
  1257. -- Accepts arguments 'playerId' (memory ID) and 'slot' (weapon slot) which is a number from 0 to 3.
  1258. -- Slot is an optional argument. If not passed, then getplayerweaponid returns player's current weapon ID.
  1259. -- Returns the weapon's object ID, or 0xFFFFFFFF if no weapon, or nil if the player is dead
  1260. local function getplayerweaponid(playerId, slot)
  1261.  
  1262.         local m_playerObj = getplayerobject(playerId)
  1263.  
  1264.         if not m_playerObj then
  1265.                 return
  1266.         end
  1267.  
  1268.         -- Return vehicle's weapon if they are in a vehicle.
  1269.         local m_vehicleObj = getplayervehicle(playerId)
  1270.         if m_vehicleObj then
  1271.                 return readdword(m_vehicleObj + 0x2F8)
  1272.         end
  1273.  
  1274.         -- Return current weapon if no slot passed.
  1275.         if not slot then
  1276.                 return readdword(m_playerObj + 0x118)
  1277.         end
  1278.  
  1279.         -- Return weapon at the specified slot.
  1280.         return readdword(m_playerObj + 0x2F8 + slot*4)
  1281. end
  1282.  
  1283. -- This is the same as getplayerweaponid except it returns BOTH the weapon's object struct and weaponObjId.
  1284. -- Returns the weapon's object struct and weapon's object ID, or nil.
  1285. local function getplayerweapon(playerId, slot)
  1286.         local weaponObjId = getplayerweaponid(playerId, slot)
  1287.         return getobject(weaponObjId), weaponObjId
  1288. end
  1289.  
  1290. -- This function will determine if the given playerId is currently in a vehicle or not.
  1291. -- This function will return a boolean.
  1292. -- This function was created because I was having problems with Phasor's isinvehicle function.
  1293. local function isinvehicle(playerId)
  1294.         return not not getplayervehicle(playerId)
  1295. end
  1296.  
  1297. local function cleanupdrones(playerId)
  1298.         local player = PlayerClass[playerId]
  1299.         local vehicleObjId
  1300.         for i = 1,#player.drones do vehicleObjId = player.drones[i]
  1301.                 if getobject(vehicleObjId) then
  1302.                         destroyobject(vehicleObjId)
  1303.                 end
  1304.                 player.drones[i] = nil
  1305.         end
  1306. end
  1307.  
  1308. local function setspeed(playerId, speed)
  1309.         local m_player = assert(getplayer(playerId), "bad argument #1 to 'setspeed' (valid playerId expected, got '" .. type(playerId) .. "')")
  1310.         speed = assert(tonumber(speed), "bad argument #2 to 'setspeed' (number expected, got '" .. type(speed) .. "')")
  1311.         writefloat(m_player + 0x6C, speed and speed < 999999999999999999999999999999 and speed or 999999999999999999999999999999)
  1312. end
  1313.  
  1314. -- This function will set a player's color
  1315. -- Accepts playerId, and color carried as a number enumerator.
  1316. local function setcolor(playerId, color)
  1317.         local m_player = assert(getplayer(playerId), "bad argument #1 to 'setspeed' (valid playerId expected, got '" .. type(playerId) .. "')")
  1318.         color = assert(tonumber(color), "bad argument #2 to 'setcolor' (number expected, got '" .. type(color) .. "')")
  1319.         writebyte(m_player + 0x60, color)
  1320.  
  1321.         local m_playerObj, playerObjId = getplayerobject(playerId)
  1322.  
  1323.         if not m_playerObj then
  1324.                 return
  1325.         end
  1326.        
  1327.         local player = PlayerClass[playerId]
  1328.         local x,y,z = getobjectcoords(playerObjId)
  1329.         player.colorspawn = true
  1330.         player.x,player.y,player.z = x,y,z
  1331.         destroyobject(playerObjId)
  1332. end
  1333.  
  1334.  
  1335. -- This local function gets the player's memory ID from their object struct
  1336. -- Accepts the object ID of the player as an argument
  1337. -- Returns the playerId, or nil.
  1338. local function objectidtoplayer(objectId)
  1339.         local m_object = getobject(objectId)
  1340.         if m_object then
  1341.                 local playerId = readword(m_object + 0xC0)
  1342.                 local m_player = getplayer(playerId)
  1343.                 if m_player then
  1344.                         return playerId
  1345.                 end
  1346.         end
  1347. end
  1348.  
  1349. --[[ General lua functions ]]--
  1350.  
  1351. local function round(val, place)
  1352.         place = place or 0 return floor(val * (10 ^ place) + 0.5) * (10 ^ -place)
  1353. end
  1354.  
  1355. local function formatTime(time)
  1356.         time = tonumber(time)
  1357.         if time == -1 then
  1358.                 return "--"
  1359.         elseif time then
  1360.                 local temp = time
  1361.                 local centuries = floor(temp / 3153600000)
  1362.                 temp = temp - centuries * 3153600000
  1363.                 local years = floor(temp / 31536000)
  1364.                 temp = temp - years * 31536000
  1365.                 local weeks = floor(temp / 604800)
  1366.                 temp = temp - weeks * 604800
  1367.                 local days = floor(temp / 86400)
  1368.                 temp = temp - days * 86400
  1369.                 local hours = floor(temp / 3600)
  1370.                 temp = temp - hours * 3600
  1371.                 local minutes = floor(temp / 60)
  1372.                 temp = temp - minutes * 60
  1373.                 local seconds = floor(temp)
  1374.                 return format("%02d:%02d:%02d:%02d:%02d:%02d", years, weeks, days, hours, minutes, seconds)
  1375.         else
  1376.                 return time
  1377.         end
  1378. end
  1379.  
  1380. local function wordtotime(timeStr, bancount)
  1381.         local length = #timeStr
  1382.         local time
  1383.  
  1384.         -- Check if timeStr is in the correct format before we do anything.
  1385.         if tonumber(sub(timeStr, 1, length-1)) and match(sub(timeStr, length, length), "[cywdhms]") then
  1386.                 time = 0
  1387.                 local num = ""
  1388.                 local holder, tempnum, char
  1389.                 for i = 1,length do
  1390.                         char = sub(timeStr, i, i)
  1391.                         if tonumber(char) then
  1392.                                 num = num .. char
  1393.                         else
  1394.                                 tempnum = tonumber(num)
  1395.                                 holder = 0
  1396.                                 holder = char == "s" and tempnum
  1397.                                 holder = char == "m" and tempnum * 60 or holder
  1398.                                 holder = char == "h" and tempnum * 3600 or holder
  1399.                                 holder = char == "d" and tempnum * 86400 or holder
  1400.                                 holder = char == "w" and tempnum * 604800 or holder
  1401.                                 holder = char == "y" and tempnum * 31536000 or holder
  1402.                                 holder = char == "c" and tempnum * 3153600000 or holder
  1403.                                 holder = holder or 1
  1404.                                 time = time + holder
  1405.                         end
  1406.                 end
  1407.                 if time > 0 then
  1408.                         return time, char
  1409.                 end
  1410.         end
  1411.         time = tonumber(timeStr)
  1412.         if time == 0 and bancount then
  1413.                 return ban_penalty[bancount], "?"
  1414.         elseif time then
  1415.                 return time, "*"
  1416.         end
  1417. end
  1418.  
  1419. local function timetoword(time)
  1420.         time = tonumber(time)
  1421.         if time then
  1422.                 local returntime = ""
  1423.                 local centuries = floor(time / 3153600000)
  1424.                 time = time - centuries * 3153600000
  1425.                 local years = floor(time / 31536000)
  1426.                 time = time - years * 31536000
  1427.                 local weeks = floor(time / 604800)
  1428.                 time = time - weeks * 604800
  1429.                 local days = floor(time / 86400)
  1430.                 time = time - days * 86400
  1431.                 local hours = floor(time / 3600)
  1432.                 time = time - hours * 3600
  1433.                 local minutes = floor(time / 60)
  1434.                 time = time - minutes * 60
  1435.                 local seconds = floor(time)
  1436.  
  1437.                 returntime = seconds > 0 and (seconds == 1 and "1 second" or seconds .. " seconds") or returntime
  1438.                 returntime = minutes > 0 and (minutes == 1 and "1 minute" or minutes .. " minutes " .. returntime) or returntime
  1439.                 returntime = hours > 0 and (hours == 1 and "1 hour" or hours .. " hours " .. returntime) or returntime
  1440.                 returntime = days > 0 and (days == 1 and "1 day" or days .. " days " .. returntime) or returntime
  1441.                 returntime = weeks > 0 and (weeks == 1 and "1 week" or weeks .. " weeks " .. returntime) or returntime
  1442.                 returntime = years > 0 and (years == 1 and "1 year" or years .. " years " .. returntime) or returntime
  1443.                 returntime = centuries > 0 and (centuries == 1 and "1 century" or centuries .. " centuries " .. returntime) or returntime
  1444.                 return returntime ~= "" and returntime or "0 seconds"
  1445.         end
  1446. end
  1447.  
  1448. local function getTimeAndReason(timeandreason)
  1449.         local words = tokenizecmdstring(timeandreason)
  1450.         local count = #words
  1451.  
  1452.         local time
  1453.         local reasons = {}--TM.New()
  1454.  
  1455.         local timetypes_used = ""
  1456.         local word, addTime, timetype, reasons_started
  1457.         for i = 1,count do word = words[i]
  1458.                 if not reasons_started then
  1459.                         addTime, timetype = wordtotime(word)
  1460.                         if timetype and (find(timetypes_used, timetype) or find(timetypes_used, "[%?%*]")) then
  1461.                                 return "You can only use 1 of each time type (cywdhms)\nIf you specified a number, you cannot specify multiple time arguments.\nCheck the guide for more information"
  1462.                         elseif addTime then
  1463.                                 time = (time or 0) + addTime
  1464.                                 if not find(timetypes_used, timetype) then
  1465.                                         timetypes_used = timetypes_used .. timetype
  1466.                                 end
  1467.                         else
  1468.                                 reasons_started = true
  1469.                                 reasons[#reasons+1] = word
  1470.                         end
  1471.                 else
  1472.                         reasons[#reasons+1] = word
  1473.                 end
  1474.         end
  1475.         return (time or -1), (reasons[2] and concat(reasons, " ") or reasons[1] or "None Given")
  1476. end
  1477.  
  1478. local function gettag(type_or_id, tagname)
  1479.         if tagname then
  1480.                 return tag_table[type_or_id] and tag_table[type_or_id][tagname] or nil
  1481.         elseif tag_table[type_or_id] then
  1482.                 return tag_table[type_or_id].tag_class, tag_table[type_or_id].tag_name
  1483.         end
  1484. end
  1485.  
  1486. local function getObjType(objectId)
  1487.         local m_object = getobject(objectId)
  1488.         return m_object and readword(m_object + 0xB4) or nil
  1489. end
  1490.  
  1491. local function resetweapons(playerId)
  1492.         if getplayerweapon(playerId) then
  1493.                 for slot = 0,3 do
  1494.                         local m_weapon, weaponObjId = getplayerweapon(playerId, slot)
  1495.                         if m_weapon then
  1496.                                 destroyobject(weaponObjId)
  1497.                         end
  1498.                 end
  1499.         end
  1500.         local obj_tag_id = readdword(true and getplayerobject(playerId)) -- Confirmed with HMT. Tag Meta ID / MapID / TagID.
  1501.         local tag_address = gettagaddress(obj_tag_id)
  1502.         local address_tag_data = readdword(tag_address + 0x14)
  1503.         local number_of_starting_weapons = readdword(address_tag_data, 0x2D8)
  1504.         local tagdata_unit_weapons = readdword(address_tag_data + 0x2D8+0x4)
  1505.         for i = 0,3 do
  1506.                 local weapon_mapId = readdword(tagdata_unit_weapons + i*0x24 + 0xC)
  1507.                 if weapon_mapId and weapon_mapId ~= 0 then
  1508.                         assignweapon(playerId, createobject(weapon_mapId, 0, 60, false, 3, 2, 1))
  1509.                 end
  1510.         end
  1511.         --[[local address_starting_profile = readdword(address_tag_data + 0x348 + 0x4)
  1512.         local primary_weapon_mapId = readdword(address_tag_data + 0x348 + 0x4 + 0x28 + 0xC)
  1513.         local secondary_weapon_mapId = readdword(address_tag_data + 0x348 + 0x4 + 0x3C + 0xC)
  1514.         say(tostring(primary_weapon_mapId))
  1515.         if primary_weapon_mapId then
  1516.                 assignweapon(playerId, createobject(primary_weapon_mapId, 0, 60, false, 3, 2, 1))
  1517.         end
  1518.         if secondary_weapon_mapId then
  1519.                 assignweapon(playerId, createobject(secondary_weapon_mapId, 0, 60, false, 1, 2, 3))
  1520.         end--]]
  1521. end
  1522.  
  1523. local function ResetPlayer(playerId)
  1524.         local player = PlayerClass[playerId]
  1525.         local m_playerObj, playerObjId = getplayerobject(playerId)
  1526.         if playerObjId ~= 0xFFFFFFFF and m_playerObj then
  1527.                 player.godmode = false
  1528.                 cleanupdrones(playerId)
  1529.                 resetweapons(playerId)
  1530.         end
  1531.         player.bulletmode = false
  1532.         player.disarmed = false
  1533.         player.dmgmultiplier = 1
  1534.         player.ghostmode = false
  1535.         player.hidden = false
  1536.         player.objspawnid = false
  1537.         player.suspended = false
  1538. end
  1539.  
  1540. local function remoteBanCheck(ban_entry)
  1541.         local centralbanlist = defaults.remote_bansystem
  1542.         return not centralbanlist or (ban_entry.remote and centralbanlist.mode >= 1 or not ban_entry.remote and centralbanlist.mode ~= 2)
  1543. end
  1544.  
  1545. local function findBanlistEntry(name, hash, ip, bantype)
  1546.         local retBanEntry
  1547.         local cur_time = os.time()
  1548.         for id = 1,#ban_table do ban_entry = ban_table[id]
  1549.                 if remoteBanCheck(ban_entry) then
  1550.                         if not ban_entry.time or (ban_entry.time == -1 or ban_entry.time > cur_time) then
  1551.                                 if not bantype or bantype == ban_entry.type then
  1552.                                         if ban_entry.type == "hash" and ban_entry.hash == hash or ban_entry.type == "ip" and netMatch(ban_entry.ip, ip) then
  1553.                                                 --hprintf("NAME: " .. tostring(ban_entry.name) .. " REMOTECHECK: " .. tostring(remoteBanCheck(ban_entry)) .. " BANENTRY TIME: " .. tostring(ban_entry.time) .. " CURTIME: " .. tostring(cur_time))
  1554.                                                 return ban_entry
  1555.                                         elseif (ban_entry.type == "name" and ban_entry.name == name) or ban_entry.type == "chat" and (ban_entry.hash == hash or netMatch(ban_entry.ip, ip)) then
  1556.                                                 retBanEntry = ban_entry
  1557.                                         end
  1558.                                 end
  1559.                         end
  1560.                 end
  1561.         end
  1562.         if retBanEntry then
  1563.                 --hprintf("NAME: " .. tostring(retBanEntry.name) .. " REMOTECHECK: " .. tostring(remoteBanCheck(retBanEntry)) .. " BANENTRY TIME: " .. tostring(retBanEntry.time) .. " CURTIME: " .. tostring(cur_time))
  1564.         end
  1565.         return retBanEntry
  1566. end
  1567.  
  1568. local function punishIfOnBanlist(playerId)
  1569.         local ban_entry = findBanlistEntry(getname(playerId), gethash(playerId), getip(playerId))
  1570.        
  1571.         if ban_entry then
  1572.                 if ban_entry.type == "hash" or ban_entry.type == "ip" then
  1573.                         tempBanPlayer(resolveplayer(playerId))
  1574.                 elseif ban_entry.type == "chat" then
  1575.                         PlayerClass[playerId].muted = true
  1576.                 elseif ban_entry.type == "name" and ban_entry.name == name then
  1577.                         svcmd("sv_kick " .. resolveplayer(playerId) .. " Found on the Name Banlist.")
  1578.                 end
  1579.                 return true
  1580.         end
  1581.        
  1582.         return false
  1583. end
  1584.  
  1585. local function loadBanTextEntry(line, bantype, filename)
  1586.  
  1587.         -- Sometimes a random blank line appears in the file, let's make sure not to error for those
  1588.         if not match(line, "%g%g%g+") or sub(line, 1, 1) == "#" then
  1589.                 return
  1590.         end
  1591.  
  1592.         local t = {}--TM.New()
  1593.         local pos, endline, timestr, formatBantype, count, unique_index
  1594.  
  1595.         -- create a new table for the ban entry
  1596.         local b = {}--TM.New()
  1597.  
  1598.         -- A line can ONLY have the following format: Name,HashOrIp[:IP][,Bancount[,Time[,Bantype]]]
  1599.         -- The exception is namebans, which will always be: Name[,,,,Bantype] where Bantype can only be the string 'name' and nothing else
  1600.         -- Examples of valid chat bans:
  1601.         --  ,123456789abcdef123456789abcdef:127.0.0.1
  1602.         --      wizard,123456789abcdef123456789abcdef:127.0.0.1
  1603.         --      wizard,123456789abcdef123456789abcdef:127.0.0.1,1
  1604.         --      wizard,123456789abcdef123456789abcdef:127.0.0.1,1,--
  1605.         --      wizard,123456789abcdef123456789abcdef:127.0.0.1,1,--,chat
  1606.         --      wizard,123456789abcdef123456789abcdef:127.0.0.1,1,2015-03-27 04:10:52,chat,he was spamming
  1607.  
  1608.         -- First half: Match chatban (name,hash:ip) or hashban (name,hash) or nameban (name[,,,,bantype])
  1609.  
  1610.         -- if this matches then this is a chatban
  1611.         b.name,b.hash,b.ip,endline = match(line, "^(.-[^,]?),?(%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x+)[:,]([^,]+)(.-)$")
  1612.         b.ip = b.ip and (b.ip == "" and b.ip or validate_ipv4(b.ip))
  1613.         if b.hash and b.ip then
  1614.                 formatBantype = "chat"
  1615.                 unique_index = b.hash .. (b.ip or "") .. "chat"
  1616.                 goto ReadSecondHalf
  1617.         end
  1618.  
  1619.         -- this will match a hash ban
  1620.         b.name,b.hash,endline = match(line, "^(.-[^,]?),?(%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x+)(.*)$")
  1621.         if b.hash then
  1622.                 formatBantype = "hash"
  1623.                 unique_index = b.hash .. "hash"
  1624.                 goto ReadSecondHalf
  1625.         end
  1626.  
  1627.         -- this will match an IP ban.
  1628.         b.name,b.ip,endline = match(line, "^(.-[^,]?),([^,]+)(.-)$")
  1629.         b.ip = b.ip and validate_ipv4(b.ip)
  1630.         if not b.ip then
  1631.                 -- sapp's ipbans.txt
  1632.                 b.name,b.ip,b.reason = match(line, "^(.-[^:]?):([^:]+)(.-)$")
  1633.                 b.ip = b.ip and validate_ipv4(b.ip)
  1634.         end
  1635.         if b.ip then
  1636.                 formatBantype = "ip"
  1637.                 unique_index = b.ip .. "ip"
  1638.                 goto ReadSecondHalf
  1639.         end
  1640.  
  1641.         -- and lastly this will match a name ban.
  1642.         b.name,b.type = match(line, "^(.-),+(%w-)")
  1643.         if b.name then
  1644.                 formatBantype = "name"
  1645.                 unique_index = b.name .. "name"
  1646.                 goto ReadSecondHalf
  1647.         end
  1648.  
  1649.         error(filename .. " is formatted incorrectly! Line: " .. line)
  1650.  
  1651.         ::ReadSecondHalf::
  1652.         -- This part matches the rest of the line. Here are some examples that it will match (starting from the end of the string)
  1653.         --                                                                                                                       (nothing here)
  1654.         --              ,1                                                                                                      ,(count)
  1655.         --              ,5,--                                                                                           ,(count),(infinite time)
  1656.         --              ,2,2015-06-13 05:29:51,chat                                                     ,(count),(time),(bantype)
  1657.         --              ,3,-1,chat,offensive language                                           ,(count),(infinite time),(bantype),(reason)
  1658.  
  1659.         if not b.reason and endline and endline ~= "" then
  1660.                 count,timestr,b.type,b.reason = match(endline, "^,(%d+),?([%d%s%-:]*),?(%w*),?(.-)$")
  1661.                 b.reason = b.reason and b.reason ~= "" and gsub(b.reason, ",", "") or "None Given"
  1662.  
  1663.         -- these don't exist in a nameban
  1664.         elseif not b.type or b.type == "" then
  1665.                 b.count = nil
  1666.                 b.type = nil
  1667.                 b.reason = nil
  1668.                 timestr = nil
  1669.         end
  1670.  
  1671.         -- Check if the ban types are what they should be.
  1672.         -- I try to make my code stop using tons of checks, but this is the one time where it actually needs them
  1673.         b.type = b.type and b.type ~= "" and b.type
  1674.         assert(not b.type or b.type == formatBantype, filename .. " is formatted incorrectly! expected bantype " .. formatBantype .. " instead got " .. tostring(b.type) .. " on line: " .. line)
  1675.         assert(not bantype or bantype == formatBantype, filename .. " should only have '" .. tostring(bantype) .. "' bans. Ban of type '" .. formatBantype .. "' was found in the line: " .. line)
  1676.  
  1677.         b.type = formatBantype
  1678.  
  1679.         if timestr then
  1680.                 -- Match the time.
  1681.                 -- Time format: YYYY-MM-DD HH:MM:SS
  1682.                 t.year, t.month, t.day, t.hour, t.min, t.sec = match(timestr, "^(%d%d%d%d)%-(%d%d)%-(%d%d)%s+(%d%d):(%d%d):(%d%d)$")
  1683.  
  1684.                 -- convert 'ban expiration date' to 'seconds remaining until ban expires'
  1685.                 if t.sec then
  1686.                         b.time = os.time(t)
  1687.                 -- else time will become -1 (infinite)
  1688.                 else
  1689.                         b.time = -1
  1690.                 end
  1691.         -- time not specified in ban file, should be indefinite.
  1692.         else
  1693.                 b.time = -1
  1694.         end
  1695.  
  1696.         if formatBantype ~= "name" then
  1697.                 count = tonumber(count)
  1698.                 b.count = count and count > 0 and count or 1
  1699.         end
  1700.  
  1701.         b.name = b.name == "" and "Unnamed" or gsub(b.name, ",", "")
  1702.  
  1703.         return b, unique_index
  1704. end
  1705.  
  1706. local function toBanTextEntry(entry)
  1707.         local time, bantype = entry.time, entry.type
  1708.         time = time == -1 and "--" or os.date("%Y-%m-%d %X", time)
  1709.  
  1710.         if bantype == "chat" then
  1711.                 return entry.name..","..entry.hash..":"..(entry.ip or "1.2.3.4")..","..entry.count..","..time..","..bantype..","..entry.reason
  1712.         elseif bantype == "hash" then
  1713.                 return entry.name..","..entry.hash..","..entry.count..","..time..","..bantype..","..entry.reason
  1714.         elseif bantype == "ip" then
  1715.                 return entry.name..","..entry.ip..","..entry.count..","..time..","..bantype..","..entry.reason
  1716.         elseif bantype == "name" then
  1717.                 return entry.name..",,,,"..bantype--..","..reason               reasons don't exist for namebans.. yet
  1718.         end
  1719. end
  1720.  
  1721. local function download(host, file, port, output)
  1722.         port = port or 80
  1723.        
  1724.         local client = socket.tcp()
  1725.        
  1726.         client:settimeout(1, 'b')
  1727.         client:settimeout(1, 't')
  1728.        
  1729.         local status, status2, err = pcall(client.connect, client, host, port)
  1730.        
  1731.         if not status or not status2 then
  1732.                 print("Connection failed with error : " .. err)
  1733.                 return
  1734.         end
  1735.        
  1736.         coroutine.yield(client)
  1737.        
  1738.         -- Stop luasocket from blocking. You can play with these values
  1739.         client:settimeout(0.01, 'b')
  1740.         client:settimeout(0.01, 't')
  1741.        
  1742.         client:send("GET " .. file .. " HTTP/1.1\r\nHOST: " .. host .. ":" .. port .. "\r\n\r\n")
  1743.        
  1744.         local count = 0 -- counts number of bytes read
  1745.         local buffer, status, overflow
  1746.         while true do
  1747.                 coroutine.yield(client)
  1748.                 buffer, status, overflow = client:receive(65536)
  1749.                 -- If buffer is not nil the call was a success (changed in LuaSocket 2.0)
  1750.                 if buffer then
  1751.                         --print("Successfully received " .. #buffer .. " without timeout.")
  1752.                         output[#output+1] = buffer
  1753.                         count = count + #buffer
  1754.                 else
  1755.                         --print('"' .. status .. '" with ' .. #overflow .. ' bytes of ' .. file)
  1756.                         output[#output+1] = overflow
  1757.                         count = count + #overflow
  1758.                 end
  1759.                 if status == "closed" then break end
  1760.         end
  1761.         client:close()
  1762. end
  1763.  
  1764. local function OnHttpReceive(output)
  1765.         local entry, unique_index, hash, ip, name, bantype
  1766.         local cur_time = os.time()
  1767.         for line in string.gmatch(output, "[^\r\n]*") do
  1768.                 entry, unique_index = loadBanTextEntry(line, nil, "remote banlist")
  1769.                 if entry and not ban_table[unique_index] then
  1770.                         entry.remote = true
  1771.                         ban_table[#ban_table+1] = entry
  1772.                         ban_table[unique_index] = #ban_table
  1773.                         if not entry.time or entry.time > cur_time then
  1774.                                 for playerId = 0,15 do
  1775.                                         if getplayer(playerId) then
  1776.                                                 hash, ip, name = gethash(playerId), getip(playerId), getname(playerId)
  1777.                                                 bantype = entry.type
  1778.                                                 if bantype == "hash" and hash == entry.hash or entry.type == "ip" and netMatch(entry.ip, ip) then
  1779.                                                         tempBanPlayer(resolveplayer(playerId))
  1780.                                                         break
  1781.                                                 elseif bantype == "chat" and (entry.hash == hash or netMatch(entry.ip, ip)) then
  1782.                                                         PlayerClass[playerId].muted = true
  1783.                                                 elseif bantype == "name" and entry.name == name then
  1784.                                                         svcmd("sv_kick " .. resolveplayer(playerId) .. " Found in a Remote Ban Entry during a reload.")
  1785.                                                         break
  1786.                                                 end
  1787.                                         end
  1788.                                 end
  1789.                         end
  1790.                 end
  1791.         end
  1792. end
  1793.  
  1794. function handleHttpRequest(id, count, userdata) -- userdata = {[1] = coroutine thread, [2] = extraArg}
  1795.  
  1796.         --local start = os.clock()
  1797.         local status, res = coroutine.resume(userdata[1])
  1798.         --local finish = os.clock()
  1799.         --print("TIME: " .. (finish - start) * 1000 .. " ms")
  1800.        
  1801.         if not res then -- thread finished its task?
  1802.                 --print("Finished")
  1803.                 output = concat(userdata[3])
  1804.                 local sizeOfFile = match(output, "Content%-Length: (%d+)")
  1805.                 OnHttpReceive(sizeOfFile and sub(output, -sizeOfFile, -2) or output, userdata[2])
  1806.                 http_receiving = false
  1807.                 return false
  1808.         end
  1809.         return true
  1810. end
  1811.  
  1812. local function HTTPGET(host, file, port, extraArg)
  1813.        
  1814.         if http_receiving then
  1815.                 return
  1816.         end
  1817.  
  1818.         local output = {}
  1819.     -- create coroutine, and return the handle for the timer.
  1820.     local timerID = registertimer(
  1821.                 1000,
  1822.                 "handleHttpRequest",
  1823.                 {      
  1824.                         coroutine.create(
  1825.                                 function()
  1826.                                         download(host, file, port, output)
  1827.                                 end
  1828.                         ),
  1829.                         extraArg,
  1830.                         output
  1831.                 }
  1832.         )
  1833.         http_receiving = true
  1834.         return timerID
  1835. end
  1836.  
  1837. local function reloadRemoteBanlist()
  1838.         local args = defaults.remote_bansystem
  1839.         HTTPGET(args.host, args.banfile, args.port)
  1840. end
  1841.  
  1842. local function loadBanFile(filename, bantype)
  1843.         local file = io.open(filename)
  1844.         if not file then
  1845.                 return
  1846.         end
  1847.  
  1848.         local ban_entry, unique_index
  1849.         for line in file:lines() do
  1850.  
  1851.                 ban_entry, unique_index = loadBanTextEntry(line, bantype, filename)
  1852.  
  1853.                 -- Make sure this entry hasn't already been added before we add it.
  1854.                 if ban_entry and not ban_table[unique_index] then
  1855.                         ban_table[#ban_table+1] = ban_entry
  1856.                         ban_table[unique_index] = #ban_table
  1857.                 end
  1858.         end
  1859.         file:close()
  1860. end
  1861.  
  1862. -- Save all bans to banned.txt
  1863. local function updateBanFiles(bantype)
  1864.         local writetbl = {}--TM.New()
  1865.         local entry
  1866.         local file, err = io.open(profilepath .. defaults.banlist_file .. ".txt", "w")
  1867.         local time, type
  1868.         local args = defaults.remote_bansystem
  1869.         for id = 1,#ban_table do entry = ban_table[id]
  1870.                 if not args or (entry.remote and args.mode >= 1 or not entry.remote and args.mode ~= 2) then
  1871.                         writetbl[#writetbl+1] = toBanTextEntry(entry)
  1872.                 end
  1873.         end
  1874.         local output = concat(writetbl, "\n")
  1875.         file:write(output)
  1876.         file:close()
  1877.  
  1878.         -- Update the remote banlist.
  1879.         if args and args.mode >= 2 then
  1880.                 http.request(args.http_source, "&bans=" .. output .. ",&unban=false")
  1881.         end
  1882. end
  1883.  
  1884. local function updateAdminFiles()
  1885.         local file = io.open(profilepath .. defaults.admin_file .. ".txt", "w")
  1886.         local writetbl = {}--TM.New()
  1887.         for index,admin_entry in next,admin_table do
  1888.                 writetbl[#writetbl+1] = admin_entry.name .. "," .. index .. "," .. admin_entry.level .. "," .. admin_entry.type
  1889.         end
  1890.         file:write(concat(writetbl, "\n"))
  1891.         file:close()
  1892. end
  1893.  
  1894. local function loadAllAdminFiles()
  1895.         local timestamp = os.date"%Y_%m_%d_%H_%M_%S"
  1896.         local file = io.open(profilepath .. "admin.txt")
  1897.         if file then
  1898.                 local name, hash, level
  1899.                 for line in file:lines() do
  1900.  
  1901.                         -- format the line (name, hash, level)
  1902.                         name, hash, level = match(line, "^(%w-),(%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x+),(%d+)%c*$")
  1903.                         if hash then
  1904.                                 admin_table[hash] = {name = name, level = tonumber(level), type = "hash"}
  1905.                         end
  1906.                 end
  1907.                 file:close()
  1908.                 os.rename(profilepath .. "admin.txt", profilepath .. "old_admin_" .. timestamp .. ".txt")
  1909.         end
  1910.  
  1911.         -- Now stores IP admins as well.
  1912.         file = io.open(profilepath .. defaults.admin_file .. ".txt")
  1913.         if file then
  1914.                 local name, hash, ip, level, admintype
  1915.                 for line in file:lines() do
  1916.                         -- format the line (name, hash, level)
  1917.                         name, hash, level = match(line, "^(%w-),(%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x+),(%d+)")
  1918.                         if hash then
  1919.                                 admin_table[hash] = {name = name, level = tonumber(level), type = "hash"}
  1920.                         else
  1921.                                 -- format the line (name, hash, level, ip)
  1922.                                 name, ip, level = match(line, "^(%w-),(%g-),(%d+)")
  1923.                                 ip = validate_ipv4(ip)
  1924.                                 assert(ip, defaults.admin_file .. ".txt is incorrectly formatted! Line: " .. line)
  1925.  
  1926.                                 admin_table[ip] = {name = name, level = tonumber(level), type = "ip"}
  1927.                         end
  1928.                 end
  1929.                 file:close()
  1930.         end
  1931.  
  1932.         -- IP Admins now use admins.txt, but this is here for backwards compatibility.
  1933.         file = io.open(profilepath .. "ipadmins.txt")
  1934.         if file then
  1935.                 local name, level, ip
  1936.                 for line in file:lines() do
  1937.  
  1938.                         -- format the line (name, hash, level, ip)
  1939.                         name, ip, level = match(line, "^(%w-),(%g-),(%d+)%c*$")
  1940.                         ip = validate_ipv4(ip)
  1941.                         if ip then
  1942.                                 admin_table[ip] = {name = name, level = tonumber(level), type = "ip"}
  1943.                         end
  1944.                 end
  1945.                 file:close()
  1946.                 os.rename(profilepath .. "ipadmins.txt", profilepath .. "old_ipadmins_" .. timestamp .. ".txt")
  1947.         end
  1948.  
  1949.         updateAdminFiles()
  1950. end
  1951.  
  1952. function remoteTimer(id, count)
  1953.  
  1954.         -- This code will check if someone is trying to login to this TCP server.
  1955.         local client, err = server:accept()
  1956.         local length = #clients
  1957.         if not err then
  1958.                 client:send"Welcome to the Remote Control interface provided in Wizard's Command Script!\r\n Type 'rcon password command' to execute a command. The Rcon password you use MUST be a global rcon (with sv_rcon_add)\r\n"
  1959.                 length = length + 1
  1960.                 clients[length] = {socket = client, info = client:getpeername(), client.logged_in}
  1961.                 client:settimeout(0)
  1962.         elseif err ~= "timeout" then
  1963.                 hprintf("There was an error accepting a client.\r\nError: " .. err)
  1964.         end
  1965.        
  1966.         local command, rcon
  1967.         for i = 1,length do client = clients[i]
  1968.                 command, err = client.socket:receive()
  1969.                
  1970.                 -- Problem getting response from client.
  1971.                 if err == "timeout" then
  1972.                         goto continue
  1973.                 elseif err then
  1974.                         client.socket:close()
  1975.                         hprintf("Remote client '" .. client.info .. "' disconnected with error: " .. err)
  1976.                         remove(clients, i)
  1977.                         goto continue
  1978.                
  1979.                 -- Logout code.
  1980.                 elseif command == "logout" or command == "quit" or command == "exit" or command == "close" then
  1981.                         client.socket:send"You have been disconnected. Have a good day"
  1982.                         client.socket:close()
  1983.                         remove(clients, i)
  1984.                         goto continue
  1985.                 end
  1986.  
  1987.                 rcon, command = match(command, "^rcon (%w%w%w%w%w%w%w%w) (%g+)%c*$")
  1988.                
  1989.                 -- Validate the command.
  1990.                 if not rcon or not command then
  1991.                         client.socket:send"Invalid Command attempt.\r\nrcon <password> <command>\r\n"
  1992.                         goto continue
  1993.                 elseif OnServerCommandAttempt(-1, command, rcon) == false then
  1994.                         client.socket:send"Bad rcon password given. This will be reported."
  1995.                         goto continue
  1996.                 end
  1997.                
  1998.                 hprintf("Remote client '" .. client.info .. "' executed '" .. command .. "'")
  1999.                 local response
  2000.                 if command ~= "sv_script_unload" and command ~= "sv_script_reload" and command ~= "sv_script_load" and command ~= "reload" and command ~= "load" and command ~= "unload" and command ~= "lua_load" and command ~= "lua_unload" then
  2001.                         response = svcmd(command, true)
  2002.                 end
  2003.                 response = type(response) == "table" and response[1] and concat(response) or "Failed to receive output for command."
  2004.                 client.socket:send(response .. "\r\nType your command here:")
  2005.  
  2006.                
  2007.                 ::continue::
  2008.         end
  2009.         return true
  2010. end
  2011.  
  2012. -- changes 'sv_myCommand', '/myCommand', and '\myCommand' into 'myCommand'
  2013. local function getvalidformat(command)
  2014.         if not command then return end
  2015.         local sv_found, slash_found, both_found = sub(command, 1, 3), sub(command, 1, 1), sub(command, 1, 4)
  2016.         command = gsub(gsub(command, "_", ""), " ", "")
  2017.         if sv_found == "sv_" then
  2018.                 return sub(command, 3)
  2019.         elseif slash_found == "\\" or slash_found == "/" then
  2020.                 return sub(command, 2)
  2021.         elseif both_found == "\\sv_" or both_found == "/sv_" then
  2022.                 return sub(command, 4)
  2023.         end
  2024.         return command
  2025. end
  2026.  
  2027. local function getaccess(playerId)
  2028.         if playerId then
  2029.                 local adminEntry = PlayerClass[playerId].admin_entry
  2030.                 return adminEntry and adminEntry.level
  2031.         else
  2032.                 return 0
  2033.         end
  2034. end
  2035.  
  2036. local function SelectRandomPlayer(team)
  2037.         local t = {}--TM.New()
  2038.         local size = 0
  2039.         for playerId = 0,15 do
  2040.                 if getplayer(playerId) and (not team or getteam(playerId) == team) then
  2041.                         size = size + 1
  2042.                         t[size] = playerId
  2043.                 end
  2044.         end
  2045.         if size > 0 then
  2046.                 return t[rand(1, size)]
  2047.         end
  2048. end
  2049.  
  2050. -- Origstring is the string to search in, wild is the string to search for.
  2051. local function wildstring(origstring, wild, case_sensative)
  2052.  
  2053.         -- If not case sensitive then make all arguments lowercase.
  2054.         if not case_sensative then
  2055.                 origstring, wild = lower(origstring), lower(wild)
  2056.         end
  2057.  
  2058.         wild = gsub(gsub(gsub(wild, "?", "."), "*", ".*"), " ", "%s")
  2059.  
  2060.         local found = match(origstring, wild)
  2061.         if found and #found == #origstring then
  2062.                 return true
  2063.         end
  2064.         return false
  2065. end
  2066.  
  2067. local function getvalidplayers(expression, executorPlayerId)
  2068.         local players = {}--TM.New()
  2069.         if cur_players ~= 0 and expression then
  2070.                 if expression == "*" then
  2071.                         for playerId = 0,15 do
  2072.                                 if getplayer(playerId) then
  2073.                                         players[#players+1] = playerId
  2074.                                 end
  2075.                         end
  2076.                 elseif expression == "me" then
  2077.                         if executorPlayerId then
  2078.                                 players[1] = executorPlayerId
  2079.                         end
  2080.                 elseif player_colors[expression] then
  2081.                         local color = player_colors[expression]
  2082.                         local m_player
  2083.                         for playerId = 0,15 do
  2084.                                 m_player = getplayer(playerId)
  2085.                                 if m_player then
  2086.                                         if color == readword(m_player + 0x60) then
  2087.                                                 players[#players+1] = playerId
  2088.                                         end
  2089.                                 end
  2090.                         end
  2091.                 elseif expression == "red" then
  2092.                         for playerId = 0,15 do
  2093.                                 if getplayer(playerId) and getteam(playerId) == 0 then
  2094.                                         players[#players+1] = playerId
  2095.                                 end
  2096.                         end
  2097.                 elseif expression == "blue" then
  2098.                         for playerId = 0,15 do
  2099.                                 if getplayer(playerId) and getteam(playerId) == 1 then
  2100.                                         players[#players+1] = playerId
  2101.                                 end
  2102.                         end
  2103.                 elseif expression == "randomred" or expression == "randred" then
  2104.                         players[1] = SelectRandomPlayer(0)
  2105.                 elseif expression == "randomblue" or expression == "randblue" then
  2106.                         players[1] = SelectRandomPlayer(1)
  2107.                 elseif (tonumber(expression) or 0) >= 1 and tonumber(expression) <= 16 then
  2108.                         local playerId = tonumber(expression)
  2109.                         if getplayer(rresolveplayer(playerId)) then
  2110.                                 players[1] = rresolveplayer(playerId)
  2111.                         end
  2112.                 elseif expression == "rand" or expression == "random" then
  2113.                         players[1] = SelectRandomPlayer()
  2114.                 elseif expression == "admins" then
  2115.                         for playerId = 0,15 do
  2116.                                 if getplayer(playerId) and getaccess(playerId) then
  2117.                                         players[#players+1] = playerId
  2118.                                 end
  2119.                         end
  2120.                 elseif expression == "nearest" or expression == "closest" then
  2121.                         local m_playerObj, playerObjId = getplayerobject(executorPlayerId)
  2122.                         local m_playerObj2, X, Y, Z, dist, closest_player
  2123.                         if m_playerObj then
  2124.                                 local x,y,z = getobjectcoords(playerObjId)
  2125.                                 local min_dist = 9001
  2126.                                 for playerId = 0,15 do
  2127.                                         if playerId ~= executorPlayerId then
  2128.                                                 m_playerObj2, playerObjId2 = getplayerobject(playerId)
  2129.                                                 if m_playerObj2 then
  2130.                                                         X,Y,Z = getobjectcoords(playerObjId2)
  2131.                                                         dist = (X - x)^2 + (Y - y)^2 + (Z - z)^2 -- dont need square root since its a comparison
  2132.                                                         if min_dist > dist then
  2133.                                                                 min_dist = dist
  2134.                                                                 closest_player = playerId
  2135.                                                         end
  2136.                                                 end
  2137.                                         end
  2138.                                 end
  2139.                                 players[1] = closest_player
  2140.                         end
  2141.                 elseif expression == "farthest" then
  2142.                         local m_playerObj, playerObjId = getplayerobject(executorPlayerId)
  2143.                         local m_playerObj2, X, Y, Z, dist, farthest_player
  2144.                         if m_playerObj then
  2145.                                 local x,y,z = getobjectcoords(playerObjId)
  2146.                                 local max_dist = 0
  2147.                                 for playerId = 0,15 do
  2148.                                         if playerId ~= executorPlayerId then
  2149.                                                 m_playerObj2, playerObjId2 = getplayerobject(playerId)
  2150.                                                 if m_playerObj2 then
  2151.                                                         X,Y,Z = getobjectcoords(playerObjId2)
  2152.                                                         dist = (X - x)^2 + (Y - y)^2 + (Z - z)^2 -- dont need square root since its a comparison
  2153.                                                         if max_dist < dist then
  2154.                                                                 max_dist = dist
  2155.                                                                 farthest_player = playerId
  2156.                                                         end
  2157.                                                 end
  2158.                                         end
  2159.                                 end
  2160.                                 players[1] = farthest_player
  2161.                         end
  2162.                 else
  2163.                         for playerId = 0,15 do
  2164.                                 if getplayer(playerId) and wildstring(getname(playerId), expression) then
  2165.                                         players[#players+1] = playerId
  2166.                                 end
  2167.                         end
  2168.                 end
  2169.                 return next(players) and players
  2170.         end
  2171.         return false
  2172. end
  2173.  
  2174. local tag_vehi_types = { -- 0x2F4
  2175.         [0] = "Human Tank",
  2176.         "Human Jeep",
  2177.         "Human Boat",
  2178.         "Human Plane",
  2179.         "Alien Scout",
  2180.         "Alien Fighter",
  2181.         "Turret",
  2182. }
  2183.  
  2184. local tag_weap_types = { -- 0x30C
  2185.         [0] = "None",
  2186.         [1694528097] = "AI: Sentinel Beam (ar)",
  2187.         [98] = "Special: Skull Ball (b)",
  2188.         [1811939430] = "Special: Flag (f)",
  2189.         [7239015] = "Vehicle (Covenant): Type-26 Banshee; Type-32 RAV Ghost; Type-25 (and Type-52?) Wraith (gun)",
  2190.         [29799] = "Vehicle (Covenant): Type-26 ASG Shade Gun Turret; DX-class Spirit Dropship (gt)",
  2191.         [26467] = "Vehicle (Human): M12 Warthog (cg)",
  2192.         [1852727651] = "Vehicle (Human): M808B MBT Scorpion (cannon)",
  2193.         [1811968609] = "Weapon (Human): MA5 Assault Rifle; Gravity Rifle (ar)",
  2194.         [1811969126] = "Weapon (Human): M7057 Flamethrower (ft)",
  2195.         [28776] = "Weapon (Human): M6 Pistol (hp)",
  2196.         [1811967090] = "Weapon (Human): M19 SSM / M41 SSR Rocket Launcher (rl)",
  2197.         [1811965811] = "Weapon (Human): M90 CAWS Shotgun (sg)",
  2198.         [1811968627] = "Weapon (Human): 99D-S2 Sniper Rifle (sr)",
  2199.         [25190] = "Weapon, Melee (Covenant): Energy Sword (fb)",
  2200.         [1811968614] = "Weapon (Covenant): Type-33 Fuel Rod Gun (fr)",
  2201.         [1811965294] = "Weapon (Covenant): Type-33 GML Needler (ne)",
  2202.         [1811968112] = "Weapon (Covenant): Plasma Pistol (pp)",
  2203.         [1811968624] = "Weapon (Covenant): Plasma Rifle (pr)",
  2204.         [1811964784] = "Weapon (Covenant): Plasma Cannon (pc)"
  2205. }
  2206.  
  2207. local function LoadTags()
  2208.        
  2209.         local map_base = 0x40440000
  2210.         local tag_table_base = readdword(map_base) -- Confirmed. (0x40440028)
  2211.         local tag_table_count = readdword(map_base + 0xC) -- Confirmed. Number of tags in the tag table.
  2212.         local tag_table_size = 0x20 -- Confirmed.
  2213.  
  2214.         local reverse = string.reverse
  2215.         local tag_class, tag_id, tag_name_address, tag_name, address_tag_data, vehi_type, weap_type
  2216.         for i=0,(tag_table_count - 1) do
  2217.                 tag_class = reverse(readstring(tag_table_base + (tag_table_size * i), 4))
  2218.                 tag_id = readdword(tag_table_base + 0xC + (tag_table_size * i))
  2219.                 tag_name_address = readdword(tag_table_base + 0x10 + tag_table_size * i)
  2220.                 tag_name = readstring(tag_name_address)
  2221.                 tag_table[tag_class] = tag_table[tag_class] or {tag_id}
  2222.                 tag_table[tag_class][tag_name] = tag_id
  2223.                 tag_table[tag_class][#tag_table[tag_class]+1] = tag_id
  2224.                 tag_table[tag_id] = {}--{}--TM.New()
  2225.                 tag_table[tag_id].tag_name = tag_name
  2226.                 tag_table[tag_id].tag_class = tag_class
  2227.                 tag_address = readdword(map_base) + i * tag_table_size -- 0x40440028
  2228.                 if tag_class == "vehi" then
  2229.                         address_tag_data = readdword(tag_address, 0x14)
  2230.                         vehi_type = readword(address_tag_data + 0x2F4)
  2231.                         tag_table[tag_id].vehi_type = tag_vehi_types[vehi_type]
  2232.                         if vehi_type and vehi_type >= 0 and vehi_type <= 6 and not tag_table[vehi_type] then
  2233.                                 tag_table[vehi_type] = tag_id
  2234.                         end
  2235.                 elseif tag_class == "weap" then
  2236.                         address_tag_data = readdword(tag_address, 0x14)
  2237.                         weap_type = readword(address_tag_data + 0x2F4)
  2238.                         tag_table[tag_id].weap_type = tag_weap_types[weap_type]
  2239.                         if not weap_type or not tag_table[weap_type] then
  2240.                                 tag_table[weap_type] = tag_id
  2241.                         end
  2242.                 end
  2243.         end
  2244.        
  2245.         hprintf("Found " .. tostring(#tag_table.vehi) .. " vehicles and " .. tostring(#tag_table.weap) .. " weapons")
  2246.  
  2247.         flameid = gettagid("proj", "weapons\\flamethrower\\flame")
  2248.        
  2249.         objects = {
  2250.                 cyborg = {"bot, masterchief", type = "bipd", mapId = gettag("bipd", "characters\\cyborg_mp\\cyborg_mp"), name = "Cyborg"},
  2251.                 captain = {"keyes", type = "bipd", mapId = gettag("bipd", "characters\\captain\\captain"), name = "Captain Keyes"},
  2252.                 cortana = {type = "bipd", mapId = gettag("bipd", "characters\\cortana\\cortana"), name = "Cortana"},
  2253.                 cortana2 = {type = "bipd", mapId = gettag("bipd", "characters\\cortana\\halo_enhanced\\halo_enhanced"), name = "Cortana2"},
  2254.                 crewman = {type = "bipd", mapId = gettag("bipd", "characters\\crewman\\crewman"), name = "Crewman"},
  2255.                 elite = {type = "bipd", mapId = gettag("bipd", "characters\\elite\\elite"), name = "Elite"},
  2256.                 elite2 = {type = "bipd", mapId = gettag("bipd", "characters\\elite\\elite special"), name = "Elite Special"},
  2257.                 engineer = {type = "bipd", mapId = gettag("bipd", "characters\\engineer\\engineer"), name = "Engineer"},
  2258.                 flood = {type = "bipd", mapId = gettag("bipd", "characters\\flood_captain\\flood_captain"), name = "Flood Captain"},
  2259.                 flood2 = {type = "bipd", mapId = gettag("bipd", "characters\\flood_infection\\flood_infection"), name = "Flood Infection"},
  2260.                 flood3 = {type = "bipd", mapId = gettag("bipd", "characters\\floodcarrier\\floodcarrier"), name = "Flood Carrier"},
  2261.                 floodelite = {type = "bipd", mapId = gettag("bipd", "characters\\floodcombat elite\\floodcombat elite"), name = "FloodCombat Elite"},
  2262.                 floodhuman = {type = "bipd", mapId = gettag("bipd", "characters\\floodcombat_human\\floodcombat_human"), name = "FloodCombat Human"},
  2263.                 grunt = {"pedobear", type = "bipd", mapId = gettag("bipd", "characters\\grunt\\grunt"), name = "Pedobear"},
  2264.                 hunter = {type = "bipd", mapId = gettag("bipd", "characters\\hunter\\hunter"), name = "Hunter"},
  2265.                 marine = {type = "bipd", mapId = gettag("bipd", "characters\\marine\\marine"), name = "Marine"},
  2266.                 marine2 = {"marinesuicide", type = "bipd", mapId = gettag("bipd", "characters\\marine_suicidal\\marine_suicidal"), name = "Marine Suicidal"},
  2267.                 monitor = {type = "bipd", mapId = gettag("bipd", "characters\\monitor\\monitor"), name = "Monitor"},
  2268.                 sentinel = {type = "bipd", mapId = gettag("bipd", "characters\\sentinel\\sentinel"), name = "Sentinel"},
  2269.                 johnson = {type = "bipd", mapId = gettag("bipd", "characters\\johnson\\johnson"), name = "Sgt. Johnson"},
  2270.                 camouflage = {"camo", type = "eqip", mapId = gettag("eqip", "powerups\\active camouflage"), name = "Camouflage"},
  2271.                 doublespeed = {"dblspd", type = "eqip", mapId = gettag("eqip", "powerups\\double speed"), name = "Double Speed"},
  2272.                 fullspec = {"fullspectrum", type = "eqip", mapId = gettag("eqip", "powerups\\full-spectrum vision"), name = "Full-Spectrum Vision"},
  2273.                 fnade = {"nades", "frag", "frags", type = "eqip",  mapId = gettag("eqip", "weapons\\frag grenade\\frag grenade"), name = "Frag Grenade"},
  2274.                 pnade = {"plasmas", "plasma", type = "eqip", mapId = gettag("eqip", "weapons\\plasma grenade\\plasma grenade"), name = "Plasma Grenade"},
  2275.                 overshield = {"os", type = "eqip", mapId = gettag("eqip", "powerups\\over shield"), name = "Overshield"},
  2276.                 rifleammo = {type = "eqip", mapId = gettag("eqip", "powerups\\assault rifle ammo\\assault rifle ammo"), name = "Assault Rifle Ammo"},
  2277.                 healthpack = {"health", type = "eqip", mapId = gettag("eqip", "powerups\\health pack"), name = "Health Pack"},
  2278.                 needlerammo = {"needleammo", type = "eqip", mapId = gettag("eqip", "powerups\\needler ammo\\needler ammo"), name = "Needler Ammo"},
  2279.                 pistolammo = {type = "eqip", mapId = gettag("eqip", "powerups\\pistol ammo\\pistol ammo"), name = "Pistol Ammo"},
  2280.                 rocketammo = {type = "eqip", mapId = gettag("eqip", "powerups\\rocket launcher ammo\\rocket launcher ammo"), name = "Rocket Ammo"},
  2281.                 shottyammo = {"shotgunammo", type = "eqip", mapId = gettag("eqip", "powerups\\shotgun ammo\\shotgun ammo"), name = "Shotgun Ammo"},
  2282.                 sniperammo = {type = "eqip", mapId = gettag("eqip", "powerups\\sniper rifle ammo\\sniper rifle ammo"), name = "Sniper Ammo"},
  2283.                 flameammo = {type = "eqip", mapId = gettag("eqip", "powerups\\flamethrower ammo\\flamethrower ammo"), name = "Flamethrower Ammo"},
  2284.                 energysword = {"esword", type = "weap", mapId = gettag("weap", "weapons\\energy sword\\energy sword"), name = "Energy Sword"},
  2285.                 oddball = {"ball", type = "weap", mapId = gettag("weap", "weapons\\ball\\ball"), name = "Oddball"},
  2286.                 flag = {type = "weap", mapId = gettag("weap", "weapons\\flag\\flag"), name = "Flag"},
  2287.                 fuelrod = {"frg", "rod", "plasmacannon", type = "weap", mapId = gettag("weap", "weapons\\plasma_cannon\\plasma_cannon"), name = "Fuel Rod"},
  2288.                 gravitygun = {"ggun", type = "weap", mapId = gettag("weap", "weapons\\gravity rifle\\gravity rifle"), name = "Gravity Gun"},
  2289.                 needler = {type = "weap", mapId = gettag("weap", "weapons\\needler\\mp_needler"), name = "Needler"},
  2290.                 pistol = {type = "weap", mapId = gettag("weap", "weapons\\pistol\\pistol"), name = "Pistol"},
  2291.                 plasmapistol = {"ppistol", type = "weap", mapId = gettag("weap", "weapons\\plasma pistol\\plasma pistol"), name = "Plasma Pistol"},
  2292.                 plasmarifle = {"prifle", type = "weap", mapId = gettag("weap", "weapons\\plasma rifle\\plasma rifle"), name = "Plasma Rifle"},
  2293.                 assaultrifle = {"rifle", "arifle", "assault", type = "weap", mapId = gettag("weap", "weapons\\assault rifle\\assault rifle"), name = "Assault Rifle"},
  2294.                 rocketlauncher = {"rocket", "rox", type = "weap", mapId = gettag("weap", "weapons\\rocket launcher\\rocket launcher"), name = "Rocket Launcher"},
  2295.                 shotgun = {"shotty", type = "weap", mapId = gettag("weap", "weapons\\shotgun\\shotgun"), name = "Shotgun"},
  2296.                 sniper = {"sniperrifle", type = "weap", mapId = gettag("weap", "weapons\\sniper rifle\\sniper rifle"), name = "Sniper Rifle"},
  2297.                 flamethrower = {"flamer", type = "weap", mapId = gettag("weap", "weapons\\flamethrower\\flamethrower"), name = "Flamethrower"},
  2298.                 wraith = {type = "vehi", mapId = gettag("vehi", "vehicles\\wraith\\wraith"), name = "Wraith"},
  2299.                 pelican = {"peli", type = "vehi", mapId = gettag("vehi", "vehicles\\pelican\\pelican"), name = "Pelican"},
  2300.                 ghost = {type = "vehi", type = "vehi", mapId = gettag("vehi", "vehicles\\ghost\\ghost_mp"), name = "Ghost"},
  2301.                 warthog = {"hog", type = "vehi",  mapId = gettag("vehi", "vehicles\\warthog\\mp_warthog"), name = "Warthog"},
  2302.                 rocketwarthog = {"rhog", type = "vehi", mapId = gettag("vehi", "vehicles\\rwarthog\\rwarthog"), name = "Rocket Warthog"},
  2303.                 banshee = {"shee", type = "vehi", mapId = gettag("vehi", "vehicles\\banshee\\banshee_mp"), name = "Banshee"},
  2304.                 tank = {"scorpion", type = "vehi", mapId = gettag("vehi", "vehicles\\scorpion\\scorpion_mp"), name = "Tank"},
  2305.                 turret = {"shade", type = "vehi", mapId = gettag("vehi", "vehicles\\c gun turret\\c gun turret_mp"), name = "Gun Turret"},
  2306.                 sheebolt = {type = "proj", mapId = gettag("proj", "vehicles\\banshee\\banshee bolt"), name = "Shee Bolt"},
  2307.                 sheerod = {type = "proj", mapId = gettag("proj", "vehicles\\banshee\\mp_banshee fuel rod"), name = "Shee Rod"},
  2308.                 turretbolt = {type = "proj", mapId = gettag("proj", "vehicles\\c gun turret\\mp gun turret"), name = "Turret Bullet"},
  2309.                 ghostbolt = {type = "proj", mapId = gettag("proj", "vehicles\\ghost\\ghost bolt"), name = "Ghost Bolt"},
  2310.                 tankshot = {type = "proj", mapId = gettag("proj", "vehicles\\scorpion\\bullet"), name = "Tank Bullet"},
  2311.                 tankshell = {type = "proj", mapId = gettag("proj", "vehicles\\scorpion\\tank shell"), name = "Tank Shell"},
  2312.                 hogshot = {type = "proj", mapId = gettag("proj", "vehicles\\warthog\\bullet"), name = "Hog Bullet"},
  2313.                 rifleshot = {type = "proj", mapId = gettag("proj", "weapons\\assault rifle\\bullet"), name = "Rifle Bullet"},
  2314.                 flame = {type = "proj", mapId = gettag("proj", "weapons\\flamethrower\\flame"), name = "Flame Projectile"},
  2315.                 needlershot = {type = "proj", mapId = gettag("proj", "weapons\\needler\\mp_needle"), name = "Needler Shard"},
  2316.                 pistolshot = {type = "proj", mapId = gettag("proj", "weapons\\pistol\\bullet"), name = "Pistol Bullet"},
  2317.                 priflebolt = {type = "proj", mapId = gettag("proj", "weapons\\plasma pistol\\bolt"), name = "Plasma Rifle Bolt"},
  2318.                 ppistolbolt = {type = "proj", mapId = gettag("proj", "weapons\\plasma rifle\\bolt"), name = "Plasma Pistol Bolt"},
  2319.                 ppistolcbolt = {type = "proj", mapId = gettag("proj", "weapons\\plasma rifle\\charged bolt"), name = "Plasma Pistol Charged Bolt"},
  2320.                 rocketproj = {type = "proj", mapId = gettag("proj", "weapons\\rocket launcher\\rocket"), name = "Rocket Projectile"},
  2321.                 shottyshot = {type = "proj", mapId = gettag("proj", "weapons\\shotgun\\pellet"), name = "Shotgun Pellet"},
  2322.                 snipershot = {type = "proj", mapId = gettag("proj", "weapons\\sniper rifle\\sniper bullet"), name = "Sniper Bullet"},
  2323.                 fuelrodshot = {type = "proj", mapId = gettag("proj", "weapons\\plasma_cannon\\plasma_cannon"), name = "Fuel Rod Bolt"},
  2324.         }
  2325.        
  2326.         -- Use enum in vehi_type as a backup way to determine default vehicles tag ids without their tagnames.
  2327.         objects.tank.mapId = objects.tank.mapId or tag_table[0] and tag_table[0]
  2328.         objects.warthog.mapId = objects.warthog.mapId or tag_table[1] and tag_table[1]
  2329.         objects.rocketwarthog.mapId = objects.rocketwarthog.mapId or tag_table[1] and tag_table[1]
  2330.         objects.pelican.mapId = objects.pelican.mapId or tag_table[3] and tag_table[3]
  2331.         objects.ghost.mapId = objects.ghost.mapId or tag_table[4] and tag_table[4]
  2332.         objects.banshee.mapId = objects.banshee.mapId or tag_table[5] and tag_table[5]
  2333.         objects.turret.mapId = objects.turret.mapId or tag_table[6] and tag_table[6]
  2334.        
  2335.         objects.oddball.mapId = objects.oddball.mapId or tag_table[98] and say("Oddball: USING BACKUP TAG") or tag_table[98]
  2336.         objects.energysword.mapId = objects.energysword.mapId or tag_table[25190] and say("Energy Sword: USING BACKUP TAG") or tag_table[25190]
  2337.         objects.pistol.mapId = objects.pistol.mapId or tag_table[28776] and say("Pistol: USING BACKUP TAG") or tag_table[28776]
  2338.         objects.flag.mapId = objects.flag.mapId or tag_table[1811939430] and say("Flag: USING BACKUP TAG") or tag_table[1811939430]
  2339.         objects.needler.mapId = objects.needler.mapId or tag_table[1811965294] and say("Needler: USING BACKUP TAG") or tag_table[1811965294]
  2340.         objects.plasmapistol.mapId = objects.plasmapistol.mapId or tag_table[1811968112] and say("Plasma Pistol: USING BACKUP TAG") or tag_table[1811968112]
  2341.         objects.fuelrod.mapId = objects.fuelrod.mapId or tag_table[1811968614] and say("Fuelrod: USING BACKUP TAG") or tag_table[1811968614]
  2342.         objects.plasmarifle.mapId = objects.plasmarifle.mapId or tag_table[1811968624] and say("Plasma Rifle: USING BACKUP TAG") or tag_table[1811968624]
  2343.         objects.assaultrifle.mapId = objects.assaultrifle.mapId or tag_table[1811968609] and say("Assault Rifle: USING BACKUP TAG") or tag_table[1811968609]
  2344.         objects.rocketlauncher.mapId = objects.rocketlauncher.mapId or tag_table[1811967090] and say("Rocket Launcher: USING BACKUP TAG") or tag_table[1811967090]
  2345.         objects.shotgun.mapId = objects.shotgun.mapId or tag_table[1811965811] and say("Shotgun: USING BACKUP TAG") or tag_table[1811965811]
  2346.         objects.sniper.mapId = objects.sniper.mapId or tag_table[1811968627] and say("Sniper: USING BACKUP TAG") or tag_table[1811968627]
  2347.         objects.flamethrower.mapId = objects.flamethrower.mapId or tag_table[1811969126] and say("Flamethrower: USING BACKUP TAG") or tag_table[1811969126]
  2348.        
  2349.         -- Set Object Aliases.
  2350.         for name,thisObject in next,objects do
  2351.                 for i = 1,#thisObject do
  2352.                         objects[thisObject[i]] = thisObject
  2353.                 end
  2354.                 objects[name] = thisObject
  2355.         end
  2356.  
  2357. end
  2358.  
  2359. function votekickTimer(id, count)
  2360.         say("The VoteKick on " .. getname(votekickPlayerId) .. " has expired!")
  2361.         votekicktimer = nil
  2362.         votekickPlayerId = nil
  2363.         votekick_counter = -defaults.votekick_timeout
  2364.         for playerId = 0,15 do
  2365.                 PlayerClass[playerId].used_votekick = false
  2366.         end
  2367.         return false
  2368. end
  2369.  
  2370. local function WriteLog(filename, logStr)
  2371.         local file = io.open(filename, "a")
  2372.         if file then
  2373.                 file:write(format("%s\t%s\n", os.date"%Y/%m/%d %H:%M:%S", logStr))
  2374.                 file:close()
  2375.         end
  2376. end
  2377.  
  2378. local function cmdlog(message)
  2379.         WriteLog(profilepath .. "logs\\" .. defaults.commands_file .. ".log", message)
  2380. end
  2381.  
  2382. -- This local function makes sure that we never have any commands that output more than 8 lines
  2383. -- We need this because Halo only lets you see 8 lines of text at a time in the chat
  2384. -- This local function will also split lines into more lines if a line is too long.
  2385. -- If a person is executing \pl in the chat, we want them to be able to see players 1 through 16, therefore this local function is born.
  2386. -- Arguments are output, maximum number of lines to use (MIGHT go over maxlines if the line length is already too long)
  2387. -- maxlinesize is the maximum number of characters a line can have
  2388. -- If output is a table, this local function will check for the keys 'align', 'header', 'delim', and 'separator', otherwise it'll read it as a string.
  2389. local function formatOutput(output, maxlines, maxlinesize)
  2390.         -- error if the arguments were passed incorrectly.
  2391.         if type(output) ~= "string" and type(output) ~= "table" then
  2392.                 error("bad argument #1 to 'formatOutput' (expected table or string, got " .. type(output) .. ")" .. " Value: " .. tostring(output))
  2393.         elseif maxlines and type(maxlines) ~= "number" then
  2394.                 error("bad argument #2 to 'formatOutput' (expected number, got " .. type(maxlines) .. " Value: " .. tostring(maxlines))
  2395.         elseif maxlinesize and type(maxlinesize) ~= "number" then
  2396.                 error("bad argument #3 to 'formatOutput' (expected number, got " .. type(maxlinesize) .. " Value: " .. tostring(maxlinesize))
  2397.         end
  2398.  
  2399.         -- handle our arguments
  2400.         maxlines = maxlines or 9001
  2401.         maxlinesize = maxlinesize or 9001
  2402.         local newOutput = type(output) == "string" and tokenizestring(output, "\n") or output
  2403.  
  2404.         -- Get number of lines from our output. Respects __len metamethod.
  2405.         local mt = type(output) == "table" and getmetatable(output)
  2406.         local len = mt and mt.__len
  2407.         local numOfLines = len and len(newOutput) or #newOutput
  2408.  
  2409.         local function insert(t, pos, v)
  2410.                 if v then
  2411.                         assert(type(pos) == "number", "bad argument #2 to 'insert' expected number, got " .. type(pos))
  2412.                         for i = numOfLines+1,pos+1,-1 do
  2413.                                 rawset(t, i, rawget(t, i-1))
  2414.                         end
  2415.                         t[pos] = v
  2416.                 else
  2417.                         t[numOfLines+1] = pos
  2418.                 end
  2419.                 numOfLines = numOfLines + 1
  2420.         end
  2421.  
  2422.         local function remove(t, key)
  2423.                 t = type(t) == "table" and t or error("bad argument #1 to 'remove' expected table, got " .. type(t))
  2424.                 key = key or numOfLines
  2425.                 t[key] = nil
  2426.                 if type(key) == "number" then
  2427.                         for i = key,numOfLines do
  2428.                                 rawset(t, i, rawget(t, i+1))
  2429.                         end
  2430.                 end
  2431.                 numOfLines = numOfLines - 1
  2432.         end
  2433.  
  2434.         local header = newOutput.header
  2435.         local linesCombined = ceil((numOfLines + (header and 1 or 0))/maxlines)
  2436.  
  2437.         -- duplicate the header by linesCombined times.
  2438.         if header then
  2439.                 assert(type(header) == "string", "Output header needs to be a string, got " .. type(header))
  2440.  
  2441.                 for i = 1,linesCombined do
  2442.                         insert(newOutput, 1, header)
  2443.                 end
  2444.         end
  2445.  
  2446.         -- If we have no more output, then we don't need to do anything more.
  2447.         if numOfLines == 1 and header or numOfLines == 0 then
  2448.                 return newOutput[1]
  2449.         end
  2450.  
  2451.         if newOutput.align then
  2452.  
  2453.                 local delimiter = newOutput.delim or "|"
  2454.                 local separator = newOutput.separator or delimiter
  2455.  
  2456.                 local mins = {}--TM.New()
  2457.                 local length, lineParts
  2458.  
  2459.                 -- This loop gets the largest possible size for each line part.
  2460.                 for i = 1,numOfLines do
  2461.                         lineParts = tokenizestring(newOutput[i], delimiter)
  2462.                         for j = 1,#lineParts do
  2463.                                 -- Format every string to the biggest length possible for that entry.
  2464.                                 length = #lineParts[j]
  2465.                                 if not mins[j] or length > mins[j] then
  2466.                                         mins[j] = length
  2467.                                 end
  2468.                         end
  2469.                 end
  2470.  
  2471.                 -- This loop will make all line parts the same size (by adding spaces). string.format makes this easy for us.
  2472.                 local numOfLineParts
  2473.                 for i = 1,numOfLines do
  2474.                         lineParts = tokenizestring(newOutput[i], delimiter)
  2475.                         numOfLineParts = #lineParts
  2476.                         for j = 1,numOfLineParts do
  2477.                                 lineParts[j] = format("%-" .. mins[j] .. "s", lineParts[j])
  2478.                         end
  2479.                         lineParts[numOfLineParts] = sub(lineParts[numOfLineParts], 1, -1) -- removes the last delimiter in the line
  2480.                         newOutput[i] = concat(lineParts, separator)
  2481.                 end
  2482.         end
  2483.  
  2484.         -- If our output uses too many lines, then we now need to combine lines until we are within maxlines
  2485.         local lineIter = 1
  2486.         local numOfLinesToCombine = floor(numOfLines / linesCombined)
  2487.         while numOfLines > maxlines do
  2488.  
  2489.                 -- combine 'linesCombined' lines into 1 line.
  2490.                 for i = 2,linesCombined do -- should be 1,linesCombined-1 but 2,linesCombined works fine
  2491.                         if lineIter+1 > numOfLines then break end
  2492.                         newOutput[lineIter] = newOutput[lineIter] .. " - " .. newOutput[lineIter+1]
  2493.                         remove(newOutput, lineIter+1)
  2494.                 end
  2495.                 lineIter = lineIter + 1
  2496.  
  2497.                 -- Check if we need to increase the number of lines to combine per iteration.
  2498.                 -- Also checks if we're done
  2499.                 if numOfLinesToCombine == 1 then
  2500.                         linesCombined = linesCombined - 1
  2501.                         if linesCombined < 2 then break end
  2502.                         numOfLinesToCombine = floor(numOfLines / linesCombined)
  2503.  
  2504.                 -- This is our counter.
  2505.                 else
  2506.                         numOfLinesToCombine = numOfLinesToCombine - 1
  2507.                 end
  2508.         end
  2509.  
  2510.         -- Make sure the line isn't too long.
  2511.         -- If it is, we put the part that's too long into a new table which we will put back into output later.
  2512.         lineIter = 1
  2513.         local line, length
  2514.         while newOutput[lineIter] do
  2515.                 line = newOutput[lineIter]
  2516.                 length = #line
  2517.                 if length > maxlinesize then
  2518.                         newOutput[lineIter] = sub(line, 1, maxlinesize) -- first part of line
  2519.                         insert(newOutput, lineIter+1, sub(line, maxlinesize)) -- insert a new line right after this one with the second part
  2520.                 end
  2521.                 lineIter = lineIter + 1
  2522.         end
  2523.         return concat(newOutput, "\n", 1, numOfLines)
  2524. end
  2525.  
  2526. function delayMsg(id, count, arguments)
  2527.         local msg, playerId = arguments[1], arguments[2]
  2528.         msg = type(msg) == "table" and concat(msg, "\n") or msg
  2529.         if playerId then
  2530.                 if output_environment == 2 then
  2531.                         say(msg, false)
  2532.                 elseif output_environment == 3 then
  2533.                         privatesay(playerId, msg, false)
  2534.                 end
  2535.         end
  2536.         return false
  2537. end
  2538.  
  2539. local function sendresponse(message, playerId, log)
  2540.         if not message then
  2541.                 -- this will reset all the properties of cmdreply
  2542.                 cmdreply(true)
  2543.                 return
  2544.         end
  2545.  
  2546.         local playerCheck = getplayer(playerId)
  2547.         local tempmessage = message
  2548.  
  2549.         if not playerCheck then
  2550.                 message = formatOutput(message)
  2551.                 hprintf(message)
  2552.         elseif output_environment == 1 then
  2553.                 message = formatOutput(message, 20, 76)
  2554.                 sendconsoletext(playerId, message)
  2555.         elseif output_environment == 2 or output_environment == 3 then
  2556.                 message = formatOutput(message, 6, 100)
  2557.                 registertimer(0, "delayMsg", {message, playerId})
  2558.         end
  2559.         if log and playerCheck then
  2560.                 cmdlog("Response to " .. getname(playerId) .. " (" .. PlayerClass[playerId].admin_entry.name .. "): " .. message)
  2561.         end
  2562.  
  2563.         -- this will reset all the properties of cmdreply
  2564.         if tempmessage == cmdreply then cmdreply(true) end
  2565. end
  2566.  
  2567. local function getscorelimit()
  2568.         if gametype == 1 then
  2569.                 return readint(addresses.ctf_globals + 0x18)
  2570.         elseif gametype == 2 then
  2571.                 return scorelimit
  2572.                 --return readbyte(addresses.gametype_base + 0x58, score)
  2573.         elseif gametype == 3 then
  2574.                 return readint(addresses.oddball_globals)
  2575.         elseif gametype == 4 then
  2576.                 return readint(addresses.koth_globals + team*4)
  2577.         elseif gametype == 5 then
  2578.                 return readint(addresses.race_globals + team*4 + 0x88)
  2579.         else
  2580.                 return readbyte(addresses.gametype_base + 0x58)
  2581.         end
  2582. end
  2583.  
  2584. local function setscorelimit(score)
  2585.         if gametype == 1 then
  2586.                 writeint(addresses.ctf_globals + 0x18, score)
  2587.         elseif gametype == 2 then
  2588.                 scorelimit = score
  2589.                 --writebyte(addresses.gametype_base + 0x58, score)
  2590.         elseif gametype == 3 then
  2591.                 writeint(addresses.oddball_globals, score)
  2592.         elseif gametype == 4 then
  2593.                 writeint(addresses.koth_globals + team*4, score)
  2594.         elseif gametype == 5 then
  2595.                 writeint(addresses.race_globals + team*4 + 0x88, score)
  2596.         else
  2597.                 writebyte(addresses.gametype_base + 0x58, score)
  2598.         end
  2599. end
  2600.  
  2601. local function getscore(playerId)
  2602.         if gametype == 1 then
  2603.                 return readshort(getplayer(playerId) + 0xC8)
  2604.         elseif gametype == 2 then
  2605.                 return player_scores[playerId]
  2606.                 --return readint(addresses.slayer_globals + 0x40 + playerId*4)
  2607.         elseif gametype == 3 then
  2608.                 local oddball_game = readbyte(addresses.gametype_base + 0x8C)
  2609.                 if oddball_game == 0 or oddball_game == 1 then
  2610.                         return readint(addresses.oddball_globals + 0x84 + playerId*4)
  2611.                 else
  2612.                         return readint(addresses.oddball_globals + player*4 + 0x104)
  2613.                 end
  2614.         elseif gametype == 4 then
  2615.                 return readshort(getplayer(playerId) + 0xC4)
  2616.         elseif gametype == 5 then
  2617.                 return readshort(getplayer(playerId) + 0xC6)
  2618.         end
  2619. end
  2620.  
  2621. local function setscore(playerId, score)
  2622.         if gametype == 1 then
  2623.                 writeshort(getplayer(playerId)+0xC8, score)
  2624.         elseif gametype == 2 then
  2625.                 if team_play then
  2626.                         local curscore = player_scores[playerId]
  2627.                         local diff = math.abs(score - curscore)
  2628.                         if curscore > score then
  2629.                                 team_scores[getteam(playerId)] = team_scores[getteam(playerId)] + curscore
  2630.                         elseif curscore < score then
  2631.                                 team_scores[getteam(playerId)] = team_scores[getteam(playerId)] - curscore
  2632.                         end
  2633.                 end
  2634.                 player_scores[playerId] = score
  2635.                 writeshort(addresses.slayer_globals + 0x40 + playerId*4, score)
  2636.         elseif gametype == 3 then
  2637.                 local oddball_game = readbyte(addresses.gametype_base + 0x8C)
  2638.                 if oddball_game == 0 or oddball_game == 1 then
  2639.                         writeint(addresses.oddball_globals + 0x84 + playerId*4, score * 30)
  2640.                 else
  2641.                         writeint(addresses.oddball_globals + 0x84 + playerId*4, score)
  2642.                 end
  2643.         elseif gametype == 4 then
  2644.                 writeshort(getplayer(playerId) + 0xC4, score * 30)
  2645.         elseif gametype == 5 then
  2646.                 writeshort(getplayer(playerId) + 0xC6, score)
  2647.         end
  2648. end
  2649.  
  2650. local function getteamscore(team)
  2651.         if gametype == 1 then
  2652.                 return readint(addresses.ctf_globals + team*4 + 0x10)
  2653.         elseif gametype == 2 then
  2654.                 return team_scores[team]
  2655.                 --return readint(addresses.slayer_globals + team*4)
  2656.         elseif gametype == 3 then
  2657.                 return readint(addresses.oddball_globals + team*4 + 0x4)
  2658.         elseif gametype == 4 then
  2659.                 return readint(addresses.koth_globals + team*4)
  2660.         elseif gametype == 5 then
  2661.                 return readint(addresses.race_globals + team*4 + 0x88)
  2662.         end
  2663. end
  2664.  
  2665. local function setteamscore(team, score)
  2666.         if gametype == 1 then
  2667.                 writeint(addresses.ctf_globals + team*4 + 0x10, score)
  2668.         elseif gametype == 2 then
  2669.                 team_scores[team] = score
  2670.                 --writeint(addresses.slayer_globals + team*4, score)
  2671.         elseif gametype == 3 then
  2672.                 writeint(addresses.oddball_globals + team*4 + 0x4, score)
  2673.         elseif gametype == 4 then
  2674.                 writeint(addresses.koth_globals + team*4, score)
  2675.         elseif gametype == 5 then
  2676.                 writeint(addresses.race_globals + team*4 + 0x88, score)
  2677.         end
  2678. end
  2679.  
  2680. local function WriteChangeLog()
  2681.         local file = io.open("changelog_" .. script_version .. ".txt", "w")
  2682.         local changelog = {}--TM.New()
  2683.         changelog[#changelog+1] = "Changelog for Commands Script"
  2684.         changelog[#changelog+1] = "--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"
  2685.         changelog[#changelog+1] = "Commands Version 5.2 The 'Nimbus' Release (Released __, 2015)"
  2686.         changelog[#changelog+1] = "--Added a firefist command, to be explained later."
  2687.         changelog[#changelog+1] = "--Readded the ghost and unghost commands due to many requests"
  2688.         changelog[#changelog+1] = "--Added a central (remote) banlist feature. Please check out the readme for information on how to use it."
  2689.         changelog[#changelog+1] = "--Ban reasons will now show 'unbanned by (admin name)' when they have been unbanned."
  2690.         changelog[#changelog+1] = "--Even more bug fixes and small features, including compatibility with Sapp."
  2691.         changelog[#changelog+1] = "--Removed all the memoizing crap that I put in the previous version. It was causing a lot of rare issues, and probably wasn't helping performance much"
  2692.         changelog[#changelog+1] = "--Fixed an issue that happened onload that was caused by people using windows default notepad to edit the init.txt"
  2693.         changelog[#changelog+1] = "--Added 'farthest' expression for player arguments, so you can now do things like /kill farthest to kill the farthest player to you."
  2694.         changelog[#changelog+1] = "--Added 'nearest' expression for player arguments, so you can now do things like /kill nearest to kill the nearest player to you."
  2695.         changelog[#changelog+1] = "--Added colors as player arguments, you can now pass colors as player expressions like /kill yellow will kill all yellow people in the server"
  2696.         changelog[#changelog+1] = "--Improved the killingspree feature. It will now announce when someone's spree has ended, and how many kills they racked up."
  2697.         changelog[#changelog+1] = "--Removed 'log' as an alias for the login command so as not to interfere with the already existing 'log' command from Sapp."
  2698.         changelog[#changelog+1] = "--Fixed a bug that would cause the 'list' command to crash if there were no admins on the server."
  2699.         changelog[#changelog+1] = "--Fixed a bug with the Noweapons command."
  2700.         changelog[#changelog+1] = "--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"
  2701.         changelog[#changelog+1] = "Commands Version 5.13 The 'Nimbus' Release (Released February 21th, 2015)"
  2702.         changelog[#changelog+1] = "--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"
  2703.         changelog[#changelog+1] = "--Fixed a few minor bugs, there was a couple of issues with the banlist saving/reloading that happened in rare situations"
  2704.         changelog[#changelog+1] = "--Revamped even more code for optimization, and added lots of memoizing."
  2705.         changelog[#changelog+1] = "--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"
  2706.         changelog[#changelog+1] = "Commands Version 5.01 The 'Nimbus' Release (Released February 20th, 2015)"
  2707.         changelog[#changelog+1] = "--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"
  2708.         changelog[#changelog+1] = "--Fixed a very minor issue with banlist reloading. (was still loading from unused_banlist.txt, not a major problem)"
  2709.         changelog[#changelog+1] = "--Found out that the 'tbag' feature Aelite added was only supposed to apply to kills, so that is now a thing"
  2710.         changelog[#changelog+1] = "--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"
  2711.         changelog[#changelog+1] = "Commands Version 5.0 The 'Nimbus' Release (**WIZARD IS BACK** Released February 19, 2015)"
  2712.         changelog[#changelog+1] = "--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"
  2713.         changelog[#changelog+1] = "--You can change logfile and text files with the following new commands: sv_sharedhash_file, sv_banlist_file, sv_admin_file, sv_commands_file and sv_kickbans_file"
  2714.         changelog[#changelog+1] = "--This script will no longer load the defaults.txt if this script was loaded from the persistent folder (Phasor Only)"
  2715.         changelog[#changelog+1] = "--Time arguments for all commands have changed. All now use the format 5d 3s 2y 5m etc. If you do not specify a character (cywdhms) at the end of your time, then it will default to seconds, so something like sv_respawn_time 500m is different than sv_respawn_time 500."
  2716.         changelog[#changelog+1] = "--Commands that use a player's IP now appends a /24 at the end, which contains the range of 256 addresses within the range of the original IP address. That means if you use sv_ipban 1 and player 1 has an IP of 192.168.0.1, it will ban all the IPs from 192.168.0.0 through 192.168.0.255. This is just a default, you can change it to the old way by appending a /32 at the end of the player."
  2717.         changelog[#changelog+1] = "--The mute command is now the same command as textban, unmute will now untextban a player while sv_unban will unban a textban ID (from sv_banlist)"
  2718.         changelog[#changelog+1] = "--Timestamps added to the ban files are now YY-MM-DD HH:MM:SS instead of just a huge number that represents seconds (you can modify ban expire time a lot easier now)"
  2719.         changelog[#changelog+1] = "--This script will now load sapp's 'ipbans.txt' file."
  2720.         changelog[#changelog+1] = "--Loading/saving of admins/bans are much more reliable now, the script will error if your file is formatted incorrectly"
  2721.         changelog[#changelog+1] = "--All scripted game variables (like deathless, infinite ammo, noweapons) now are integrated completely and will not spam the output window onnewgame anymore"
  2722.         changelog[#changelog+1] = "--Kick/Ban/Mute commands now let you pass reason and time normally, you don't have to use _ for reasons anymore."
  2723.         changelog[#changelog+1] = "--All structures now follow a global naming scheme that I plan to use in all my scripts from now on"
  2724.         changelog[#changelog+1] = "--All code now uses my phasor functions to get addresses (getplayerobject, getplayerweapon, etc) for increased performance"
  2725.         changelog[#changelog+1] = "--This script now uses local variables to optimize performance."
  2726.         changelog[#changelog+1] = "--Revamped ALL functions to execute faster with better code. Some of them I haven't even touched since version 1.0. The first version."
  2727.         changelog[#changelog+1] = "--Added a /disconnect and sv_disconnect command which will give a player a 'lost network connection' screen"
  2728.         changelog[#changelog+1] = "--Removed a few useless commands that never did work but somehow people found out about (probably because of /list lol)"
  2729.         changelog[#changelog+1] = "--Gethelp added. Usage: sv_gethelp (command) or /help (command) without ()"
  2730.         changelog[#changelog+1] = "--Global rcons functionality updated (see guide for details)"
  2731.         changelog[#changelog+1] = "--List command updated so it will not take a 'page' argument. To get a list of commands, type sv_list or /list"
  2732.         changelog[#changelog+1] = "--Organized all commands by table so now all commands are fully documented with help notes and everything"
  2733.         changelog[#changelog+1] = "--textbanlist, iprangebanlist, ipbanlist, and namebanlist are all deprecated. Commands will save the bans in the 'banned.txt' (or whatever sv_banlist_file is set to (banned.txt by default)) file from now on."
  2734.         changelog[#changelog+1] = "--Commands now uses admins.txt for both hash admins and IP admins, ipadmins.txt will still be read for backwards compatibility."
  2735.         changelog[#changelog+1] = "--The old deprecated ban files will now have 'archived_' in front of them. Same goes for the admin files."
  2736.         changelog[#changelog+1] = "--Commands will automatically load the old ban files for backwards compatibility so you do not need to do anything with that. Just know that the old files won't get updated anymore."
  2737.         changelog[#changelog+1] = "--All banlist and unban commands are now deprecated and have been removed. Use sv_banlist and sv_unban to view the banlist and unban people."
  2738.         changelog[#changelog+1] = "--Anyone joining with a hacked hash (like hash 'myfakehashtrollu') will not be allowed into the server"
  2739.         changelog[#changelog+1] = "--Chat commands now directly execute server commands (/e is now deprecated) and the script is much smaller because of it"
  2740.         changelog[#changelog+1] = "--Rewrote BOS functionality"
  2741.         changelog[#changelog+1] = "--Rewrote the Spawn functionality and made the code 10x smaller by using better and more efficient coding"
  2742.         changelog[#changelog+1] = "--I've fixed things here and there, added things that were removed from my original Commands."
  2743.         changelog[#changelog+1] = "--Fixed /read (it never worked). Also accepts number structs."
  2744.         changelog[#changelog+1] = "--Fixed /write to not break on invalid syntax. Also accepts number structs.."
  2745.         changelog[#changelog+1] = "--Added randomred and randomblue to expressions list."
  2746.         changelog[#changelog+1] = "--Added a control command (FINALLY)"
  2747.         changelog[#changelog+1] = "--Fixed sending negative numbers to setplasmas and setfrags"
  2748.         changelog[#changelog+1] = "--Fixed occasional glitches with setammo."
  2749.         changelog[#changelog+1] = "--Rewrote the access system. You now do not have to start at lvl 0 being the highest, you can now have levels in any order"
  2750.         changelog[#changelog+1] = "--Script also tells you if your access.ini is not formatted correctly and why and then raises an error."
  2751.         changelog[#changelog+1] = "--Changed sv_status to sv_status_more as to not interfere with normal sv_status"
  2752.         changelog[#changelog+1] = "--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"
  2753.         changelog[#changelog+1] = "--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"
  2754.         changelog[#changelog+1] = "Commands Version 4.2(Released August 3rd, 2013)"
  2755.         changelog[#changelog+1] = "--Update Reason: Bug fixes and Added Features"
  2756.         changelog[#changelog+1] = "--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"
  2757.                 changelog[#changelog+1] = ""
  2758.         changelog[#changelog+1] = "--Added the time limit address thanks to wizard. sv_time_cur is now compatible with Halo CE"
  2759.         changelog[#changelog+1] = "--Added the sendconsoletext overload to the command scripts. Thank you Nuggetz."
  2760.         changelog[#changelog+1] = "--Added 'sv_hash_duplicates' command which enables/disables the checking of duplicate keys in the server."
  2761.         changelog[#changelog+1] = "--Added 'sv_multiteam_vehicles' Enables/Disables the ability to enter a vehicle with another playerId in FFA."
  2762.                 changelog[#changelog+1] = ""
  2763.         changelog[#changelog+1] = "--Modified all tables that used the hash for identication, and changed it to IP"
  2764.                 changelog[#changelog+1] = ""
  2765.         changelog[#changelog+1] = "--Fixed minor bug with votekick"
  2766.         changelog[#changelog+1] = "--Fixed issue with sv_superban"
  2767.         changelog[#changelog+1] = "--Fixed AntiCaps command. WARNING: If you use a chatfilter script. Load that script before this one."
  2768.         changelog[#changelog+1] = "--Fixed issue with team play detection"
  2769.         changelog[#changelog+1] = "--Fixed minor bugs."
  2770.                 changelog[#changelog+1] = ""
  2771.         changelog[#changelog+1] = "--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"
  2772.         changelog[#changelog+1] = "Commands Version 4.1(Released July 23rd, 2013)"
  2773.         changelog[#changelog+1] = "--Update Reason: Bug fixes"
  2774.         changelog[#changelog+1] = "--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"
  2775.                 changelog[#changelog+1] = ""
  2776.         changelog[#changelog+1] = "--Fixed minor bugs"
  2777.                 changelog[#changelog+1] = ""
  2778.         changelog[#changelog+1] = "--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"
  2779.         changelog[#changelog+1] = "Commands Version 4.0(Released July 21st, 2013)"
  2780.         changelog[#changelog+1] = "--Update Reason: make it compatible with new Phasor"
  2781.         changelog[#changelog+1] = "--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"
  2782.                 changelog[#changelog+1] = ""
  2783.         changelog[#changelog+1] = "--Added 'sv_addrcon' This gives you the ability to have more than one rcon."
  2784.         changelog[#changelog+1] = "--Added 'sv_delrcon' Removes rcons from the rcon list."
  2785.         changelog[#changelog+1] = "--Added 'sv_rconlist' Displays all available rcons"
  2786.         changelog[#changelog+1] = "--Added 'sv_iprangeban' bans entire IP Range"
  2787.         changelog[#changelog+1] = "--Added 'sv_iprangeunban' Removes an IP from the banlist"
  2788.         changelog[#changelog+1] = "--Added 'sv_iprangebanlist' Shows all IP's banned"
  2789.         changelog[#changelog+1] = "--Added 'sv_read' command."
  2790.         changelog[#changelog+1] = "--Added 'sv_load' shortcut for sv_script_load."
  2791.         changelog[#changelog+1] = "--Added 'sv_unload' shortcut for sv_script_unload."
  2792.         changelog[#changelog+1] = "--Added 'sv_resetplayer' and '/resetplayer' removes all troll command settings."
  2793.         changelog[#changelog+1] = "--Added 'sv_damage' Increases playerId damage."
  2794.         changelog[#changelog+1] = "--Added reason to sv_textban"
  2795.         changelog[#changelog+1] = "--Added Command_Balance local function since sv_team_balance is not a command anymore for the moment."
  2796.                 changelog[#changelog+1] = ""
  2797.         changelog[#changelog+1] = "--Modified Reason is said to the public"
  2798.         changelog[#changelog+1] = "--Modified Adminblocker the admin is notified if a command is being executed on them."
  2799.         changelog[#changelog+1] = "--Modified AntiSpam changed from boolean to a type: all, players, off"
  2800.         changelog[#changelog+1] = "--Modified 'sv_commands' -> 'sv_cmds'"
  2801.         changelog[#changelog+1] = "--Modified 'sv_ipban' now accepts IP's'"
  2802.         changelog[#changelog+1] = "--Modified AFK detection. Works better"
  2803.         changelog[#changelog+1] = "--Modified NameBan. Banned names are changed to 'playerId' on join."
  2804.         changelog[#changelog+1] = "--Modified Tbag Detection: Detects victim location. You must be near the victims death to be able to tbag him/her."
  2805.                 changelog[#changelog+1] = ""
  2806.         changelog[#changelog+1] = "--Fixed most bugs with the new phasor"
  2807.         changelog[#changelog+1] = "--Fixed 'sv_scrimmode'"
  2808.         changelog[#changelog+1] = "--Fixed @ bug"
  2809.         changelog[#changelog+1] = "--Fixed AccessMerging bug when -1 was given to any other level other than 0"
  2810.         changelog[#changelog+1] = "--Fixed minor Bos Bug"
  2811.         changelog[#changelog+1] = "--Fixed setscore bug"
  2812.                 changelog[#changelog+1] = ""
  2813.         changelog[#changelog+1] = "--Removed sv_pinglist command"
  2814.         changelog[#changelog+1] = "--Removed sv_hash_check, sv_version, and sv_version_check. Phasor has them built in."
  2815.         changelog[#changelog+1] = "--Removed Portal Blocking"
  2816.         changelog[#changelog+1] = "--Removed /setname until further notice"
  2817.                 changelog[#changelog+1] = ""
  2818.         changelog[#changelog+1] = "--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"
  2819.         changelog[#changelog+1] = "Commands Version 3.0.1 (Released February 28th, 2013)"
  2820.         changelog[#changelog+1] = "--Update Reason: Mainly Bug Fixes"
  2821.         changelog[#changelog+1] = "--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"
  2822.                         changelog[#changelog+1] = ""
  2823.         changelog[#changelog+1] = "--Added sv_scrimmode"
  2824.                         changelog[#changelog+1] = ""
  2825.         changelog[#changelog+1] = "--Modified ipadminadd now accepts IP's"
  2826.         changelog[#changelog+1] = "--Modified 'sv_admin_add: Combined sv_admin_add and sv_admin_addh"
  2827.                         changelog[#changelog+1] = ""
  2828.         changelog[#changelog+1] = "--Fixed /e bug"
  2829.         changelog[#changelog+1] = "--Fixed spam_max and spam_timeout bugs"
  2830.         changelog[#changelog+1] = "--Fixed textban bugs"
  2831.         changelog[#changelog+1] = "--Fixed superban bugs"
  2832.         changelog[#changelog+1] = "--Fixed ipban bug"
  2833.                 changelog[#changelog+1] = ""
  2834.         changelog[#changelog+1] = "--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"
  2835.         changelog[#changelog+1] = "Commands Version 3.0.0 (released February 11th, 2013)"
  2836.         changelog[#changelog+1] = "--Update Reason: Bug Fixes and New Features"
  2837.         changelog[#changelog+1] = "--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"
  2838.                 changelog[#changelog+1] = ""
  2839.         changelog[#changelog+1] = "--Added safe guards for the admin system."
  2840.         changelog[#changelog+1] = "--Added Ping List command. Displays the Players ID, Name and Ping level."
  2841.         changelog[#changelog+1] = "--Added System for no Hash and/or IP admins"
  2842.         changelog[#changelog+1] = "--Added a sv_login command so if you are using the System for no Hash and/or IP Admins people with rcon are able to use chat commands"
  2843.         changelog[#changelog+1] = "--Added a sv_status command. It will show the current status of defaults.txt commands."
  2844.         changelog[#changelog+1] = "--Added info shown to the sv_info command"
  2845.         changelog[#changelog+1] = "--Added 'sv_chatcommands' to enable or disable admin chat commands"
  2846.         changelog[#changelog+1] = "--Added 'sv_adminblocker' enables,disables or limits the abiliy of an admin to kick/ban another admin"
  2847.         changelog[#changelog+1] = "--Added 'sv_anticaps'  Enables or Disables the use of caps in the server"
  2848.         changelog[#changelog+1] = "--Added 'sv_antispam' Enables or Disables the antispam system of the script"
  2849.         changelog[#changelog+1] = "--Added 'sv_spammax' Changes the max amount of messages that can be sent in 1 minute"
  2850.         changelog[#changelog+1] = "--Added 'sv_spamtimeout' Changes the time you are muted for spamming"
  2851.         changelog[#changelog+1] = "--Added a time amount that a playerId can be textbanned 'sv_textban [playerId] {time}'. if no time is set then the default is -1 which is forever."
  2852.         changelog[#changelog+1] = "--Added a way to use any command without the 'sv_' Ex: 'sv_admin_add' can be written as 'admin_add' "
  2853.         changelog[#changelog+1] = "--Added a time amount that a playerId can be ipbanned 'sv_ipban [playerId] {time} {message}'. if no time is set then the default is -1 which is forever."
  2854.         changelog[#changelog+1] = "--Added 'sv_bosplayers' to see the available players that can be banned on sight."
  2855.                 changelog[#changelog+1] = ""
  2856.         changelog[#changelog+1] = "--Modified how defaults.txt works. If a command is not specified in the defaults.txt file it is set to default by the script so no errors occur during game."
  2857.         changelog[#changelog+1] = "--Modified Tbag function"
  2858.         changelog[#changelog+1] = "--Modified ban, you are now able to send reason(s) for the ban which will be written a file."
  2859.         changelog[#changelog+1] = "--Modified kick, you are now able to send reason(s) for the kick which will be written a file."
  2860.         changelog[#changelog+1] = "--Modified Ipban, you are now able to send reason(s) for the ipban which will be written a file."
  2861.         changelog[#changelog+1] = "--Modified Superban, you are now able to send reason(s) for the superban which will be written a file."
  2862.         changelog[#changelog+1] = "--Modified Votekick. You can only votekick again after 1 minute has passed since last votekick."
  2863.         changelog[#changelog+1] = "--Modified ChangeLevel. The coding for this command has been improved."
  2864.         changelog[#changelog+1] = "--Modified GetHelp"
  2865.                 changelog[#changelog+1] = ""
  2866.         changelog[#changelog+1] = "--Fixed Ipban bug"
  2867.         changelog[#changelog+1] = "--Fixed the '/e' command."
  2868.         changelog[#changelog+1] = "--Fixed the 'sv_unmute' command."
  2869.                 changelog[#changelog+1] = ""
  2870.         changelog[#changelog+1] = "--Removed sv_command_type and all content related to it"
  2871.         changelog[#changelog+1] = "--Removed redundant code."
  2872.         changelog[#changelog+1] = "--Renamed 'sv_sa_message' to 'sv_serveradmin_message'"
  2873.         changelog[#changelog+1] = "--Renamed 'sv_fj_message' to 'sv_firstjoin_message'"
  2874.         changelog[#changelog+1] = "--Renamed 'sv_wb_message' to 'sv_welcomeback_message'"
  2875.                 changelog[#changelog+1] = ""
  2876.         changelog[#changelog+1] = "--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"
  2877.         changelog[#changelog+1] = "Commands Version 2.4.3 (released September 23rd,2012"
  2878.         changelog[#changelog+1] = "--Update Reason: Fixed issues with ServerChat Function."
  2879.         changelog[#changelog+1] = "--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"
  2880.                 changelog[#changelog+1] = ""
  2881.         changelog[#changelog+1] = "--Fixed OnServerChat local function error caused Chat Commands to fail."
  2882.                 changelog[#changelog+1] = ""
  2883.         changelog[#changelog+1] = "--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"
  2884.         changelog[#changelog+1] = "Commands Version 2.4.2 (released August 29th,2012"
  2885.         changelog[#changelog+1] = "--Update Reason: Fixed issues from version 2.4.1 and new commands."
  2886.         changelog[#changelog+1] = "--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"
  2887.                 changelog[#changelog+1] = ""
  2888.         changelog[#changelog+1] = "--Added a change admin level command. You can now change the level of an IP and/or Hash admin  level. 'sv_change_level'"
  2889.         changelog[#changelog+1] = "--Added a Command Type command. You can now change between the way Wizard setup his chat commands, and my way. 'sv_command_type'"
  2890.         changelog[#changelog+1] = "--Added a Command Type command to the default.txt"
  2891.                 changelog[#changelog+1] = ""
  2892.         changelog[#changelog+1] = "--ReAdded Private say command"
  2893.         changelog[#changelog+1] = "--ReAdded '/e' command."
  2894.         changelog[#changelog+1] = "--ReAdded Nuke Command."
  2895.         changelog[#changelog+1] = "--ReAdded SetName Command."
  2896.                 changelog[#changelog+1] = ""
  2897.         changelog[#changelog+1] = "--Modified Tbag function"
  2898.                 changelog[#changelog+1] = ""
  2899.         changelog[#changelog+1] = "--Fixed 'sv_info' command error"
  2900.         changelog[#changelog+1] = "--Fixed OnServerChat, now you wont get 'You are not allowed to use this command' every time you type."
  2901.                 changelog[#changelog+1] = ""
  2902.         changelog[#changelog+1] = "--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"
  2903.         changelog[#changelog+1] = "Commands Version 2.4.1 (released August 22nd,2012"
  2904.         changelog[#changelog+1] = "--Update Reason: Fixed issues from version 2.4"
  2905.         changelog[#changelog+1] = "--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"
  2906.                 changelog[#changelog+1] = ""
  2907.         changelog[#changelog+1] = "--Fixed 'sv_players_more' issue."
  2908.         changelog[#changelog+1] = "--Fixed 'sv_players' issue."
  2909.         changelog[#changelog+1] = "--Fixed Chat Command issue. If you were admin, then you could do any In-Chat in the script. Check Manual for info on how to add new commands."
  2910.         changelog[#changelog+1] = "--Fixed Command issue. If you were admin, then you could do any In-Console in the script. Check Manual for info on how to add new commands."
  2911.                 changelog[#changelog+1] = ""
  2912.         changelog[#changelog+1] = "--Removed Restriction of Portal Blocking. Warning: Modded Maps with portals might cause the server to crash. Hope to fix soon."
  2913.                 changelog[#changelog+1] = ""
  2914.         changelog[#changelog+1] = "--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"
  2915.         changelog[#changelog+1] = "Commands Version 2.4 (released August 19th,2012"
  2916.         changelog[#changelog+1] = "--Update Reason: New Features, Fix minor bugs, Remove non-working commands(will be re-add later), and Organize the Script"
  2917.         changelog[#changelog+1] = "--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"
  2918.                 changelog[#changelog+1] = ""
  2919.         changelog[#changelog+1] = "--Added Admin Add Hash. You can now add people via hash. So you are able to add people without them being in the server. 'sv_admin_addh'. This is mostly used if you have 5 or more people you want to add at the same time. Another Script is needed to succesfully use this command."
  2920.         changelog[#changelog+1] = "--Added a Portal Blocking Command. If someone is blocking a portal they will be killed. 'sv_pbdet'"
  2921.         changelog[#changelog+1] = "--Added a Private Messaging boolean for the @ in chat. 'sv_pvtmessage'"
  2922.         changelog[#changelog+1] = "--Added Private Messaging to the default.txt"
  2923.         changelog[#changelog+1] = "--Added a Tbagging function."
  2924.         changelog[#changelog+1] = "--Added a Tbagging Boolean Command. 'sv_tbagdet'"
  2925.         changelog[#changelog+1] = "--Added Tbagging Command to the default.txt"
  2926.         changelog[#changelog+1] = "--Added Killing spree Notifications"
  2927.         changelog[#changelog+1] = "--Added a Killing spree Notification command 'sv_killspree'"
  2928.         changelog[#changelog+1] = "--Added Killing spree Notification to the default.txt"
  2929.         changelog[#changelog+1] = "--Added a 'Welcome back' message in EventPlayerJoin. It will only say it if you are not an IP/Hash Admin"
  2930.         changelog[#changelog+1] = "--Added a Welcome back message command. 'sv_welcomeback_message'"
  2931.         changelog[#changelog+1] = "--Added Welcome Back Message to the default.txt"
  2932.         changelog[#changelog+1] = "--Added a 'This is the players first time joining the server' message in EventPlayerJoin"
  2933.         changelog[#changelog+1] = "--Added a Command for Server Admin Message. 'sv_sa_message'"
  2934.         changelog[#changelog+1] = "--Added Server Admin message to the default.txt"
  2935.         changelog[#changelog+1] = "--Added a Command for First Joining Message 'sv_firstjoin_message'"
  2936.         changelog[#changelog+1] = "--Added First Joining Message to the default.txt"
  2937.         changelog[#changelog+1] = "--Added a Command for Unique Counting. 'sv_uniques_enabled'"
  2938.         changelog[#changelog+1] = "--Added Unique Counting to the default.txt"
  2939.         changelog[#changelog+1] = "--Added a 'sv_players_more' Command. This does the same local function as 'sv_players' but it gives you more information on each playerId."
  2940.         changelog[#changelog+1] = "--Added IP to the info Command."
  2941.                 changelog[#changelog+1] = ""
  2942.         changelog[#changelog+1] = "--Fixed RTV Enabled Command "
  2943.         changelog[#changelog+1] = "--Fixed VoteKick Enabled Command "
  2944.         changelog[#changelog+1] = "--Fixed info command. "
  2945.         changelog[#changelog+1] = "--Fixed time current command. "
  2946.         changelog[#changelog+1] = "--Fixed Set Teleport Shortcut. 'sv_st' "
  2947.         changelog[#changelog+1] = "--Fixed Teleport Delete Shortcut. 'sv_t del'"
  2948.                 changelog[#changelog+1] = ""
  2949.         changelog[#changelog+1] = "--Modified 'sv_players' command. Now it only gives you playerId ID, playerId Name, and playerId Team."
  2950.         changelog[#changelog+1] = "--Modified 'sv_count' command. Now disables if 'sv_uniques_enabled' is disabled."
  2951.         changelog[#changelog+1] = "--Modified Chat Commands: all commands can now be used in chat. Note: Some shortcuts have been transfered but not all."
  2952.         changelog[#changelog+1] = "--Modified GetHelp Commnad"
  2953.         changelog[#changelog+1] = "--Modified List Command"
  2954.                 changelog[#changelog+1] = ""
  2955.         changelog[#changelog+1] = "--Removed 'sv_unban' command from the script. Caused the OnSeverCommand local function to crash."
  2956.         changelog[#changelog+1] = "--Removed Private say command"
  2957.         changelog[#changelog+1] = "--Removed '/e' command."
  2958.         changelog[#changelog+1] = "--Removed Nuke Command."
  2959.         changelog[#changelog+1] = "--Removed SetName Command."
  2960.                 changelog[#changelog+1] = ""
  2961.         changelog[#changelog+1] = "--Access level crash fixes and a few other features"
  2962.         changelog[#changelog+1] = "--Organized all of the Functions"
  2963.         changelog[#changelog+1] = "--Commented all Main Functions. This will only show on the Commented version of the script"
  2964.                 changelog[#changelog+1] = ""
  2965.         changelog[#changelog+1] = "--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"
  2966.         changelog[#changelog+1] = "Commands Version 2.3 (released June 13th 2012"
  2967.         changelog[#changelog+1] = "Mainly fixed up the admin system"
  2968.         changelog[#changelog+1] = "--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"
  2969.                 changelog[#changelog+1] = ""
  2970.         changelog[#changelog+1] = "--Added a /crash [playerId] and a sv_crash [playerId] command. Do not use it when the game is ending... you've been warned..."
  2971.         changelog[#changelog+1] = "--Added a /scorelimit and a sv_scorelimit command"
  2972.         changelog[#changelog+1] = "--Added a /a del command."
  2973.         changelog[#changelog+1] = "--Added sv_ipadminadd to the rcon"
  2974.         changelog[#changelog+1] = "--Added a patch that will allow new gametypes to be added without restarting the server"
  2975.         changelog[#changelog+1] = "--Added textbanning. It's like the mute command except it's a permanent"
  2976.                 changelog[#changelog+1] = ""
  2977.         changelog[#changelog+1] = "--Modified the AdminList command. Shows a lot more now."
  2978.                 changelog[#changelog+1] = ""
  2979.         changelog[#changelog+1] = "--Fixed rcon commands so that the responses show up with the /e command"
  2980.         changelog[#changelog+1] = "--Fixed the ipban command (whoops)"
  2981.         changelog[#changelog+1] = "--Fixed a very small problem with the setcolor command."
  2982.         changelog[#changelog+1] = "--Fixed up the timelimit command :)"
  2983.         changelog[#changelog+1] = "--Fixed ipadmins. They can now use the rcon."
  2984.                 changelog[#changelog+1] = ""
  2985.         changelog[#changelog+1] = "--This script no longer uses Phasor admins, if it sees that you are, it will delete all of them and add them to mine, so if you see admin.txt"
  2986.         changelog[#changelog+1] = "turned into admins.txt, don't worry, it's supposed to do that."
  2987.         changelog[#changelog+1] = "--This script enables CE devmode commands (cheat_deathless_player, cheat_medusa, etc)"
  2988.         changelog[#changelog+1] = "--IP admins no longer get removed when you unload the commands script"
  2989.                 changelog[#changelog+1] = ""
  2990.         changelog[#changelog+1] = "--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"
  2991.         changelog[#changelog+1] = "Commands Version 2.201 (released on June 5th 2012)"
  2992.         changelog[#changelog+1] = "This is mainly just a bug fix version"
  2993.         changelog[#changelog+1] = "--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"
  2994.                 changelog[#changelog+1] = ""
  2995.         changelog[#changelog+1] = "--Added a /setscore and sv_setscore command."
  2996.         changelog[#changelog+1] = "--Added New functionality to /hax and /unhax"
  2997.                 changelog[#changelog+1] = ""
  2998.         changelog[#changelog+1] = "--Modified setkills, setassists, and setdeaths to work a little cleaner"
  2999.                 changelog[#changelog+1] = ""
  3000.         changelog[#changelog+1] = "--Fixed /commands to show all of the commands."
  3001.         changelog[#changelog+1] = "--Fixed a weird problem with the /enter command (when you ejected it would crash your game)"
  3002.         changelog[#changelog+1] = "--Fixed /a list (i forgot to check for the /, i was only checking for \\a list, so /a list wouldn't work)"
  3003.         changelog[#changelog+1] = "--Fixed falldamage (and also made it so longer falls don't kill you)"
  3004.         changelog[#changelog+1] = "--Fixed /hide, when you leave and another person rejoins with your playerId number, it will no longer hide them (thank you mitch... lol)"
  3005.         changelog[#changelog+1] = "--Fixed a bug when loading this script first, it wouldn't let other scripts control the weapons being assigned on spawn (can't believe i didn't see it earlier, ty nuggets + others)"
  3006.                 changelog[#changelog+1] = ""
  3007.         changelog[#changelog+1] = "-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"
  3008.         changelog[#changelog+1] = "Commands Version 2.2 (Released on June 1st 2012)"
  3009.         changelog[#changelog+1] = "-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"
  3010.                 changelog[#changelog+1] = ""
  3011.         changelog[#changelog+1] = "--Added unique player tracking. It will keep track of the number of unique players who joined the server"
  3012.         changelog[#changelog+1] = "--Added '/count' Get the number of unique players"
  3013.         changelog[#changelog+1] = "--Added /balance, it executes sv_teams_balance in console"
  3014.         changelog[#changelog+1] = "--Added sv_privatesay. Looks like i forgot the sv_command for that"
  3015.         changelog[#changelog+1] = "--Added private chat. Use @(playerId expression) to message someone"
  3016.         changelog[#changelog+1] = "--Added /setcolor. Only works in FFA gametypes."
  3017.         changelog[#changelog+1] = "--Added namebanning to superban."
  3018.         changelog[#changelog+1] = "--Added a \ameban command."
  3019.         changelog[#changelog+1] = "--Added ipadmin deleting"
  3020.                 changelog[#changelog+1] = ""
  3021.         changelog[#changelog+1] = "--Modifed 'sv_map' made it so that the sv_map and /m loads the commands script by default"
  3022.         changelog[#changelog+1] = "--Modified the AdminList local function to be a lot more useful"
  3023.                 changelog[#changelog+1] = ""
  3024.         changelog[#changelog+1] = "--Fixed Bugs"
  3025.         changelog[#changelog+1] = "--Fixed votekick."
  3026.         changelog[#changelog+1] = "--Fixed /privatesay. It would only let you message one word before"
  3027.         changelog[#changelog+1] = "--Fixed access levels for the script. It kind of worked, but not really."
  3028.         changelog[#changelog+1] = "--Fixed an issue with /timelimit and sv_time_cur"
  3029.         changelog[#changelog+1] = "--Fixed deathless glitch"
  3030.                 changelog[#changelog+1] = ""
  3031.         changelog[#changelog+1] = "--Removed some of the spam when the script loads (it was an easy way to accomplish what i wanted at the time)"
  3032.                 changelog[#changelog+1] = ""
  3033.         changelog[#changelog+1] = "-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"
  3034.         changelog[#changelog+1] = "Commands Version 2.001"
  3035.         changelog[#changelog+1] = "--Fixed /ipadminadd "
  3036.         changelog[#changelog+1] = "-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"
  3037.         changelog[#changelog+1] = "Commands Version 2.0(Released April 22nd, 2012)"
  3038.         changelog[#changelog+1] = "This is pretty much a rewrite of the entire script. So many new features were added. So many that I don't even want to make this changelog. Anyway, I'm forcing myself to make it. So here it is:"
  3039.         changelog[#changelog+1] = "-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"
  3040.                 changelog[#changelog+1] = ""
  3041.         changelog[#changelog+1] = "--Added hash check manipulation. You can now disable or enable hash checking (meaning that cracked versions can join your server if its disabled, but that also means everyone is unbannable if they spoof their key (which most people don't know how to do)."
  3042.         changelog[#changelog+1] = "--Added sv_revoke and /revoke. The syntax is /revoke [playerId]. This will take away someone's admin who is currently in the server."
  3043.         changelog[#changelog+1] = "--Added /os, this will give you an Overshield (syntax is /os [playerId])"
  3044.         changelog[#changelog+1] = "--Added a command to view all admins on the server, syntax is: /a list"
  3045.         changelog[#changelog+1] = "--Added a command to view the current admins in the server. Syntax is: /viewadmins"
  3046.         changelog[#changelog+1] = "--Added a command to view the server specs (processor speed, model name, manufacturer). Syntax is /specs, or sv_specs in console"
  3047.         changelog[#changelog+1] = "--Added another playerId expression. Now you are able to use 'rand' as a playerId name So like '/k rand' would kick a rand person"
  3048.         changelog[#changelog+1] = "--Added version changing. You can change to any version of Halo. Syntax is: /version {version} or sv_version {version} in console."
  3049.         changelog[#changelog+1] = "--Added version check removal. You can enable or disable version checking. Having this disabled means that any person on any version can join your server (please note that your server will only appear on the server you specify in the version command)"
  3050.         changelog[#changelog+1] = "--Added a defaults.txt. This text file gets called on server startup."
  3051.         changelog[#changelog+1] = "--Added a sv_commands and a /commands. This will show all the commands that exist for this script."
  3052.         changelog[#changelog+1] = "--Added a /hide and a /unhide command, these will make you totally hidden from everyone else in the server. It also removes you from the scoreboard, however, it only works with players that join after you execute it. People in the server at the time that you use it will still see you on the scoreboard."
  3053.         changelog[#changelog+1] = "--Added a parameter to '/spd'. Syntax is /spd [playerId] {speed}. Doing /spd [playerId] will show you their current speed."
  3054.         changelog[#changelog+1] = "--Added another parameter to '/t'. Syntax is '/t list', this will show you the list of teleports for the map."
  3055.         changelog[#changelog+1] = "--Added infinite nades to sv_infinite_ammo"
  3056.         changelog[#changelog+1] = "--Added a '/banlist' command to chat. This will show you the banlist."
  3057.         changelog[#changelog+1] = "--Added a '/alias' command to chat. This will show aliases of the playerId you pick. Kinda glitchy, oxide's fault, not mine."
  3058.         changelog[#changelog+1] = "--Added sv_rtv_needed [decimal 0 to 1]"
  3059.         changelog[#changelog+1] = "--Added sv_votekick_needed [decimal 0 to 1]"
  3060.         changelog[#changelog+1] = "--Added sv_rtv_enabled [true or false, 1 or 0]"
  3061.         changelog[#changelog+1] = "--Added sv_votekick_enabled [true or false, 1 or 0]"
  3062.         changelog[#changelog+1] = "--Added ipbanning. Syntax is sv_ipban, sv_ipbanlist, sv_ipunban, and for chat: /ipban, /ipbanlist, /ipunban."
  3063.         changelog[#changelog+1] = "--Added 'sv_launch' and '/launch'. Syntax is 'sv_launch [playerId]' or '/launch [playerId]'"
  3064.         changelog[#changelog+1] = "--Added a control command. Syntax is 'sv_control [victim] [controller]' or '/c [victim] [controller]' in chat."
  3065.         changelog[#changelog+1] = "--Added a privatesay command. Syntax is 'sv_privatesay [playerId] [message]' or '/privatesay [playerId] [message]' in chat"
  3066.         changelog[#changelog+1] = "--Added temp.txt managing, basically so values that you set the previous map won't be erased when the next map loads (like when you do sv_respawn_time 5, and it goes back to default everytime you reload the script)"
  3067.         changelog[#changelog+1] = "--Added ipadminadding. You can add admins via IP now. Syntax is sv_ipadminadd (playerId) (nickname) (level) or /ipadminadd (playerId) (nickname) (level) in chat."
  3068.         changelog[#changelog+1] = "--Added: Now includes logging. This will log directly to commands.log in the log folder"
  3069.         changelog[#changelog+1] = "--Added: If you do not have an access file, this script will make one for you."
  3070.         changelog[#changelog+1] = "--Added '/e' command."
  3071.                 changelog[#changelog+1] = ""
  3072.         changelog[#changelog+1] = "--Modified /timelimit and sv_timelimit. It will change the ingame timelimit (time remaining) as well as the timelimit for every game after that. This still breaks with sv_reloadscripts."
  3073.         changelog[#changelog+1] = "--Modified sv_afk was changed to 'sv_setafk'. Thought it was a better name for the command."
  3074.                 changelog[#changelog+1] = ""
  3075.         changelog[#changelog+1] = "--Fixed /setname, it will change your name, but for others to see it it requires a rejoin."
  3076.         changelog[#changelog+1] = "--Fixed the admin system, before you had to do sv_reloadscripts after you added someone, that's been fixed."
  3077.         changelog[#changelog+1] = "--Fixed access.ini not syncing with chat commands, meaning if you have sv_kick in your access level, you can now use /k in the chat."
  3078.         changelog[#changelog+1] = "--Fixed a bug with /ammo, this now works correctly. Syntax is: /ammo [playerId] [type (1 or 2)] [ammo] or sv_setammo in console."
  3079.         changelog[#changelog+1] = "--Fixed smiley's BOS commands, thanks to bvigil for telling me what it was supposed to do."
  3080.         changelog[#changelog+1] = "--Fixed a bug with /tp and sv_teleport_pl, which were crashing when the other playerId was dead."
  3081.         changelog[#changelog+1] = "--Fixed /setplasmas, thank you sanity for the notice..."
  3082.         changelog[#changelog+1] = "--Fixed a reported bug with /noweapons... I was never able to reproduce it, so I must have indirectly fixed it."
  3083.         changelog[#changelog+1] = "--Fixed /info which would crash when you used a playerId expression that was not a number."
  3084.         changelog[#changelog+1] = "--Fixed a couple of bugs with rtv and votekick (whoops)"
  3085.         changelog[#changelog+1] = "--Fixed a bug with 'sv_mute' and '/mute'. You can no longer mute admins."
  3086.         changelog[#changelog+1] = "--Fixed /st. This will set a teleport location to wherever you are standing. Syntax is: /st [teleport name]"
  3087.         changelog[#changelog+1] = "--Fixed the spawngun. Syntax is /setmode (playerId) spawngun (object)"
  3088.         changelog[#changelog+1] = "--Fixed Smiley's BoS (Ban On Sight) system. I had to rewrite 80% of it to work with the new Phasor. It now also bans the person's IP."
  3089.                 changelog[#changelog+1] = ""
  3090.         changelog[#changelog+1] = "--Sorry this took so long, it took a while to rewrite the whole script. Technically it's been done for a while, i was just waiting for Oxide to release phasor 059. But that won't happen until after june, and there's no way i'm keeping you all waiting. Hope you enjoy it."
  3091.                 changelog[#changelog+1] = ""
  3092.         changelog[#changelog+1] = "-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"
  3093.         changelog[#changelog+1] = "Commands Version 1.0"
  3094.         changelog[#changelog+1] = "First Official Release (January or something 2012)"
  3095.         changelog[#changelog+1] = "-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"
  3096.                 changelog[#changelog+1] = ""
  3097.         changelog[#changelog+1] = "--Script Created"
  3098.         file:write(concat(changelog, "\n"))
  3099.         file:close()
  3100. end
  3101.  
  3102. function tempBanPlayer(playerIndex)
  3103.         halo_svcmd("sv_ban " .. playerIndex .. " 1s")
  3104.         return false
  3105. end
  3106.  
  3107. local function addBan(player_name, player_hash, player_ip, ban_time, ban_type, ban_reason)
  3108.  
  3109.         local banid, ban_entry, bancount, name, reason
  3110.         local args = defaults.remote_bansystem
  3111.         -- check if they've already been banned before
  3112.         for id = 1,#ban_table do ban_entry = ban_table[id]
  3113.                 if not args or (ban_entry.remote and args.mode >= 1 or not ban_entry.remote and args.mode ~= 2) then
  3114.                         if ban_type == ban_entry.type then
  3115.                                 if player_hash and player_hash == ban_entry.hash then
  3116.                                         banid = id
  3117.                                         break
  3118.                                 elseif player_ip and ban_entry.ip and netMatch(ban_entry.ip, player_ip) then
  3119.                                         banid = id
  3120.                                         break
  3121.                                 end
  3122.                         end
  3123.                 end
  3124.         end
  3125.  
  3126.         if banid then
  3127.                 bancount = ban_entry.count + 1
  3128.                 name = ban_entry.name
  3129.                 reason = ban_entry.reason
  3130.         else
  3131.                 bancount = 1
  3132.                 banid = #ban_table+1
  3133.                 ban_entry = {}--TM.New()
  3134.         end
  3135.  
  3136.         ban_entry.count = bancount
  3137.         ban_entry.name = (name and not find(name, player_name) and (name .. " / ") or "") .. player_name
  3138.         ban_entry.hash = player_hash
  3139.         ban_entry.ip = player_ip
  3140.         ban_entry.time = ban_time > 0 and ban_time + os.time() or ban_time == -1 and -1 or ban_penalty[bancount]
  3141.         ban_entry.type = ban_type
  3142.         ban_entry.reason = (reason and not find(reason, ban_reason) and (reason .. " / ") or "") .. ban_reason
  3143.  
  3144.         ban_table[banid] = ban_entry
  3145.  
  3146.         if args and args.mode >= 2 then
  3147.                 --print("ADDING TO REMOTE BANLIST. Host: " .. tostring(args.http_source) .. " Body: " .. tostring("&bans=" .. toBanTextEntry(ban_entry)))
  3148.                 print(http.request(args.http_source, "&bans=" .. toBanTextEntry(ban_entry)) .. ",&unban=false")
  3149.         end
  3150.  
  3151.         updateBanFiles(bantype)
  3152. end
  3153.  
  3154. local function checkaccess(command, access)
  3155.         command = Commands[command] or command
  3156.         local access_entry = access_table[access]
  3157.         return access_entry and (access_entry.allcommands or access_entry[command]) or false
  3158. end
  3159.  
  3160. -- Returns true if equal, false if both not admin, or the playerId more powerful.
  3161. -- It's important to note that if the Server is being compared as a playerId, it's value is nil.
  3162. local function getMorePowerfulPlayer(playerId1, playerId2)
  3163.         -- the Server is the most powerful admin.
  3164.         if not playerId1 then
  3165.                 return not playerId2 or playerId1
  3166.         elseif not playerId2 then
  3167.                 return not playerId1 or playerId2
  3168.         end
  3169.  
  3170.         local access1 = access_table[getaccess(playerId1)]
  3171.         access1 = access1 and #access1 or -1
  3172.         local access2 = access_table[getaccess(playerId2)]
  3173.         access2 = access2 and #access2 or -1
  3174.         return access1 > access2 and playerId1 or access1 < access2 and playerId2 or access1 > 0
  3175. end
  3176.  
  3177. local function checkAdminBlockerAccess(executorPlayerId, playerId)
  3178.         local allowed = getMorePowerfulPlayer(executorPlayerId, playerId)
  3179.         return defaults.adminblocker == "Unsafe" or defaults.adminblocker == "Lenient" and (allowed == executorPlayerId or allowed == true) or defaults.adminblocker == "Moderate" and allowed == executorPlayerId or access_table[getaccess(executorPlayerId)] == -1
  3180. end
  3181.  
  3182. local function Say(message)
  3183.         for playerId = 0,15 do
  3184.                 if getplayer(playerId) then
  3185.                         sendconsoletext(playerId, message)
  3186.                 end
  3187.         end
  3188. end
  3189.  
  3190. local function capitalizeWords(str, amount)
  3191.         return gsub(str, '(%a)(%a+)', function(cap, rest) return ('%1'):upper() .. '%2' end)
  3192. end
  3193.  
  3194. local function trim(str)
  3195.         return gsub(str, "^%s*(.-)%s*$", "%1")
  3196. end
  3197.  
  3198. local function tokenizecmdstring(str, tblToReuse)
  3199.         local strParts = --[[type(tblToReuse) == "table" and tblToReuse:deleteEntries() and tblToReuse or--]] {}--TM.New()
  3200.  
  3201.         --remove spaces at beginning and end
  3202.         str = trim(str) .. " "
  3203.  
  3204.         -- return if no delims found
  3205.         if not find(str, '[%s"]') then strParts[1] = str return strParts end
  3206.  
  3207.         local strPart, newPos, pos
  3208.         repeat
  3209.                 strPart,newPos = match(str, '^"(.-)"%s+()', pos)
  3210.                 if not strPart then
  3211.                         strPart,newPos = match(str, '([^%s]+)%s+()', pos)
  3212.                 elseif find(strPart, " ") then
  3213.                         strPart = '"' .. strPart .. '"' -- keep quotes around em
  3214.                 end
  3215.                 pos = newPos
  3216.                 strParts[#strParts+1] = strPart
  3217.         until not strPart
  3218.  
  3219.         ::returnSplits::
  3220.  
  3221.         return strParts
  3222. end
  3223.  
  3224. local function tokenizestring(str, ...)
  3225.  
  3226.         local tblToReuse = select(-1, ...)
  3227.         tblToReuse = not tblToReuse and error("bad argument #2 to 'tokenizestring' (delimeter string expected, got nil)") or type(tblToReuse) == "table" and TM.deleteEntries(tblToReuse) and tblToReuse or nil
  3228.  
  3229.         local subs = type(tblToReuse) == "table" and tblToReuse or {}--TM.New()
  3230.         if (...) == "" then
  3231.                 for i = 1,#str do
  3232.                         subs[#subs+1] = sub(str, i, i)
  3233.                 end
  3234.                 return subs
  3235.         end
  3236.  
  3237.         local strPart = ""
  3238.         local char, iter, delim
  3239.         for i = 1,#str do
  3240.                 char = sub(str, i, i)
  3241.                 iter = 1
  3242.                 delim = select(iter, ...)
  3243.                 repeat
  3244.                         if delim == char then
  3245.                                 strPart = sub(strPart, 1, -1)
  3246.                                 if strPart ~= "" then
  3247.                                         subs[#subs+1] = strPart
  3248.                                         strPart = ""
  3249.                                         goto continue
  3250.                                 end
  3251.                         end
  3252.                         iter = iter + 1
  3253.                         delim = select(iter, ...)
  3254.                 until not delim or delim == select(-1, ...)
  3255.                 strPart = strPart .. char
  3256.                 ::continue::
  3257.         end
  3258.  
  3259.         if strPart ~= "" then
  3260.                 subs[#subs+1] = strPart
  3261.         end
  3262.  
  3263.         return subs
  3264. end
  3265.  
  3266. -- This function is responsible for creating a command.
  3267. -- I am using table functions in this manner because it is easier to parse commands in groups, it saves me from copying and pasting already existing code.
  3268. -- It also allows me to check to see if the command was coded correctly (before LUA sends an unhelpful error message)
  3269. -- This also allows me to use command aliases, merge help info. It basically keeps all information about a command in one place.
  3270. -- To an average joe reading this script, it may be harder to use this method if you aren't familiar with table structures, but it's better in the end because it is more organized.
  3271. -- Arguements are the name of the command, the commandInfo (as a table, containing aliases, help, and boolean information like scrimMode usage, and each function called for different command arguments (for example, /enter (player) (player2) (seat) vs /enter (vehicleName))
  3272. function Commands.Create(cmdName, commandInfo, ...)
  3273.  
  3274.         -- Display errors if a command was created incorrectly.
  3275.         if not commandInfo.help then error("NO HELP INFO IN COMMAND CREATION: " .. cmdName) end
  3276.         local thisCmdFunc
  3277.         for i = 1,select("#", ...) do
  3278.                 thisCmdFunc = select(i, ...)
  3279.                 assert(thisCmdFunc.arguments, "NO ARGUMENTS IN COMMAND CREATION: " .. cmdName)
  3280.                 assert(thisCmdFunc.arguments.minArgs, "NO MINIMUM ARGUMENTS IN COMMAND CREATION: " .. cmdName)
  3281.                 assert(thisCmdFunc.arguments.maxArgs, "NO MAXIMUM ARGUMENTS IN COMMAND CREATION: " .. cmdName)
  3282.                 assert(type(thisCmdFunc.func) == "function", "BAD FUNCTION IN COMMAND CREATION: " .. type(thisCmdFunc.func) .. ": " .. cmdName)
  3283.         end
  3284.  
  3285.         --stupid tabs...
  3286.         commandInfo.help = gsub(commandInfo.help, "\t", "")
  3287.  
  3288.         -- Create this command with properties from the Commands class.
  3289.         local newCommand = {name = cmdName, info = commandInfo, ...}
  3290.         setmetatable(newCommand, Commands)
  3291.  
  3292.         -- set command aliases
  3293.         local aliases = commandInfo.aliases
  3294.         if aliases then
  3295.                 local commandAliases = Commands.commandAliases
  3296.                 local alias_name
  3297.                 for i = 1,#aliases do alias_name = aliases[i]
  3298.                         commandAliases[alias_name] = cmdName
  3299.                         Commands[alias_name] = newCommand
  3300.                 end
  3301.         end
  3302.  
  3303.         -- Add this command to the Command list.
  3304.         Commands[cmdName] = newCommand
  3305.         Commands[#Commands+1] = cmdName
  3306. end
  3307.  
  3308. -- This function is called whenever a command is attempting to execute.
  3309. -- Execution function is responsible for determining which overloaded command function to execute
  3310. -- In the case of the user using incorrect arguments, this function will parse an error message to respond with.
  3311. function Commands.Execute(thisCommand, arguments, command, executorPlayerId)
  3312.         local parsedArguments, newErr
  3313.         local err = ""
  3314.         -- Loop through all overloaded functions for this command.
  3315.         for i = 1,#thisCommand do
  3316.                 -- Validate and parse all arguments
  3317.                 parsedArguments, newErr = thisCommand:validateArguments(arguments, thisCommand[i].arguments, command, executorPlayerId)
  3318.                 -- have they passed the correct arguments for this overloaded command?
  3319.                 if parsedArguments == true then
  3320.                         sendresponse(newErr, executorPlayerId)
  3321.                         return
  3322.                 elseif newErr == "" then
  3323.                         -- call the command, and stop looping.
  3324.                         thisCommand[i].func(executorPlayerId, thisCommand:unpackArguments(parsedArguments))
  3325.                         return
  3326.                 else
  3327.                         err = err .. "\n" .. newErr
  3328.                 end
  3329.         end
  3330.         sendresponse(err .. gsub(thisCommand.info.help, ".-Syntax: %%s(.-)", "\nInvalid Syntax: " .. command .. "%1"), executorPlayerId)
  3331. end
  3332.  
  3333. -- Needed because I need to pass a specific amount of arguments to command functions.
  3334. function Commands:unpackArguments(myTable)
  3335.         for i = 1,#myTable do
  3336.                 if myTable[i] == nil then
  3337.                         myTable[i] = false
  3338.                 end
  3339.         end
  3340.         return table.unpack(myTable, 1, #myTable)
  3341. end
  3342.  
  3343. -- Validates and parses an argument table
  3344. -- Returns command arguments into lua arguments the Execute function can use.
  3345. -- This function alone is one of the main reasons I converted my if-elseif commands into a metatable. It saved me sooo much duplicate lines of code in the individual commands.
  3346. function Commands:validateArguments(arguments, argumentTypes, command, executorPlayerId)
  3347.         local err = ""
  3348.         local minArgs, maxArgs = argumentTypes.minArgs, argumentTypes.maxArgs
  3349.  
  3350.         -- Make sure they are actually passing a second argument.
  3351.         if not arguments[1] and minArgs > 0 then
  3352.                 return true, gsub(self.info.help, "^.-Syntax:.-%%s(.-)$", "\nCorrect Syntax: " .. command .. "%1")
  3353.         end
  3354.  
  3355.         local argument, argType, length
  3356.         local parsedArguments = {}--TM.New()
  3357.  
  3358.         for i = 1,#argumentTypes do argument = arguments[i]
  3359.                 argType = argumentTypes[i]
  3360.                 if not argument then
  3361.                         if #parsedArguments < minArgs then
  3362.                                 err = err .. "\nToo few arguments passed to this command! Expected " .. minArgs  .. " - " .. maxArgs .. " got " .. #parsedArguments
  3363.                                 goto ReturnArgs
  3364.                         end
  3365.                 else
  3366.                         length = #argument
  3367.                 end
  3368.                 if argType == "Boolean" then
  3369.                         if argument == "true" or argument == "1" then
  3370.                                 argument = true
  3371.                         elseif argument == "false" or argument == "0" then
  3372.                                 argument = false
  3373.                         elseif arguments[i] then
  3374.                                 err = err .. "\n'" .. arguments[i] .. "' is not a valid Boolean!\nBooleans must be: true or 1 to Enable/Turn On, false or 0 to Disable/Turn Off"
  3375.                         end
  3376.                 elseif argType == "Percent" then
  3377.                         argument = tonumber(argument)
  3378.                         argument = argument and (argument <= 1 and argument >= 0 and argument * 100 or argument <= 100 and argument)
  3379.                         err = not argument and "Invalid Percentage. Percent arguments must be between 0 and 100." or err
  3380.                 elseif argType == "Player" then
  3381.                         local players = getvalidplayers(argument, executorPlayerId)
  3382.                         if players then
  3383.                                 argument = players
  3384.                         elseif argument == nil and #arguments >= minArgs and executorPlayerId then
  3385.                                 argument = {executorPlayerId}
  3386.                         elseif not argument then
  3387.                                 err = err .. "\nYou did not specify a Player!"
  3388.                         else
  3389.                                 err = err .. "\n'" .. arguments[i] .. "' is not a valid Player!"
  3390.                         end
  3391.                 elseif argType == "Single Player" then
  3392.                         local players = getvalidplayers(argument, executorPlayerId)
  3393.                         if players then
  3394.                                 argument = players[1]
  3395.                                 err = players[2] and err .. "\n'" .. arguments[i] .. "' cannot reference more than one player in this command!" or err
  3396.                        
  3397.                         -- if player wasn't specified, use the executing player.
  3398.                         elseif argument == nil and #arguments >= minArgs and executorPlayerId then
  3399.                                 argument = executorPlayerId
  3400.                         elseif not argument then
  3401.                                 err = err .. "\nYou did not specify a player!"
  3402.                         else
  3403.                                 err = err .. "\n'" .. arguments[i] .. "' is not a valid player!"
  3404.                         end
  3405.                 elseif argType == "Hash" then
  3406.                         argument = match(argument, "%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x+")
  3407.                         err = not argument and arguments[i] and err .."\n'" .. arguments[i] .. "' is not a valid Hash!" or err
  3408.                 elseif argType == "IP Address" then
  3409.                         argument = validate_ipv4(argument)
  3410.                         err = not argument and arguments[i] and err .."\n'" .. arguments[i] .. "' is not a valid IP Address!" or err
  3411.                 elseif argType == "Team" then
  3412.                         argument = team_play and ("Red" and 0 or "Blue" and 1) or tonumber(argument)
  3413.                         argument = argument and argument >= 0 and argument <= 15 and argument
  3414.                         err = not argument and "The '" .. arguments[i] .. "' team does not exist!" or err
  3415.                 elseif argType == "Time And Reason" then
  3416.                         argument, parsedArguments[i+1] = getTimeAndReason(concat(arguments, " ", i), self.usesBanCounts and (PlayerClass[parsedArguments[i-1]].bancount or 0) or nil)
  3417.                 elseif argType == "Time" then
  3418.                         local temp
  3419.                         argument, temp = getTimeAndReason(concat(arguments, " ", i), self.usesBanCounts and (PlayerClass[parsedArguments[i-1]].bancount or 0) or nil)
  3420.                         err = temp ~= "None Given" and err .. "\n'" ..  concat(arguments, " ", i) .. "' is an Incorrect Time!\nCorrect Examples: 5d 2h 3m 4s" or err
  3421.                 elseif argType == "Time String" then
  3422.                         local timeErr = not argument and "'" .. argument .. "' is an Incorrect Time!\nCorrect Examples: 5d 2h 3m 4s"
  3423.                         if timeErr then goto TimeStringEnd end
  3424.                         for j = i+1,#arguments do
  3425.                                 timeErr = not arguments[j] and "'" .. arguments[j] .. "' is an Incorrect Time!\nCorrect Examples: 5d 2h 3m 4s"
  3426.                                 if timeErr then break end
  3427.                                 argument = argument .. " " .. arguments[j]
  3428.                         end
  3429.                         ::TimeStringEnd::
  3430.                         err = timeErr and err .. "\n" .. timeErr or err
  3431.                 elseif argType == "Remaining Arguments" then
  3432.                         --hprintf(concat(arguments, " ", i))
  3433.                         argument = concat(arguments, " ", i)
  3434.                 elseif argType == "Access Level" then
  3435.                         argument = tonumber(argument)
  3436.                         argument = argument and access_table[argument] and argument
  3437.                         err = not argument and arguments[i] and err .."\n'" .. arguments[i] .. "' is not a valid Access Level.\nUse 'sv_access_levels' for available levels!" or err
  3438.                 elseif argType == "Whole Number" then
  3439.                         argument = tonumber(argument)
  3440.                         argument = argument and argument >= 0 and argument
  3441.                         err = not argument and arguments[i] and err .."\n'" .. arguments[i] .. "' is not a Whole (Non-Negative) Number!" or err
  3442.                 elseif argType == "Positive Number" then
  3443.                         argument = tonumber(argument)
  3444.                         err = not argument and arguments[i] and err .."\n'" .. arguments[i] .. "' is not a Positive Number!" or err
  3445.                 elseif argType == "Number" then
  3446.                         argument = tonumber(argument)
  3447.                         err = not argument and arguments[i] and err .."\n'" .. arguments[i] .. "' is not a Number!" or err
  3448.                 elseif argType == "Password" then
  3449.                         argument = length and length >= 4 and length <= 8 and argument
  3450.                         err = not argument and arguments[i] and err .."\n'" .. arguments[i] .. "' is not a Password between 4 and 8 characters!" or err
  3451.                 elseif argType == "Map" then
  3452.                         argument = valid_maps[argument] and argument
  3453.                         err = not argument and arguments[i] and err .."\n'" .. arguments[i] .. "' is not a valid Map!" or err
  3454.                 elseif argType == "Object" then
  3455.                         argument = objects[argument]
  3456.                         err = not argument and arguments[i] and err .."\n'" .. arguments[i] .. "' is not a valid Object!" or err
  3457.                 elseif argType == "Gametype" then
  3458.                         argument = valid_gametypes[argument] and argument
  3459.                         err = not argument and arguments[i] and err .."\n'" .. arguments[i] .. "' is not a valid GameType!" or err
  3460.                 elseif argType == "String" then
  3461.                         err = not argument and arguments[i] and err .."\n'" .. arguments[i] .. "' is not a valid argument for this command!" or err
  3462.                 elseif argType == "AdminBlocker Type" then
  3463.                         argument = tonumber(argument) or argument
  3464.                         argument = (argument == 0 or argument == "Unsafe") and 0 or (argument == 1 or argument == "Lenient") and 1 or (argument == 2 or argument == "Moderate") and 2 or (argument == 3 or argument == "Strict") and 3 or argument == false and 0
  3465.                         err = not argument and arguments[i] and err .."\n'" .. arguments[i] .. "' is not a valid AdminBlocker Type!" or err
  3466.                 elseif argType == "Remote Banlist Mode" then
  3467.                         argument = tonumber(argument) or argument
  3468.                         argument = (argument == 0 or argument == "read-only") and 0 or (argument == 1 or argument == "one-way sync") and 1 or (argument == 2 or argument == "remote-only") and 2 or (argument == 3 or argument == "sync") and 3
  3469.                 elseif argType == "Datatype" then
  3470.                         argument = (argument == "bit8" or argument == "bit16" or argument == "bit32" or argument == "char" or argument == "byte" or argument == "short" or argument == "word" or argument == "int" or argument == "dword" or argument == "float" or argument == "string" or argument == "widestring") and argument
  3471.                         err = not argument and arguments[i] and err .."\n'" .. arguments[i] .. "' is not a valid DataType!" or err
  3472.                 elseif argType == "Struct" then
  3473.                         argument = (argument == "player" or argument == "object" or argument == "weapon" or argument == "vehicle") and argument
  3474.                         err = not argument and arguments[i] and err .."\n'" .. arguments[i] .. "' is not a valid Struct!" or err
  3475.                 elseif argType == "Bit" then
  3476.                         if argument == "true" or argument == "1" then
  3477.                                 argument = 1
  3478.                         elseif argument == "false" or argument == "0" then
  3479.                                 argument = 0
  3480.                         else
  3481.                                 err = err .. "\n'" .. arguments[i] .. "' is not a valid Bit!\nBits must be one of the following: true or 1, false or 0"
  3482.                         end
  3483.                 end
  3484.                 parsedArguments[i] = argument
  3485.         end
  3486.  
  3487.         if #parsedArguments > maxArgs and #arguments > maxArgs then
  3488.                 err = err .. "\nToo many Arguments passed to this command! Expected " .. minArgs  .. " - " .. maxArgs .. " got " .. #parsedArguments
  3489.         end
  3490.  
  3491.         ::ReturnArgs::
  3492.         return parsedArguments, err
  3493. end
  3494.  
  3495. function Commands:AliasToCommand(cmd)
  3496.         return self.realname
  3497. end
  3498.  
  3499. local function getOperatingSystem()
  3500.         local slash_ident = package.config:sub(1,1)
  3501.         if slash_ident == "\\" then
  3502.                 return "Windows"
  3503.         elseif slash_ident == "/" then
  3504.                 return "Unix"
  3505.         end
  3506. end
  3507.  
  3508. -- I don't remember why I didn't want to use LFS, maybe I was worried it wouldn't work with SAPP.
  3509. local function lookupFiles(dir)
  3510.         local files = {}
  3511.        
  3512.         local OS = getOperatingSystem()
  3513.         local p
  3514.         if OS == "Windows" then
  3515.                 p = io.popen("dir " .. dir .. " /B /O:gn")
  3516.         elseif OS == "Unix" then
  3517.                 p = io.popen('find "' .. dir .. '" -type f')
  3518.         end
  3519.         for file in p:lines() do
  3520.                 insert(files, file)
  3521.         end
  3522. end
  3523.  
  3524. -- Script Validation
  3525. function Commands:validscripts(...)
  3526.        
  3527.         local dir = getprofilepath() .. "\\scripts\\"
  3528.         local scripts = {}
  3529.        
  3530.         --LuaFileSystem Method.
  3531.         --[[
  3532.         require "lfs"
  3533.         for f in lfs.dir(dir) do
  3534.                 if string.find(f, ".lua") then
  3535.                         local file = io.open(dir .. f, "r")
  3536.                         local str = file:read("*all")
  3537.                         if string.find(str, "function GetRequiredVersion()") then
  3538.                                 if f ~= "gametypes.lua" then
  3539.                                         local script = string.gsub(f, ".lua", "")
  3540.                                         if not table.find(scripts, script) then
  3541.                                                 table.insert(scripts, script)
  3542.                                         end
  3543.                                 end
  3544.                         end
  3545.                 end
  3546.         end--]]
  3547.         local bool
  3548.         local files = lookupFiles(dir)
  3549.         local scriptname = ""
  3550.         for i = 1,select("#", ...) do
  3551.                 bool = false
  3552.                 for i = 1,files do
  3553.                         if files[i] == select(1, ...) then
  3554.                                 bool = true
  3555.                                 break
  3556.                         end
  3557.                 end
  3558.                 if not bool then
  3559.                         break
  3560.                 end
  3561.         end
  3562. end
  3563.  
  3564. Commands.Create(
  3565.         "rconadd",
  3566.         {
  3567.                 aliases = {"addrcon"},
  3568.                 help = [[-- Add Rcon Password
  3569.                 -- Syntax: %s [Password] {Level}
  3570.                 -- Adds a global Rcon password to the list.
  3571.                 -- These RCon passwords are different from the normal sv_rcon_password
  3572.                 -- The difference is non-admins can use these, while sv_rcon_password only admins can use]]
  3573.         },
  3574.         {
  3575.                 arguments = {"Password", "Access Level", minArgs = 1, maxArgs = 2},
  3576.                 func = function(executorPlayerId, password, level)
  3577.  
  3578.                         if rcon_passwords[password] then
  3579.                                 sendresponse(password .. " is already a rcon password", executorPlayerId)
  3580.                                 return
  3581.                         end
  3582.  
  3583.                         rcon_passwords[password] = level or 0
  3584.                         sendresponse(password .. " has been added as a rcon password", executorPlayerId)
  3585.                 end
  3586.         }
  3587. )
  3588.  
  3589. Commands.Create(
  3590.         "adminadd",
  3591.         {
  3592.                 aliases = {"a", "addadmin"},
  3593.                 help = [[-- Admin Add
  3594.                 -- Syntax: %s [Player or Hash or IP Address] [Nickname] {Level} {Admin Type 'Hash' or 'IP'}
  3595.                 -- Adds a person to the admin list.
  3596.                 -- This command can take a Hash, IP, or Player as an argument.
  3597.                 -- If you do not pass a level, they will automatically be added as a level 0 admin.
  3598.                 -- The default for Admin Type is 'hash'. This argument is also case insensitive.]]
  3599.         },
  3600.         {
  3601.                 arguments = {"Single Player", "String", "Access Level", "String", minArgs = 2, maxArgs = 4},
  3602.                 func = function(executorPlayerId, playerId, nickname, level, admin_type)
  3603.  
  3604.                         admin_type = admin_type and lower(admin_type) or "hash"
  3605.                         if admin_type ~= "hash" and admin_type ~= "ip" then
  3606.                                 sendresponse("'" .. admin_type .. "' is not a valid Admin Type!", executorPlayerId)
  3607.                                 return
  3608.                         end
  3609.  
  3610.                         if not match(nickname, "[%l%u]+") then
  3611.                                 sendresponse("Admin nicknames must only contain letters.", executorPlayerId)
  3612.                                 return
  3613.                         end
  3614.  
  3615.                         if PlayerClass[playerId].admin_entry then
  3616.                                 sendresponse(getname(playerId) .. " is already a " .. admin_type .. " admin", executorPlayerId)
  3617.                                 return
  3618.                         end
  3619.  
  3620.                         -- Create a new admin entry
  3621.                         local newAdmin = {}--TM.New()
  3622.                         newAdmin.name = nickname
  3623.                         newAdmin.level = level or 0
  3624.                         newAdmin.type = admin_type
  3625.  
  3626.                         -- Add the admin entry to the admin table.
  3627.                         admin_table[admin_type == "hash" and gethash(playerId) or (getip(playerId) .. "/24")] = newAdmin
  3628.                         PlayerClass[playerId].admin_entry = newAdmin
  3629.                         updateAdminFiles()
  3630.                         sendresponse(getname(playerId) .. " is now a " .. admin_type .. " admin!", executorPlayerId)
  3631.                 end
  3632.         },
  3633.         {
  3634.                 arguments = {"Hash", "String", "Access Level", minArgs = 2, maxArgs = 3},
  3635.                 func = function(executorPlayerId, hash, nickname, level)
  3636.  
  3637.                         if not match(nickname, "%l+") then
  3638.                                 sendresponse("Admin nicknames must only contain letters.", executorPlayerId)
  3639.                                 return
  3640.                         end
  3641.  
  3642.                         for i = 1,#sharedhashes do
  3643.                                 if hash == sharedhashes[i] then
  3644.                                         sendresponse("This is a shared hash, and therefore cannot become an admin.", executorPlayerId)
  3645.                                         return
  3646.                                 end
  3647.                         end
  3648.  
  3649.                         if admin_table[hash] then
  3650.                                 sendresponse("That Hash is already an admin.", executorPlayerId)
  3651.                                 return
  3652.                         end
  3653.  
  3654.                         -- Create a new admin entry
  3655.                         local newAdmin = {}--TM.New()
  3656.                         newAdmin.name = nickname
  3657.                         newAdmin.level = level or 0
  3658.                         newAdmin.type = "hash"
  3659.  
  3660.                         -- Add the admin entry to the admin table.
  3661.                         admin_table[hash] = newAdmin
  3662.                         for playerId = 0,15 do
  3663.                                 if gethash(playerId) == hash then
  3664.                                         PlayerClass[playerId].admin_entry = newAdmin
  3665.                                 end
  3666.                         end
  3667.                         updateAdminFiles()
  3668.                         sendresponse("That hash has been successfully added to the admin list.", executorPlayerId)
  3669.                 end
  3670.         },
  3671.         {
  3672.                 arguments = {"IP Address", "String", "Access Level", minArgs = 2, maxArgs = 3},
  3673.                 func = function(executorPlayerId, ip_address, nickname, level)
  3674.  
  3675.                         if not match(nickname, "%l+") then
  3676.                                 sendresponse("Admin nicknames must only contain letters.", executorPlayerId)
  3677.                                 return
  3678.                         end
  3679.  
  3680.                         for index,thisAdmin in next,admin_table do
  3681.                                 if thisAdmin.type == "ip" and netMatch(index, ip_address) then
  3682.                                         sendresponse("That IP Address is already an admin.", executorPlayerId)
  3683.                                         return
  3684.                                 end
  3685.                         end
  3686.  
  3687.                         -- Create a new admin entry
  3688.                         local newAdmin = {}--TM.New()
  3689.                         newAdmin.name = nickname
  3690.                         newAdmin.level = level or 0
  3691.                         newAdmin.type = "ip"
  3692.  
  3693.                         -- Add the admin entry to the admin table.
  3694.                         admin_table[ip_address] = newAdmin
  3695.                         for playerId = 0,15 do
  3696.                                 if getplayer(playerId) then
  3697.                                         if netMatch(ip_address, getip(playerId)) then
  3698.                                                 PlayerClass[playerId].admin_entry = newAdmin
  3699.                                         end
  3700.                                 end
  3701.                         end
  3702.                         updateAdminFiles()
  3703.  
  3704.                         sendresponse("That IP Address has been successfully added to the admin list.", executorPlayerId)
  3705.                 end
  3706.         }
  3707. )
  3708.  
  3709. Commands.Create(
  3710.         "admindel",
  3711.         {
  3712.                 aliases = {"deladmin", "revoke", "a del", "adminrevoke", "revokeadmin"},
  3713.                 help = [[-- Admin Delete
  3714.                 -- Syntax: %s [Player or Admin Nickname]
  3715.                 -- Removes an admin from the admin list by nickname or playerIndex.
  3716.                 -- Use 'sv_admin_list' to get a list of admins for their nickname.
  3717.                 -- You can also pass a player to this command.]]
  3718.         },
  3719.         {
  3720.                 arguments = {"Player", minArgs = 1, maxArgs = 1},
  3721.                 func = function(executorPlayerId, players)
  3722.                         local playerId, ip
  3723.                         for i = 1,#players do playerId = players[i]
  3724.  
  3725.                                 if not PlayerClass[playerId].admin_entry then
  3726.                                         cmdreply[cmdreply()] = getname(playerId) .. " was never an admin"
  3727.                                         goto continue
  3728.                                 end
  3729.  
  3730.                                 admin_table[gethash(playerId)] = nil
  3731.                                 ip = getip(playerId)
  3732.  
  3733.                                 for index,thisAdmin in next,admin_table do
  3734.                                         if thisAdmin.type == "ip" and netMatch(index, ip) then
  3735.                                                 admin_table[index] = nil
  3736.                                         end
  3737.                                 end
  3738.  
  3739.                                 PlayerClass[playerId].admin_entry = false
  3740.  
  3741.                                 updateAdminFiles()
  3742.                                 cmdreply[cmdreply()] = getname(playerId) .. " is no longer an admin"
  3743.  
  3744.                                 ::continue::
  3745.                         end
  3746.  
  3747.                         sendresponse(cmdreply, playerId)
  3748.                 end
  3749.         },
  3750.         {
  3751.                 arguments = {"String", minArgs = 1, maxArgs = 1},
  3752.                 func = function(executorPlayerId, nickname)
  3753.  
  3754.                         if not next(admin_table) then
  3755.                                 sendresponse("There are no admins on this server.", executorPlayerId)
  3756.                                 return
  3757.                         end
  3758.  
  3759.                         for k,thisAdmin in next,admin_table do
  3760.                                 if thisAdmin.name == nickname then
  3761.                                         admin_table[k] = nil
  3762.                                         sendresponse(nickname .. " is no longer an admin", executorPlayerId)
  3763.                                         updateAdminFiles()
  3764.  
  3765.                                         local player
  3766.                                         for playerId = 0,15 do player = PlayerClass[playerId]
  3767.                                                 if getplayer(playerId) and player and player.admin_entry == thisAdmin then
  3768.                                                         player.admin_entry = false
  3769.                                                 end
  3770.                                         end
  3771.  
  3772.                                         return
  3773.                                 end
  3774.                         end
  3775.  
  3776.                         sendresponse("There are no admins with a nickname of '" .. nickname .. "'", executorPlayerId)
  3777.                 end
  3778.         }
  3779. )
  3780.  
  3781. Commands.Create(
  3782.         "setafk",
  3783.         {
  3784.                 aliases = {"afk"},
  3785.                 help = [[-- Set AFK Player
  3786.                 -- Syntax: %s [Player]
  3787.                 -- Sets the specified player to the AFK status.]]
  3788.         },
  3789.         {
  3790.                 arguments = {"Player", minArgs = 1, maxArgs = 1},
  3791.                 func = function(executorPlayerId, players)
  3792.  
  3793.                         local playerId
  3794.                         for i = 1,#players do playerId = players[i]
  3795.                                 PlayerClass[playerId].afk = true
  3796.                                 cmdreply[cmdreply()] = getname(playerId) .. " is now afk"
  3797.                         end
  3798.  
  3799.                         sendresponse(cmdreply, executorPlayerId)
  3800.                 end
  3801.         }
  3802. )
  3803.  
  3804. local function check_in_circle(x,y, X, Y, R)
  3805.         return (X - x)^2 + (Y - y)^2 <= R
  3806. end
  3807.  
  3808. -- Not finished
  3809. Commands.Create(
  3810.         "bubble",
  3811.         {
  3812.                 help = [[-- Bubble
  3813.                 -- Syntax: %s [Player] {Radius}
  3814.                 -- Puts a bubble around the player, so people cannot enter it.]]
  3815.         },
  3816.         {
  3817.                 arguments = {"Player", minArgs = 1, maxArgs = 2},
  3818.                 func = function(executorPlayerId, players, radius)
  3819.                         radius = radius or 5
  3820.                         local playerId
  3821.                         for i = 1,#players do playerId = players[i]
  3822.                                 PlayerClass[playerId].bubble = true
  3823.                                 cmdreply[cmdreply()] = getname(playerId) .. " is now in a bubble with radius of " .. radius
  3824.                         end
  3825.                        
  3826.                         sendresponse(cmdreply, executorPlayerId)
  3827.                 end
  3828.         }
  3829. )
  3830.  
  3831. Commands.Create(
  3832.         "adminlist",
  3833.         {
  3834.                 aliases = {"admins", "listadmins", "alist"},
  3835.                 help = [[-- Admin List
  3836.                 -- Syntax: %s {Type}
  3837.                 -- Shows a list of all Admins
  3838.                 -- Type can be either 'IP', 'Hash', or 'Temp' for filtering.]]
  3839.         },
  3840.         {
  3841.                 arguments = {"String", minArgs = 0, maxArgs = 1},
  3842.                 func = function(executorPlayerId, admin_type)
  3843.                         cmdreply.header = "[Name | Level | Admin Type]"
  3844.                         cmdreply.align = true
  3845.                         cmdreply.delim = "|"
  3846.                         local hash_admin, ip_admin
  3847.                         for index,thisAdmin in next,admin_table do
  3848.                                 cmdreply[cmdreply()] = thisAdmin.name .. " | " .. thisAdmin.level .. " | " .. thisAdmin.type .. " Admin"
  3849.                         end
  3850.  
  3851.                         sendresponse(cmdreply, executorPlayerId)
  3852.                 end
  3853.         }
  3854. )
  3855.  
  3856. Commands.Create(
  3857.         "aliasold",
  3858.         {
  3859.                 aliases = {"oldalias"},
  3860.                 help = [[-- Alias
  3861.                 -- Syntax: %s [player]
  3862.                 -- Shows all names used with the hash]]
  3863.         },
  3864.         {
  3865.                 arguments = {"Player", minArgs = 1, maxArgs = 1},
  3866.                 func = function(executorPlayerId, players)
  3867.                         local playerId
  3868.                         for i = 1,#players do playerId = players[i]
  3869.                                 halo_svcmdplayer("sv_alias_hash " .. resolveplayer(playerId), executorPlayerId)
  3870.                         end
  3871.                 end
  3872.         }
  3873. )
  3874.  
  3875. -- whatever local function that calls this one will set the reference of 'boolCheck' to whatever newBool is, or will do nothing if newBool is nil.
  3876. local function ReturnScriptBooleanCheck(boolName, boolCheck, argBool)
  3877.         local are_is = sub(boolName, -1, -1) == "s" and "are" or "is" -- lol
  3878.         if argBool == nil then
  3879.                 return nil, boolName .. " is currently " .. (boolCheck == true and "On" or boolCheck == false and "Off" or tostring(boolCheck))
  3880.         end
  3881.  
  3882.         local response
  3883.  
  3884.         if argBool then
  3885.                 if not boolCheck then
  3886.                         response = boolName .. " " .. are_is .. " now on"
  3887.                 else
  3888.                         argBool = nil
  3889.                         response = "(*) " .. boolName .. " " .. are_is .. " already enabled"
  3890.                 end
  3891.         elseif not argBool then
  3892.                 if boolCheck then
  3893.                         response = boolName .. " " .. are_is .. " now off"
  3894.                 else
  3895.                         argBool = nil
  3896.                         response = "(*) " .. boolName .. " " .. are_is .. " already off"
  3897.                 end
  3898.         end
  3899.  
  3900.         return argBool, response
  3901. end
  3902.  
  3903. -- Scripted Game Boolean Commands
  3904.  
  3905. Commands.Create(
  3906.         "remotecontrol",
  3907.         {
  3908.                 help = [[-- Remote Control
  3909.                         -- Syntax: %s {Port or Boolean}
  3910.                         -- Starts or shuts down the remote control feature.
  3911.                         -- Use a telnet client like Putty to connect
  3912.                         -- This command REQUIRES using global rcons (sv_rcon_add). This may be changed later.]],
  3913.         },
  3914.         {
  3915.                 arguments = {"Positive Number", minArgs = 0, maxArgs = 1},
  3916.                 func = function(executorPlayerId, port)
  3917.                         if not socket then
  3918.                                 socket = require"socket"
  3919.                                 http = require"socket.http"
  3920.                                 http.TIMEOUT = 1
  3921.                         end
  3922.                         port = port <= 65535 and port > 800 and port or error("Ports must be between 65535 and 800. 2303 is recommended.")
  3923.                         if port then
  3924.                                 server = socket.tcp()
  3925.                                
  3926.                                 assert(server:bind("*", port))
  3927.                                 assert(server:listen(9001))
  3928.  
  3929.                                 server:settimeout(0)
  3930.                                 sendresponse("The Remote Control server has been opened on port " .. tostring(port))
  3931.                                 defaults.remotecontrol = registertimer(500, "remoteTimer")
  3932.                         else
  3933.                                 sendresponse("Remote Control is enabled and currently has " .. #clients .. " clients connected.", executorPlayerId)
  3934.                         end
  3935.                 end
  3936.         },
  3937.         {
  3938.                 arguments = {"Boolean", minArgs = 0, maxArgs = 1},
  3939.                 func = function(executorPlayerId, strBool)
  3940.                         local newBool = ReturnScriptBooleanCheck("Remote Control", defaults.remotecontrol, strBool)
  3941.                         if newBool then
  3942.                                
  3943.                                 if not socket then
  3944.                                         socket = require"socket"
  3945.                                         http = require"socket.http"
  3946.                                         http.TIMEOUT = 1
  3947.                                 end
  3948.                                
  3949.                                 server = socket.tcp()
  3950.                                
  3951.                                 assert(server:bind("*", 2303))
  3952.                                 assert(server:listen(9001))
  3953.  
  3954.                                 server:settimeout(0)
  3955.                                 sendresponse("The Remote Control server has been opened on port 2303", executorPlayerId)
  3956.                                 defaults.remotecontrol = registertimer(500, "remoteTimer")
  3957.                         elseif newBool == false then
  3958.                                 for i = 1,#clients do
  3959.                                         clients[i].socket:close()
  3960.                                 end
  3961.                                 server:close()
  3962.                                 server = nil
  3963.                         end
  3964.                 end
  3965.         }
  3966. )
  3967.  
  3968. Commands.Create(
  3969.         "anticaps",
  3970.         {
  3971.                 aliases = {"disablecaps", "nocaps"},
  3972.                 help = [[-- AntiCaps
  3973.                 -- Syntax: %s {Boolean}
  3974.                 -- Enables or Disables the use of caps in the server]]
  3975.         },
  3976.         {
  3977.                 arguments = {"Boolean", minArgs = 0, maxArgs = 1},
  3978.                 func = function(executorPlayerId, strBool)
  3979.                         local newBool, response = ReturnScriptBooleanCheck("Anticaps", defaults.anticaps, strBool)
  3980.  
  3981.                         if newBool ~= nil then
  3982.                                 defaults.anticaps = newBool
  3983.                         end
  3984.  
  3985.                         sendresponse(response, executorPlayerId)
  3986.  
  3987.                 end
  3988.         }
  3989. )
  3990.  
  3991. Commands.Create(
  3992.         "chatcommands",
  3993.         {
  3994.                 help = [[-- ChatCommands
  3995.                 -- Syntax: %s {Boolean}
  3996.                 -- Enable/Disable Chat Commands for Admins]]
  3997.         },
  3998.         {
  3999.                 arguments = {"Boolean", minArgs = 0, maxArgs = 1},
  4000.                 func = function(executorPlayerId, strBool)
  4001.                         local newBool, response = ReturnScriptBooleanCheck("Chat Commands", defaults.chatcommands, strBool)
  4002.  
  4003.                         if newBool ~= nil then
  4004.                                 defaults.chatcommands = newBool
  4005.                         end
  4006.  
  4007.                         sendresponse(response, executorPlayerId)
  4008.  
  4009.                 end
  4010.         }
  4011. )
  4012.  
  4013. Commands.Create(
  4014.         "chatids",
  4015.         {
  4016.                 aliases = {"chatid", "showplayernumbers"},
  4017.                 help = [[-- Show Player IDs In Chat
  4018.                 -- Syntax: %s {Boolean}
  4019.                 -- Enable/Disable the showing of Player IDs in the chat.]]
  4020.         },
  4021.         {
  4022.                 arguments = {"Boolean", minArgs = 0, maxArgs = 1},
  4023.                 func = function(executorPlayerId, strBool)
  4024.                         local newBool, response = ReturnScriptBooleanCheck("Chat IDs", defaults.chatids, strBool)
  4025.                         if newBool ~= nil then
  4026.                                 defaults.chatids = newBool
  4027.                         end
  4028.  
  4029.                         sendresponse(response, executorPlayerId)
  4030.  
  4031.                 end
  4032.         }
  4033. )
  4034.  
  4035. function invisCrouch(id, count)
  4036.         if not defaults.crouch_camo then return false end
  4037.         for playerId = 0,15 do
  4038.                 local m_playerObj = getplayerobject(playerId)
  4039.                 if m_playerObj and readbyte(m_playerObj + 0x2A0) == 3 then
  4040.                         applycamo(playerId, 1)
  4041.                 end
  4042.         end
  4043.         return true
  4044. end
  4045.  
  4046. Commands.Create(
  4047.         "crouchcamo",
  4048.         {
  4049.                 aliases = {"tigermode", "ninja", "ninjamode"},
  4050.                 help = [[--Enable or Disable Crouch Camo
  4051.                 -- Syntax: %s {Boolean}
  4052.                 -- Enables or Disables the use of crouch camo]],
  4053.         },
  4054.         {
  4055.                 arguments = {"Boolean",