Normalizing line endings

This commit is contained in:
Nayruden 2015-11-07 17:08:41 -05:00
parent 73abcc103d
commit 37771eb26b
35 changed files with 8886 additions and 8886 deletions

View File

@ -1,11 +1,11 @@
"AddonInfo"
{
"name" "ULX"
"version" "3.70"
"up_date" "00/00/00"
"author_name" "Team Ulysses"
"author_email" "teamulysses@ulyssesmod.net"
"author_url" "http://www.ulyssesmod.net/"
"info" "Advanced Admin Mod"
"override" "0"
}
"AddonInfo"
{
"name" "ULX"
"version" "3.70"
"up_date" "00/00/00"
"author_name" "Team Ulysses"
"author_email" "teamulysses@ulyssesmod.net"
"author_url" "http://www.ulyssesmod.net/"
"info" "Advanced Admin Mod"
"override" "0"
}

View File

@ -1,9 +1,9 @@
;ATTENTION! /ADDONS/ulx/data/ulx/<this file> is a DEFAULT file. DO NOT EDIT THE /ADDONS/ULX/DATA/ULX FOLDER FILE!!!
; Instead, edit the version in <garrysmod root>/data/ulx/ if it exists. If it doesn't, copy this file to <gmod root>/data/ulx/ to create it.
; You can add forced downloads here. Add as many as you want, one file or
; folder per line. You can also add these to your map- or game-specific files.
; You can add a folder to add all files inside that folder recursively.
; Any line starting with ';' is a comment and WILL NOT be processed!!!
; Examples:
;sound/cheeseman.mp3 <-- Adds the file 'cheeseman.mp3' under the sound folder
;ATTENTION! /ADDONS/ulx/data/ulx/<this file> is a DEFAULT file. DO NOT EDIT THE /ADDONS/ULX/DATA/ULX FOLDER FILE!!!
; Instead, edit the version in <garrysmod root>/data/ulx/ if it exists. If it doesn't, copy this file to <gmod root>/data/ulx/ to create it.
; You can add forced downloads here. Add as many as you want, one file or
; folder per line. You can also add these to your map- or game-specific files.
; You can add a folder to add all files inside that folder recursively.
; Any line starting with ';' is a comment and WILL NOT be processed!!!
; Examples:
;sound/cheeseman.mp3 <-- Adds the file 'cheeseman.mp3' under the sound folder
;sound/my_music <-- Adds all files within the my_music folder, inside the sound folder

View File

@ -1,5 +1,5 @@
if SERVER then
include( "ulx/init.lua" )
else
include( "ulx/cl_init.lua" )
end
if SERVER then
include( "ulx/init.lua" )
else
include( "ulx/cl_init.lua" )
end

View File

@ -1,120 +1,120 @@
--[[
Title: Base
Sets up some things for ulx.
]]
ulx.cvars = ulx.cvars or {} -- Used to decipher spaced cvars into actual implementation of underscored cvars (see below)
--[[
Function: convar
This is what will set up ULX's convars, it makes them under the command "ulx"
Parameters:
command - The console command. IE, "sv_kickminge".
value - The value to start off at.
help - *(Optional)* A help string for using the command.
access - *(Optional, defaults to ACCESS_ALL)* Restricted access.
]]
function ulx.convar( command, value, help, access )
help = help or ""
access = access or ULib.ACCESS_ALL
ULib.ucl.registerAccess( "ulx " .. command, access, help, "Cvar" )
-- table.insert( ulx.convarhelp[ currentCategory ], { cmd=command, access=access, help=help } ) -- TODO
local nospaceCommand = command:gsub( " ", "_" )
ulx.cvars[ command:lower() ] = { help=help, cvar=nospaceCommand, original=command }
local cvarName = "ulx_" .. nospaceCommand
local obj = ULib.replicatedWritableCvar( cvarName, cvarName, value, false, false, "ulx " .. command )
return obj
end
function ulx.addToHelpManually( category, cmd, string, access_tag )
ulx.cmdsByCategory[ category ] = ulx.cmdsByCategory[ category ] or {}
for i, existingCmd in pairs( ulx.cmdsByCategory[ category ]) do
if existingCmd.cmd == cmd and existingCmd.manual == true then
table.remove( ulx.cmdsByCategory[ category ], i)
break
end
end
table.insert( ulx.cmdsByCategory[ category ], { access_tag=access_tag, cmd=cmd, helpStr=string, manual=true } )
end
--------------------------------------
--Now for boring initilization stuff--
--------------------------------------
-- Setup the maps table
do
ulx.maps = {}
local maps = file.Find( "maps/*.bsp", "GAME" )
for _, map in ipairs( maps ) do
table.insert( ulx.maps, map:sub( 1, -5 ):lower() ) -- Take off the .bsp
end
table.sort( ulx.maps ) -- Make sure it's alphabetical
ulx.gamemodes = {}
local fromEngine = engine.GetGamemodes()
for i=1, #fromEngine do
table.insert( ulx.gamemodes, fromEngine[ i ].name:lower() )
end
table.sort( ulx.gamemodes ) -- Alphabetize
end
ulx.common_kick_reasons = ulx.common_kick_reasons or {}
function ulx.addKickReason( reason )
table.insert( ulx.common_kick_reasons, reason )
table.sort( ulx.common_kick_reasons )
end
local function sendAutocompletes( ply )
if ply:query( "ulx map" ) or ply:query( "ulx votemap2" ) then -- Only send if they have access to this.
ULib.clientRPC( ply, "ulx.populateClMaps", ulx.maps )
ULib.clientRPC( ply, "ulx.populateClGamemodes", ulx.gamemodes )
end
ULib.clientRPC( ply, "ulx.populateClVotemaps", ulx.votemaps )
ULib.clientRPC( ply, "ulx.populateKickReasons", ulx.common_kick_reasons )
end
hook.Add( ULib.HOOK_UCLAUTH, "sendAutoCompletes", sendAutocompletes )
hook.Add( "PlayerInitialSpawn", "sendAutoCompletes", sendAutocompletes )
-- This will load ULX client side
local function playerInit( ply )
local _, v, r = ulx.getVersion()
ULib.clientRPC( ply, "ulx.clInit", v, r )
end
hook.Add( "PlayerInitialSpawn", "ULXInitPlayer", playerInit )
-- Cvar saving
function cvarChanged( sv_cvar, cl_cvar, ply, old_value, new_value )
if not sv_cvar:find( "^ulx_" ) then return end
local command = sv_cvar:gsub( "^ulx_", "" ):lower() -- Strip it off for lookup below
if not ulx.cvars[ command ] then return end
sv_cvar = ulx.cvars[ command ].original -- Make sure we have intended casing
local path = "data/ulx/config.txt"
if not ULib.fileExists( path ) then
Msg( "[ULX ERROR] Config doesn't exist at " .. path .. "\n" )
return
end
sv_cvar = sv_cvar:gsub( "_", " " ) -- Convert back to space notation
if new_value:find( "[%s:']" ) then new_value = string.format( "%q", new_value ) end
local replacement = string.format( "%s %s ", sv_cvar, new_value:gsub( "%%", "%%%%" ) ) -- Because we're feeding it through gsub below, need to expand '%'s
local config = ULib.fileRead( path )
config, found = config:gsub( ULib.makePatternSafe( sv_cvar ):gsub( "%a", function( c ) return "[" .. c:lower() .. c:upper() .. "]" end ) .. "%s+[^;\r\n]*", replacement ) -- The gsub makes us case neutral
if found == 0 then -- Configuration option does not exist in config- append it
newline = config:match("\r?\n")
if not config:find("\r?\n$") then config = config .. newline end
config = config .. "ulx " .. replacement .. "; " .. ulx.cvars[ command ].help .. newline
end
ULib.fileWrite( path, config )
end
hook.Add( ulx.HOOK_ULXDONELOADING, "AddCvarHook", function() hook.Add( ULib.HOOK_REPCVARCHANGED, "ULXCheckCvar", cvarChanged ) end ) -- We're not interested in changing cvars till after load
--[[
Title: Base
Sets up some things for ulx.
]]
ulx.cvars = ulx.cvars or {} -- Used to decipher spaced cvars into actual implementation of underscored cvars (see below)
--[[
Function: convar
This is what will set up ULX's convars, it makes them under the command "ulx"
Parameters:
command - The console command. IE, "sv_kickminge".
value - The value to start off at.
help - *(Optional)* A help string for using the command.
access - *(Optional, defaults to ACCESS_ALL)* Restricted access.
]]
function ulx.convar( command, value, help, access )
help = help or ""
access = access or ULib.ACCESS_ALL
ULib.ucl.registerAccess( "ulx " .. command, access, help, "Cvar" )
-- table.insert( ulx.convarhelp[ currentCategory ], { cmd=command, access=access, help=help } ) -- TODO
local nospaceCommand = command:gsub( " ", "_" )
ulx.cvars[ command:lower() ] = { help=help, cvar=nospaceCommand, original=command }
local cvarName = "ulx_" .. nospaceCommand
local obj = ULib.replicatedWritableCvar( cvarName, cvarName, value, false, false, "ulx " .. command )
return obj
end
function ulx.addToHelpManually( category, cmd, string, access_tag )
ulx.cmdsByCategory[ category ] = ulx.cmdsByCategory[ category ] or {}
for i, existingCmd in pairs( ulx.cmdsByCategory[ category ]) do
if existingCmd.cmd == cmd and existingCmd.manual == true then
table.remove( ulx.cmdsByCategory[ category ], i)
break
end
end
table.insert( ulx.cmdsByCategory[ category ], { access_tag=access_tag, cmd=cmd, helpStr=string, manual=true } )
end
--------------------------------------
--Now for boring initilization stuff--
--------------------------------------
-- Setup the maps table
do
ulx.maps = {}
local maps = file.Find( "maps/*.bsp", "GAME" )
for _, map in ipairs( maps ) do
table.insert( ulx.maps, map:sub( 1, -5 ):lower() ) -- Take off the .bsp
end
table.sort( ulx.maps ) -- Make sure it's alphabetical
ulx.gamemodes = {}
local fromEngine = engine.GetGamemodes()
for i=1, #fromEngine do
table.insert( ulx.gamemodes, fromEngine[ i ].name:lower() )
end
table.sort( ulx.gamemodes ) -- Alphabetize
end
ulx.common_kick_reasons = ulx.common_kick_reasons or {}
function ulx.addKickReason( reason )
table.insert( ulx.common_kick_reasons, reason )
table.sort( ulx.common_kick_reasons )
end
local function sendAutocompletes( ply )
if ply:query( "ulx map" ) or ply:query( "ulx votemap2" ) then -- Only send if they have access to this.
ULib.clientRPC( ply, "ulx.populateClMaps", ulx.maps )
ULib.clientRPC( ply, "ulx.populateClGamemodes", ulx.gamemodes )
end
ULib.clientRPC( ply, "ulx.populateClVotemaps", ulx.votemaps )
ULib.clientRPC( ply, "ulx.populateKickReasons", ulx.common_kick_reasons )
end
hook.Add( ULib.HOOK_UCLAUTH, "sendAutoCompletes", sendAutocompletes )
hook.Add( "PlayerInitialSpawn", "sendAutoCompletes", sendAutocompletes )
-- This will load ULX client side
local function playerInit( ply )
local _, v, r = ulx.getVersion()
ULib.clientRPC( ply, "ulx.clInit", v, r )
end
hook.Add( "PlayerInitialSpawn", "ULXInitPlayer", playerInit )
-- Cvar saving
function cvarChanged( sv_cvar, cl_cvar, ply, old_value, new_value )
if not sv_cvar:find( "^ulx_" ) then return end
local command = sv_cvar:gsub( "^ulx_", "" ):lower() -- Strip it off for lookup below
if not ulx.cvars[ command ] then return end
sv_cvar = ulx.cvars[ command ].original -- Make sure we have intended casing
local path = "data/ulx/config.txt"
if not ULib.fileExists( path ) then
Msg( "[ULX ERROR] Config doesn't exist at " .. path .. "\n" )
return
end
sv_cvar = sv_cvar:gsub( "_", " " ) -- Convert back to space notation
if new_value:find( "[%s:']" ) then new_value = string.format( "%q", new_value ) end
local replacement = string.format( "%s %s ", sv_cvar, new_value:gsub( "%%", "%%%%" ) ) -- Because we're feeding it through gsub below, need to expand '%'s
local config = ULib.fileRead( path )
config, found = config:gsub( ULib.makePatternSafe( sv_cvar ):gsub( "%a", function( c ) return "[" .. c:lower() .. c:upper() .. "]" end ) .. "%s+[^;\r\n]*", replacement ) -- The gsub makes us case neutral
if found == 0 then -- Configuration option does not exist in config- append it
newline = config:match("\r?\n")
if not config:find("\r?\n$") then config = config .. newline end
config = config .. "ulx " .. replacement .. "; " .. ulx.cvars[ command ].help .. newline
end
ULib.fileWrite( path, config )
end
hook.Add( ulx.HOOK_ULXDONELOADING, "AddCvarHook", function() hook.Add( ULib.HOOK_REPCVARCHANGED, "ULXCheckCvar", cvarChanged ) end ) -- We're not interested in changing cvars till after load

View File

@ -1,28 +1,28 @@
if not ulx then
ulx = {}
include( "ulx/sh_defines.lua" )
include( "ulx/cl_lib.lua" )
include( "ulx/sh_base.lua" )
local sh_modules = file.Find( "ulx/modules/sh/*.lua", "LUA" )
local cl_modules = file.Find( "ulx/modules/cl/*.lua", "LUA" )
for _, file in ipairs( cl_modules ) do
Msg( "[ULX] Loading CLIENT module: " .. file .. "\n" )
include( "ulx/modules/cl/" .. file )
end
for _, file in ipairs( sh_modules ) do
Msg( "[ULX] Loading SHARED module: " .. file .. "\n" )
include( "ulx/modules/sh/" .. file )
end
end
function ulx.clInit( v, r )
-- Number conversion to ensure we're not getting an incredibly complex floating number
ulx.version = tonumber( string.format( "%.2f", v ) ) -- Yah, I know, we should have the version from shared anyways.... but doesn't make sense to send one and not the other.
ulx.revision = r
Msg( "ULX version " .. ulx.getVersion() .. " loaded.\n" )
end
usermessage.Hook( "ulx_initplayer", init )
if not ulx then
ulx = {}
include( "ulx/sh_defines.lua" )
include( "ulx/cl_lib.lua" )
include( "ulx/sh_base.lua" )
local sh_modules = file.Find( "ulx/modules/sh/*.lua", "LUA" )
local cl_modules = file.Find( "ulx/modules/cl/*.lua", "LUA" )
for _, file in ipairs( cl_modules ) do
Msg( "[ULX] Loading CLIENT module: " .. file .. "\n" )
include( "ulx/modules/cl/" .. file )
end
for _, file in ipairs( sh_modules ) do
Msg( "[ULX] Loading SHARED module: " .. file .. "\n" )
include( "ulx/modules/sh/" .. file )
end
end
function ulx.clInit( v, r )
-- Number conversion to ensure we're not getting an incredibly complex floating number
ulx.version = tonumber( string.format( "%.2f", v ) ) -- Yah, I know, we should have the version from shared anyways.... but doesn't make sense to send one and not the other.
ulx.revision = r
Msg( "ULX version " .. ulx.getVersion() .. " loaded.\n" )
end
usermessage.Hook( "ulx_initplayer", init )

View File

@ -1,127 +1,127 @@
ulx.common_kick_reasons = ulx.common_kick_reasons or {}
function ulx.populateKickReasons( reasons )
table.Empty( ulx.common_kick_reasons )
table.Merge( ulx.common_kick_reasons, reasons )
end
ulx.maps = ulx.maps or {}
function ulx.populateClMaps( maps )
table.Empty( ulx.maps )
table.Merge( ulx.maps, maps )
end
ulx.gamemodes = ulx.gamemodes or {}
function ulx.populateClGamemodes( gamemodes )
table.Empty( ulx.gamemodes )
table.Merge( ulx.gamemodes, gamemodes )
end
ulx.votemaps = ulx.votemaps or {}
function ulx.populateClVotemaps( votemaps )
table.Empty( ulx.votemaps )
table.Merge( ulx.votemaps, votemaps )
end
function ulx.soundComplete( ply, args )
local targs = string.Trim( args )
local soundList = {}
local relpath = targs:GetPathFromFilename()
local sounds = file.Find( "sound/" .. relpath .. "*", "GAME" )
for _, sound in ipairs( sounds ) do
if targs:len() == 0 or (relpath .. sound):sub( 1, targs:len() ) == targs then
table.insert( soundList, relpath .. sound )
end
end
return soundList
end
function ulx.blindUser( bool, amt )
if bool then
local function blind()
draw.RoundedBox( 0, 0, 0, ScrW(), ScrH(), Color( 255, 255, 255, amt ) )
end
hook.Add( "HUDPaint", "ulx_blind", blind )
else
hook.Remove( "HUDPaint", "ulx_blind" )
end
end
local function rcvBlind( um )
local bool = um:ReadBool()
local amt = um:ReadShort()
ulx.blindUser( bool, amt )
end
usermessage.Hook( "ulx_blind", rcvBlind )
local curVote
local function optionsDraw()
if not curVote then return end
local title = curVote.title
local options = curVote.options
local endtime = curVote.endtime
if CurTime() > endtime then return end -- Expired
surface.SetFont( "Default" )
local w, h = surface.GetTextSize( title )
w = math.max( 200, w )
local totalh = h * 12 + 20
draw.RoundedBox( 8, 10, ScrH()*0.4 - 10, w + 20, totalh, Color( 111, 124, 138, 200 ) )
optiontxt = ""
for i=1, 10 do
if options[ i ] and options[ i ] ~= "" then
optiontxt = optiontxt .. math.modf( i, 10 ) .. ". " .. options[ i ]
end
optiontxt = optiontxt .. "\n"
end
draw.DrawText( title .. "\n\n" .. optiontxt, "Default", 20, ScrH()*0.4, Color( 255, 255, 255, 255 ), TEXT_ALIGN_LEFT )
end
local function rcvVote( um )
local title = um:ReadString()
local timeout = um:ReadShort()
local options = ULib.umsgRcv( um )
local function callback( id )
if id == 0 then id = 10 end
if not options[ id ] then
return -- Returning nil will keep our hook
end
RunConsoleCommand( "ulx_vote", id )
curVote = nil
return true -- Let it know we're done here
end
LocalPlayer():AddPlayerOption( title, timeout, callback, optionsDraw )
curVote = { title=title, options=options, endtime=CurTime()+timeout }
end
usermessage.Hook( "ulx_vote", rcvVote )
function ulx.getVersion() -- This exists on the server as well, so feel free to use it!
if ulx.release then
version = string.format( "%.02f", ulx.version )
elseif ulx.revision > 0 then -- SVN version?
version = string.format( "<SVN> revision %i", ulx.revision )
else
version = string.format( "<SVN> unknown revision" )
end
return version, ulx.version, ulx.revision
end
function ulx.addToMenu( menuid, label, data ) -- TODO, remove
Msg( "Warning: ulx.addToMenu was called, which is being phased out!\n" )
end
-- Any language stuff for ULX should go here...
language.Add( "Undone_ulx_ent", "Undone ulx ent command" )
ulx.common_kick_reasons = ulx.common_kick_reasons or {}
function ulx.populateKickReasons( reasons )
table.Empty( ulx.common_kick_reasons )
table.Merge( ulx.common_kick_reasons, reasons )
end
ulx.maps = ulx.maps or {}
function ulx.populateClMaps( maps )
table.Empty( ulx.maps )
table.Merge( ulx.maps, maps )
end
ulx.gamemodes = ulx.gamemodes or {}
function ulx.populateClGamemodes( gamemodes )
table.Empty( ulx.gamemodes )
table.Merge( ulx.gamemodes, gamemodes )
end
ulx.votemaps = ulx.votemaps or {}
function ulx.populateClVotemaps( votemaps )
table.Empty( ulx.votemaps )
table.Merge( ulx.votemaps, votemaps )
end
function ulx.soundComplete( ply, args )
local targs = string.Trim( args )
local soundList = {}
local relpath = targs:GetPathFromFilename()
local sounds = file.Find( "sound/" .. relpath .. "*", "GAME" )
for _, sound in ipairs( sounds ) do
if targs:len() == 0 or (relpath .. sound):sub( 1, targs:len() ) == targs then
table.insert( soundList, relpath .. sound )
end
end
return soundList
end
function ulx.blindUser( bool, amt )
if bool then
local function blind()
draw.RoundedBox( 0, 0, 0, ScrW(), ScrH(), Color( 255, 255, 255, amt ) )
end
hook.Add( "HUDPaint", "ulx_blind", blind )
else
hook.Remove( "HUDPaint", "ulx_blind" )
end
end
local function rcvBlind( um )
local bool = um:ReadBool()
local amt = um:ReadShort()
ulx.blindUser( bool, amt )
end
usermessage.Hook( "ulx_blind", rcvBlind )
local curVote
local function optionsDraw()
if not curVote then return end
local title = curVote.title
local options = curVote.options
local endtime = curVote.endtime
if CurTime() > endtime then return end -- Expired
surface.SetFont( "Default" )
local w, h = surface.GetTextSize( title )
w = math.max( 200, w )
local totalh = h * 12 + 20
draw.RoundedBox( 8, 10, ScrH()*0.4 - 10, w + 20, totalh, Color( 111, 124, 138, 200 ) )
optiontxt = ""
for i=1, 10 do
if options[ i ] and options[ i ] ~= "" then
optiontxt = optiontxt .. math.modf( i, 10 ) .. ". " .. options[ i ]
end
optiontxt = optiontxt .. "\n"
end
draw.DrawText( title .. "\n\n" .. optiontxt, "Default", 20, ScrH()*0.4, Color( 255, 255, 255, 255 ), TEXT_ALIGN_LEFT )
end
local function rcvVote( um )
local title = um:ReadString()
local timeout = um:ReadShort()
local options = ULib.umsgRcv( um )
local function callback( id )
if id == 0 then id = 10 end
if not options[ id ] then
return -- Returning nil will keep our hook
end
RunConsoleCommand( "ulx_vote", id )
curVote = nil
return true -- Let it know we're done here
end
LocalPlayer():AddPlayerOption( title, timeout, callback, optionsDraw )
curVote = { title=title, options=options, endtime=CurTime()+timeout }
end
usermessage.Hook( "ulx_vote", rcvVote )
function ulx.getVersion() -- This exists on the server as well, so feel free to use it!
if ulx.release then
version = string.format( "%.02f", ulx.version )
elseif ulx.revision > 0 then -- SVN version?
version = string.format( "<SVN> revision %i", ulx.revision )
else
version = string.format( "<SVN> unknown revision" )
end
return version, ulx.version, ulx.revision
end
function ulx.addToMenu( menuid, label, data ) -- TODO, remove
Msg( "Warning: ulx.addToMenu was called, which is being phased out!\n" )
end
-- Any language stuff for ULX should go here...
language.Add( "Undone_ulx_ent", "Undone ulx ent command" )

View File

@ -1,142 +1,142 @@
-- Load our configs
local function init()
-- Load our banned users
if ULib.fileExists( "cfg/banned_user.cfg", true ) then
ULib.execFile( "cfg/banned_user.cfg", "ULX-EXEC", true )
end
end
hook.Add( "Initialize", "ULXInitialize", init )
local function doMainCfg( path, noMount )
ULib.execString( ULib.stripComments( ULib.fileRead( path, noMount ), ";" ), "ULXConfigExec" )
end
local function doDownloadCfg( path, noMount )
-- Does the module exist for this?
if not ulx.addForcedDownload then
return
end
local lines = ULib.explode( "\n+", ULib.stripComments( ULib.fileRead( path, noMount ), ";" ) )
for _, line in ipairs( lines ) do
line = line:Trim()
if line:len() > 0 then
ulx.addForcedDownload( ULib.stripQuotes( line ) )
end
end
end
local function doGimpCfg( path, noMount )
-- Does the module exist for this?
if not ulx.clearGimpSays then
return
end
ulx.clearGimpSays()
local lines = ULib.explode( "\n+", ULib.stripComments( ULib.fileRead( path, noMount ), ";" ) )
for _, line in ipairs( lines ) do
line = line:Trim()
if line:len() > 0 then
ulx.addGimpSay( ULib.stripQuotes( line ) )
end
end
end
local function doAdvertCfg( path, noMount )
-- Does the module exist for this?
if not ulx.addAdvert then
return
end
local data_root, err = ULib.parseKeyValues( ULib.stripComments( ULib.fileRead( path, noMount ), ";" ) )
if not data_root then Msg( "[ULX] Error in advert config: " .. err .. "\n" ) return end
for group_name, row in pairs( data_root ) do
if type( group_name ) == "number" then -- Must not be a group
local color = Color( tonumber( row.red ) or ULib.DEFAULT_TSAY_COLOR.r, tonumber( row.green ) or ULib.DEFAULT_TSAY_COLOR.g, tonumber( row.blue ) or ULib.DEFAULT_TSAY_COLOR.b )
ulx.addAdvert( row.text or "NO TEXT SUPPLIED FOR THIS ADVERT", tonumber( row.time ) or 300, _, color, tonumber( row.time_on_screen ) )
else -- Must be a group
if type( row ) ~= "table" then Msg( "[ULX] Error in advert config: Adverts are not properly formatted!\n" ) return end
for i=1, #row do
local row2 = row[ i ]
local color = Color( tonumber( row2.red ) or 151, tonumber( row2.green ) or 211, tonumber( row2.blue ) or 255 )
ulx.addAdvert( row2.text or "NO TEXT SUPPLIED FOR THIS ADVERT", tonumber( row2.time ) or 300, group_name, color, tonumber( row2.time_on_screen ) )
end
end
end
end
local function doVotemapsCfg( path, noMount )
-- Does the module exist for this?
if not ulx.clearVotemaps then
return
end
ulx.clearVotemaps()
local lines = ULib.explode( "\n+", ULib.stripComments( ULib.fileRead( path, noMount ), ";" ) )
for _, line in ipairs( lines ) do
line = line:Trim()
if line:len() > 0 then
ulx.votemapAddMap( line )
end
end
end
local function doReasonsCfg( path, noMount )
-- Does the module exist for this?
if not ulx.addKickReason then
return
end
local lines = ULib.explode( "\n+", ULib.stripComments( ULib.fileRead( path, noMount ), ";" ) )
for _, line in ipairs( lines ) do
line = line:Trim()
if line:len() > 0 then
ulx.addKickReason( ULib.stripQuotes( line ) )
end
end
end
local function doCfg()
local things_to_execute = { -- Indexed by name, value of function to execute
["config.txt"] = doMainCfg,
["downloads.txt"] = doDownloadCfg,
["gimps.txt"] = doGimpCfg,
["adverts.txt"] = doAdvertCfg,
["votemaps.txt"] = doVotemapsCfg,
["banreasons.txt"] = doReasonsCfg,
}
local gamemode_name = GAMEMODE.Name:lower()
local map_name = game.GetMap()
for filename, fn in pairs( things_to_execute ) do
-- Global config
if ULib.fileExists( "data/ulx/" .. filename ) then
fn( "data/ulx/" .. filename )
end
-- Per gamemode config
if ULib.fileExists( "data/ulx/gamemodes/" .. gamemode_name .. "/" .. filename, true ) then
fn( "data/ulx/gamemodes/" .. gamemode_name .. "/" .. filename, true )
end
-- Per map config
if ULib.fileExists( "data/ulx/maps/" .. map_name .. "/" .. filename, true ) then
fn( "data/ulx/maps/" .. map_name .. "/" .. filename, true )
end
end
ULib.queueFunctionCall( hook.Call, ulx.HOOK_ULXDONELOADING, _ ) -- We're done loading! Wait a tick so the configs load.
if not game.IsDedicated() then
hook.Remove( "PlayerInitialSpawn", "ULXDoCfg" )
end
end
if game.IsDedicated() then
hook.Add( "Initialize", "ULXDoCfg", doCfg, HOOK_MONITOR_HIGH )
else
hook.Add( "PlayerInitialSpawn", "ULXDoCfg", doCfg, HOOK_MONITOR_HIGH ) -- TODO can we make this initialize too?
end
-- Load our configs
local function init()
-- Load our banned users
if ULib.fileExists( "cfg/banned_user.cfg", true ) then
ULib.execFile( "cfg/banned_user.cfg", "ULX-EXEC", true )
end
end
hook.Add( "Initialize", "ULXInitialize", init )
local function doMainCfg( path, noMount )
ULib.execString( ULib.stripComments( ULib.fileRead( path, noMount ), ";" ), "ULXConfigExec" )
end
local function doDownloadCfg( path, noMount )
-- Does the module exist for this?
if not ulx.addForcedDownload then
return
end
local lines = ULib.explode( "\n+", ULib.stripComments( ULib.fileRead( path, noMount ), ";" ) )
for _, line in ipairs( lines ) do
line = line:Trim()
if line:len() > 0 then
ulx.addForcedDownload( ULib.stripQuotes( line ) )
end
end
end
local function doGimpCfg( path, noMount )
-- Does the module exist for this?
if not ulx.clearGimpSays then
return
end
ulx.clearGimpSays()
local lines = ULib.explode( "\n+", ULib.stripComments( ULib.fileRead( path, noMount ), ";" ) )
for _, line in ipairs( lines ) do
line = line:Trim()
if line:len() > 0 then
ulx.addGimpSay( ULib.stripQuotes( line ) )
end
end
end
local function doAdvertCfg( path, noMount )
-- Does the module exist for this?
if not ulx.addAdvert then
return
end
local data_root, err = ULib.parseKeyValues( ULib.stripComments( ULib.fileRead( path, noMount ), ";" ) )
if not data_root then Msg( "[ULX] Error in advert config: " .. err .. "\n" ) return end
for group_name, row in pairs( data_root ) do
if type( group_name ) == "number" then -- Must not be a group
local color = Color( tonumber( row.red ) or ULib.DEFAULT_TSAY_COLOR.r, tonumber( row.green ) or ULib.DEFAULT_TSAY_COLOR.g, tonumber( row.blue ) or ULib.DEFAULT_TSAY_COLOR.b )
ulx.addAdvert( row.text or "NO TEXT SUPPLIED FOR THIS ADVERT", tonumber( row.time ) or 300, _, color, tonumber( row.time_on_screen ) )
else -- Must be a group
if type( row ) ~= "table" then Msg( "[ULX] Error in advert config: Adverts are not properly formatted!\n" ) return end
for i=1, #row do
local row2 = row[ i ]
local color = Color( tonumber( row2.red ) or 151, tonumber( row2.green ) or 211, tonumber( row2.blue ) or 255 )
ulx.addAdvert( row2.text or "NO TEXT SUPPLIED FOR THIS ADVERT", tonumber( row2.time ) or 300, group_name, color, tonumber( row2.time_on_screen ) )
end
end
end
end
local function doVotemapsCfg( path, noMount )
-- Does the module exist for this?
if not ulx.clearVotemaps then
return
end
ulx.clearVotemaps()
local lines = ULib.explode( "\n+", ULib.stripComments( ULib.fileRead( path, noMount ), ";" ) )
for _, line in ipairs( lines ) do
line = line:Trim()
if line:len() > 0 then
ulx.votemapAddMap( line )
end
end
end
local function doReasonsCfg( path, noMount )
-- Does the module exist for this?
if not ulx.addKickReason then
return
end
local lines = ULib.explode( "\n+", ULib.stripComments( ULib.fileRead( path, noMount ), ";" ) )
for _, line in ipairs( lines ) do
line = line:Trim()
if line:len() > 0 then
ulx.addKickReason( ULib.stripQuotes( line ) )
end
end
end
local function doCfg()
local things_to_execute = { -- Indexed by name, value of function to execute
["config.txt"] = doMainCfg,
["downloads.txt"] = doDownloadCfg,
["gimps.txt"] = doGimpCfg,
["adverts.txt"] = doAdvertCfg,
["votemaps.txt"] = doVotemapsCfg,
["banreasons.txt"] = doReasonsCfg,
}
local gamemode_name = GAMEMODE.Name:lower()
local map_name = game.GetMap()
for filename, fn in pairs( things_to_execute ) do
-- Global config
if ULib.fileExists( "data/ulx/" .. filename ) then
fn( "data/ulx/" .. filename )
end
-- Per gamemode config
if ULib.fileExists( "data/ulx/gamemodes/" .. gamemode_name .. "/" .. filename, true ) then
fn( "data/ulx/gamemodes/" .. gamemode_name .. "/" .. filename, true )
end
-- Per map config
if ULib.fileExists( "data/ulx/maps/" .. map_name .. "/" .. filename, true ) then
fn( "data/ulx/maps/" .. map_name .. "/" .. filename, true )
end
end
ULib.queueFunctionCall( hook.Call, ulx.HOOK_ULXDONELOADING, _ ) -- We're done loading! Wait a tick so the configs load.
if not game.IsDedicated() then
hook.Remove( "PlayerInitialSpawn", "ULXDoCfg" )
end
end
if game.IsDedicated() then
hook.Add( "Initialize", "ULXDoCfg", doCfg, HOOK_MONITOR_HIGH )
else
hook.Add( "PlayerInitialSpawn", "ULXDoCfg", doCfg, HOOK_MONITOR_HIGH ) -- TODO can we make this initialize too?
end

View File

@ -1,54 +1,54 @@
if not ulx then
ulx = {}
ULib.fileCreateDir( "data/ulx" )
local sv_modules = file.Find( "ulx/modules/*.lua", "LUA" )
local sh_modules = file.Find( "ulx/modules/sh/*.lua", "LUA" )
local cl_modules = file.Find( "ulx/modules/cl/*.lua", "LUA" )
Msg( "///////////////////////////////\n" )
Msg( "// ULX Admin Mod //\n" )
Msg( "///////////////////////////////\n" )
Msg( "// Loading... //\n" )
Msg( "// sh_defines.lua //\n" )
include( "sh_defines.lua" )
Msg( "// lib.lua //\n" )
include( "lib.lua" )
Msg( "// base.lua //\n" )
include( "base.lua" )
Msg( "// sh_base.lua //\n" )
include( "sh_base.lua" )
Msg( "// log.lua //\n" )
include( "log.lua" )
for _, file in ipairs( sv_modules ) do
Msg( "// MODULE: " .. file .. string.rep( " ", 17 - file:len() ) .. "//\n" )
include( "modules/" .. file )
end
for _, file in ipairs( sh_modules ) do
Msg( "// MODULE: " .. file .. string.rep( " ", 17 - file:len() ) .. "//\n" )
include( "modules/sh/" .. file )
end
Msg( "// end.lua //\n" )
include( "end.lua" )
Msg( "// Load Complete! //\n" )
Msg( "///////////////////////////////\n" )
AddCSLuaFile( "ulx/cl_init.lua" )
AddCSLuaFile( "ulx/sh_defines.lua" )
AddCSLuaFile( "ulx/sh_base.lua" )
AddCSLuaFile( "ulx/cl_lib.lua" )
-- Find c-side modules and load them
for _, file in ipairs( cl_modules ) do
AddCSLuaFile( "ulx/modules/cl/" .. file )
end
for _, file in ipairs( sh_modules ) do
AddCSLuaFile( "ulx/modules/sh/" .. file )
end
end
if not ulx then
ulx = {}
ULib.fileCreateDir( "data/ulx" )
local sv_modules = file.Find( "ulx/modules/*.lua", "LUA" )
local sh_modules = file.Find( "ulx/modules/sh/*.lua", "LUA" )
local cl_modules = file.Find( "ulx/modules/cl/*.lua", "LUA" )
Msg( "///////////////////////////////\n" )
Msg( "// ULX Admin Mod //\n" )
Msg( "///////////////////////////////\n" )
Msg( "// Loading... //\n" )
Msg( "// sh_defines.lua //\n" )
include( "sh_defines.lua" )
Msg( "// lib.lua //\n" )
include( "lib.lua" )
Msg( "// base.lua //\n" )
include( "base.lua" )
Msg( "// sh_base.lua //\n" )
include( "sh_base.lua" )
Msg( "// log.lua //\n" )
include( "log.lua" )
for _, file in ipairs( sv_modules ) do
Msg( "// MODULE: " .. file .. string.rep( " ", 17 - file:len() ) .. "//\n" )
include( "modules/" .. file )
end
for _, file in ipairs( sh_modules ) do
Msg( "// MODULE: " .. file .. string.rep( " ", 17 - file:len() ) .. "//\n" )
include( "modules/sh/" .. file )
end
Msg( "// end.lua //\n" )
include( "end.lua" )
Msg( "// Load Complete! //\n" )
Msg( "///////////////////////////////\n" )
AddCSLuaFile( "ulx/cl_init.lua" )
AddCSLuaFile( "ulx/sh_defines.lua" )
AddCSLuaFile( "ulx/sh_base.lua" )
AddCSLuaFile( "ulx/cl_lib.lua" )
-- Find c-side modules and load them
for _, file in ipairs( cl_modules ) do
AddCSLuaFile( "ulx/modules/cl/" .. file )
end
for _, file in ipairs( sh_modules ) do
AddCSLuaFile( "ulx/modules/sh/" .. file )
end
end

View File

@ -1,105 +1,105 @@
-- Set exclusive command. Commands can check if an exclusive command is set with getExclusive()
-- and process no further. Only "big" things like jail, maul, etc should be checking and setting this.
function ulx.setExclusive( ply, action )
ply.ULXExclusive = action
end
function ulx.getExclusive( target, ply )
if not target.ULXExclusive then return end
if target == ply then
return "You are " .. target.ULXExclusive .. "!"
else
return target:Nick() .. " is " .. target.ULXExclusive .. "!"
end
end
function ulx.clearExclusive( ply )
ply.ULXExclusive = nil
end
--- No die. Don't allow the player to die!
function ulx.setNoDie( ply, bool )
ULib.getSpawnInfo( ply )
ply.ulxNoDie = bool
end
local function checkDeath( ply, weapon, killer )
if ply.frozen then
ULib.queueFunctionCall( function()
if ply and ply:IsValid() then
ply:UnLock()
ply:Lock()
end
end )
end
if ply.ulxNoDie then
ply:AddDeaths( -1 ) -- Won't show on scoreboard
if killer == ply then -- Suicide
ply:AddFrags( 1 ) -- Won't show on scoreboard
end
local pos = ply:GetPos()
local ang = ply:EyeAngles()
ULib.queueFunctionCall( function() -- Run next frame
if not ply:IsValid() then return end -- Gotta make sure it's still valid since this is a timer
ULib.spawn( ply, true )
ply:SetPos( pos )
ply:SetEyeAngles( ang )
end )
return true -- Don't register their death on HUD
end
end
hook.Add( "PlayerDeath", "ULXCheckDeath", checkDeath, HOOK_HIGH ) -- Hook it first because we're blocking their death.
local function checkSuicide( ply )
if ply.ulxNoDie then
return false
end
end
hook.Add( "CanPlayerSuicide", "ULXCheckSuicide", checkSuicide, HOOK_HIGH )
function ulx.getVersion() -- This exists on the client as well, so feel free to use it!
local version
local r = 0
if ulx.release then
version = string.format( "%.02f", ulx.version )
else
if ULib.fileExists( "addons/ulx/.svn/wc.db" ) then -- SVN's new format
-- The following code would probably work if garry allowed us to read this file...
--[[local raw = ULib.fileRead( "addons/ulx/.svn/wc.db" )
local highest = 0
for rev in string.gmatch( raw, "/ulx/!svn/ver/%d+/" ) do
if rev > highest then
highest = rev
end
end
r = highest]]
elseif ULib.fileExists( "addons/ulx/lua/ulx/.svn/entries" ) then
-- Garry broke the following around 05/11/2010, then fixed it again around 11/10/2010!
local lines = string.Explode( "\n", ULib.fileRead( "lua/ulx/.svn/entries" ) )
r = tonumber( lines[ 4 ] )
end
if r and r > 0 then
version = string.format( "<SVN> revision %i", r )
else
version = string.format( "<SVN> unknown revision" )
end
end
return version, ulx.version, r
end
function ulx.addToMenu( menuid, label, data ) -- TODO: Remove
Msg( "Warning: ulx.addToMenu was called, which is being phased out!\n" )
end
function ulx.standardizeModel( model ) -- This will convert all model strings to be of the same type, using linux notation and single dashes.
model = model:lower()
model = model:gsub( "\\", "/" )
model = model:gsub( "/+", "/" ) -- Multiple dashes
return model
end
-- Set exclusive command. Commands can check if an exclusive command is set with getExclusive()
-- and process no further. Only "big" things like jail, maul, etc should be checking and setting this.
function ulx.setExclusive( ply, action )
ply.ULXExclusive = action
end
function ulx.getExclusive( target, ply )
if not target.ULXExclusive then return end
if target == ply then
return "You are " .. target.ULXExclusive .. "!"
else
return target:Nick() .. " is " .. target.ULXExclusive .. "!"
end
end
function ulx.clearExclusive( ply )
ply.ULXExclusive = nil
end
--- No die. Don't allow the player to die!
function ulx.setNoDie( ply, bool )
ULib.getSpawnInfo( ply )
ply.ulxNoDie = bool
end
local function checkDeath( ply, weapon, killer )
if ply.frozen then
ULib.queueFunctionCall( function()
if ply and ply:IsValid() then
ply:UnLock()
ply:Lock()
end
end )
end
if ply.ulxNoDie then
ply:AddDeaths( -1 ) -- Won't show on scoreboard
if killer == ply then -- Suicide
ply:AddFrags( 1 ) -- Won't show on scoreboard
end
local pos = ply:GetPos()
local ang = ply:EyeAngles()
ULib.queueFunctionCall( function() -- Run next frame
if not ply:IsValid() then return end -- Gotta make sure it's still valid since this is a timer
ULib.spawn( ply, true )
ply:SetPos( pos )
ply:SetEyeAngles( ang )
end )
return true -- Don't register their death on HUD
end
end
hook.Add( "PlayerDeath", "ULXCheckDeath", checkDeath, HOOK_HIGH ) -- Hook it first because we're blocking their death.
local function checkSuicide( ply )
if ply.ulxNoDie then
return false
end
end
hook.Add( "CanPlayerSuicide", "ULXCheckSuicide", checkSuicide, HOOK_HIGH )
function ulx.getVersion() -- This exists on the client as well, so feel free to use it!
local version
local r = 0
if ulx.release then
version = string.format( "%.02f", ulx.version )
else
if ULib.fileExists( "addons/ulx/.svn/wc.db" ) then -- SVN's new format
-- The following code would probably work if garry allowed us to read this file...
--[[local raw = ULib.fileRead( "addons/ulx/.svn/wc.db" )
local highest = 0
for rev in string.gmatch( raw, "/ulx/!svn/ver/%d+/" ) do
if rev > highest then
highest = rev
end
end
r = highest]]
elseif ULib.fileExists( "addons/ulx/lua/ulx/.svn/entries" ) then
-- Garry broke the following around 05/11/2010, then fixed it again around 11/10/2010!
local lines = string.Explode( "\n", ULib.fileRead( "lua/ulx/.svn/entries" ) )
r = tonumber( lines[ 4 ] )
end
if r and r > 0 then
version = string.format( "<SVN> revision %i", r )
else
version = string.format( "<SVN> unknown revision" )
end
end
return version, ulx.version, r
end
function ulx.addToMenu( menuid, label, data ) -- TODO: Remove
Msg( "Warning: ulx.addToMenu was called, which is being phased out!\n" )
end
function ulx.standardizeModel( model ) -- This will convert all model strings to be of the same type, using linux notation and single dashes.
model = model:lower()
model = model:gsub( "\\", "/" )
model = model:gsub( "/+", "/" ) -- Multiple dashes
return model
end

File diff suppressed because it is too large Load Diff

View File

@ -1,49 +1,49 @@
ulx.motdmenu_exists = true
local isUrl
local url
function ulx.showMotdMenu( steamid )
local window = vgui.Create( "DFrame" )
if ScrW() > 640 then -- Make it larger if we can.
window:SetSize( ScrW()*0.9, ScrH()*0.9 )
else
window:SetSize( 640, 480 )
end
window:Center()
window:SetTitle( "ULX MOTD" )
window:SetVisible( true )
window:MakePopup()
local html = vgui.Create( "DHTML", window )
--html:SetAllowLua( true ) -- Too much of a security risk for us to enable. Feel free to uncomment if you know what you're doing.
local button = vgui.Create( "DButton", window )
button:SetText( "Close" )
button.DoClick = function() window:Close() end
button:SetSize( 100, 40 )
button:SetPos( (window:GetWide() - button:GetWide()) / 2, window:GetTall() - button:GetTall() - 10 )
html:SetSize( window:GetWide() - 20, window:GetTall() - button:GetTall() - 50 )
html:SetPos( 10, 30 )
if not isUrl then
html:SetHTML( ULib.fileRead( "data/ulx_motd.txt" ) or "" )
else
url = string.gsub( url, "%%curmap%%", game.GetMap() )
url = string.gsub( url, "%%steamid%%", steamid )
html:OpenURL( url )
end
end
function ulx.rcvMotd( isUrl_, text )
isUrl = isUrl_
if not isUrl then
ULib.fileWrite( "data/ulx_motd.txt", text )
else
if text:find( "://", 1, true ) then
url = text
else
url = "http://" .. text
end
end
end
ulx.motdmenu_exists = true
local isUrl
local url
function ulx.showMotdMenu( steamid )
local window = vgui.Create( "DFrame" )
if ScrW() > 640 then -- Make it larger if we can.
window:SetSize( ScrW()*0.9, ScrH()*0.9 )
else
window:SetSize( 640, 480 )
end
window:Center()
window:SetTitle( "ULX MOTD" )
window:SetVisible( true )
window:MakePopup()
local html = vgui.Create( "DHTML", window )
--html:SetAllowLua( true ) -- Too much of a security risk for us to enable. Feel free to uncomment if you know what you're doing.
local button = vgui.Create( "DButton", window )
button:SetText( "Close" )
button.DoClick = function() window:Close() end
button:SetSize( 100, 40 )
button:SetPos( (window:GetWide() - button:GetWide()) / 2, window:GetTall() - button:GetTall() - 10 )
html:SetSize( window:GetWide() - 20, window:GetTall() - button:GetTall() - 50 )
html:SetPos( 10, 30 )
if not isUrl then
html:SetHTML( ULib.fileRead( "data/ulx_motd.txt" ) or "" )
else
url = string.gsub( url, "%%curmap%%", game.GetMap() )
url = string.gsub( url, "%%steamid%%", steamid )
html:OpenURL( url )
end
end
function ulx.rcvMotd( isUrl_, text )
isUrl = isUrl_
if not isUrl then
ULib.fileWrite( "data/ulx_motd.txt", text )
else
if text:find( "://", 1, true ) then
url = text
else
url = "http://" .. text
end
end
end

File diff suppressed because it is too large Load Diff

View File

@ -1,406 +1,406 @@
--xgui_helpers -- by Stickly Man!
--A set of generic functions to help with various XGUI-related things.
function xgui.load_helpers()
--These handle keyboard focus for textboxes within XGUI.
local function getKeyboardFocus( pnl )
if pnl:HasParent( xgui.base ) then
xgui.anchor:SetKeyboardInputEnabled( true )
end
if pnl.selectAll then
pnl:SelectAllText()
end
end
hook.Add( "OnTextEntryGetFocus", "XGUI_GetKeyboardFocus", getKeyboardFocus )
local function loseKeyboardFocus( pnl )
if pnl:HasParent( xgui.base ) then
xgui.anchor:SetKeyboardInputEnabled( false )
end
end
hook.Add( "OnTextEntryLoseFocus", "XGUI_LoseKeyboardFocus", loseKeyboardFocus )
---------------------------------
--Code for creating the XGUI base
---------------------------------
function xgui.makeXGUIbase()
xgui.anchor = xlib.makeXpanel{ w=600, h=420, x=ScrW()/2-300, y=ScrH()/2-270 }
xgui.anchor:SetVisible( false )
xgui.anchor:SetKeyboardInputEnabled( false )
xgui.anchor.Paint = function( self, w, h ) hook.Call( "XLIBDoAnimation" ) end
xgui.anchor:SetAlpha( 0 )
xgui.base = xlib.makepropertysheet{ x=0, y=0, w=600, h=400, parent=xgui.anchor, offloadparent=xgui.null }
xgui.base.animOpen = function() --First 4 are fade animations, last (or invalid choice) is the default fade animation.
xgui.settings.animIntype = tonumber( xgui.settings.animIntype )
if xgui.settings.animIntype == 2 then
xlib.addToAnimQueue( function() xgui.anchor:SetAlpha(255) end )
xlib.addToAnimQueue( "pnlSlide", { panel=xgui.anchor, startx=xgui.x, starty=-490, endx=xgui.x, endy=xgui.y, setvisible=true } )
elseif xgui.settings.animIntype == 3 then
xlib.addToAnimQueue( function() xgui.anchor:SetAlpha(255) end )
xlib.addToAnimQueue( "pnlSlide", { panel=xgui.anchor, startx=-610, starty=xgui.y, endx=xgui.x, endy=xgui.y, setvisible=true } )
elseif xgui.settings.animIntype == 4 then
xlib.addToAnimQueue( function() xgui.anchor:SetAlpha(255) end )
xlib.addToAnimQueue( "pnlSlide", { panel=xgui.anchor, startx=xgui.x, starty=ScrH(), endx=xgui.x, endy=xgui.y, setvisible=true } )
elseif xgui.settings.animIntype == 5 then
xlib.addToAnimQueue( function() xgui.anchor:SetAlpha(255) end )
xlib.addToAnimQueue( "pnlSlide", { panel=xgui.anchor, startx=ScrW(), starty=xgui.y, endx=xgui.x, endy=xgui.y, setvisible=true } )
else
xlib.addToAnimQueue( function() xgui.anchor:SetPos( xgui.x, xgui.y ) end )
xlib.addToAnimQueue( "pnlFade", { panelIn=xgui.anchor } )
end
xlib.animQueue_start()
end
xgui.base.animClose = function()
xgui.settings.animOuttype = tonumber( xgui.settings.animOuttype )
if xgui.settings.animOuttype == 2 then
xlib.addToAnimQueue( "pnlSlide", { panel=xgui.anchor, startx=xgui.x, starty=xgui.y, endx=xgui.x, endy=-490, setvisible=false } )
xlib.addToAnimQueue( function() xgui.anchor:SetAlpha(0) end )
elseif xgui.settings.animOuttype == 3 then
xlib.addToAnimQueue( "pnlSlide", { panel=xgui.anchor, startx=xgui.x, starty=xgui.y, endx=-610, endy=xgui.y, setvisible=false } )
xlib.addToAnimQueue( function() xgui.anchor:SetAlpha(0) end )
elseif xgui.settings.animOuttype == 4 then
xlib.addToAnimQueue( "pnlSlide", { panel=xgui.anchor, startx=xgui.x, starty=xgui.y, endx=xgui.x, endy=ScrH(), setvisible=false } )
xlib.addToAnimQueue( function() xgui.anchor:SetAlpha(0) end )
elseif xgui.settings.animOuttype == 5 then
xlib.addToAnimQueue( "pnlSlide", { panel=xgui.anchor, startx=xgui.x, starty=xgui.y, endx=ScrW(), endy=xgui.y, setvisible=false } )
xlib.addToAnimQueue( function() xgui.anchor:SetAlpha(0) end )
else
xlib.addToAnimQueue( function() xgui.anchor:SetPos( xgui.x, xgui.y ) end )
xlib.addToAnimQueue( "pnlFade", { panelOut=xgui.anchor } )
end
xlib.animQueue_start()
end
function xgui.SetPos( pos, xoff, yoff, ignoreanim ) --Sets the position of XGUI based on "pos", and checks to make sure that with whatever offset and pos combination, XGUI does not go off the screen.
pos = tonumber( pos )
xoff = tonumber( xoff )
yoff = tonumber( yoff )
if not xoff then xoff = 0 end
if not yoff then yoff = 0 end
if not pos then pos = 5 end
if pos == 1 or pos == 4 or pos == 7 then --Left side of the screen
if xoff < -10 then
xoff = -10
elseif xoff > ScrW()-610 then
xoff = ScrW()-610
end
xgui.x = 10+xoff
elseif pos == 3 or pos == 6 or pos == 9 then --Right side of the screen
if xoff < -ScrW()+610 then
xoff = -ScrW()+610
elseif xoff > 10 then
xoff = 10
end
xgui.x = ScrW()-610+xoff
else --Center
if xoff < -ScrW()/2+300 then
xoff = -ScrW()/2+300
elseif xoff > ScrW()/2-300 then
xoff = ScrW()/2-300
end
xgui.x = ScrW()/2-300+xoff
end
if pos == 1 or pos == 2 or pos == 3 then --Bottom of the screen
if yoff < -ScrH()+430 then
yoff = -ScrH()+430
elseif yoff > 30 then
yoff = 30
end
xgui.y = ScrH()-430+yoff
elseif pos == 7 or pos == 8 or pos == 9 then --Top of the screen
if yoff < -10 then
yoff = -10
elseif yoff > ScrH()-410 then
yoff = ScrH()-410
end
xgui.y = yoff+10
else --Center
if yoff < -ScrH()/2+210 then
yoff = -ScrH()/2+210
elseif yoff > ScrH()/2-190 then
yoff = ScrH()/2-190
end
xgui.y = ScrH()/2-210+yoff
end
if ignoreanim then
xgui.anchor:SetPos( xgui.x, xgui.y )
else
local curx, cury = xgui.anchor:GetPos()
xlib.addToAnimQueue( "pnlSlide", { panel=xgui.anchor, startx=curx, starty=cury, endx=xgui.x, endy=xgui.y } )
xlib.animQueue_start()
end
end
xgui.SetPos( xgui.settings.xguipos.pos, xgui.settings.xguipos.xoff, xgui.settings.xguipos.yoff )
function xgui.base:SetActiveTab( active, ignoreAnim )
if ( self.m_pActiveTab == active ) then return end
if ( self.m_pActiveTab ) then
if not ignoreAnim then
xlib.addToAnimQueue( "pnlFade", { panelOut=self.m_pActiveTab:GetPanel(), panelIn=active:GetPanel() } )
else
--Run this when module permissions have changed.
xlib.addToAnimQueue( "pnlFade", { panelOut=nil, panelIn=active:GetPanel() }, 0 )
end
xlib.animQueue_start()
end
self.m_pActiveTab = active
self:InvalidateLayout()
end
--Progress bar
xgui.chunkbox = xlib.makeprogressbar{ x=420, w=180, h=20, visible=false, skin=xgui.settings.skin, parent=xgui.anchor }
function xgui.chunkbox:Progress( datatype )
self.value = self.value + 1
self:SetFraction( self.value / self.max )
self.Label:SetText( "Getting data: " .. datatype .. " - " .. string.format("%.2f", (self.value / self.max) * 100) .. "%" )
if self.value == self.max then
xgui.expectingdata = nil
self.Label:SetText( "Waiting for clientside processing..." )
xgui.queueFunctionCall( xgui.chunkbox.SetVisible, "chunkbox", xgui.chunkbox, false )
RunConsoleCommand( "_xgui", "dataComplete" )
end
end
end
------------------------
--XGUI QueueFunctionCall
------------------------
--This is essentially a straight copy of Megiddo's queueFunctionCall; Since XGUI tends to use it quite a lot, I decided to seperate it to prevent delays in ULib's stuff
--I also now get to add a method of flushing the queue based on a tag in the event that new data needs to be updated.
local stack = {}
local function onThink()
local num = #stack
if num > 0 then
for i=1,3 do --Run 3 lines per frame
if stack[1] ~= nil then
local b, e = pcall( stack[ 1 ].fn, unpack( stack[ 1 ], 1, stack[ 1 ].n ) )
if not b then
ErrorNoHalt( "XGUI queue error: " .. tostring( e ) .. "\n" )
end
end
table.remove( stack, 1 ) -- Remove the first inserted item. This is FIFO
end
else
hook.Remove( "Think", "XGUIQueueThink" )
end
end
function xgui.queueFunctionCall( fn, tag, ... )
if type( fn ) ~= "function" then
error( "queueFunctionCall received a bad function", 2 )
return
end
table.insert( stack, { fn=fn, tag=tag, n=select( "#", ... ), ... } )
hook.Add( "Think", "XGUIQueueThink", onThink, HOOK_MONITOR_HIGH )
end
function xgui.flushQueue( tag )
local removeIndecies = {}
for i, fncall in ipairs( stack ) do
if fncall.tag == tag then
table.insert( removeIndecies, i )
end
end
for i=#removeIndecies,1,-1 do --Remove the queue functions backwards to prevent desynchronization of pairs
table.remove( stack, removeIndecies[i] )
end
end
-------------------
--ULIB XGUI helpers
-------------------
--Helper function to parse access tag for a particular argument
function ulx.getTagArgNum( tag, argnum )
return tag and ULib.splitArgs( tag, "<", ">" )[argnum]
end
--Load control interpretations for ULib argument types
function ULib.cmds.BaseArg.x_getcontrol( arg, argnum, parent )
return xlib.makelabel{ label="Not Supported", parent=parent }
end
function ULib.cmds.NumArg.x_getcontrol( arg, argnum, parent )
local access, tag = LocalPlayer():query( arg.cmd )
local restrictions = {}
ULib.cmds.NumArg.processRestrictions( restrictions, arg, ulx.getTagArgNum( tag, argnum ) )
if table.HasValue( arg, ULib.cmds.allowTimeString ) then
local min = restrictions.min or 0
local max = restrictions.max or 10 * 60 * 24 * 365 --default slider max 10 years
local outPanel = xlib.makepanel{ h=40, parent=parent }
xlib.makelabel{ x=5, y=3, label="Ban Length:", parent=outPanel }
outPanel.interval = xlib.makecombobox{ x=90, w=75, parent=outPanel }
outPanel.val = xlib.makeslider{ w=165, y=20, label="<--->", min=min, max=max, value=min, decimal=0, parent=outPanel }
local divisor = {}
local sensiblemax = {}
if min == 0 then outPanel.interval:AddChoice( "Permanent" ) table.insert( divisor, 1 ) table.insert( sensiblemax, 0 ) end
if max >= 1 and min <= 60*24 then outPanel.interval:AddChoice( "Minutes" ) table.insert( divisor, 1 ) table.insert( sensiblemax, 60*24 ) end
if max >= 60 and min <= 60*24*7 then outPanel.interval:AddChoice( "Hours" ) table.insert( divisor, 60 ) table.insert( sensiblemax, 24*7 ) end
if max >= ( 60*24 ) and min <= 60*24*120 then outPanel.interval:AddChoice( "Days" ) table.insert( divisor, 60*24 ) table.insert( sensiblemax, 120 ) end
if max >= ( 60*24*7 ) and min <= 60*24*7*52 then outPanel.interval:AddChoice( "Weeks" ) table.insert( divisor, 60*24*7 ) table.insert( sensiblemax, 52 ) end
if max >= ( 60*24*365 ) then outPanel.interval:AddChoice( "Years" ) table.insert( divisor, 60*24*365 ) table.insert( sensiblemax, 10 ) end
outPanel.interval.OnSelect = function( self, index, value, data )
outPanel.val:SetDisabled( value == "Permanent" )
outPanel.val.maxvalue = math.min( max / divisor[index], sensiblemax[index] )
outPanel.val.minvalue = math.max( min / divisor[index], 0 )
outPanel.val:SetMax( outPanel.val.maxvalue )
outPanel.val:SetMin( outPanel.val.minvalue )
outPanel.val:SetValue( math.Clamp( tonumber( outPanel.val:GetValue() ), outPanel.val.minvalue, outPanel.val.maxvalue ) )
end
function outPanel.val:ValueChanged( val )
val = math.Clamp( tonumber( val ), self.minvalue or 0, self.maxvalue or 0 )
self.Slider:SetSlideX( self.Scratch:GetFraction( val ) )
if ( self.TextArea != vgui.GetKeyboardFocus() ) then
self.TextArea:SetValue( self.Scratch:GetTextValue() )
end
self:OnValueChanged( val )
end
if #outPanel.interval.Choices ~= 0 then
outPanel.interval:ChooseOptionID( 1 )
end
outPanel.GetValue = function( self )
local val, char = self:GetRawValue()
return val .. char
end
outPanel.GetRawValue = function( self )
local char = string.lower( self.interval:GetValue():sub(1,1) )
if char == "m" or char == "p" or tonumber( self.val:GetValue() ) == 0 then char = "" end
return self.val:GetValue(), char
end
outPanel.GetMinutes = function( self )
local btime, char = self:GetRawValue()
if char == "h" then btime = btime * 60
elseif char == "d" then btime = btime * 1440
elseif char == "w" then btime = btime * 10080
elseif char == "y" then btime = btime * 525600 end
return btime
end
outPanel.TextArea = outPanel.val.TextArea
return outPanel
else
local defvalue = arg.min
if table.HasValue( arg, ULib.cmds.optional ) then defvalue = arg.default end
if not defvalue then defvalue = 0 end --No default was set for this command, so we'll use 0.
local maxvalue = restrictions.max
local minvalue = restrictions.min or 0
if maxvalue == nil then
if defvalue > 100 then
maxvalue = defvalue
else
maxvalue = 100
end
end
local decimal = 0
if not table.HasValue( arg, ULib.cmds.round ) then
local minMaxDelta = maxvalue - minvalue
if minMaxDelta < 5 then
decimal = 2
elseif minMaxDelta <= 10 then
decimal = 1
end
end
local outPanel = xlib.makepanel{ h=35, parent=parent }
xlib.makelabel{ label=arg.hint or "NumArg", parent=outPanel }
outPanel.val = xlib.makeslider{ y=15, w=165, min=minvalue, max=maxvalue, value=defvalue, decimal=decimal, label="<--->", parent=outPanel }
outPanel.GetValue = function( self ) return outPanel.val.GetValue( outPanel.val ) end
outPanel.TextArea = outPanel.val.TextArea
return outPanel
end
end
function ULib.cmds.NumArg.getTime( arg )
if arg == nil or arg == "" then return nil, nil end
if arg == 0 or tonumber( arg ) == 0 then
return "Permanent", 0
end
local charPriority = { "y", "w", "d", "h" }
local charMap = { "Years", "Weeks", "Days", "Hours" }
local divisor = { 60 * 24 * 365, 60 * 24 * 7, 60 * 24, 60 }
for i, v in ipairs( charPriority ) do
if arg:find( v, 1, true ) then
if not charMap[ i ] or not divisor [ i ] or not ULib.stringTimeToSeconds( arg ) then return nil, nil end
local val = ULib.stringTimeToSeconds( arg ) / divisor[ i ]
if val == 0 then return "Permanent", 0 end
return charMap[ i ], val
end
end
return "Minutes", ULib.stringTimeToSeconds( arg )
end
function ULib.cmds.StringArg.x_getcontrol( arg, argnum, parent )
local access, tag = LocalPlayer():query( arg.cmd )
local restrictions = {}
ULib.cmds.StringArg.processRestrictions( restrictions, arg, ulx.getTagArgNum( tag, argnum ) )
local is_restricted_to_completes = table.HasValue( arg, ULib.cmds.restrictToCompletes ) -- Program-level restriction (IE, ulx map)
or restrictions.playerLevelRestriction -- The player's tag specifies only certain strings
if is_restricted_to_completes then
return xlib.makecombobox{ text=arg.hint or "StringArg", choices=restrictions.restrictedCompletes, parent=parent }
elseif restrictions.restrictedCompletes and table.Count( restrictions.restrictedCompletes ) > 0 then
-- This is where there needs to be both a drop down AND an input box
local outPanel = xlib.makecombobox{ text=arg.hint, choices=restrictions.restrictedCompletes, enableinput=true, selectall=true, parent=parent }
outPanel.OnEnter = function( self )
self:GetParent():OnEnter()
end
return outPanel
else
return xlib.maketextbox{ text=arg.hint or "StringArg", selectall=true, parent=parent }
end
end
function ULib.cmds.PlayerArg.x_getcontrol( arg, argnum, parent )
local access, tag = LocalPlayer():query( arg.cmd )
local restrictions = {}
ULib.cmds.PlayerArg.processRestrictions( restrictions, LocalPlayer(), arg, ulx.getTagArgNum( tag, argnum ) )
local outPanel = xlib.makecombobox{ text=arg.hint, parent=parent }
local targets = restrictions.restrictedTargets
if targets == false then -- No one allowed
targets = {}
elseif targets == nil then -- Everyone allowed
targets = player.GetAll()
end
for _, ply in ipairs( targets ) do
outPanel:AddChoice( ply:Nick() )
end
return outPanel
end
function ULib.cmds.CallingPlayerArg.x_getcontrol( arg, argnum, parent )
return xlib.makelabel{ label=arg.hint or "CallingPlayer", parent=parent }
end
function ULib.cmds.BoolArg.x_getcontrol( arg, argnum, parent )
local access, tag = LocalPlayer():query( arg.cmd )
local restrictions = {}
ULib.cmds.BoolArg.processRestrictions( restrictions, arg, ulx.getTagArgNum( tag, argnum ) )
local outPanel = xlib.makecheckbox{ label=arg.hint or "BoolArg", value=restrictions.restrictedTo, parent=parent }
if restrictions.restrictedTo ~= nil then outPanel:SetDisabled( true ) end
outPanel.GetValue = function( self )
return self:GetChecked() and 1 or 0
end
return outPanel
end
--xgui_helpers -- by Stickly Man!
--A set of generic functions to help with various XGUI-related things.
function xgui.load_helpers()
--These handle keyboard focus for textboxes within XGUI.
local function getKeyboardFocus( pnl )
if pnl:HasParent( xgui.base ) then
xgui.anchor:SetKeyboardInputEnabled( true )
end
if pnl.selectAll then
pnl:SelectAllText()
end
end
hook.Add( "OnTextEntryGetFocus", "XGUI_GetKeyboardFocus", getKeyboardFocus )
local function loseKeyboardFocus( pnl )
if pnl:HasParent( xgui.base ) then
xgui.anchor:SetKeyboardInputEnabled( false )
end
end
hook.Add( "OnTextEntryLoseFocus", "XGUI_LoseKeyboardFocus", loseKeyboardFocus )
---------------------------------
--Code for creating the XGUI base
---------------------------------
function xgui.makeXGUIbase()
xgui.anchor = xlib.makeXpanel{ w=600, h=420, x=ScrW()/2-300, y=ScrH()/2-270 }
xgui.anchor:SetVisible( false )
xgui.anchor:SetKeyboardInputEnabled( false )
xgui.anchor.Paint = function( self, w, h ) hook.Call( "XLIBDoAnimation" ) end
xgui.anchor:SetAlpha( 0 )
xgui.base = xlib.makepropertysheet{ x=0, y=0, w=600, h=400, parent=xgui.anchor, offloadparent=xgui.null }
xgui.base.animOpen = function() --First 4 are fade animations, last (or invalid choice) is the default fade animation.
xgui.settings.animIntype = tonumber( xgui.settings.animIntype )
if xgui.settings.animIntype == 2 then
xlib.addToAnimQueue( function() xgui.anchor:SetAlpha(255) end )
xlib.addToAnimQueue( "pnlSlide", { panel=xgui.anchor, startx=xgui.x, starty=-490, endx=xgui.x, endy=xgui.y, setvisible=true } )
elseif xgui.settings.animIntype == 3 then
xlib.addToAnimQueue( function() xgui.anchor:SetAlpha(255) end )
xlib.addToAnimQueue( "pnlSlide", { panel=xgui.anchor, startx=-610, starty=xgui.y, endx=xgui.x, endy=xgui.y, setvisible=true } )
elseif xgui.settings.animIntype == 4 then
xlib.addToAnimQueue( function() xgui.anchor:SetAlpha(255) end )
xlib.addToAnimQueue( "pnlSlide", { panel=xgui.anchor, startx=xgui.x, starty=ScrH(), endx=xgui.x, endy=xgui.y, setvisible=true } )
elseif xgui.settings.animIntype == 5 then
xlib.addToAnimQueue( function() xgui.anchor:SetAlpha(255) end )
xlib.addToAnimQueue( "pnlSlide", { panel=xgui.anchor, startx=ScrW(), starty=xgui.y, endx=xgui.x, endy=xgui.y, setvisible=true } )
else
xlib.addToAnimQueue( function() xgui.anchor:SetPos( xgui.x, xgui.y ) end )
xlib.addToAnimQueue( "pnlFade", { panelIn=xgui.anchor } )
end
xlib.animQueue_start()
end
xgui.base.animClose = function()
xgui.settings.animOuttype = tonumber( xgui.settings.animOuttype )
if xgui.settings.animOuttype == 2 then
xlib.addToAnimQueue( "pnlSlide", { panel=xgui.anchor, startx=xgui.x, starty=xgui.y, endx=xgui.x, endy=-490, setvisible=false } )
xlib.addToAnimQueue( function() xgui.anchor:SetAlpha(0) end )
elseif xgui.settings.animOuttype == 3 then
xlib.addToAnimQueue( "pnlSlide", { panel=xgui.anchor, startx=xgui.x, starty=xgui.y, endx=-610, endy=xgui.y, setvisible=false } )
xlib.addToAnimQueue( function() xgui.anchor:SetAlpha(0) end )
elseif xgui.settings.animOuttype == 4 then
xlib.addToAnimQueue( "pnlSlide", { panel=xgui.anchor, startx=xgui.x, starty=xgui.y, endx=xgui.x, endy=ScrH(), setvisible=false } )
xlib.addToAnimQueue( function() xgui.anchor:SetAlpha(0) end )
elseif xgui.settings.animOuttype == 5 then
xlib.addToAnimQueue( "pnlSlide", { panel=xgui.anchor, startx=xgui.x, starty=xgui.y, endx=ScrW(), endy=xgui.y, setvisible=false } )
xlib.addToAnimQueue( function() xgui.anchor:SetAlpha(0) end )
else
xlib.addToAnimQueue( function() xgui.anchor:SetPos( xgui.x, xgui.y ) end )
xlib.addToAnimQueue( "pnlFade", { panelOut=xgui.anchor } )
end
xlib.animQueue_start()
end
function xgui.SetPos( pos, xoff, yoff, ignoreanim ) --Sets the position of XGUI based on "pos", and checks to make sure that with whatever offset and pos combination, XGUI does not go off the screen.
pos = tonumber( pos )
xoff = tonumber( xoff )
yoff = tonumber( yoff )
if not xoff then xoff = 0 end
if not yoff then yoff = 0 end
if not pos then pos = 5 end
if pos == 1 or pos == 4 or pos == 7 then --Left side of the screen
if xoff < -10 then
xoff = -10
elseif xoff > ScrW()-610 then
xoff = ScrW()-610
end
xgui.x = 10+xoff
elseif pos == 3 or pos == 6 or pos == 9 then --Right side of the screen
if xoff < -ScrW()+610 then
xoff = -ScrW()+610
elseif xoff > 10 then
xoff = 10
end
xgui.x = ScrW()-610+xoff
else --Center
if xoff < -ScrW()/2+300 then
xoff = -ScrW()/2+300
elseif xoff > ScrW()/2-300 then
xoff = ScrW()/2-300
end
xgui.x = ScrW()/2-300+xoff
end
if pos == 1 or pos == 2 or pos == 3 then --Bottom of the screen
if yoff < -ScrH()+430 then
yoff = -ScrH()+430
elseif yoff > 30 then
yoff = 30
end
xgui.y = ScrH()-430+yoff
elseif pos == 7 or pos == 8 or pos == 9 then --Top of the screen
if yoff < -10 then
yoff = -10
elseif yoff > ScrH()-410 then
yoff = ScrH()-410
end
xgui.y = yoff+10
else --Center
if yoff < -ScrH()/2+210 then
yoff = -ScrH()/2+210
elseif yoff > ScrH()/2-190 then
yoff = ScrH()/2-190
end
xgui.y = ScrH()/2-210+yoff
end
if ignoreanim then
xgui.anchor:SetPos( xgui.x, xgui.y )
else
local curx, cury = xgui.anchor:GetPos()
xlib.addToAnimQueue( "pnlSlide", { panel=xgui.anchor, startx=curx, starty=cury, endx=xgui.x, endy=xgui.y } )
xlib.animQueue_start()
end
end
xgui.SetPos( xgui.settings.xguipos.pos, xgui.settings.xguipos.xoff, xgui.settings.xguipos.yoff )
function xgui.base:SetActiveTab( active, ignoreAnim )
if ( self.m_pActiveTab == active ) then return end
if ( self.m_pActiveTab ) then
if not ignoreAnim then
xlib.addToAnimQueue( "pnlFade", { panelOut=self.m_pActiveTab:GetPanel(), panelIn=active:GetPanel() } )
else
--Run this when module permissions have changed.
xlib.addToAnimQueue( "pnlFade", { panelOut=nil, panelIn=active:GetPanel() }, 0 )
end
xlib.animQueue_start()
end
self.m_pActiveTab = active
self:InvalidateLayout()
end
--Progress bar
xgui.chunkbox = xlib.makeprogressbar{ x=420, w=180, h=20, visible=false, skin=xgui.settings.skin, parent=xgui.anchor }
function xgui.chunkbox:Progress( datatype )
self.value = self.value + 1
self:SetFraction( self.value / self.max )
self.Label:SetText( "Getting data: " .. datatype .. " - " .. string.format("%.2f", (self.value / self.max) * 100) .. "%" )
if self.value == self.max then
xgui.expectingdata = nil
self.Label:SetText( "Waiting for clientside processing..." )
xgui.queueFunctionCall( xgui.chunkbox.SetVisible, "chunkbox", xgui.chunkbox, false )
RunConsoleCommand( "_xgui", "dataComplete" )
end
end
end
------------------------
--XGUI QueueFunctionCall
------------------------
--This is essentially a straight copy of Megiddo's queueFunctionCall; Since XGUI tends to use it quite a lot, I decided to seperate it to prevent delays in ULib's stuff
--I also now get to add a method of flushing the queue based on a tag in the event that new data needs to be updated.
local stack = {}
local function onThink()
local num = #stack
if num > 0 then
for i=1,3 do --Run 3 lines per frame
if stack[1] ~= nil then
local b, e = pcall( stack[ 1 ].fn, unpack( stack[ 1 ], 1, stack[ 1 ].n ) )
if not b then
ErrorNoHalt( "XGUI queue error: " .. tostring( e ) .. "\n" )
end
end
table.remove( stack, 1 ) -- Remove the first inserted item. This is FIFO
end
else
hook.Remove( "Think", "XGUIQueueThink" )
end
end
function xgui.queueFunctionCall( fn, tag, ... )
if type( fn ) ~= "function" then
error( "queueFunctionCall received a bad function", 2 )
return
end
table.insert( stack, { fn=fn, tag=tag, n=select( "#", ... ), ... } )
hook.Add( "Think", "XGUIQueueThink", onThink, HOOK_MONITOR_HIGH )
end
function xgui.flushQueue( tag )
local removeIndecies = {}
for i, fncall in ipairs( stack ) do
if fncall.tag == tag then
table.insert( removeIndecies, i )
end
end
for i=#removeIndecies,1,-1 do --Remove the queue functions backwards to prevent desynchronization of pairs
table.remove( stack, removeIndecies[i] )
end
end
-------------------
--ULIB XGUI helpers
-------------------
--Helper function to parse access tag for a particular argument
function ulx.getTagArgNum( tag, argnum )
return tag and ULib.splitArgs( tag, "<", ">" )[argnum]
end
--Load control interpretations for ULib argument types
function ULib.cmds.BaseArg.x_getcontrol( arg, argnum, parent )
return xlib.makelabel{ label="Not Supported", parent=parent }
end
function ULib.cmds.NumArg.x_getcontrol( arg, argnum, parent )
local access, tag = LocalPlayer():query( arg.cmd )
local restrictions = {}
ULib.cmds.NumArg.processRestrictions( restrictions, arg, ulx.getTagArgNum( tag, argnum ) )
if table.HasValue( arg, ULib.cmds.allowTimeString ) then
local min = restrictions.min or 0
local max = restrictions.max or 10 * 60 * 24 * 365 --default slider max 10 years
local outPanel = xlib.makepanel{ h=40, parent=parent }
xlib.makelabel{ x=5, y=3, label="Ban Length:", parent=outPanel }
outPanel.interval = xlib.makecombobox{ x=90, w=75, parent=outPanel }
outPanel.val = xlib.makeslider{ w=165, y=20, label="<--->", min=min, max=max, value=min, decimal=0, parent=outPanel }
local divisor = {}
local sensiblemax = {}
if min == 0 then outPanel.interval:AddChoice( "Permanent" ) table.insert( divisor, 1 ) table.insert( sensiblemax, 0 ) end
if max >= 1 and min <= 60*24 then outPanel.interval:AddChoice( "Minutes" ) table.insert( divisor, 1 ) table.insert( sensiblemax, 60*24 ) end
if max >= 60 and min <= 60*24*7 then outPanel.interval:AddChoice( "Hours" ) table.insert( divisor, 60 ) table.insert( sensiblemax, 24*7 ) end
if max >= ( 60*24 ) and min <= 60*24*120 then outPanel.interval:AddChoice( "Days" ) table.insert( divisor, 60*24 ) table.insert( sensiblemax, 120 ) end
if max >= ( 60*24*7 ) and min <= 60*24*7*52 then outPanel.interval:AddChoice( "Weeks" ) table.insert( divisor, 60*24*7 ) table.insert( sensiblemax, 52 ) end
if max >= ( 60*24*365 ) then outPanel.interval:AddChoice( "Years" ) table.insert( divisor, 60*24*365 ) table.insert( sensiblemax, 10 ) end
outPanel.interval.OnSelect = function( self, index, value, data )
outPanel.val:SetDisabled( value == "Permanent" )
outPanel.val.maxvalue = math.min( max / divisor[index], sensiblemax[index] )
outPanel.val.minvalue = math.max( min / divisor[index], 0 )
outPanel.val:SetMax( outPanel.val.maxvalue )
outPanel.val:SetMin( outPanel.val.minvalue )
outPanel.val:SetValue( math.Clamp( tonumber( outPanel.val:GetValue() ), outPanel.val.minvalue, outPanel.val.maxvalue ) )
end
function outPanel.val:ValueChanged( val )
val = math.Clamp( tonumber( val ), self.minvalue or 0, self.maxvalue or 0 )
self.Slider:SetSlideX( self.Scratch:GetFraction( val ) )
if ( self.TextArea != vgui.GetKeyboardFocus() ) then
self.TextArea:SetValue( self.Scratch:GetTextValue() )
end
self:OnValueChanged( val )
end
if #outPanel.interval.Choices ~= 0 then
outPanel.interval:ChooseOptionID( 1 )
end
outPanel.GetValue = function( self )
local val, char = self:GetRawValue()
return val .. char
end
outPanel.GetRawValue = function( self )
local char = string.lower( self.interval:GetValue():sub(1,1) )
if char == "m" or char == "p" or tonumber( self.val:GetValue() ) == 0 then char = "" end
return self.val:GetValue(), char
end
outPanel.GetMinutes = function( self )
local btime, char = self:GetRawValue()
if char == "h" then btime = btime * 60
elseif char == "d" then btime = btime * 1440
elseif char == "w" then btime = btime * 10080
elseif char == "y" then btime = btime * 525600 end
return btime
end
outPanel.TextArea = outPanel.val.TextArea
return outPanel
else
local defvalue = arg.min
if table.HasValue( arg, ULib.cmds.optional ) then defvalue = arg.default end
if not defvalue then defvalue = 0 end --No default was set for this command, so we'll use 0.
local maxvalue = restrictions.max
local minvalue = restrictions.min or 0
if maxvalue == nil then
if defvalue > 100 then
maxvalue = defvalue
else
maxvalue = 100
end
end
local decimal = 0
if not table.HasValue( arg, ULib.cmds.round ) then
local minMaxDelta = maxvalue - minvalue
if minMaxDelta < 5 then
decimal = 2
elseif minMaxDelta <= 10 then
decimal = 1
end
end
local outPanel = xlib.makepanel{ h=35, parent=parent }
xlib.makelabel{ label=arg.hint or "NumArg", parent=outPanel }
outPanel.val = xlib.makeslider{ y=15, w=165, min=minvalue, max=maxvalue, value=defvalue, decimal=decimal, label="<--->", parent=outPanel }
outPanel.GetValue = function( self ) return outPanel.val.GetValue( outPanel.val ) end
outPanel.TextArea = outPanel.val.TextArea
return outPanel
end
end
function ULib.cmds.NumArg.getTime( arg )
if arg == nil or arg == "" then return nil, nil end
if arg == 0 or tonumber( arg ) == 0 then
return "Permanent", 0
end
local charPriority = { "y", "w", "d", "h" }
local charMap = { "Years", "Weeks", "Days", "Hours" }
local divisor = { 60 * 24 * 365, 60 * 24 * 7, 60 * 24, 60 }
for i, v in ipairs( charPriority ) do
if arg:find( v, 1, true ) then
if not charMap[ i ] or not divisor [ i ] or not ULib.stringTimeToSeconds( arg ) then return nil, nil end
local val = ULib.stringTimeToSeconds( arg ) / divisor[ i ]
if val == 0 then return "Permanent", 0 end
return charMap[ i ], val
end
end
return "Minutes", ULib.stringTimeToSeconds( arg )
end
function ULib.cmds.StringArg.x_getcontrol( arg, argnum, parent )
local access, tag = LocalPlayer():query( arg.cmd )
local restrictions = {}
ULib.cmds.StringArg.processRestrictions( restrictions, arg, ulx.getTagArgNum( tag, argnum ) )
local is_restricted_to_completes = table.HasValue( arg, ULib.cmds.restrictToCompletes ) -- Program-level restriction (IE, ulx map)
or restrictions.playerLevelRestriction -- The player's tag specifies only certain strings
if is_restricted_to_completes then
return xlib.makecombobox{ text=arg.hint or "StringArg", choices=restrictions.restrictedCompletes, parent=parent }
elseif restrictions.restrictedCompletes and table.Count( restrictions.restrictedCompletes ) > 0 then
-- This is where there needs to be both a drop down AND an input box
local outPanel = xlib.makecombobox{ text=arg.hint, choices=restrictions.restrictedCompletes, enableinput=true, selectall=true, parent=parent }
outPanel.OnEnter = function( self )
self:GetParent():OnEnter()
end
return outPanel
else
return xlib.maketextbox{ text=arg.hint or "StringArg", selectall=true, parent=parent }
end
end
function ULib.cmds.PlayerArg.x_getcontrol( arg, argnum, parent )
local access, tag = LocalPlayer():query( arg.cmd )
local restrictions = {}
ULib.cmds.PlayerArg.processRestrictions( restrictions, LocalPlayer(), arg, ulx.getTagArgNum( tag, argnum ) )
local outPanel = xlib.makecombobox{ text=arg.hint, parent=parent }
local targets = restrictions.restrictedTargets
if targets == false then -- No one allowed
targets = {}
elseif targets == nil then -- Everyone allowed
targets = player.GetAll()
end
for _, ply in ipairs( targets ) do
outPanel:AddChoice( ply:Nick() )
end
return outPanel
end
function ULib.cmds.CallingPlayerArg.x_getcontrol( arg, argnum, parent )
return xlib.makelabel{ label=arg.hint or "CallingPlayer", parent=parent }
end
function ULib.cmds.BoolArg.x_getcontrol( arg, argnum, parent )
local access, tag = LocalPlayer():query( arg.cmd )
local restrictions = {}
ULib.cmds.BoolArg.processRestrictions( restrictions, arg, ulx.getTagArgNum( tag, argnum ) )
local outPanel = xlib.makecheckbox{ label=arg.hint or "BoolArg", value=restrictions.restrictedTo, parent=parent }
if restrictions.restrictedTo ~= nil then outPanel:SetDisabled( true ) end
outPanel.GetValue = function( self )
return self:GetChecked() and 1 or 0
end
return outPanel
end
end

File diff suppressed because it is too large Load Diff

View File

@ -1,48 +1,48 @@
local CATEGORY_NAME = "Menus"
if ULib.fileExists( "lua/ulx/modules/cl/motdmenu.lua" ) or ulx.motdmenu_exists then
CreateConVar( "motdfile", "ulx_motd.txt" ) -- Garry likes to add and remove this cvar a lot, so it's here just in case he removes it again.
local function sendMotd( ply, showMotd )
if showMotd == "1" then -- Assume it's a file
if ply.ulxHasMotd then return end -- This player already has the motd
if not ULib.fileExists( GetConVarString( "motdfile" ) ) then return end -- Invalid
local f = ULib.fileRead( GetConVarString( "motdfile" ) )
ULib.clientRPC( ply, "ulx.rcvMotd", false, f )
ply.ulxHasMotd = true
else -- Assume URL
ULib.clientRPC( ply, "ulx.rcvMotd", true, showMotd )
ply.ulxHasMotd = nil
end
end
local function showMotd( ply )
local showMotd = GetConVarString( "ulx_showMotd" )
if showMotd == "0" then return end
if not ply:IsValid() then return end -- They left, doh!
sendMotd( ply, showMotd )
ULib.clientRPC( ply, "ulx.showMotdMenu", ply:SteamID() ) -- Passing it because they may get it before LocalPlayer() is valid
end
hook.Add( "PlayerInitialSpawn", "showMotd", showMotd )
function ulx.motd( calling_ply )
if not calling_ply:IsValid() then
Msg( "You can't see the motd from the console.\n" )
return
end
if GetConVarString( "ulx_showMotd" ) == "0" then
ULib.tsay( calling_ply, "The MOTD has been disabled on this server." )
return
end
showMotd( calling_ply )
end
local motdmenu = ulx.command( CATEGORY_NAME, "ulx motd", ulx.motd, "!motd" )
motdmenu:defaultAccess( ULib.ACCESS_ALL )
motdmenu:help( "Show the message of the day." )
if SERVER then ulx.convar( "showMotd", "1", " <0/1/(url)> - Shows the motd to clients on startup. Can specify URL here.", ULib.ACCESS_ADMIN ) end
end
local CATEGORY_NAME = "Menus"
if ULib.fileExists( "lua/ulx/modules/cl/motdmenu.lua" ) or ulx.motdmenu_exists then
CreateConVar( "motdfile", "ulx_motd.txt" ) -- Garry likes to add and remove this cvar a lot, so it's here just in case he removes it again.
local function sendMotd( ply, showMotd )
if showMotd == "1" then -- Assume it's a file
if ply.ulxHasMotd then return end -- This player already has the motd
if not ULib.fileExists( GetConVarString( "motdfile" ) ) then return end -- Invalid
local f = ULib.fileRead( GetConVarString( "motdfile" ) )
ULib.clientRPC( ply, "ulx.rcvMotd", false, f )
ply.ulxHasMotd = true
else -- Assume URL
ULib.clientRPC( ply, "ulx.rcvMotd", true, showMotd )
ply.ulxHasMotd = nil
end
end
local function showMotd( ply )
local showMotd = GetConVarString( "ulx_showMotd" )
if showMotd == "0" then return end
if not ply:IsValid() then return end -- They left, doh!
sendMotd( ply, showMotd )
ULib.clientRPC( ply, "ulx.showMotdMenu", ply:SteamID() ) -- Passing it because they may get it before LocalPlayer() is valid
end
hook.Add( "PlayerInitialSpawn", "showMotd", showMotd )
function ulx.motd( calling_ply )
if not calling_ply:IsValid() then
Msg( "You can't see the motd from the console.\n" )
return
end
if GetConVarString( "ulx_showMotd" ) == "0" then
ULib.tsay( calling_ply, "The MOTD has been disabled on this server." )
return
end
showMotd( calling_ply )
end
local motdmenu = ulx.command( CATEGORY_NAME, "ulx motd", ulx.motd, "!motd" )
motdmenu:defaultAccess( ULib.ACCESS_ALL )
motdmenu:help( "Show the message of the day." )
if SERVER then ulx.convar( "showMotd", "1", " <0/1/(url)> - Shows the motd to clients on startup. Can specify URL here.", ULib.ACCESS_ADMIN ) end
end

View File

@ -1,85 +1,85 @@
local help = [[
General User Management Concepts:
User access is driven by ULib's Ulysses Control List (UCL). This list contains users and groups
which in turn contains lists of allowed and denied accesses. The allow and deny lists contain
access strings like "ulx slap" or "ulx phygunplayer" to show what a user and/or group does and does
not have access to. If a user has "ulx slap" in their user allow list or in the allow list of one
of the groups they belong to, they have access to slap. If a user has "ulx slap" in their user deny
list they are DENIED the command, even if they have the command in one of their group's allow
lists. In this way, deny takes precedence over allow.
ULib supports immunity by being able to specify what various users and groups are allowed to
target. This is often used to make it so lower admins cannot target higher admins. EG, by default
admins can't target superadmins, but superadmins can still target admins.
More Advanced Concepts:
Groups have inheritance. You can specify what group they inherit from in the addgroup command. If a
user is in a group that has inheritance, UCL will check all groups connected in the inheritance
chain. Note that groups do not support deny lists for the sake of simplicity. If you feel that a
group needs to be denied something, you should split your groups up instead.
The "user" group applies to everyone who does not otherwise belong in a group. You can use
groupallow on this group just like any other, just remember that everyone is being allowed access.
ULib supports an advanced, highly configurable permission system by using "access tags". Access
tags specify what a user is allowed to pass as arguments to a command. For example, you can make it
so that admins are only allowed to slay users with "killme" somewhere in their name, or you can
give everyone access to the "ulx teleport" command, but only allow them to teleport themselves.
Examples of using access tags are given below in the userallow and groupallow commands. The format
for access tags is as follows. Each argument that is passed to the command can be limited by the
access tag. Each argument being limited must be listed in the same order as in the command,
separated by spaces. If you don't want to limit an argument, use a star ("*"). EG, to limit "ulx
slap" damage to 0 through 10, but still allow it to be used on anyone, use the tag "* 0:10".
User Management Commands:
ulx adduser <user> <group> - Add the specified CONNECTED player to the specified group.
The group MUST exist for this command to succeed. Use operator, admin, superadmin, or see ulx
addgroup. You can only specify one group. See above for explanation on immunity.
Ex 1. ulx adduser "Someguy" superadmin -- This will add the connected "Someguy" as a superadmin
Ex 2. ulx adduser "Dood" monkey -- This will add the connected "Dood" to the group monkey
on the condition that the group exists
ulx removeuser <user> - Remove the specified connected player from the permanent access list.
Ex 1. ulx removeuser "Foo bar" -- This removes the user "Foo bar"
ulx userallow <user> <access> [<access tag>] - Puts the access on the USER'S ALLOW list, with
optional access tag (see above)
See above for explanation of allow list vs. deny list, as well as how access strings/tags work.
Ex 1. ulx userallow "Pi" "ulx slap" -- This grants the user access to "ulx slap"
Ex 2. ulx userallow "Pi" "ulx slap" "!%admin 0" -- This grants the user access to "ulx slap"
-- but they can only slap users lower than an admin, and they can only slap for 0 damage
ulx userdeny <user> <access> [<revoke>] - Removes a player's access. If revoke is true, this simply
removes the access string from the user's allow/deny lists instead of adding it to the user's
deny list. See above for an explanation on the deny list.
ulx addgroup <group> [<inherits from>] - Creates a group, optionally inheriting from the specified
group. See above for explanation on inheritance.
ulx removegroup <group> - Removes a group PERMANENTLY. Also removes the group from all connected
users and all users who connect in the future. If a user has no group besides this, they will
become guests. Please be VERY careful with this command!
ulx renamegroup <current group> <new group> - Renames a group
ulx setgroupcantarget <group> [<target string>] - Limits what users a group can target. Pass no
argument to clear the restriction.
Ex 1. ulx setgroupcantarget user !%admin - Guests cannot target admins or above
Ex 2. ulx setgroupcantarget admin !^ - Admins cannot target themselves
ulx groupallow <group> <access> [<access tag>] - Puts the access on the group's allow list. See
above on how access strings/tags work.
ulx groupdeny <group> <access> - Removes the group's access.
]]
function ulx.showUserHelp()
local lines = ULib.explode( "\n", help )
for _, line in ipairs( lines ) do
Msg( line .. "\n" )
end
end
local help = [[
General User Management Concepts:
User access is driven by ULib's Ulysses Control List (UCL). This list contains users and groups
which in turn contains lists of allowed and denied accesses. The allow and deny lists contain
access strings like "ulx slap" or "ulx phygunplayer" to show what a user and/or group does and does
not have access to. If a user has "ulx slap" in their user allow list or in the allow list of one
of the groups they belong to, they have access to slap. If a user has "ulx slap" in their user deny
list they are DENIED the command, even if they have the command in one of their group's allow
lists. In this way, deny takes precedence over allow.
ULib supports immunity by being able to specify what various users and groups are allowed to
target. This is often used to make it so lower admins cannot target higher admins. EG, by default
admins can't target superadmins, but superadmins can still target admins.
More Advanced Concepts:
Groups have inheritance. You can specify what group they inherit from in the addgroup command. If a
user is in a group that has inheritance, UCL will check all groups connected in the inheritance
chain. Note that groups do not support deny lists for the sake of simplicity. If you feel that a
group needs to be denied something, you should split your groups up instead.
The "user" group applies to everyone who does not otherwise belong in a group. You can use
groupallow on this group just like any other, just remember that everyone is being allowed access.
ULib supports an advanced, highly configurable permission system by using "access tags". Access
tags specify what a user is allowed to pass as arguments to a command. For example, you can make it
so that admins are only allowed to slay users with "killme" somewhere in their name, or you can
give everyone access to the "ulx teleport" command, but only allow them to teleport themselves.
Examples of using access tags are given below in the userallow and groupallow commands. The format
for access tags is as follows. Each argument that is passed to the command can be limited by the
access tag. Each argument being limited must be listed in the same order as in the command,
separated by spaces. If you don't want to limit an argument, use a star ("*"). EG, to limit "ulx
slap" damage to 0 through 10, but still allow it to be used on anyone, use the tag "* 0:10".
User Management Commands:
ulx adduser <user> <group> - Add the specified CONNECTED player to the specified group.
The group MUST exist for this command to succeed. Use operator, admin, superadmin, or see ulx
addgroup. You can only specify one group. See above for explanation on immunity.
Ex 1. ulx adduser "Someguy" superadmin -- This will add the connected "Someguy" as a superadmin
Ex 2. ulx adduser "Dood" monkey -- This will add the connected "Dood" to the group monkey
on the condition that the group exists
ulx removeuser <user> - Remove the specified connected player from the permanent access list.
Ex 1. ulx removeuser "Foo bar" -- This removes the user "Foo bar"
ulx userallow <user> <access> [<access tag>] - Puts the access on the USER'S ALLOW list, with
optional access tag (see above)
See above for explanation of allow list vs. deny list, as well as how access strings/tags work.
Ex 1. ulx userallow "Pi" "ulx slap" -- This grants the user access to "ulx slap"
Ex 2. ulx userallow "Pi" "ulx slap" "!%admin 0" -- This grants the user access to "ulx slap"
-- but they can only slap users lower than an admin, and they can only slap for 0 damage
ulx userdeny <user> <access> [<revoke>] - Removes a player's access. If revoke is true, this simply
removes the access string from the user's allow/deny lists instead of adding it to the user's
deny list. See above for an explanation on the deny list.
ulx addgroup <group> [<inherits from>] - Creates a group, optionally inheriting from the specified
group. See above for explanation on inheritance.
ulx removegroup <group> - Removes a group PERMANENTLY. Also removes the group from all connected
users and all users who connect in the future. If a user has no group besides this, they will
become guests. Please be VERY careful with this command!
ulx renamegroup <current group> <new group> - Renames a group
ulx setgroupcantarget <group> [<target string>] - Limits what users a group can target. Pass no
argument to clear the restriction.
Ex 1. ulx setgroupcantarget user !%admin - Guests cannot target admins or above
Ex 2. ulx setgroupcantarget admin !^ - Admins cannot target themselves
ulx groupallow <group> <access> [<access tag>] - Puts the access on the group's allow list. See
above on how access strings/tags work.
ulx groupdeny <group> <access> - Removes the group's access.
]]
function ulx.showUserHelp()
local lines = ULib.explode( "\n", help )
for _, line in ipairs( lines ) do
Msg( line .. "\n" )
end
end

View File

@ -1,89 +1,89 @@
ulx.convar( "rslotsMode", "0", " - Sets the slots mode. See config for more information.", ULib.ACCESS_ADMIN )
ulx.convar( "rslots", "2", " - Sets the number of reserved slots, only applicable for modes 1 and 2.", ULib.ACCESS_ADMIN )
ulx.convar( "rslotsVisible", "1", " - Sets whether slots are visible. See config for more information.", ULib.ACCESS_ADMIN )
local access = "ulx reservedslots" -- Access string needed for reserved slots
ULib.ucl.registerAccess( access, ULib.ACCESS_ADMIN, "Access to reserved slots", "Other" ) -- Give admins access to reserved slots by default
function calcSlots( disconnect )
local mode = GetConVarNumber( "ulx_rslotsMode" )
if mode == 3 then return 1 end -- Only one slot on this mode
local slots = GetConVarNumber( "ulx_rslots" )
if mode == 2 then return slots end
if mode == 1 then
local admins = 0 -- Keep track of how many people with access we have
local players = player.GetAll()
for _, player in ipairs( players ) do
if player:IsConnected() and player:query( access ) then
admins = admins + 1
end
end
if disconnect then admins = admins - 1 end -- Otherwise we're counting the disconnecting admin
if admins < 0 then admins = 0 end -- Just to be safe!
local rslots = slots - admins
if rslots < 0 then rslots = 0 end -- If we have more admins right now then slots for them, we don't want to return a negative number.
return rslots
end
return 0 -- We're actually having an error if we get here, but let's handle it gracefully
end
local function updateSlots( ply, disconnect )
local visible = ULib.toBool( GetConVarString( "ulx_rslotsVisible" ) )
if not visible then -- Make sure our visible slots is up to date
local slots = calcSlots( disconnect )
local max = game.MaxPlayers()
game.ConsoleCommand( "sv_visiblemaxplayers " .. max - slots .. "\n" )
end
end
hook.Add( "PlayerDisconnected", "ulxSlotsDisconnect", function( ply ) updateSlots( ply, ply:query( access ) ) end )
hook.Add( ulx.HOOK_ULXDONELOADING, "ULXUpdateSlots", updateSlots )
local function playerAccess( ply )
local mode = GetConVarNumber( "ulx_rslotsMode" )
if mode == 0 then return end -- Off!
local visible = ULib.toBool( GetConVarString( "ulx_rslotsVisible" ) )
local slots = calcSlots()
local cur = #player.GetAll()
local max = game.MaxPlayers()
if ply:query( access ) then -- If they have access, handle this differently
if not visible then -- Make sure our visible slots is up to date
updateSlots()
end
if mode == 3 and cur + slots > max then -- We've got some kicking to do!
local shortestply
local shortesttime = math.huge
local players = player.GetAll()
for _, player in ipairs( players ) do
if not ULib.ucl.query( player, access ) then
if player:TimeConnected() < shortesttime then
shortesttime = player:TimeConnected()
shortestply = player
end
end
end
if not shortestply then -- We've got a server filled to the brim with admins? Odd but okay
return
end
ULib.kick( shortestply, "[ULX] Freeing slot. Sorry, you had the shortest connection time." )
end
return
end
if cur + slots > max then
ULib.queueFunctionCall( ULib.kick, ply, "[ULX] Reserved slot, sorry!" ) -- Wait a frame so all access hooks can be called properly.
end
end
hook.Add( ULib.HOOK_UCLAUTH, "ULXReservedSlots", playerAccess, HOOK_MONITOR_LOW ) -- Run at the end of auth
ulx.convar( "rslotsMode", "0", " - Sets the slots mode. See config for more information.", ULib.ACCESS_ADMIN )
ulx.convar( "rslots", "2", " - Sets the number of reserved slots, only applicable for modes 1 and 2.", ULib.ACCESS_ADMIN )
ulx.convar( "rslotsVisible", "1", " - Sets whether slots are visible. See config for more information.", ULib.ACCESS_ADMIN )
local access = "ulx reservedslots" -- Access string needed for reserved slots
ULib.ucl.registerAccess( access, ULib.ACCESS_ADMIN, "Access to reserved slots", "Other" ) -- Give admins access to reserved slots by default
function calcSlots( disconnect )
local mode = GetConVarNumber( "ulx_rslotsMode" )
if mode == 3 then return 1 end -- Only one slot on this mode
local slots = GetConVarNumber( "ulx_rslots" )
if mode == 2 then return slots end
if mode == 1 then
local admins = 0 -- Keep track of how many people with access we have
local players = player.GetAll()
for _, player in ipairs( players ) do
if player:IsConnected() and player:query( access ) then
admins = admins + 1
end
end
if disconnect then admins = admins - 1 end -- Otherwise we're counting the disconnecting admin
if admins < 0 then admins = 0 end -- Just to be safe!
local rslots = slots - admins
if rslots < 0 then rslots = 0 end -- If we have more admins right now then slots for them, we don't want to return a negative number.
return rslots
end
return 0 -- We're actually having an error if we get here, but let's handle it gracefully
end
local function updateSlots( ply, disconnect )
local visible = ULib.toBool( GetConVarString( "ulx_rslotsVisible" ) )
if not visible then -- Make sure our visible slots is up to date
local slots = calcSlots( disconnect )
local max = game.MaxPlayers()
game.ConsoleCommand( "sv_visiblemaxplayers " .. max - slots .. "\n" )
end
end
hook.Add( "PlayerDisconnected", "ulxSlotsDisconnect", function( ply ) updateSlots( ply, ply:query( access ) ) end )
hook.Add( ulx.HOOK_ULXDONELOADING, "ULXUpdateSlots", updateSlots )
local function playerAccess( ply )
local mode = GetConVarNumber( "ulx_rslotsMode" )
if mode == 0 then return end -- Off!
local visible = ULib.toBool( GetConVarString( "ulx_rslotsVisible" ) )
local slots = calcSlots()
local cur = #player.GetAll()
local max = game.MaxPlayers()
if ply:query( access ) then -- If they have access, handle this differently
if not visible then -- Make sure our visible slots is up to date
updateSlots()
end
if mode == 3 and cur + slots > max then -- We've got some kicking to do!
local shortestply
local shortesttime = math.huge
local players = player.GetAll()
for _, player in ipairs( players ) do
if not ULib.ucl.query( player, access ) then
if player:TimeConnected() < shortesttime then
shortesttime = player:TimeConnected()
shortestply = player
end
end
end
if not shortestply then -- We've got a server filled to the brim with admins? Odd but okay
return
end
ULib.kick( shortestply, "[ULX] Freeing slot. Sorry, you had the shortest connection time." )
end
return
end
if cur + slots > max then
ULib.queueFunctionCall( ULib.kick, ply, "[ULX] Reserved slot, sorry!" ) -- Wait a frame so all access hooks can be called properly.
end
end
hook.Add( ULib.HOOK_UCLAUTH, "ULXReservedSlots", playerAccess, HOOK_MONITOR_LOW ) -- Run at the end of auth

View File

@ -1,181 +1,181 @@
------------------
--Public votemap--
------------------
ulx.votemaps = ulx.votemaps or {}
local specifiedMaps = {}
local function init()
local mode = GetConVarNumber( "ulx_votemapMapmode" ) or 1
if mode == 1 then -- Add all but specified
local maps = file.Find( "maps/*.bsp", "GAME" )
for _, map in ipairs( maps ) do
map = map:sub( 1, -5 ) -- Take off .bsp
if not specifiedMaps[ map ] then
table.insert( ulx.votemaps, map )
end
end
else
for map, _ in pairs( specifiedMaps ) do
if ULib.fileExists( "maps/" .. map .. ".bsp" ) then
table.insert( ulx.votemaps, map )
end
end
end
-- Now, let's sort!
table.sort( ulx.votemaps )
end
hook.Add( ulx.HOOK_ULXDONELOADING, "ULXInitConfigs", init ) -- Time for configs
local userMapvote = {} -- Indexed by player.
local mapvotes = {} -- Indexed by map.
ulx.timedVeto = nil
ulx.convar( "votemapEnabled", "1", _, ULib.ACCESS_ADMIN ) -- Enable/Disable the entire votemap command
ulx.convar( "votemapMintime", "10", _, ULib.ACCESS_ADMIN ) -- Time after map change before votes count.
ulx.convar( "votemapWaittime", "5", _, ULib.ACCESS_ADMIN ) -- Time before a user must wait before they can change their vote.
ulx.convar( "votemapSuccessratio", "0.5", _, ULib.ACCESS_ADMIN ) -- Ratio of (votes for map)/(total players) needed to change map. (Rounds up)
ulx.convar( "votemapMinvotes", "3", _, ULib.ACCESS_ADMIN ) -- Number of minimum votes needed to change map (Prevents llamas). This supercedes the above convar on small servers.
ulx.convar( "votemapVetotime", "30", _, ULib.ACCESS_ADMIN ) -- Time in seconds an admin has after a successful votemap to veto the vote. Set to 0 to disable.
ulx.convar( "votemapMapmode", "1", _, ULib.ACCESS_ADMIN ) -- 1 = Use all maps but what's specified below, 2 = Use only the maps specified below.
function ulx.votemapVeto( calling_ply )
if not ulx.timedVeto then
ULib.tsayError( calling_ply, "There's nothing to veto!", true )
return
end
timer.Remove( "ULXVotemap" )
ulx.timedVeto = nil
hook.Call( ulx.HOOK_VETO )
ULib.tsay( _, "Votemap changelevel halted.", true )
ulx.logServAct( calling_ply, "#A vetoed the votemap" )
end
-- The command is defined at the end of vote.lua
function ulx.votemapAddMap( map )
specifiedMaps[ map ] = true
end
function ulx.clearVotemaps()
table.Empty( specifiedMaps )
end
function ulx.votemap( calling_ply, map )
if not ULib.toBool( GetConVarNumber( "ulx_votemapEnabled" ) ) then
ULib.tsayError( calling_ply, "The votemap command has been disabled by a server admin.", true )
return
end
if not calling_ply:IsValid() then
Msg( "You can't use votemap from the dedicated server console.\n" )
return
end
if ulx.timedVeto then
ULib.tsayError( calling_ply, "You cannot vote right now, another map has already won and is pending approval.", true )
return
end
if not map or map == "" then
ULib.tsay( calling_ply, "Map list printed to console", true )
ULib.console( calling_ply, "Use \"votemap <id>\" to vote for a map. Map list:" )
for id, map in ipairs( ulx.votemaps ) do
ULib.console( calling_ply, " " .. id .. " -\t" .. map )
end
return
end
local mintime = tonumber( GetConVarString( "ulx_votemapMintime" ) ) or 10
if CurTime() < mintime * 60 then -- Minutes -> seconds
ULib.tsayError( calling_ply, "Sorry, you must wait " .. mintime .. " minutes after a map change before you can vote for another map.", true )
local timediff = mintime*60 - CurTime()
ULib.tsayError( calling_ply, "That means you must wait " .. string.FormattedTime( math.fmod( timediff, 3600 ), (mintime < 60) and "%02i:%02i" or math.floor( timediff/3600 ) .. " hour(s) and %02i:%02i" ) .. " more minutes.", true )
return
end
if userMapvote[ calling_ply ] then
local waittime = tonumber( GetConVarString( "ulx_votemapWaittime" ) ) or 5
if CurTime() - userMapvote[ calling_ply ].time < waittime * 60 then -- Minutes -> seconds
ULib.tsayError( calling_ply, "Sorry, you must wait " .. waittime .. " minutes before changing your vote.", true )
local timediff = waittime*60 - (CurTime() - userMapvote[ calling_ply ].time)
ULib.tsayError( calling_ply, "That means you must wait " .. string.FormattedTime( math.fmod( timediff, 3600 ), (waittime < 60) and "%02i:%02i" or math.floor( timediff/3600 ) .. " hour(s) and %02i:%02i" ) .. " more minutes.", true )
return
end
end
local mapid
if tonumber( map ) then
mapid = tonumber( map )
if not ulx.votemaps[ mapid ] then
ULib.tsayError( calling_ply, "Invalid map id!", true )
return
end
else
if string.sub( map, -4 ) == ".bsp" then
map = string.sub( map, 1, -5 ) -- Take off the .bsp
end
mapid = ULib.findInTable( ulx.votemaps, map )
if not mapid then
ULib.tsayError( calling_ply, "Invalid map!", true )
return
end
end
if userMapvote[ calling_ply ] then -- Take away from their previous vote
mapvotes[ userMapvote[ calling_ply ].mapid ] = mapvotes[ userMapvote[ calling_ply ].mapid ] - 1
end
userMapvote[ calling_ply ] = { mapid=mapid, time=CurTime() }
mapvotes[ mapid ] = mapvotes[ mapid ] or 0
mapvotes[ mapid ] = mapvotes[ mapid ] + 1
local minvotes = tonumber( GetConVarString( "ulx_votemapMinvotes" ) ) or 0
local successratio = tonumber( GetConVarString( "ulx_votemapSuccessratio" ) ) or 0.5
local votes_needed = math.ceil( math.max( minvotes, successratio * #player.GetAll() ) ) -- Round up whatever the largest is.
-- TODO, color?
ULib.tsay( _, string.format( "%s voted for %s (%i/%i). Say \"!votemap %i\" to vote for this map too.", calling_ply:Nick(), ulx.votemaps[ mapid ], mapvotes[ mapid ], votes_needed, mapid ), true )
ulx.logString( string.format( "%s voted for %s (%i/%i)", calling_ply:Nick(), ulx.votemaps[ mapid ], mapvotes[ mapid ], votes_needed ) )
if mapvotes[ mapid ] >= votes_needed then
local vetotime = tonumber( GetConVarString( "ulx_votemapVetotime" ) ) or 30
local admins = {}
local players = player.GetAll()
for _, player in ipairs( players ) do
if player:IsConnected() then
if ULib.ucl.query( player, "ulx veto" ) then
table.insert( admins, player )
end
end
end
if #admins <= 0 or vetotime < 1 then
ULib.tsay( _, "Vote for map " .. ulx.votemaps[ mapid ] .. " successful! Changing levels now.", true ) -- TODO, color?
ulx.logString( "Votemap for " .. ulx.votemaps[ mapid ] .. " won." )
game.ConsoleCommand( "changelevel " .. ulx.votemaps[ mapid ] .. "\n" )
else
ULib.tsay( _, "Vote for map " .. ulx.votemaps[ mapid ] .. " successful! Now pending admin approval. (" .. vetotime .. " seconds)", true ) -- TODO, color?
for _, player in ipairs( admins ) do
ULib.tsay( player, "To veto this vote, just say \"!veto\"", true ) -- TODO, color?
end
ulx.logString( "Votemap for " .. ulx.votemaps[ mapid ] .. " won. Pending admin veto." )
ulx.timedVeto = true
hook.Call( ulx.HOOK_VETO )
timer.Create( "ULXVotemap", vetotime, 1, function() game.ConsoleCommand( "changelevel " .. ulx.votemaps[ mapid ] .. "\n" ) end )
end
end
end
-- This command is defined at the bottom of vote.lua
function ulx.votemap_disconnect( ply ) -- We use this to clear out old people's votes
if userMapvote[ ply ] then -- Take away from their previous vote
mapvotes[ userMapvote[ ply ].mapid ] = mapvotes[ userMapvote[ ply ].mapid ] - 1
userMapvote[ ply ] = nil
end
end
hook.Add( "PlayerDisconnected", "ULXVoteDisconnect", ulx.votemap_disconnect )
------------------
--Public votemap--
------------------
ulx.votemaps = ulx.votemaps or {}
local specifiedMaps = {}
local function init()
local mode = GetConVarNumber( "ulx_votemapMapmode" ) or 1
if mode == 1 then -- Add all but specified
local maps = file.Find( "maps/*.bsp", "GAME" )
for _, map in ipairs( maps ) do
map = map:sub( 1, -5 ) -- Take off .bsp
if not specifiedMaps[ map ] then
table.insert( ulx.votemaps, map )
end
end
else
for map, _ in pairs( specifiedMaps ) do
if ULib.fileExists( "maps/" .. map .. ".bsp" ) then
table.insert( ulx.votemaps, map )
end
end
end
-- Now, let's sort!
table.sort( ulx.votemaps )
end
hook.Add( ulx.HOOK_ULXDONELOADING, "ULXInitConfigs", init ) -- Time for configs
local userMapvote = {} -- Indexed by player.
local mapvotes = {} -- Indexed by map.
ulx.timedVeto = nil
ulx.convar( "votemapEnabled", "1", _, ULib.ACCESS_ADMIN ) -- Enable/Disable the entire votemap command
ulx.convar( "votemapMintime", "10", _, ULib.ACCESS_ADMIN ) -- Time after map change before votes count.
ulx.convar( "votemapWaittime", "5", _, ULib.ACCESS_ADMIN ) -- Time before a user must wait before they can change their vote.
ulx.convar( "votemapSuccessratio", "0.5", _, ULib.ACCESS_ADMIN ) -- Ratio of (votes for map)/(total players) needed to change map. (Rounds up)
ulx.convar( "votemapMinvotes", "3", _, ULib.ACCESS_ADMIN ) -- Number of minimum votes needed to change map (Prevents llamas). This supercedes the above convar on small servers.
ulx.convar( "votemapVetotime", "30", _, ULib.ACCESS_ADMIN ) -- Time in seconds an admin has after a successful votemap to veto the vote. Set to 0 to disable.
ulx.convar( "votemapMapmode", "1", _, ULib.ACCESS_ADMIN ) -- 1 = Use all maps but what's specified below, 2 = Use only the maps specified below.
function ulx.votemapVeto( calling_ply )
if not ulx.timedVeto then
ULib.tsayError( calling_ply, "There's nothing to veto!", true )
return
end
timer.Remove( "ULXVotemap" )
ulx.timedVeto = nil
hook.Call( ulx.HOOK_VETO )
ULib.tsay( _, "Votemap changelevel halted.", true )
ulx.logServAct( calling_ply, "#A vetoed the votemap" )
end
-- The command is defined at the end of vote.lua
function ulx.votemapAddMap( map )
specifiedMaps[ map ] = true
end
function ulx.clearVotemaps()
table.Empty( specifiedMaps )
end
function ulx.votemap( calling_ply, map )
if not ULib.toBool( GetConVarNumber( "ulx_votemapEnabled" ) ) then
ULib.tsayError( calling_ply, "The votemap command has been disabled by a server admin.", true )
return
end
if not calling_ply:IsValid() then
Msg( "You can't use votemap from the dedicated server console.\n" )
return
end
if ulx.timedVeto then
ULib.tsayError( calling_ply, "You cannot vote right now, another map has already won and is pending approval.", true )
return
end
if not map or map == "" then
ULib.tsay( calling_ply, "Map list printed to console", true )
ULib.console( calling_ply, "Use \"votemap <id>\" to vote for a map. Map list:" )
for id, map in ipairs( ulx.votemaps ) do
ULib.console( calling_ply, " " .. id .. " -\t" .. map )
end
return
end
local mintime = tonumber( GetConVarString( "ulx_votemapMintime" ) ) or 10
if CurTime() < mintime * 60 then -- Minutes -> seconds
ULib.tsayError( calling_ply, "Sorry, you must wait " .. mintime .. " minutes after a map change before you can vote for another map.", true )
local timediff = mintime*60 - CurTime()
ULib.tsayError( calling_ply, "That means you must wait " .. string.FormattedTime( math.fmod( timediff, 3600 ), (mintime < 60) and "%02i:%02i" or math.floor( timediff/3600 ) .. " hour(s) and %02i:%02i" ) .. " more minutes.", true )
return
end
if userMapvote[ calling_ply ] then
local waittime = tonumber( GetConVarString( "ulx_votemapWaittime" ) ) or 5
if CurTime() - userMapvote[ calling_ply ].time < waittime * 60 then -- Minutes -> seconds
ULib.tsayError( calling_ply, "Sorry, you must wait " .. waittime .. " minutes before changing your vote.", true )
local timediff = waittime*60 - (CurTime() - userMapvote[ calling_ply ].time)
ULib.tsayError( calling_ply, "That means you must wait " .. string.FormattedTime( math.fmod( timediff, 3600 ), (waittime < 60) and "%02i:%02i" or math.floor( timediff/3600 ) .. " hour(s) and %02i:%02i" ) .. " more minutes.", true )
return
end
end
local mapid
if tonumber( map ) then
mapid = tonumber( map )
if not ulx.votemaps[ mapid ] then
ULib.tsayError( calling_ply, "Invalid map id!", true )
return
end
else
if string.sub( map, -4 ) == ".bsp" then
map = string.sub( map, 1, -5 ) -- Take off the .bsp
end
mapid = ULib.findInTable( ulx.votemaps, map )
if not mapid then
ULib.tsayError( calling_ply, "Invalid map!", true )
return
end
end
if userMapvote[ calling_ply ] then -- Take away from their previous vote
mapvotes[ userMapvote[ calling_ply ].mapid ] = mapvotes[ userMapvote[ calling_ply ].mapid ] - 1
end
userMapvote[ calling_ply ] = { mapid=mapid, time=CurTime() }
mapvotes[ mapid ] = mapvotes[ mapid ] or 0
mapvotes[ mapid ] = mapvotes[ mapid ] + 1
local minvotes = tonumber( GetConVarString( "ulx_votemapMinvotes" ) ) or 0
local successratio = tonumber( GetConVarString( "ulx_votemapSuccessratio" ) ) or 0.5
local votes_needed = math.ceil( math.max( minvotes, successratio * #player.GetAll() ) ) -- Round up whatever the largest is.
-- TODO, color?
ULib.tsay( _, string.format( "%s voted for %s (%i/%i). Say \"!votemap %i\" to vote for this map too.", calling_ply:Nick(), ulx.votemaps[ mapid ], mapvotes[ mapid ], votes_needed, mapid ), true )
ulx.logString( string.format( "%s voted for %s (%i/%i)", calling_ply:Nick(), ulx.votemaps[ mapid ], mapvotes[ mapid ], votes_needed ) )
if mapvotes[ mapid ] >= votes_needed then
local vetotime = tonumber( GetConVarString( "ulx_votemapVetotime" ) ) or 30
local admins = {}
local players = player.GetAll()
for _, player in ipairs( players ) do
if player:IsConnected() then
if ULib.ucl.query( player, "ulx veto" ) then
table.insert( admins, player )
end
end
end
if #admins <= 0 or vetotime < 1 then
ULib.tsay( _, "Vote for map " .. ulx.votemaps[ mapid ] .. " successful! Changing levels now.", true ) -- TODO, color?
ulx.logString( "Votemap for " .. ulx.votemaps[ mapid ] .. " won." )
game.ConsoleCommand( "changelevel " .. ulx.votemaps[ mapid ] .. "\n" )
else
ULib.tsay( _, "Vote for map " .. ulx.votemaps[ mapid ] .. " successful! Now pending admin approval. (" .. vetotime .. " seconds)", true ) -- TODO, color?
for _, player in ipairs( admins ) do
ULib.tsay( player, "To veto this vote, just say \"!veto\"", true ) -- TODO, color?
end
ulx.logString( "Votemap for " .. ulx.votemaps[ mapid ] .. " won. Pending admin veto." )
ulx.timedVeto = true
hook.Call( ulx.HOOK_VETO )
timer.Create( "ULXVotemap", vetotime, 1, function() game.ConsoleCommand( "changelevel " .. ulx.votemaps[ mapid ] .. "\n" ) end )
end
end
end
-- This command is defined at the bottom of vote.lua
function ulx.votemap_disconnect( ply ) -- We use this to clear out old people's votes
if userMapvote[ ply ] then -- Take away from their previous vote
mapvotes[ userMapvote[ ply ].mapid ] = mapvotes[ userMapvote[ ply ].mapid ] - 1
userMapvote[ ply ] = nil
end
end
hook.Add( "PlayerDisconnected", "ULXVoteDisconnect", ulx.votemap_disconnect )

View File

@ -1,302 +1,302 @@
--Server stuff for the GUI for ULX --by Stickly Man!
xgui = xgui or {}
xgui.svmodules = {}
function xgui.addSVModule( name, initFunc, postinitFunc )
local found = false
for i, svmodule in pairs( xgui.svmodules ) do
if svmodule.name == name then
table.remove( xgui.svmodules, i )
found = true
break
end
end
table.insert( xgui.svmodules, { name=name, init=initFunc, postinit=postinitFunc } )
if found then -- Autorefresh
initFunc()
postinitFunc()
end
end
Msg( "///////////////////////////////\n" )
Msg( "// ULX GUI -- by Stickly Man //\n" )
Msg( "///////////////////////////////\n" )
Msg( "// Adding Main Modules.. //\n" )
for _, file in ipairs( file.Find( "ulx/xgui/*.lua", "LUA" ) ) do
AddCSLuaFile( "ulx/xgui/" .. file )
Msg( "// " .. file .. string.rep( " ", 25 - file:len() ) .. "//\n" )
end
Msg( "// Adding Setting Modules.. //\n" )
for _, file in ipairs( file.Find( "ulx/xgui/settings/*.lua", "LUA" ) ) do
AddCSLuaFile( "ulx/xgui/settings/" .. file )
Msg( "// " .. file .. string.rep( " ", 25 - file:len() ) .. "//\n" )
end
Msg( "// Adding Gamemode Modules.. //\n" )
for _, file in ipairs( file.Find( "ulx/xgui/gamemodes/*.lua", "LUA" ) ) do
AddCSLuaFile( "ulx/xgui/gamemodes/" .. file )
Msg( "// " .. file .. string.rep( " ", 25 - file:len() ) .. "//\n" )
end
Msg( "// Loading Server Modules.. //\n" )
for _, file in ipairs( file.Find( "ulx/xgui/server/*.lua", "LUA" ) ) do
include( "ulx/xgui/server/" .. file )
Msg( "// " .. file .. string.rep( " ", 25 - file:len() ) .. "//\n" )
end
Msg( "// XGUI modules added! //\n" )
Msg( "///////////////////////////////\n" )
function xgui.init()
local function xgui_chatCommand( ply, func, args )
if ply:IsValid() then
ULib.clientRPC( ply, "xgui.toggle", args )
end
end
ULib.addSayCommand( "!xgui", xgui_chatCommand )
ULib.addSayCommand( "!menu", xgui_chatCommand )
--XGUI command stuff
xgui.cmds = {}
function xgui.addCmd( name, func )
xgui.cmds[name] = func
end
function xgui.cmd( ply, cmd, args )
local name=args[1]
table.remove( args, 1 )
if xgui.cmds[name] then
xgui.cmds[name]( ply, args )
else
ULib.tsay( ply, "XGUI: Command " .. ( name or "<none>" ) .. " not recognized!" )
end
end
concommand.Add( "_xgui", xgui.cmd )
-----------------
--XGUI data stuff
-----------------
xgui.dataTypes = {}
xgui.activeUsers = {} --Set up a table to list users who are actively transferring data
xgui.readyPlayers = {} --Set up a table to store users who are ready to receive data.
function xgui.addDataType( name, retrievalFunc, access, maxChunkSize, priority )
xgui.dataTypes[name] = { getData=retrievalFunc, access=access, maxchunk=maxChunkSize }
table.insert( xgui.dataTypes, { name=name, priority=priority or 0 } )
table.sort( xgui.dataTypes, function(a,b) return a.priority < b.priority end )
end
function xgui.getdata( ply, args )
xgui.sendDataTable( ply, args )
end
xgui.addCmd( "getdata", xgui.getdata )
--Let the server know when players are/aren't ready to receive data.
function xgui.getInstalled( ply )
ULib.clientRPC( ply, "xgui.getInstalled" )
xgui.readyPlayers[ply:UniqueID()] = 1
end
xgui.addCmd( "getInstalled", xgui.getInstalled )
function xgui.onDisconnect( ply )
xgui.activeUsers[ply:UniqueID()] = nil
xgui.readyPlayers[ply:UniqueID()] = nil
end
hook.Add( "PlayerDisconnected", "xgui_ply_disconnect", xgui.onDisconnect )
function xgui.refreshdata( ply, existingDatas )
local t = {}
if #existingDatas ~= 0 then
for _, data in ipairs( xgui.dataTypes ) do
--Only refresh data that the user says they don't have, and refresh datatypes they do not have access to
if not table.HasValue( existingDatas, data.name ) or not ULib.ucl.query( ply, xgui.dataTypes[data.name].access ) then
table.insert( t, data.name )
end
end
if #t == 0 then return end --If no updates were needed, then just end the function
end
xgui.sendDataTable( ply, t )
end
xgui.addCmd( "refreshdata", xgui.refreshdata )
local function plyToTable( plys ) --If plys is empty, then return the full playerlist.
if type( plys ) == "Player" then
plys = { plys }
elseif #plys == 0 then
for _, v in pairs( player.GetAll() ) do
table.insert( plys, v )
end
end
return plys
end
--Send an entire table of data to the client(s)
function xgui.sendDataTable( plys, datatypes, forceSend )
if type( datatypes ) ~= "table" then
datatypes = { datatypes }
elseif #datatypes == 0 then
for _, k in ipairs( xgui.dataTypes ) do
table.insert( datatypes, k.name )
end
end
plys = plyToTable( plys )
for k, ply in pairs( plys ) do
if not xgui.readyPlayers[ply:UniqueID()] then return end --Ignore requests to players who are not ready, they'll get the data as soon as they can.
if xgui.activeUsers[ply:UniqueID()] and not forceSend then --If data is currently being sent to the client
for _, dtype in ipairs( datatypes ) do
local exists = false
for _,existingArg in ipairs(xgui.activeUsers[ply:UniqueID()].tables) do
if dtype == existingArg then exists=true break end
end
if not exists then table.insert( xgui.activeUsers[ply:UniqueID()].tables, dtype ) end
--Clear any events relating to this data type, since those changes will be reflected whenever the new table is sent.
for i=#xgui.activeUsers[ply:UniqueID()].events,1,-1 do
if xgui.activeUsers[ply:UniqueID()].events[i][2] == dtype then
table.remove( xgui.activeUsers[ply:UniqueID()].events, i )
end
end
end
return
end
local chunks = {}
for _, dtype in ipairs( datatypes ) do
if xgui.dataTypes[dtype] then
local data = xgui.dataTypes[dtype]
if ULib.ucl.query( ply, data.access ) then
local t = data.getData()
local size = data.maxchunk or 0 --Split the table into "chunks" of per-datatype specified size to even out data flow. 0 to disable
if t and table.Count( t ) > size and size != 0 then
table.insert( chunks, { 5, dtype } ) --Signify beginning of split chunks
local c = 1
local part = {}
for key, data in pairs( t ) do
part[key] = data
c = c + 1
if c > size then
table.insert( chunks, { 1, dtype, part } )
part = {}
c = 1
end
end
table.insert( chunks, { 1, dtype, part } )
table.insert( chunks, { 6, dtype } ) --Signify end of split chunks
else
table.insert( chunks, { 1, dtype, data.getData() } )
end
else
table.insert( chunks, { 0, dtype } )
end
end
end
if #chunks ~= 0 then
xgui.sendChunks( ply, chunks )
else
xgui.chunksFinished( ply )
end
end
end
--Send a portion of table data to the client(s). The table "data" gets table.Merge'd with the existing table on the client.
function xgui.addData( plys, dtype, data )
xgui.sendDataEvent( plys, 2, dtype, data )
end
--(Same as add, you can call them differently in your code if you need to be able to determine if one is added vs. updated)
function xgui.updateData( plys, dtype, data )
xgui.sendDataEvent( plys, 3, dtype, data )
end
--Removes a single key from the table -- The table "data" should be structured that it contains the set of tables to a single key which will be removed. (i.e. data = {base={subtable1="key"}} )
--It can also remove multiple values. Here are a few examples:
--xgui.removeData( {}, "adverts", {[2]={[1]={"rpt"} } } ) --This would remove the repeat time of the first advert in the second advert group.
--xgui.removeData( {}, "votemaps", {3, 3, 3, 3} ) --This will remove votemaps numbered 3-6 in xgui.data.votemaps. (It uses table.remove, but you can alternatively do {6,5,4,3} to produce the same effect)
function xgui.removeData( plys, dtype, data )
xgui.sendDataEvent( plys, 4, dtype, data )
end
function xgui.sendDataEvent( plys, evtype, dtype, entry )
plys = plyToTable( plys )
for k, ply in pairs( plys ) do
if xgui.activeUsers[ply:UniqueID()] then
table.insert( xgui.activeUsers[ply:UniqueID()].events, { evtype, dtype, entry } )
return
end
local chunks = {}
table.insert( chunks, { evtype, dtype, entry } )
xgui.sendChunks( ply, chunks )
end
end
function xgui.sendChunks( ply, chunks )
ULib.clientRPC( ply, "xgui.expectChunks", #chunks )
if not xgui.activeUsers[ply:UniqueID()] then xgui.activeUsers[ply:UniqueID()] = { tables={}, events={} } end
for _, chunk in ipairs( chunks ) do
ULib.queueFunctionCall( ULib.clientRPC, ply, "xgui.getChunk", chunk[1], chunk[2], chunk[3] )
end
end
function xgui.chunksFinished( ply )
if xgui.activeUsers[ply:UniqueID()] then
if #xgui.activeUsers[ply:UniqueID()].tables > 0 then --Data tables have been requested while the player was transferring data
xgui.sendDataTable( ply, xgui.activeUsers[ply:UniqueID()].tables, true )
xgui.activeUsers[ply:UniqueID()].tables = {}
elseif #xgui.activeUsers[ply:UniqueID()].events > 0 then --No data tables are needed, and events have occurred while the player was transferring data
local chunks = {}
for _,v in ipairs( xgui.activeUsers[ply:UniqueID()].events ) do
table.insert( chunks, v )
end
xgui.sendChunks( ply, chunks )
xgui.activeUsers[ply:UniqueID()].events = {}
else --Client is up-to-date!
xgui.activeUsers[ply:UniqueID()] = nil
end
end
end
xgui.addCmd( "dataComplete", xgui.chunksFinished )
------------
--Misc Stuff
------------
function xgui.getCommentHeader( data, comment_char )
comment_char = comment_char or ";"
local lines = ULib.explode( "\n", data )
local end_comment_line = 0
for _, line in ipairs( lines ) do
local trimmed = line:Trim()
if trimmed == "" or trimmed:sub( 1, 1 ) == comment_char then
end_comment_line = end_comment_line + 1
else
break
end
end
local comment = table.concat( lines, "\n", 1, end_comment_line )
if comment ~= "" then comment = comment .. "\n" end
return comment
end
--Initialize the server modules!
for _, v in ipairs( xgui.svmodules ) do v.init() end
ulx.addToHelpManually( "Menus", "xgui", "<show, hide, toggle> - Opens and/or closes XGUI. (say: !xgui, !menu) (alias: ulx menu)" )
end
--Init the code when the server is ready
hook.Add( "Initialize", "XGUI_InitServer", xgui.init, HOOK_HIGH )
--Call the modules postinit function when ULX is done loading. Should be called well after the Initialize hook.
function xgui.postInit()
for _, v in ipairs( xgui.svmodules ) do if v.postinit then v.postinit() end end
--Fix any users who requested data before the server was ready
for _, ply in pairs( player.GetAll() ) do
for UID, data in pairs( xgui.activeUsers ) do
if ply:UniqueID() == UID then
ULib.clientRPC( ply, "xgui.getChunk", -1, "Initializing..." )
end
end
end
end
hook.Add( ulx.HOOK_ULXDONELOADING, "XGUI_PostInitServer", xgui.postInit, HOOK_MONITOR_LOW )
--Server stuff for the GUI for ULX --by Stickly Man!
xgui = xgui or {}
xgui.svmodules = {}
function xgui.addSVModule( name, initFunc, postinitFunc )
local found = false
for i, svmodule in pairs( xgui.svmodules ) do
if svmodule.name == name then
table.remove( xgui.svmodules, i )
found = true
break
end
end
table.insert( xgui.svmodules, { name=name, init=initFunc, postinit=postinitFunc } )
if found then -- Autorefresh
initFunc()
postinitFunc()
end
end
Msg( "///////////////////////////////\n" )
Msg( "// ULX GUI -- by Stickly Man //\n" )
Msg( "///////////////////////////////\n" )
Msg( "// Adding Main Modules.. //\n" )
for _, file in ipairs( file.Find( "ulx/xgui/*.lua", "LUA" ) ) do
AddCSLuaFile( "ulx/xgui/" .. file )
Msg( "// " .. file .. string.rep( " ", 25 - file:len() ) .. "//\n" )
end
Msg( "// Adding Setting Modules.. //\n" )
for _, file in ipairs( file.Find( "ulx/xgui/settings/*.lua", "LUA" ) ) do
AddCSLuaFile( "ulx/xgui/settings/" .. file )
Msg( "// " .. file .. string.rep( " ", 25 - file:len() ) .. "//\n" )
end
Msg( "// Adding Gamemode Modules.. //\n" )
for _, file in ipairs( file.Find( "ulx/xgui/gamemodes/*.lua", "LUA" ) ) do
AddCSLuaFile( "ulx/xgui/gamemodes/" .. file )
Msg( "// " .. file .. string.rep( " ", 25 - file:len() ) .. "//\n" )
end
Msg( "// Loading Server Modules.. //\n" )
for _, file in ipairs( file.Find( "ulx/xgui/server/*.lua", "LUA" ) ) do
include( "ulx/xgui/server/" .. file )
Msg( "// " .. file .. string.rep( " ", 25 - file:len() ) .. "//\n" )
end
Msg( "// XGUI modules added! //\n" )
Msg( "///////////////////////////////\n" )
function xgui.init()
local function xgui_chatCommand( ply, func, args )
if ply:IsValid() then
ULib.clientRPC( ply, "xgui.toggle", args )
end
end
ULib.addSayCommand( "!xgui", xgui_chatCommand )
ULib.addSayCommand( "!menu", xgui_chatCommand )
--XGUI command stuff
xgui.cmds = {}
function xgui.addCmd( name, func )
xgui.cmds[name] = func
end
function xgui.cmd( ply, cmd, args )
local name=args[1]
table.remove( args, 1 )
if xgui.cmds[name] then
xgui.cmds[name]( ply, args )
else
ULib.tsay( ply, "XGUI: Command " .. ( name or "<none>" ) .. " not recognized!" )
end
end
concommand.Add( "_xgui", xgui.cmd )
-----------------
--XGUI data stuff
-----------------
xgui.dataTypes = {}
xgui.activeUsers = {} --Set up a table to list users who are actively transferring data
xgui.readyPlayers = {} --Set up a table to store users who are ready to receive data.
function xgui.addDataType( name, retrievalFunc, access, maxChunkSize, priority )
xgui.dataTypes[name] = { getData=retrievalFunc, access=access, maxchunk=maxChunkSize }
table.insert( xgui.dataTypes, { name=name, priority=priority or 0 } )
table.sort( xgui.dataTypes, function(a,b) return a.priority < b.priority end )
end
function xgui.getdata( ply, args )
xgui.sendDataTable( ply, args )
end
xgui.addCmd( "getdata", xgui.getdata )
--Let the server know when players are/aren't ready to receive data.
function xgui.getInstalled( ply )
ULib.clientRPC( ply, "xgui.getInstalled" )
xgui.readyPlayers[ply:UniqueID()] = 1
end
xgui.addCmd( "getInstalled", xgui.getInstalled )
function xgui.onDisconnect( ply )
xgui.activeUsers[ply:UniqueID()] = nil
xgui.readyPlayers[ply:UniqueID()] = nil
end
hook.Add( "PlayerDisconnected", "xgui_ply_disconnect", xgui.onDisconnect )
function xgui.refreshdata( ply, existingDatas )
local t = {}
if #existingDatas ~= 0 then
for _, data in ipairs( xgui.dataTypes ) do
--Only refresh data that the user says they don't have, and refresh datatypes they do not have access to
if not table.HasValue( existingDatas, data.name ) or not ULib.ucl.query( ply, xgui.dataTypes[data.name].access ) then
table.insert( t, data.name )
end
end
if #t == 0 then return end --If no updates were needed, then just end the function
end
xgui.sendDataTable( ply, t )
end
xgui.addCmd( "refreshdata", xgui.refreshdata )
local function plyToTable( plys ) --If plys is empty, then return the full playerlist.
if type( plys ) == "Player" then
plys = { plys }
elseif #plys == 0 then
for _, v in pairs( player.GetAll() ) do
table.insert( plys, v )
end
end
return plys
end
--Send an entire table of data to the client(s)
function xgui.sendDataTable( plys, datatypes, forceSend )
if type( datatypes ) ~= "table" then
datatypes = { datatypes }
elseif #datatypes == 0 then
for _, k in ipairs( xgui.dataTypes ) do
table.insert( datatypes, k.name )
end
end
plys = plyToTable( plys )
for k, ply in pairs( plys ) do
if not xgui.readyPlayers[ply:UniqueID()] then return end --Ignore requests to players who are not ready, they'll get the data as soon as they can.
if xgui.activeUsers[ply:UniqueID()] and not forceSend then --If data is currently being sent to the client
for _, dtype in ipairs( datatypes ) do
local exists = false
for _,existingArg in ipairs(xgui.activeUsers[ply:UniqueID()].tables) do
if dtype == existingArg then exists=true break end
end
if not exists then table.insert( xgui.activeUsers[ply:UniqueID()].tables, dtype ) end
--Clear any events relating to this data type, since those changes will be reflected whenever the new table is sent.
for i=#xgui.activeUsers[ply:UniqueID()].events,1,-1 do
if xgui.activeUsers[ply:UniqueID()].events[i][2] == dtype then
table.remove( xgui.activeUsers[ply:UniqueID()].events, i )
end
end
end
return
end
local chunks = {}
for _, dtype in ipairs( datatypes ) do
if xgui.dataTypes[dtype] then
local data = xgui.dataTypes[dtype]
if ULib.ucl.query( ply, data.access ) then
local t = data.getData()
local size = data.maxchunk or 0 --Split the table into "chunks" of per-datatype specified size to even out data flow. 0 to disable
if t and table.Count( t ) > size and size != 0 then
table.insert( chunks, { 5, dtype } ) --Signify beginning of split chunks
local c = 1
local part = {}
for key, data in pairs( t ) do
part[key] = data
c = c + 1
if c > size then
table.insert( chunks, { 1, dtype, part } )
part = {}
c = 1
end
end
table.insert( chunks, { 1, dtype, part } )
table.insert( chunks, { 6, dtype } ) --Signify end of split chunks
else
table.insert( chunks, { 1, dtype, data.getData() } )
end
else
table.insert( chunks, { 0, dtype } )
end
end
end
if #chunks ~= 0 then
xgui.sendChunks( ply, chunks )
else
xgui.chunksFinished( ply )
end
end
end
--Send a portion of table data to the client(s). The table "data" gets table.Merge'd with the existing table on the client.
function xgui.addData( plys, dtype, data )
xgui.sendDataEvent( plys, 2, dtype, data )
end
--(Same as add, you can call them differently in your code if you need to be able to determine if one is added vs. updated)
function xgui.updateData( plys, dtype, data )
xgui.sendDataEvent( plys, 3, dtype, data )
end
--Removes a single key from the table -- The table "data" should be structured that it contains the set of tables to a single key which will be removed. (i.e. data = {base={subtable1="key"}} )
--It can also remove multiple values. Here are a few examples:
--xgui.removeData( {}, "adverts", {[2]={[1]={"rpt"} } } ) --This would remove the repeat time of the first advert in the second advert group.
--xgui.removeData( {}, "votemaps", {3, 3, 3, 3} ) --This will remove votemaps numbered 3-6 in xgui.data.votemaps. (It uses table.remove, but you can alternatively do {6,5,4,3} to produce the same effect)
function xgui.removeData( plys, dtype, data )
xgui.sendDataEvent( plys, 4, dtype, data )
end
function xgui.sendDataEvent( plys, evtype, dtype, entry )
plys = plyToTable( plys )
for k, ply in pairs( plys ) do
if xgui.activeUsers[ply:UniqueID()] then
table.insert( xgui.activeUsers[ply:UniqueID()].events, { evtype, dtype, entry } )
return
end
local chunks = {}
table.insert( chunks, { evtype, dtype, entry } )
xgui.sendChunks( ply, chunks )
end
end
function xgui.sendChunks( ply, chunks )
ULib.clientRPC( ply, "xgui.expectChunks", #chunks )
if not xgui.activeUsers[ply:UniqueID()] then xgui.activeUsers[ply:UniqueID()] = { tables={}, events={} } end
for _, chunk in ipairs( chunks ) do
ULib.queueFunctionCall( ULib.clientRPC, ply, "xgui.getChunk", chunk[1], chunk[2], chunk[3] )
end
end
function xgui.chunksFinished( ply )
if xgui.activeUsers[ply:UniqueID()] then
if #xgui.activeUsers[ply:UniqueID()].tables > 0 then --Data tables have been requested while the player was transferring data
xgui.sendDataTable( ply, xgui.activeUsers[ply:UniqueID()].tables, true )
xgui.activeUsers[ply:UniqueID()].tables = {}
elseif #xgui.activeUsers[ply:UniqueID()].events > 0 then --No data tables are needed, and events have occurred while the player was transferring data
local chunks = {}
for _,v in ipairs( xgui.activeUsers[ply:UniqueID()].events ) do
table.insert( chunks, v )
end
xgui.sendChunks( ply, chunks )
xgui.activeUsers[ply:UniqueID()].events = {}
else --Client is up-to-date!
xgui.activeUsers[ply:UniqueID()] = nil
end
end
end
xgui.addCmd( "dataComplete", xgui.chunksFinished )
------------
--Misc Stuff
------------
function xgui.getCommentHeader( data, comment_char )
comment_char = comment_char or ";"
local lines = ULib.explode( "\n", data )
local end_comment_line = 0
for _, line in ipairs( lines ) do
local trimmed = line:Trim()
if trimmed == "" or trimmed:sub( 1, 1 ) == comment_char then
end_comment_line = end_comment_line + 1
else
break
end
end
local comment = table.concat( lines, "\n", 1, end_comment_line )
if comment ~= "" then comment = comment .. "\n" end
return comment
end
--Initialize the server modules!
for _, v in ipairs( xgui.svmodules ) do v.init() end
ulx.addToHelpManually( "Menus", "xgui", "<show, hide, toggle> - Opens and/or closes XGUI. (say: !xgui, !menu) (alias: ulx menu)" )
end
--Init the code when the server is ready
hook.Add( "Initialize", "XGUI_InitServer", xgui.init, HOOK_HIGH )
--Call the modules postinit function when ULX is done loading. Should be called well after the Initialize hook.
function xgui.postInit()
for _, v in ipairs( xgui.svmodules ) do if v.postinit then v.postinit() end end
--Fix any users who requested data before the server was ready
for _, ply in pairs( player.GetAll() ) do
for UID, data in pairs( xgui.activeUsers ) do
if ply:UniqueID() == UID then
ULib.clientRPC( ply, "xgui.getChunk", -1, "Initializing..." )
end
end
end
end
hook.Add( ulx.HOOK_ULXDONELOADING, "XGUI_PostInitServer", xgui.postInit, HOOK_MONITOR_LOW )

View File

@ -1,15 +1,15 @@
ulx.LOW_ARGS = "You did not specify enough arguments for this command. Type 'ulx help' in console for help."
ulx.version = 3.70 -- Current release version. Don't access directly, use ulx.getVersion instead. (SVN checks)
ulx.release = false -- Is this the release?
ulx.ID_ORIGINAL = 1
ulx.ID_PLAYER_HELP = 2
ulx.ID_HELP = 3
ulx.ID_MMAIN = 1
ulx.ID_MCLIENT = 2
ulx.ID_MADMIN = 3
ulx.HOOK_ULXDONELOADING = "ULXLoaded"
ulx.HOOK_VETO = "ULXVetoChanged"
ulx.LOW_ARGS = "You did not specify enough arguments for this command. Type 'ulx help' in console for help."
ulx.version = 3.70 -- Current release version. Don't access directly, use ulx.getVersion instead. (SVN checks)
ulx.release = false -- Is this the release?
ulx.ID_ORIGINAL = 1
ulx.ID_PLAYER_HELP = 2
ulx.ID_HELP = 3
ulx.ID_MMAIN = 1
ulx.ID_MCLIENT = 2
ulx.ID_MADMIN = 3
ulx.HOOK_ULXDONELOADING = "ULXLoaded"
ulx.HOOK_VETO = "ULXVetoChanged"

File diff suppressed because it is too large Load Diff

View File

@ -1,394 +1,394 @@
--Commands module (formerly players module) v2 for ULX GUI -- by Stickly Man!
--Handles all user-executable commands, such as kick, slay, ban, etc.
local cmds = xlib.makepanel{ parent=xgui.null }
cmds.selcmd = nil
cmds.mask = xlib.makepanel{ x=160, y=30, w=425, h=330, parent=cmds }
cmds.argslist = xlib.makelistlayout{ w=165, h=330, parent=cmds.mask }
cmds.argslist.secondaryPos = nil
cmds.argslist.scroll:SetVisible( false )
function cmds.argslist:Open( cmd, secondary )
if secondary then
if cmds.plist.open then
cmds.plist:Close()
elseif self.open then
self:Close()
end
end
self:openAnim( cmd, secondary )
self.open = true
end
function cmds.argslist:Close()
self:closeAnim( self.secondaryPos )
self.open = false
end
cmds.plist = xlib.makelistview{ w=250, h=330, multiselect=true, parent=cmds.mask }
function cmds.plist:Open( arg )
if cmds.argslist.secondaryPos == true then cmds.argslist:Close()
elseif self.open then self:Close() end
self:openAnim( arg )
--Determine if the arguments should be visible after changing (If a valid player will be selected)
local targets = cmds.calculateValidPlayers( arg )
local playerWillBeSelected = false
for _, line in ipairs( cmds.plist:GetSelected() ) do
if table.HasValue( targets, line.ply ) then
playerWillBeSelected = true
break
end
end
if playerWillBeSelected then
cmds.argslist:Open( ULib.cmds.translatedCmds[arg.cmd], false )
end
self.open = true
end
function cmds.plist:Close()
if cmds.argslist.open then cmds.argslist:Close() end
self:closeAnim()
self.open = false
end
cmds.plist.DoDoubleClick = function()
cmds.runCmd( cmds.selcmd )
end
cmds.plist:SetVisible( false )
cmds.plist:AddColumn( "Name" )
cmds.plist:AddColumn( "Group" )
cmds.cmds = xlib.makelistlayout{ x=5, y=30, w=150, h=330, parent=cmds, padding=1, spacing=1 }
cmds.setselected = function( selcat, LineID )
if selcat.Lines[LineID]:GetColumnText(2) == cmds.selcmd then
selcat:ClearSelection()
if cmds.plist.open then cmds.plist:Close() else cmds.argslist:Close() end
xlib.animQueue_start()
cmds.selcmd = nil
return
end
for _, cat in pairs( cmds.cmd_contents ) do
if cat ~= selcat then
cat:ClearSelection()
end
end
cmds.selcmd = selcat.Lines[LineID]:GetColumnText(2)
if cmds.permissionChanged then cmds.refreshPlist() return end
if xlib.animRunning then xlib.animQueue_forceStop() end
local cmd = ULib.cmds.translatedCmds[cmds.selcmd]
if cmd.args[2] and ( cmd.args[2].type == ULib.cmds.PlayersArg or cmd.args[2].type == ULib.cmds.PlayerArg ) then
cmds.plist:Open( cmd.args[2] )
else
cmds.argslist:Open( cmd, true )
end
xlib.animQueue_start()
end
function cmds.refreshPlist( arg )
if not arg then arg = ULib.cmds.translatedCmds[cmds.selcmd].args[2] end
if not arg or ( arg.type ~= ULib.cmds.PlayersArg and arg.type ~= ULib.cmds.PlayerArg ) then return end
local lastplys = {}
for k, Line in pairs( cmds.plist.Lines ) do
if ( Line:IsLineSelected() ) then table.insert( lastplys, Line:GetColumnText(1) ) end
end
local targets = cmds.calculateValidPlayers( arg )
cmds.plist:Clear()
cmds.plist:SetMultiSelect( arg.type == ULib.cmds.PlayersArg )
for _, ply in ipairs( targets ) do
local line = cmds.plist:AddLine( ply:Nick(), ply:GetUserGroup() )
line.ply = ply
line.OnSelect = function()
if cmds.permissionChanged then return end
if not xlib.animRunning and not cmds.argslist.open then
cmds.argslist:Open( ULib.cmds.translatedCmds[cmds.selcmd], false )
xlib.animQueue_start( )
else
if not cmds.clickedFlag then --Prevent this from happening multiple times.
cmds.clickedFlag = true
xlib.addToAnimQueue( function() if not cmds.argslist.open then
cmds.argslist:Open( ULib.cmds.translatedCmds[cmds.selcmd], false ) end
cmds.clickedFlag = nil end )
end
end
end
--Select previously selected Lines
if table.HasValue( lastplys, ply:Nick() ) then
cmds.plist:SelectItem( line )
end
end
--Select only the first item if multiselect is disabled.
if not cmds.plist:GetMultiSelect() then
local firstSelected = cmds.plist:GetSelected()[1]
cmds.plist:ClearSelection()
cmds.plist:SelectItem( firstSelected )
end
if not cmds.plist:GetSelectedLine() then
if not xlib.animRunning then
if cmds.argslist.open then
cmds.argslist:Close()
xlib.animQueue_start()
end
else
if cmds.permissionChanged then
xlib.addToAnimQueue( function() if cmds.argslist.open and cmds.plist.open then cmds.argslist:Close() end end )
end
end
end
end
function cmds.calculateValidPlayers( arg )
if not arg then arg = ULib.cmds.translatedCmds[cmds.selcmd].args[2] end
local access, tag = LocalPlayer():query( arg.cmd )
local restrictions = {}
ULib.cmds.PlayerArg.processRestrictions( restrictions, LocalPlayer(), arg, ulx.getTagArgNum( tag, 1 ) )
local targets = restrictions.restrictedTargets
if targets == false then -- No one allowed
targets = {}
elseif targets == nil then -- Everyone allowed
targets = player.GetAll()
end
return targets
end
function cmds.buildArgsList( cmd )
cmds.argslist:Clear()
cmds.curargs = {}
local argnum = 0
local zpos = 0
local expectingplayers = cmd.args[2] and ( ( cmd.args[2].type == ULib.cmds.PlayersArg ) or ( cmd.args[2].type == ULib.cmds.PlayerArg ) ) or false
for _, arg in ipairs( cmd.args ) do
if not arg.type.invisible then
argnum = argnum + 1
if not ( argnum == 1 and expectingplayers ) then
if arg.invisible ~= true then
local curitem = arg
if curitem.repeat_min then --This command repeats!
local panel = xlib.makepanel{ h=20, parent=cmds.argslist }
local choices = {}
panel.argnum = argnum
panel.xguiIgnore = true
panel.arg = curitem
panel.addbutton = xlib.makebutton{ label="Add", w=83, parent=panel }
panel.addbutton.DoClick = function( self )
local parent = self:GetParent()
local ctrl = parent.arg.type.x_getcontrol( parent.arg, parent.argnum, cmds.argslist )
cmds.argslist:Add( ctrl )
ctrl:MoveToAfter( choices[#choices] )
table.insert( choices, ctrl )
table.insert( cmds.curargs, ctrl )
panel.removebutton:SetDisabled( false )
if parent.arg.repeat_max and #choices >= parent.arg.repeat_max then self:SetDisabled( true ) end
end
panel.removebutton = xlib.makebutton{ label="Remove", x=83, w=82, disabled=true, parent=panel }
panel.removebutton.DoClick = function( self )
local parent = self:GetParent()
local ctrl = choices[#choices]
ctrl:Remove()
table.remove( choices )
table.remove( cmds.curargs )
panel.addbutton:SetDisabled( false )
if #choices <= parent.arg.repeat_min then self:SetDisabled( true ) end
end
cmds.argslist:Add( panel )
panel:SetZPos( zpos )
zpos = zpos + 1
for i=1,curitem.repeat_min do
local ctrl = arg.type.x_getcontrol( arg, argnum, cmds.argslist )
cmds.argslist:Add( ctrl )
ctrl:SetZPos( zpos )
zpos = zpos + 1
table.insert( choices, ctrl )
table.insert( cmds.curargs, ctrl )
end
else
local panel = arg.type.x_getcontrol( arg, argnum, cmds.argslist )
table.insert( cmds.curargs, panel )
if curitem.type == ULib.cmds.NumArg then
panel.TextArea.OnEnter = function( self )
cmds.runCmd( cmd.cmd )
end
elseif curitem.type == ULib.cmds.StringArg then
panel.OnEnter = function( self )
cmds.runCmd( cmd.cmd )
end
end
cmds.argslist:Add( panel )
panel:SetZPos( zpos )
zpos = zpos + 1
end
end
end
end
end
if LocalPlayer():query( cmd.cmd ) then
local panel = xlib.makebutton{ label=cmd.cmd, parent=cmds.argslist }
panel.xguiIgnore = true
panel.DoClick = function()
cmds.runCmd( cmd.cmd )
end
cmds.argslist:Add( panel )
panel:SetZPos( zpos )
zpos = zpos + 1
end
if cmd.opposite and LocalPlayer():query( cmd.opposite ) then
local panel = xlib.makebutton{ label=cmd.opposite, parent=cmds.argslist }
panel.DoClick = function()
cmds.runCmd( cmd.opposite )
end
panel.xguiIgnore = true
cmds.argslist:Add( panel )
panel:SetZPos( zpos )
zpos = zpos + 1
end
if cmd.helpStr then --If the command has a string for help
local panel = xlib.makelabel{ w=160, label=cmd.helpStr, wordwrap=true, parent=cmds.argslist }
panel.xguiIgnore = true
cmds.argslist:Add( panel )
panel:SetZPos( zpos )
zpos = zpos + 1
end
end
function cmds.runCmd( cmd )
local cmd = string.Explode( " ", cmd )
if cmds.plist:IsVisible() then
local plys = {}
for _, line in ipairs( cmds.plist:GetSelected() ) do
table.insert( plys, "$" .. ULib.getUniqueIDForPlayer( line.ply ) )
table.insert( plys, "," )
end
table.remove( plys ) --Removes the final comma
table.insert( cmd, table.concat( plys ) )
end
for _, arg in ipairs( cmds.curargs ) do
if not arg.xguiIgnore then
table.insert( cmd, arg:GetValue() )
end
end
RunConsoleCommand( unpack( cmd ) )
end
function cmds.playerNameChanged( ply, old, new )
for i, line in ipairs( cmds.plist.Lines ) do
if line:GetColumnText(1) == old then
line:SetColumnText( 1, new )
end
end
end
cmds.refresh = function( permissionChanged )
local lastcmd = cmds.selcmd
cmds.cmds:Clear()
cmds.cmd_contents = {}
cmds.expandedcat = nil
cmds.selcmd = nil
cmds.permissionChanged = true
local newcategories = {}
local sortcategories = {}
local matchedCmdFound = false
for cmd, data in pairs( ULib.cmds.translatedCmds ) do
local opposite = data.opposite or ""
if opposite ~= cmd and ( LocalPlayer():query( data.cmd ) or LocalPlayer():query( opposite ) ) then
local catname = data.category
if catname == nil or catname == "" then catname = "_Uncategorized" end
if not cmds.cmd_contents[catname] then
--Make a new category
cmds.cmd_contents[catname] = xlib.makelistview{ headerheight=0, multiselect=false, h=136 }
cmds.cmd_contents[catname].OnRowSelected = function( self, LineID ) cmds.setselected( self, LineID ) end
cmds.cmd_contents[catname]:AddColumn( "" )
local cat = xlib.makecat{ label=catname, contents=cmds.cmd_contents[catname], expanded=false, parent=xgui.null }
function cat.Header:OnMousePressed( mcode )
if ( mcode == MOUSE_LEFT ) then
self:GetParent():Toggle()
if cmds.expandedcat then
if cmds.expandedcat ~= self:GetParent() then
cmds.expandedcat:Toggle()
else
cmds.expandedcat = nil
return
end
end
cmds.expandedcat = self:GetParent()
return
end
return self:GetParent():OnMousePressed( mcode )
end
newcategories[catname] = cat
table.insert( sortcategories, catname )
end
local line = cmds.cmd_contents[catname]:AddLine( string.gsub( data.cmd, "ulx ", "" ), data.cmd )
if data.cmd == lastcmd then
cmds.cmd_contents[catname]:SelectItem( line )
cmds.expandedcat = cmds.cmd_contents[catname]:GetParent()
cmds.expandedcat:SetExpanded( true )
matchedCmdFound = true
end
end
end
if not matchedCmdFound then
if cmds.plist.open then
cmds.plist:Close()
xlib.animQueue_start()
elseif cmds.argslist.open then
cmds.argslist:Close()
xlib.animQueue_start()
end
end
table.sort( sortcategories )
for _, catname in ipairs( sortcategories ) do
local cat = newcategories[catname]
cmds.cmds:Add( cat )
cat.Contents:SortByColumn( 1 )
cat.Contents:SetHeight( 17*#cat.Contents:GetLines() )
end
cmds.permissionChanged = nil
end
--------------
--ANIMATIONS--
--------------
function cmds.plist:openAnim( arg )
xlib.addToAnimQueue( cmds.refreshPlist, arg )
xlib.addToAnimQueue( "pnlSlide", { panel=self, startx=-250, starty=0, endx=0, endy=0, setvisible=true } )
end
function cmds.plist:closeAnim()
xlib.addToAnimQueue( "pnlSlide", { panel=self, startx=0, starty=0, endx=-250, endy=0, setvisible=false } )
end
function cmds.argslist:openAnim( cmd, secondary )
xlib.addToAnimQueue( function() cmds.argslist.secondaryPos = secondary end )
xlib.addToAnimQueue( cmds.buildArgsList, cmd )
if secondary then
xlib.addToAnimQueue( "pnlSlide", { panel=self.scroll, startx=-170, starty=0, endx=0, endy=0, setvisible=true } )
else
xlib.addToAnimQueue( "pnlSlide", { panel=self.scroll, startx=80, starty=0, endx=255, endy=0, setvisible=true } )
end
end
function cmds.argslist:closeAnim( secondary )
if secondary then
xlib.addToAnimQueue( "pnlSlide", { panel=self.scroll, startx=0, starty=0, endx=-170, endy=0, setvisible=false } )
else
xlib.addToAnimQueue( "pnlSlide", { panel=self.scroll, startx=255, starty=0, endx=80, endy=0, setvisible=false } )
end
xlib.addToAnimQueue( function() cmds.argslist.secondaryPos = nil end )
end
--------------
cmds.refresh()
hook.Add( "UCLChanged", "xgui_RefreshPlayerCmds", cmds.refresh )
hook.Add( "ULibPlayerNameChanged", "xgui_plyUpdateCmds", cmds.playerNameChanged )
--Commands module (formerly players module) v2 for ULX GUI -- by Stickly Man!
--Handles all user-executable commands, such as kick, slay, ban, etc.
local cmds = xlib.makepanel{ parent=xgui.null }
cmds.selcmd = nil
cmds.mask = xlib.makepanel{ x=160, y=30, w=425, h=330, parent=cmds }
cmds.argslist = xlib.makelistlayout{ w=165, h=330, parent=cmds.mask }
cmds.argslist.secondaryPos = nil
cmds.argslist.scroll:SetVisible( false )
function cmds.argslist:Open( cmd, secondary )
if secondary then
if cmds.plist.open then
cmds.plist:Close()
elseif self.open then
self:Close()
end
end
self:openAnim( cmd, secondary )
self.open = true
end
function cmds.argslist:Close()
self:closeAnim( self.secondaryPos )
self.open = false
end
cmds.plist = xlib.makelistview{ w=250, h=330, multiselect=true, parent=cmds.mask }
function cmds.plist:Open( arg )
if cmds.argslist.secondaryPos == true then cmds.argslist:Close()
elseif self.open then self:Close() end
self:openAnim( arg )
--Determine if the arguments should be visible after changing (If a valid player will be selected)
local targets = cmds.calculateValidPlayers( arg )
local playerWillBeSelected = false
for _, line in ipairs( cmds.plist:GetSelected() ) do
if table.HasValue( targets, line.ply ) then
playerWillBeSelected = true
break
end
end
if playerWillBeSelected then
cmds.argslist:Open( ULib.cmds.translatedCmds[arg.cmd], false )
end
self.open = true
end
function cmds.plist:Close()
if cmds.argslist.open then cmds.argslist:Close() end
self:closeAnim()
self.open = false
end
cmds.plist.DoDoubleClick = function()
cmds.runCmd( cmds.selcmd )
end
cmds.plist:SetVisible( false )
cmds.plist:AddColumn( "Name" )
cmds.plist:AddColumn( "Group" )
cmds.cmds = xlib.makelistlayout{ x=5, y=30, w=150, h=330, parent=cmds, padding=1, spacing=1 }
cmds.setselected = function( selcat, LineID )
if selcat.Lines[LineID]:GetColumnText(2) == cmds.selcmd then
selcat:ClearSelection()
if cmds.plist.open then cmds.plist:Close() else cmds.argslist:Close() end
xlib.animQueue_start()
cmds.selcmd = nil
return
end
for _, cat in pairs( cmds.cmd_contents ) do
if cat ~= selcat then
cat:ClearSelection()
end
end
cmds.selcmd = selcat.Lines[LineID]:GetColumnText(2)
if cmds.permissionChanged then cmds.refreshPlist() return end
if xlib.animRunning then xlib.animQueue_forceStop() end
local cmd = ULib.cmds.translatedCmds[cmds.selcmd]
if cmd.args[2] and ( cmd.args[2].type == ULib.cmds.PlayersArg or cmd.args[2].type == ULib.cmds.PlayerArg ) then
cmds.plist:Open( cmd.args[2] )
else
cmds.argslist:Open( cmd, true )
end
xlib.animQueue_start()
end
function cmds.refreshPlist( arg )
if not arg then arg = ULib.cmds.translatedCmds[cmds.selcmd].args[2] end
if not arg or ( arg.type ~= ULib.cmds.PlayersArg and arg.type ~= ULib.cmds.PlayerArg ) then return end
local lastplys = {}
for k, Line in pairs( cmds.plist.Lines ) do
if ( Line:IsLineSelected() ) then table.insert( lastplys, Line:GetColumnText(1) ) end
end
local targets = cmds.calculateValidPlayers( arg )
cmds.plist:Clear()
cmds.plist:SetMultiSelect( arg.type == ULib.cmds.PlayersArg )
for _, ply in ipairs( targets ) do
local line = cmds.plist:AddLine( ply:Nick(), ply:GetUserGroup() )
line.ply = ply
line.OnSelect = function()
if cmds.permissionChanged then return end
if not xlib.animRunning and not cmds.argslist.open then
cmds.argslist:Open( ULib.cmds.translatedCmds[cmds.selcmd], false )
xlib.animQueue_start( )
else
if not cmds.clickedFlag then --Prevent this from happening multiple times.
cmds.clickedFlag = true
xlib.addToAnimQueue( function() if not cmds.argslist.open then
cmds.argslist:Open( ULib.cmds.translatedCmds[cmds.selcmd], false ) end
cmds.clickedFlag = nil end )
end
end
end
--Select previously selected Lines
if table.HasValue( lastplys, ply:Nick() ) then
cmds.plist:SelectItem( line )
end
end
--Select only the first item if multiselect is disabled.
if not cmds.plist:GetMultiSelect() then
local firstSelected = cmds.plist:GetSelected()[1]
cmds.plist:ClearSelection()
cmds.plist:SelectItem( firstSelected )
end
if not cmds.plist:GetSelectedLine() then
if not xlib.animRunning then
if cmds.argslist.open then
cmds.argslist:Close()
xlib.animQueue_start()
end
else
if cmds.permissionChanged then
xlib.addToAnimQueue( function() if cmds.argslist.open and cmds.plist.open then cmds.argslist:Close() end end )
end
end
end
end
function cmds.calculateValidPlayers( arg )
if not arg then arg = ULib.cmds.translatedCmds[cmds.selcmd].args[2] end
local access, tag = LocalPlayer():query( arg.cmd )
local restrictions = {}
ULib.cmds.PlayerArg.processRestrictions( restrictions, LocalPlayer(), arg, ulx.getTagArgNum( tag, 1 ) )
local targets = restrictions.restrictedTargets
if targets == false then -- No one allowed
targets = {}
elseif targets == nil then -- Everyone allowed
targets = player.GetAll()
end
return targets
end
function cmds.buildArgsList( cmd )
cmds.argslist:Clear()
cmds.curargs = {}
local argnum = 0
local zpos = 0
local expectingplayers = cmd.args[2] and ( ( cmd.args[2].type == ULib.cmds.PlayersArg ) or ( cmd.args[2].type == ULib.cmds.PlayerArg ) ) or false
for _, arg in ipairs( cmd.args ) do
if not arg.type.invisible then
argnum = argnum + 1
if not ( argnum == 1 and expectingplayers ) then
if arg.invisible ~= true then
local curitem = arg
if curitem.repeat_min then --This command repeats!
local panel = xlib.makepanel{ h=20, parent=cmds.argslist }
local choices = {}
panel.argnum = argnum
panel.xguiIgnore = true
panel.arg = curitem
panel.addbutton = xlib.makebutton{ label="Add", w=83, parent=panel }
panel.addbutton.DoClick = function( self )
local parent = self:GetParent()
local ctrl = parent.arg.type.x_getcontrol( parent.arg, parent.argnum, cmds.argslist )
cmds.argslist:Add( ctrl )
ctrl:MoveToAfter( choices[#choices] )
table.insert( choices, ctrl )
table.insert( cmds.curargs, ctrl )
panel.removebutton:SetDisabled( false )
if parent.arg.repeat_max and #choices >= parent.arg.repeat_max then self:SetDisabled( true ) end
end
panel.removebutton = xlib.makebutton{ label="Remove", x=83, w=82, disabled=true, parent=panel }
panel.removebutton.DoClick = function( self )
local parent = self:GetParent()
local ctrl = choices[#choices]
ctrl:Remove()
table.remove( choices )
table.remove( cmds.curargs )
panel.addbutton:SetDisabled( false )
if #choices <= parent.arg.repeat_min then self:SetDisabled( true ) end
end
cmds.argslist:Add( panel )
panel:SetZPos( zpos )
zpos = zpos + 1
for i=1,curitem.repeat_min do
local ctrl = arg.type.x_getcontrol( arg, argnum, cmds.argslist )
cmds.argslist:Add( ctrl )
ctrl:SetZPos( zpos )
zpos = zpos + 1
table.insert( choices, ctrl )
table.insert( cmds.curargs, ctrl )
end
else
local panel = arg.type.x_getcontrol( arg, argnum, cmds.argslist )
table.insert( cmds.curargs, panel )
if curitem.type == ULib.cmds.NumArg then
panel.TextArea.OnEnter = function( self )
cmds.runCmd( cmd.cmd )
end
elseif curitem.type == ULib.cmds.StringArg then
panel.OnEnter = function( self )
cmds.runCmd( cmd.cmd )
end
end
cmds.argslist:Add( panel )
panel:SetZPos( zpos )
zpos = zpos + 1
end
end
end
end
end
if LocalPlayer():query( cmd.cmd ) then
local panel = xlib.makebutton{ label=cmd.cmd, parent=cmds.argslist }
panel.xguiIgnore = true
panel.DoClick = function()
cmds.runCmd( cmd.cmd )
end
cmds.argslist:Add( panel )
panel:SetZPos( zpos )
zpos = zpos + 1
end
if cmd.opposite and LocalPlayer():query( cmd.opposite ) then
local panel = xlib.makebutton{ label=cmd.opposite, parent=cmds.argslist }
panel.DoClick = function()
cmds.runCmd( cmd.opposite )
end
panel.xguiIgnore = true
cmds.argslist:Add( panel )
panel:SetZPos( zpos )
zpos = zpos + 1
end
if cmd.helpStr then --If the command has a string for help
local panel = xlib.makelabel{ w=160, label=cmd.helpStr, wordwrap=true, parent=cmds.argslist }
panel.xguiIgnore = true
cmds.argslist:Add( panel )
panel:SetZPos( zpos )
zpos = zpos + 1
end
end
function cmds.runCmd( cmd )
local cmd = string.Explode( " ", cmd )
if cmds.plist:IsVisible() then
local plys = {}
for _, line in ipairs( cmds.plist:GetSelected() ) do
table.insert( plys, "$" .. ULib.getUniqueIDForPlayer( line.ply ) )
table.insert( plys, "," )
end
table.remove( plys ) --Removes the final comma
table.insert( cmd, table.concat( plys ) )
end
for _, arg in ipairs( cmds.curargs ) do
if not arg.xguiIgnore then
table.insert( cmd, arg:GetValue() )
end
end
RunConsoleCommand( unpack( cmd ) )
end
function cmds.playerNameChanged( ply, old, new )
for i, line in ipairs( cmds.plist.Lines ) do
if line:GetColumnText(1) == old then
line:SetColumnText( 1, new )
end
end
end
cmds.refresh = function( permissionChanged )
local lastcmd = cmds.selcmd
cmds.cmds:Clear()
cmds.cmd_contents = {}
cmds.expandedcat = nil
cmds.selcmd = nil
cmds.permissionChanged = true
local newcategories = {}
local sortcategories = {}
local matchedCmdFound = false
for cmd, data in pairs( ULib.cmds.translatedCmds ) do
local opposite = data.opposite or ""
if opposite ~= cmd and ( LocalPlayer():query( data.cmd ) or LocalPlayer():query( opposite ) ) then
local catname = data.category
if catname == nil or catname == "" then catname = "_Uncategorized" end
if not cmds.cmd_contents[catname] then
--Make a new category
cmds.cmd_contents[catname] = xlib.makelistview{ headerheight=0, multiselect=false, h=136 }
cmds.cmd_contents[catname].OnRowSelected = function( self, LineID ) cmds.setselected( self, LineID ) end
cmds.cmd_contents[catname]:AddColumn( "" )
local cat = xlib.makecat{ label=catname, contents=cmds.cmd_contents[catname], expanded=false, parent=xgui.null }
function cat.Header:OnMousePressed( mcode )
if ( mcode == MOUSE_LEFT ) then
self:GetParent():Toggle()
if cmds.expandedcat then
if cmds.expandedcat ~= self:GetParent() then
cmds.expandedcat:Toggle()
else
cmds.expandedcat = nil
return
end
end
cmds.expandedcat = self:GetParent()
return
end
return self:GetParent():OnMousePressed( mcode )
end
newcategories[catname] = cat
table.insert( sortcategories, catname )
end
local line = cmds.cmd_contents[catname]:AddLine( string.gsub( data.cmd, "ulx ", "" ), data.cmd )
if data.cmd == lastcmd then
cmds.cmd_contents[catname]:SelectItem( line )
cmds.expandedcat = cmds.cmd_contents[catname]:GetParent()
cmds.expandedcat:SetExpanded( true )
matchedCmdFound = true
end
end
end
if not matchedCmdFound then
if cmds.plist.open then
cmds.plist:Close()
xlib.animQueue_start()
elseif cmds.argslist.open then
cmds.argslist:Close()
xlib.animQueue_start()
end
end
table.sort( sortcategories )
for _, catname in ipairs( sortcategories ) do
local cat = newcategories[catname]
cmds.cmds:Add( cat )
cat.Contents:SortByColumn( 1 )
cat.Contents:SetHeight( 17*#cat.Contents:GetLines() )
end
cmds.permissionChanged = nil
end
--------------
--ANIMATIONS--
--------------
function cmds.plist:openAnim( arg )
xlib.addToAnimQueue( cmds.refreshPlist, arg )
xlib.addToAnimQueue( "pnlSlide", { panel=self, startx=-250, starty=0, endx=0, endy=0, setvisible=true } )
end
function cmds.plist:closeAnim()
xlib.addToAnimQueue( "pnlSlide", { panel=self, startx=0, starty=0, endx=-250, endy=0, setvisible=false } )
end
function cmds.argslist:openAnim( cmd, secondary )
xlib.addToAnimQueue( function() cmds.argslist.secondaryPos = secondary end )
xlib.addToAnimQueue( cmds.buildArgsList, cmd )
if secondary then
xlib.addToAnimQueue( "pnlSlide", { panel=self.scroll, startx=-170, starty=0, endx=0, endy=0, setvisible=true } )
else
xlib.addToAnimQueue( "pnlSlide", { panel=self.scroll, startx=80, starty=0, endx=255, endy=0, setvisible=true } )
end
end
function cmds.argslist:closeAnim( secondary )
if secondary then
xlib.addToAnimQueue( "pnlSlide", { panel=self.scroll, startx=0, starty=0, endx=-170, endy=0, setvisible=false } )
else
xlib.addToAnimQueue( "pnlSlide", { panel=self.scroll, startx=255, starty=0, endx=80, endy=0, setvisible=false } )
end
xlib.addToAnimQueue( function() cmds.argslist.secondaryPos = nil end )
end
--------------
cmds.refresh()
hook.Add( "UCLChanged", "xgui_RefreshPlayerCmds", cmds.refresh )
hook.Add( "ULibPlayerNameChanged", "xgui_plyUpdateCmds", cmds.playerNameChanged )
xgui.addModule( "Cmds", cmds, "icon16/user_gray.png" )

View File

@ -1,39 +1,39 @@
--Sandbox settings module for ULX GUI -- by Stickly Man!
--Defines sbox cvar limits and sandbox specific settings for the sandbox gamemode.
xgui.prepareDataType( "sboxlimits" )
local sbox_settings = xlib.makepanel{ parent=xgui.null }
xlib.makecheckbox{ x=10, y=10, label="Enable Noclip", repconvar="rep_sbox_noclip", parent=sbox_settings }
xlib.makecheckbox{ x=10, y=30, label="Enable Godmode", repconvar="rep_sbox_godmode", parent=sbox_settings }
xlib.makecheckbox{ x=10, y=50, label="Enable PvP Damage", repconvar="rep_sbox_playershurtplayers", parent=sbox_settings }
xlib.makecheckbox{ x=10, y=70, label="Spawn With Weapons", repconvar="rep_sbox_weapons", parent=sbox_settings }
xlib.makecheckbox{ x=10, y=90, label="Limited Physgun", repconvar="rep_physgun_limited", parent=sbox_settings }
xlib.makecheckbox{ x=10, y=130, label="Persist Props", repconvar="rep_sbox_persist", parent=sbox_settings }
xlib.makecheckbox{ x=10, y=150, label="Bone Manip. Misc", repconvar="rep_sbox_bonemanip_misc", parent=sbox_settings }
xlib.makecheckbox{ x=10, y=170, label="Bone Manip. NPC", repconvar="rep_sbox_bonemanip_npc", parent=sbox_settings }
xlib.makecheckbox{ x=10, y=190, label="Bone Manip. Player", repconvar="rep_sbox_bonemanip_player", parent=sbox_settings }
xlib.makelabel{ x=5, y=247, w=140, wordwrap=true, label="NOTE: The non-ulx cvars configurable in XGUI are provided for easy access only and DO NOT SAVE when the server is shut down or crashes.", parent=sbox_settings }
sbox_settings.plist = xlib.makelistlayout{ x=140, y=5, h=322, w=440, spacing=1, padding=2, parent=sbox_settings }
function sbox_settings.processLimits()
sbox_settings.plist:Clear()
for g, limits in ipairs( xgui.data.sboxlimits ) do
if #limits > 0 then
local panel = xlib.makepanel{ h=5+math.ceil( #limits/2 )*25 }
local i=0
for _, cvar in ipairs( limits ) do
local cvardata = string.Explode( " ", cvar ) --Split the cvarname and max slider value number
xgui.queueFunctionCall( xlib.makeslider, "sboxlimits", { x=10+(i%2*205), y=5+math.floor(i/2)*25, w=200, label="Max " .. cvardata[1]:sub(9), min=0, max=cvardata[2], repconvar="rep_"..cvardata[1], parent=panel, fixclip=true } )
i = i + 1
end
sbox_settings.plist:Add( xlib.makecat{ label=limits.title .. " (" .. #limits .. " limit" .. ((#limits > 1) and "s" or "") .. ")", contents=panel, expanded=( g==1 ) } )
end
end
end
sbox_settings.processLimits()
xgui.hookEvent( "sboxlimits", "process", sbox_settings.processLimits, "sandboxProcessLimits" )
xgui.addSettingModule( "Sandbox", sbox_settings, "icon16/box.png", "xgui_gmsettings" )
--Sandbox settings module for ULX GUI -- by Stickly Man!
--Defines sbox cvar limits and sandbox specific settings for the sandbox gamemode.
xgui.prepareDataType( "sboxlimits" )
local sbox_settings = xlib.makepanel{ parent=xgui.null }
xlib.makecheckbox{ x=10, y=10, label="Enable Noclip", repconvar="rep_sbox_noclip", parent=sbox_settings }
xlib.makecheckbox{ x=10, y=30, label="Enable Godmode", repconvar="rep_sbox_godmode", parent=sbox_settings }
xlib.makecheckbox{ x=10, y=50, label="Enable PvP Damage", repconvar="rep_sbox_playershurtplayers", parent=sbox_settings }
xlib.makecheckbox{ x=10, y=70, label="Spawn With Weapons", repconvar="rep_sbox_weapons", parent=sbox_settings }
xlib.makecheckbox{ x=10, y=90, label="Limited Physgun", repconvar="rep_physgun_limited", parent=sbox_settings }
xlib.makecheckbox{ x=10, y=130, label="Persist Props", repconvar="rep_sbox_persist", parent=sbox_settings }
xlib.makecheckbox{ x=10, y=150, label="Bone Manip. Misc", repconvar="rep_sbox_bonemanip_misc", parent=sbox_settings }
xlib.makecheckbox{ x=10, y=170, label="Bone Manip. NPC", repconvar="rep_sbox_bonemanip_npc", parent=sbox_settings }
xlib.makecheckbox{ x=10, y=190, label="Bone Manip. Player", repconvar="rep_sbox_bonemanip_player", parent=sbox_settings }
xlib.makelabel{ x=5, y=247, w=140, wordwrap=true, label="NOTE: The non-ulx cvars configurable in XGUI are provided for easy access only and DO NOT SAVE when the server is shut down or crashes.", parent=sbox_settings }
sbox_settings.plist = xlib.makelistlayout{ x=140, y=5, h=322, w=440, spacing=1, padding=2, parent=sbox_settings }
function sbox_settings.processLimits()
sbox_settings.plist:Clear()
for g, limits in ipairs( xgui.data.sboxlimits ) do
if #limits > 0 then
local panel = xlib.makepanel{ h=5+math.ceil( #limits/2 )*25 }
local i=0
for _, cvar in ipairs( limits ) do
local cvardata = string.Explode( " ", cvar ) --Split the cvarname and max slider value number
xgui.queueFunctionCall( xlib.makeslider, "sboxlimits", { x=10+(i%2*205), y=5+math.floor(i/2)*25, w=200, label="Max " .. cvardata[1]:sub(9), min=0, max=cvardata[2], repconvar="rep_"..cvardata[1], parent=panel, fixclip=true } )
i = i + 1
end
sbox_settings.plist:Add( xlib.makecat{ label=limits.title .. " (" .. #limits .. " limit" .. ((#limits > 1) and "s" or "") .. ")", contents=panel, expanded=( g==1 ) } )
end
end
end
sbox_settings.processLimits()
xgui.hookEvent( "sboxlimits", "process", sbox_settings.processLimits, "sandboxProcessLimits" )
xgui.addSettingModule( "Sandbox", sbox_settings, "icon16/box.png", "xgui_gmsettings" )

File diff suppressed because it is too large Load Diff

View File

@ -1,176 +1,176 @@
--Maps module for ULX GUI -- by Stickly Man!
--Lists maps on server, allows for map voting, changing levels, etc. All players may access this menu.
ulx.votemaps = ulx.votemaps or {}
xgui.prepareDataType( "votemaps", ulx.votemaps )
local maps = xlib.makepanel{ parent=xgui.null }
maps.maplabel = xlib.makelabel{ x=10, y=13, label="Server Votemaps: (Votemaps are highlighted)", parent=maps }
xlib.makelabel{ x=10, y=343, label="Gamemode:", parent=maps }
maps.curmap = xlib.makelabel{ x=187, y=223, w=192, label="No Map Selected", parent=maps }
maps.list = xlib.makelistview{ x=5, y=30, w=175, h=310, multiselect=true, parent=maps, headerheight=0 } --Remember to enable/disable multiselect based on admin status?
maps.list:AddColumn( "Map Name" )
maps.list.OnRowSelected = function( self, LineID, Line )
if ( ULib.fileExists( "maps/thumb/" .. maps.list:GetSelected()[1]:GetColumnText(1) .. ".png" ) ) then
maps.disp:SetMaterial( Material( "maps/thumb/" .. maps.list:GetSelected()[1]:GetColumnText(1) .. ".png" ) )
else
maps.disp:SetMaterial( Material( "maps/thumb/noicon.png" ) )
end
maps.curmap:SetText( Line:GetColumnText(1) )
maps.updateButtonStates()
end
maps.disp = vgui.Create( "DImage", maps )
maps.disp:SetPos( 185, 30 )
maps.disp:SetMaterial( Material( "maps/thumb/noicon.png" ) )
maps.disp:SetSize( 192, 192 )
maps.gamemode = xlib.makecombobox{ x=70, y=340, w=110, h=20, text="<default>", parent=maps }
maps.vote = xlib.makebutton{ x=185, y=245, w=192, h=20, label="Vote to play this map!", parent=maps }
maps.vote.DoClick = function()
if maps.curmap:GetValue() ~= "No Map Selected" then
RunConsoleCommand( "ulx", "votemap", maps.curmap:GetValue() )
end
end
maps.svote = xlib.makebutton{ x=185, y=270, w=192, h=20, label="Server-wide vote of selected map(s)", parent=maps }
maps.svote.DoClick = function()
if maps.curmap:GetValue() ~= "No Map Selected" then
local votemaps = {}
for k, v in ipairs( maps.list:GetSelected() ) do
table.insert( votemaps, maps.list:GetSelected()[k]:GetColumnText(1))
end
RunConsoleCommand( "ulx", "votemap2", unpack( votemaps ) )
end
end
maps.changemap = xlib.makebutton{ x=185, y=295, w=192, h=20, disabled=true, label="Force changelevel to this map", parent=maps }
maps.changemap.DoClick = function()
if maps.curmap:GetValue() ~= "No Map Selected" then
Derma_Query( "Are you sure you would like to change the level to \"" .. maps.curmap:GetValue() .. "\"?", "XGUI WARNING",
"Change Level", function()
RunConsoleCommand( "ulx", "map", maps.curmap:GetValue(), ( maps.gamemode:GetValue() ~= "<default>" ) and maps.gamemode:GetValue() or nil ) end,
"Cancel", function() end )
end
end
maps.vetomap = xlib.makebutton{ x=185, y=320, w=192, label="Veto a map vote", parent=maps }
maps.vetomap.DoClick = function()
RunConsoleCommand( "ulx", "veto" )
end
maps.nextLevelLabel = xlib.makelabel{ x=382, y=13, label="Nextlevel (cvar)", parent=maps }
maps.nextlevel = xlib.makecombobox{ x=382, y=30, w=180, h=20, repconvar="rep_nextlevel", convarblanklabel="<not specified>", parent=maps }
function maps.addMaptoList( mapname, lastselected )
local line = maps.list:AddLine( mapname )
if table.HasValue( lastselected, mapname ) then
maps.list:SelectItem( line )
end
line.isNotVotemap = nil
if not table.HasValue( ulx.votemaps, mapname ) then
line:SetAlpha( 128 )
line.isNotVotemap = true
end
end
function maps.updateVoteMaps()
local lastselected = {}
for k, Line in pairs( maps.list.Lines ) do
if ( Line:IsLineSelected() ) then table.insert( lastselected, Line:GetColumnText(1) ) end
end
maps.list:Clear()
maps.nextlevel:Clear()
if LocalPlayer():query( "ulx map" ) then --Show all maps for admins who have access to change the level
maps.maplabel:SetText( "Server Maps (Votemaps are highlighted)" )
maps.nextlevel:AddChoice( "<not specified>" )
maps.nextlevel.ConVarUpdated( "nextlevel", "rep_nextlevel", nil, nil, GetConVar( "rep_nextlevel" ):GetString() )
maps.nextLevelLabel:SetAlpha(255);
maps.nextlevel:SetDisabled( false )
for _,v in ipairs( ulx.maps ) do
maps.addMaptoList( v, lastselected )
maps.nextlevel:AddChoice( v )
end
else
maps.maplabel:SetText( "Server Votemaps" )
maps.nextLevelLabel:SetAlpha(0);
maps.nextlevel:SetDisabled( true )
maps.nextlevel:SetAlpha(0);
for _,v in ipairs( ulx.votemaps ) do --Show the list of votemaps for users without access to "ulx map"
maps.addMaptoList( v, lastselected )
end
end
if not maps.accessVotemap2 then --Only select the first map if they don't have access to votemap2
local l = maps.list:GetSelected()[1]
maps.list:ClearSelection()
maps.list:SelectItem( l )
end
maps.updateButtonStates()
ULib.cmds.translatedCmds["ulx votemap"].args[2].completes = xgui.data.votemaps --Set concommand completes for the ulx votemap command. (Used by XGUI in the cmds tab)
end
function maps.updateGamemodes()
local lastselected = maps.gamemode:GetValue()
maps.gamemode:Clear()
maps.gamemode:SetText( lastselected )
maps.gamemode:AddChoice( "<default>" )
-- Get allowed gamemodes
local access, tag = LocalPlayer():query( "ulx map" )
local restrictions = {}
ULib.cmds.StringArg.processRestrictions( restrictions, ULib.cmds.translatedCmds['ulx map'].args[3], ulx.getTagArgNum( tag, 2 ) )
for _, v in ipairs( restrictions.restrictedCompletes ) do
maps.gamemode:AddChoice( v )
end
end
function maps.updatePermissions()
maps.vetomap:SetDisabled( true )
RunConsoleCommand( "xgui", "getVetoState" ) --Get the proper enabled/disabled state of the veto button.
maps.accessVotemap = ( GetConVarNumber( "ulx_votemapEnabled" ) == 1 )
maps.accessVotemap2 = LocalPlayer():query( "ulx votemap2" )
maps.accessMap = LocalPlayer():query( "ulx map" )
maps.updateGamemodes()
maps.updateVoteMaps()
maps.updateButtonStates()
end
function xgui.updateVetoButton( value )
maps.vetomap:SetDisabled( not value )
end
function maps.updateButtonStates()
maps.gamemode:SetDisabled( not maps.accessMap )
maps.list:SetMultiSelect( maps.accessVotemap2 )
if maps.list:GetSelectedLine() then
maps.vote:SetDisabled( maps.list:GetSelected()[1].isNotVotemap or not maps.accessVotemap )
maps.svote:SetDisabled( not maps.accessVotemap2 )
maps.changemap:SetDisabled( not maps.accessMap )
else --No lines are selected
maps.vote:SetDisabled( true )
maps.svote:SetDisabled( true )
maps.changemap:SetDisabled( true )
maps.curmap:SetText( "No Map Selected" )
maps.disp:SetMaterial( Material( "maps/thumb/noicon.png" ) )
end
end
maps.updateVoteMaps() -- For autorefresh
--Enable/Disable the votemap button when ulx_votemapEnabled changes
function maps.ConVarUpdated( sv_cvar, cl_cvar, ply, old_val, new_val )
if cl_cvar == "ulx_votemapenabled" then
maps.accessVotemap = ( tonumber( new_val ) == 1 )
maps.updateButtonStates()
end
end
hook.Add( "ULibReplicatedCvarChanged", "XGUI_mapsUpdateVotemapEnabled", maps.ConVarUpdated )
xgui.hookEvent( "onProcessModules", nil, maps.updatePermissions, "mapsUpdatePermissions" )
xgui.hookEvent( "votemaps", "process", maps.updateVoteMaps, "mapsUpdateVotemaps" )
xgui.addModule( "Maps", maps, "icon16/map.png" )
--Maps module for ULX GUI -- by Stickly Man!
--Lists maps on server, allows for map voting, changing levels, etc. All players may access this menu.
ulx.votemaps = ulx.votemaps or {}
xgui.prepareDataType( "votemaps", ulx.votemaps )
local maps = xlib.makepanel{ parent=xgui.null }
maps.maplabel = xlib.makelabel{ x=10, y=13, label="Server Votemaps: (Votemaps are highlighted)", parent=maps }
xlib.makelabel{ x=10, y=343, label="Gamemode:", parent=maps }
maps.curmap = xlib.makelabel{ x=187, y=223, w=192, label="No Map Selected", parent=maps }
maps.list = xlib.makelistview{ x=5, y=30, w=175, h=310, multiselect=true, parent=maps, headerheight=0 } --Remember to enable/disable multiselect based on admin status?
maps.list:AddColumn( "Map Name" )
maps.list.OnRowSelected = function( self, LineID, Line )
if ( ULib.fileExists( "maps/thumb/" .. maps.list:GetSelected()[1]:GetColumnText(1) .. ".png" ) ) then
maps.disp:SetMaterial( Material( "maps/thumb/" .. maps.list:GetSelected()[1]:GetColumnText(1) .. ".png" ) )
else
maps.disp:SetMaterial( Material( "maps/thumb/noicon.png" ) )
end
maps.curmap:SetText( Line:GetColumnText(1) )
maps.updateButtonStates()
end
maps.disp = vgui.Create( "DImage", maps )
maps.disp:SetPos( 185, 30 )
maps.disp:SetMaterial( Material( "maps/thumb/noicon.png" ) )
maps.disp:SetSize( 192, 192 )
maps.gamemode = xlib.makecombobox{ x=70, y=340, w=110, h=20, text="<default>", parent=maps }
maps.vote = xlib.makebutton{ x=185, y=245, w=192, h=20, label="Vote to play this map!", parent=maps }
maps.vote.DoClick = function()
if maps.curmap:GetValue() ~= "No Map Selected" then
RunConsoleCommand( "ulx", "votemap", maps.curmap:GetValue() )
end
end
maps.svote = xlib.makebutton{ x=185, y=270, w=192, h=20, label="Server-wide vote of selected map(s)", parent=maps }
maps.svote.DoClick = function()
if maps.curmap:GetValue() ~= "No Map Selected" then
local votemaps = {}
for k, v in ipairs( maps.list:GetSelected() ) do
table.insert( votemaps, maps.list:GetSelected()[k]:GetColumnText(1))
end
RunConsoleCommand( "ulx", "votemap2", unpack( votemaps ) )
end
end
maps.changemap = xlib.makebutton{ x=185, y=295, w=192, h=20, disabled=true, label="Force changelevel to this map", parent=maps }
maps.changemap.DoClick = function()
if maps.curmap:GetValue() ~= "No Map Selected" then
Derma_Query( "Are you sure you would like to change the level to \"" .. maps.curmap:GetValue() .. "\"?", "XGUI WARNING",
"Change Level", function()
RunConsoleCommand( "ulx", "map", maps.curmap:GetValue(), ( maps.gamemode:GetValue() ~= "<default>" ) and maps.gamemode:GetValue() or nil ) end,
"Cancel", function() end )
end
end
maps.vetomap = xlib.makebutton{ x=185, y=320, w=192, label="Veto a map vote", parent=maps }
maps.vetomap.DoClick = function()
RunConsoleCommand( "ulx", "veto" )
end
maps.nextLevelLabel = xlib.makelabel{ x=382, y=13, label="Nextlevel (cvar)", parent=maps }
maps.nextlevel = xlib.makecombobox{ x=382, y=30, w=180, h=20, repconvar="rep_nextlevel", convarblanklabel="<not specified>", parent=maps }
function maps.addMaptoList( mapname, lastselected )
local line = maps.list:AddLine( mapname )
if table.HasValue( lastselected, mapname ) then
maps.list:SelectItem( line )
end
line.isNotVotemap = nil
if not table.HasValue( ulx.votemaps, mapname ) then
line:SetAlpha( 128 )
line.isNotVotemap = true
end
end
function maps.updateVoteMaps()
local lastselected = {}
for k, Line in pairs( maps.list.Lines ) do
if ( Line:IsLineSelected() ) then table.insert( lastselected, Line:GetColumnText(1) ) end
end
maps.list:Clear()
maps.nextlevel:Clear()
if LocalPlayer():query( "ulx map" ) then --Show all maps for admins who have access to change the level
maps.maplabel:SetText( "Server Maps (Votemaps are highlighted)" )
maps.nextlevel:AddChoice( "<not specified>" )
maps.nextlevel.ConVarUpdated( "nextlevel", "rep_nextlevel", nil, nil, GetConVar( "rep_nextlevel" ):GetString() )
maps.nextLevelLabel:SetAlpha(255);
maps.nextlevel:SetDisabled( false )
for _,v in ipairs( ulx.maps ) do
maps.addMaptoList( v, lastselected )
maps.nextlevel:AddChoice( v )
end
else
maps.maplabel:SetText( "Server Votemaps" )
maps.nextLevelLabel:SetAlpha(0);
maps.nextlevel:SetDisabled( true )
maps.nextlevel:SetAlpha(0);
for _,v in ipairs( ulx.votemaps ) do --Show the list of votemaps for users without access to "ulx map"
maps.addMaptoList( v, lastselected )
end
end
if not maps.accessVotemap2 then --Only select the first map if they don't have access to votemap2
local l = maps.list:GetSelected()[1]
maps.list:ClearSelection()
maps.list:SelectItem( l )
end
maps.updateButtonStates()
ULib.cmds.translatedCmds["ulx votemap"].args[2].completes = xgui.data.votemaps --Set concommand completes for the ulx votemap command. (Used by XGUI in the cmds tab)
end
function maps.updateGamemodes()
local lastselected = maps.gamemode:GetValue()
maps.gamemode:Clear()
maps.gamemode:SetText( lastselected )
maps.gamemode:AddChoice( "<default>" )
-- Get allowed gamemodes
local access, tag = LocalPlayer():query( "ulx map" )
local restrictions = {}
ULib.cmds.StringArg.processRestrictions( restrictions, ULib.cmds.translatedCmds['ulx map'].args[3], ulx.getTagArgNum( tag, 2 ) )
for _, v in ipairs( restrictions.restrictedCompletes ) do
maps.gamemode:AddChoice( v )
end
end
function maps.updatePermissions()
maps.vetomap:SetDisabled( true )
RunConsoleCommand( "xgui", "getVetoState" ) --Get the proper enabled/disabled state of the veto button.
maps.accessVotemap = ( GetConVarNumber( "ulx_votemapEnabled" ) == 1 )
maps.accessVotemap2 = LocalPlayer():query( "ulx votemap2" )
maps.accessMap = LocalPlayer():query( "ulx map" )
maps.updateGamemodes()
maps.updateVoteMaps()
maps.updateButtonStates()
end
function xgui.updateVetoButton( value )
maps.vetomap:SetDisabled( not value )
end
function maps.updateButtonStates()
maps.gamemode:SetDisabled( not maps.accessMap )
maps.list:SetMultiSelect( maps.accessVotemap2 )
if maps.list:GetSelectedLine() then
maps.vote:SetDisabled( maps.list:GetSelected()[1].isNotVotemap or not maps.accessVotemap )
maps.svote:SetDisabled( not maps.accessVotemap2 )
maps.changemap:SetDisabled( not maps.accessMap )
else --No lines are selected
maps.vote:SetDisabled( true )
maps.svote:SetDisabled( true )
maps.changemap:SetDisabled( true )
maps.curmap:SetText( "No Map Selected" )
maps.disp:SetMaterial( Material( "maps/thumb/noicon.png" ) )
end
end
maps.updateVoteMaps() -- For autorefresh
--Enable/Disable the votemap button when ulx_votemapEnabled changes
function maps.ConVarUpdated( sv_cvar, cl_cvar, ply, old_val, new_val )
if cl_cvar == "ulx_votemapenabled" then
maps.accessVotemap = ( tonumber( new_val ) == 1 )
maps.updateButtonStates()
end
end
hook.Add( "ULibReplicatedCvarChanged", "XGUI_mapsUpdateVotemapEnabled", maps.ConVarUpdated )
xgui.hookEvent( "onProcessModules", nil, maps.updatePermissions, "mapsUpdatePermissions" )
xgui.hookEvent( "votemaps", "process", maps.updateVoteMaps, "mapsUpdateVotemaps" )
xgui.addModule( "Maps", maps, "icon16/map.png" )

View File

@ -1,281 +1,281 @@
--sv_bans -- by Stickly Man!
--Server-side code related to the bans menu.
local bans={}
function bans.init()
ULib.ucl.registerAccess( "xgui_managebans", "superadmin", "Allows addition, removal, and viewing of bans in XGUI.", "XGUI" )
xgui.addDataType( "bans", function() return { count=table.Count( ULib.bans ) } end, "xgui_managebans", 30, 20 )
--Chat commands
local function xgui_banWindowChat( ply, func, args, doFreeze )
if doFreeze ~= true then doFreeze = false end
if args[1] and args[1] ~= "" then
local target = ULib.getUser( args[1] )
if target then
ULib.clientRPC( ply, "xgui.ShowBanWindow", target, target:SteamID(), doFreeze )
end
else
ULib.clientRPC( ply, "xgui.ShowBanWindow" )
end
end
ULib.addSayCommand( "!xban", xgui_banWindowChat, "ulx ban" )
local function xgui_banWindowChatFreeze( ply, func, args )
xgui_banWindowChat( ply, func, args, true )
end
ULib.addSayCommand( "!fban", xgui_banWindowChatFreeze, "ulx ban" )
--XGUI commands
function bans.updateBan( ply, args )
local access, accessTag = ULib.ucl.query( ply, "ulx ban" )
if not access then
ULib.tsayError( ply, "Error editing ban: You must have access to ulx ban, " .. ply:Nick() .. "!", true )
return
end
local steamID = args[1]
local bantime = tonumber( args[2] )
local reason = args[3]
local name = args[4]
-- Check restrictions
local cmd = ULib.cmds.translatedCmds[ "ulx ban" ]
local accessPieces = {}
if accessTag then
accessPieces = ULib.splitArgs( accessTag, "<", ">" )
end
-- Ban length
local argInfo = cmd.args[3]
local success, err = argInfo.type:parseAndValidate( ply, bantime, argInfo, accessPieces[2] )
if not success then
ULib.tsayError( ply, "Error editing ban: " .. err, true )
return
end
-- Reason
local argInfo = cmd.args[4]
local success, err = argInfo.type:parseAndValidate( ply, reason, argInfo, accessPieces[3] )
if not success then
ULib.tsayError( ply, "Error editing ban: You did not specify a valid reason, " .. ply:Nick() .. "!", true )
return
end
if not ULib.bans[steamID] then
ULib.addBan( steamID, bantime, reason, name, ply )
return
end
if name == "" then
name = nil
ULib.bans[steamID].name = nil
end
if bantime ~= 0 then
if (ULib.bans[steamID].time + bantime*60) <= os.time() then --New ban time makes the ban expired
ULib.unban( steamID, ply )
return
end
bantime = bantime - (os.time() - ULib.bans[steamID].time)/60
end
ULib.addBan( steamID, bantime, reason, name, ply )
end
xgui.addCmd( "updateBan", bans.updateBan )
--Misc functions
function bans.processBans()
bans.clearSortCache()
xgui.sendDataTable( {}, "bans" ) --Only sends the ban count, and triggers the client to clear their cache.
end
function bans.clearSortCache()
xgui.bansbyid = {}
xgui.bansbyname = {}
xgui.bansbyadmin = {}
xgui.bansbyreason = {}
xgui.bansbydate = {}
xgui.bansbyunban = {}
xgui.bansbybanlength = {}
end
function bans.getSortTable( sortType )
-- Retrieve the sorted table of bans. If type hasn't been sorted, then sort and cache.
if sortType == 1 then
-- Bans by Name
if next( xgui.bansbyname ) == nil then
for k, v in pairs( ULib.bans ) do
table.insert( xgui.bansbyname, { k, v.name and string.upper( v.name ) or nil } )
end
table.sort( xgui.bansbyname, function( a, b ) return (a[2] or "\255" .. a[1]) < (b[2] or "\255" .. b[1]) end )
end
return xgui.bansbyname
elseif sortType == 2 then
-- Bans by SteamID
if next( xgui.bansbyid ) == nil then
for k, v in pairs( ULib.bans ) do
table.insert( xgui.bansbyid, { k } )
end
table.sort( xgui.bansbyid, function( a, b ) return a[1] < b[1] end )
end
return xgui.bansbyid
elseif sortType == 3 then
-- Bans by Admin
if next( xgui.bansbyadmin ) == nil then
for k, v in pairs( ULib.bans ) do
table.insert( xgui.bansbyadmin, { k, v.admin or "" } )
end
table.sort( xgui.bansbyadmin, function( a, b ) return a[2] < b[2] end )
end
return xgui.bansbyadmin
elseif sortType == 4 then
-- Bans by Reason
if next( xgui.bansbyreason ) == nil then
for k, v in pairs( ULib.bans ) do
table.insert( xgui.bansbyreason, { k, v.reason or "" } )
end
table.sort( xgui.bansbyreason, function( a, b ) return a[2] < b[2] end )
end
return xgui.bansbyreason
elseif sortType == 5 then
-- Bans by Unban Date
if next( xgui.bansbyunban ) == nil then
for k, v in pairs( ULib.bans ) do
table.insert( xgui.bansbyunban, { k, tonumber(v.unban) or 0 } )
end
table.sort( xgui.bansbyunban, function( a, b ) return a[2] < b[2] end )
end
return xgui.bansbyunban
elseif sortType == 6 then
-- Bans by Ban Length
if next( xgui.bansbybanlength ) == nil then
for k, v in pairs( ULib.bans ) do
table.insert( xgui.bansbybanlength, { k, (tonumber(v.unban) ~= 0) and (v.unban - v.time) or nil } )
end
table.sort( xgui.bansbybanlength, function( a, b ) return (a[2] or math.huge) < (b[2] or math.huge) end )
end
return xgui.bansbybanlength
else
if next( xgui.bansbydate ) == nil then
for k, v in pairs( ULib.bans ) do
table.insert( xgui.bansbydate, { k, v.time or 0 } )
end
table.sort( xgui.bansbydate, function( a, b ) return tonumber( a[2] ) > tonumber( b[2] ) end )
end
return xgui.bansbydate
end
end
function bans.sendBansToUser( ply, args )
if not ply then return end
if not ULib.ucl.query( ply, "xgui_managebans" ) then return end
--local perfTimer = os.clock() --Debug
-- Default params
sortType = tonumber( args[1] ) or 0
filterString = args[2] ~= "" and string.lower( args[2] ) or nil
filterPermaBan = args[3] and tonumber( args[3] ) or 0
filterIncomplete = args[4] and tonumber( args[4] ) or 0
page = tonumber( args[5] ) or 1
ascending = tonumber( args[6] ) == 1 or false
-- Get cached sort table to use to reference the real data.
sortTable = bans.getSortTable( sortType )
local bansToSend = {}
-- Handle ascending or descending
local startValue = ascending and #sortTable or 1
local endValue = ascending and 1 or #sortTable
local firstEntry = (page - 1) * 17
local currentEntry = 0
local noFilter = ( filterPermaBan == 0 and filterIncomplete == 0 and filterString == nil )
for i = startValue, endValue, ascending and -1 or 1 do
local steamID = sortTable[i][1]
local bandata = ULib.bans[steamID]
-- Handle filters. This is confusing, but essentially 0 means skip check, 1 means restrict if condition IS true, 2+ means restrict if condition IS NOT true.
if not ( filterPermaBan > 0 and ( ( tonumber( bandata.unban ) == 0 ) == ( filterPermaBan == 1 ) ) ) then
if not ( filterIncomplete > 0 and ( ( bandata.time == nil ) == ( filterIncomplete == 1 ) ) ) then
-- Handle string filter
if not ( filterString and
not ( steamID and string.find( string.lower( steamID ), filterString ) or
bandata.name and string.find( string.lower( bandata.name ), filterString ) or
bandata.reason and string.find( string.lower( bandata.reason ), filterString ) or
bandata.admin and string.find( string.lower( bandata.admin ), filterString ) or
bandata.modified_admin and string.find( string.lower( bandata.modified_admin ), filterString ) )) then
--We found a valid one! .. Now for the pagination.
if #bansToSend < 17 and currentEntry >= firstEntry then
table.insert( bansToSend, bandata )
bansToSend[#bansToSend].steamID = steamID
if noFilter and #bansToSend >= 17 then break end -- If there is a filter, then don't stop the loop so we can get a "result" count.
end
currentEntry = currentEntry + 1
end
end
end
end
if not noFilter then bansToSend.count = currentEntry end
--print( "XGUI: Ban request took " .. os.clock() - perfTimer ) --Debug
-- Send bans to client via custom handling.
xgui.sendDataEvent( ply, 7, "bans", bansToSend )
end
xgui.addCmd( "getbans", bans.sendBansToUser )
--Hijack the addBan function to update XGUI's ban info.
local banfunc = ULib.addBan
ULib.addBan = function( steamid, time, reason, name, admin )
banfunc( steamid, time, reason, name, admin )
bans.processBans()
bans.unbanTimer()
end
--Hijack the unBan function to update XGUI's ban info.
local unbanfunc = ULib.unban
ULib.unban = function( steamid, admin )
unbanfunc( steamid, admin )
bans.processBans()
if timer.Exists( "xgui_unban" .. steamid ) then
timer.Remove( "xgui_unban" .. steamid )
end
end
--Create timers that will automatically perform an unban when a users ban runs out. Polls hourly.
function bans.unbanTimer()
timer.Create( "xgui_unbanTimer", 3600, 0, bans.unbanTimer )
for ID, data in pairs( ULib.bans ) do
if tonumber( data.unban ) ~= 0 then
if tonumber( data.unban ) - os.time() <= 3600 then
timer.Remove( "xgui_unban" .. ID )
timer.Create( "xgui_unban" .. ID, tonumber( data.unban ) - os.time(), 1, function() ULib.unban( ID ) end )
end
end
end
end
ulx.addToHelpManually( "Menus", "xgui fban", "<player> - Opens the add ban window, freezes the specified player, and fills out the Name/SteamID automatically. (say: !fban)" )
ulx.addToHelpManually( "Menus", "xgui xban", "<player> - Opens the add ban window and fills out Name/SteamID automatically if a player was specified. (say: !xban)" )
end
function bans.postinit()
bans.processBans()
bans.unbanTimer()
end
--sv_bans -- by Stickly Man!
--Server-side code related to the bans menu.
local bans={}
function bans.init()
ULib.ucl.registerAccess( "xgui_managebans", "superadmin", "Allows addition, removal, and viewing of bans in XGUI.", "XGUI" )
xgui.addDataType( "bans", function() return { count=table.Count( ULib.bans ) } end, "xgui_managebans", 30, 20 )
--Chat commands
local function xgui_banWindowChat( ply, func, args, doFreeze )
if doFreeze ~= true then doFreeze = false end
if args[1] and args[1] ~= "" then
local target = ULib.getUser( args[1] )
if target then
ULib.clientRPC( ply, "xgui.ShowBanWindow", target, target:SteamID(), doFreeze )
end
else
ULib.clientRPC( ply, "xgui.ShowBanWindow" )
end
end
ULib.addSayCommand( "!xban", xgui_banWindowChat, "ulx ban" )
local function xgui_banWindowChatFreeze( ply, func, args )
xgui_banWindowChat( ply, func, args, true )
end
ULib.addSayCommand( "!fban", xgui_banWindowChatFreeze, "ulx ban" )
--XGUI commands
function bans.updateBan( ply, args )
local access, accessTag = ULib.ucl.query( ply, "ulx ban" )
if not access then
ULib.tsayError( ply, "Error editing ban: You must have access to ulx ban, " .. ply:Nick() .. "!", true )
return
end
local steamID = args[1]
local bantime = tonumber( args[2] )
local reason = args[3]
local name = args[4]
-- Check restrictions
local cmd = ULib.cmds.translatedCmds[ "ulx ban" ]
local accessPieces = {}
if accessTag then
accessPieces = ULib.splitArgs( accessTag, "<", ">" )
end
-- Ban length
local argInfo = cmd.args[3]
local success, err = argInfo.type:parseAndValidate( ply, bantime, argInfo, accessPieces[2] )
if not success then
ULib.tsayError( ply, "Error editing ban: " .. err, true )
return
end
-- Reason
local argInfo = cmd.args[4]
local success, err = argInfo.type:parseAndValidate( ply, reason, argInfo, accessPieces[3] )
if not success then
ULib.tsayError( ply, "Error editing ban: You did not specify a valid reason, " .. ply:Nick() .. "!", true )
return
end
if not ULib.bans[steamID] then
ULib.addBan( steamID, bantime, reason, name, ply )
return
end
if name == "" then
name = nil
ULib.bans[steamID].name = nil
end
if bantime ~= 0 then
if (ULib.bans[steamID].time + bantime*60) <= os.time() then --New ban time makes the ban expired
ULib.unban( steamID, ply )
return
end
bantime = bantime - (os.time() - ULib.bans[steamID].time)/60
end
ULib.addBan( steamID, bantime, reason, name, ply )
end
xgui.addCmd( "updateBan", bans.updateBan )
--Misc functions
function bans.processBans()
bans.clearSortCache()
xgui.sendDataTable( {}, "bans" ) --Only sends the ban count, and triggers the client to clear their cache.
end
function bans.clearSortCache()
xgui.bansbyid = {}
xgui.bansbyname = {}
xgui.bansbyadmin = {}
xgui.bansbyreason = {}
xgui.bansbydate = {}
xgui.bansbyunban = {}
xgui.bansbybanlength = {}
end
function bans.getSortTable( sortType )
-- Retrieve the sorted table of bans. If type hasn't been sorted, then sort and cache.
if sortType == 1 then
-- Bans by Name
if next( xgui.bansbyname ) == nil then
for k, v in pairs( ULib.bans ) do
table.insert( xgui.bansbyname, { k, v.name and string.upper( v.name ) or nil } )
end
table.sort( xgui.bansbyname, function( a, b ) return (a[2] or "\255" .. a[1]) < (b[2] or "\255" .. b[1]) end )
end
return xgui.bansbyname
elseif sortType == 2 then
-- Bans by SteamID
if next( xgui.bansbyid ) == nil then
for k, v in pairs( ULib.bans ) do
table.insert( xgui.bansbyid, { k } )
end
table.sort( xgui.bansbyid, function( a, b ) return a[1] < b[1] end )
end
return xgui.bansbyid
elseif sortType == 3 then
-- Bans by Admin
if next( xgui.bansbyadmin ) == nil then
for k, v in pairs( ULib.bans ) do
table.insert( xgui.bansbyadmin, { k, v.admin or "" } )
end
table.sort( xgui.bansbyadmin, function( a, b ) return a[2] < b[2] end )
end
return xgui.bansbyadmin
elseif sortType == 4 then
-- Bans by Reason
if next( xgui.bansbyreason ) == nil then
for k, v in pairs( ULib.bans ) do
table.insert( xgui.bansbyreason, { k, v.reason or "" } )
end
table.sort( xgui.bansbyreason, function( a, b ) return a[2] < b[2] end )
end
return xgui.bansbyreason
elseif sortType == 5 then
-- Bans by Unban Date
if next( xgui.bansbyunban ) == nil then
for k, v in pairs( ULib.bans ) do
table.insert( xgui.bansbyunban, { k, tonumber(v.unban) or 0 } )
end
table.sort( xgui.bansbyunban, function( a, b ) return a[2] < b[2] end )
end
return xgui.bansbyunban
elseif sortType == 6 then
-- Bans by Ban Length
if next( xgui.bansbybanlength ) == nil then
for k, v in pairs( ULib.bans ) do
table.insert( xgui.bansbybanlength, { k, (tonumber(v.unban) ~= 0) and (v.unban - v.time) or nil } )
end
table.sort( xgui.bansbybanlength, function( a, b ) return (a[2] or math.huge) < (b[2] or math.huge) end )
end
return xgui.bansbybanlength
else
if next( xgui.bansbydate ) == nil then
for k, v in pairs( ULib.bans ) do
table.insert( xgui.bansbydate, { k, v.time or 0 } )
end
table.sort( xgui.bansbydate, function( a, b ) return tonumber( a[2] ) > tonumber( b[2] ) end )
end
return xgui.bansbydate
end
end
function bans.sendBansToUser( ply, args )
if not ply then return end
if not ULib.ucl.query( ply, "xgui_managebans" ) then return end
--local perfTimer = os.clock() --Debug
-- Default params
sortType = tonumber( args[1] ) or 0
filterString = args[2] ~= "" and string.lower( args[2] ) or nil
filterPermaBan = args[3] and tonumber( args[3] ) or 0
filterIncomplete = args[4] and tonumber( args[4] ) or 0
page = tonumber( args[5] ) or 1
ascending = tonumber( args[6] ) == 1 or false
-- Get cached sort table to use to reference the real data.
sortTable = bans.getSortTable( sortType )
local bansToSend = {}
-- Handle ascending or descending
local startValue = ascending and #sortTable or 1
local endValue = ascending and 1 or #sortTable
local firstEntry = (page - 1) * 17
local currentEntry = 0
local noFilter = ( filterPermaBan == 0 and filterIncomplete == 0 and filterString == nil )
for i = startValue, endValue, ascending and -1 or 1 do
local steamID = sortTable[i][1]
local bandata = ULib.bans[steamID]
-- Handle filters. This is confusing, but essentially 0 means skip check, 1 means restrict if condition IS true, 2+ means restrict if condition IS NOT true.
if not ( filterPermaBan > 0 and ( ( tonumber( bandata.unban ) == 0 ) == ( filterPermaBan == 1 ) ) ) then
if not ( filterIncomplete > 0 and ( ( bandata.time == nil ) == ( filterIncomplete == 1 ) ) ) then
-- Handle string filter
if not ( filterString and
not ( steamID and string.find( string.lower( steamID ), filterString ) or
bandata.name and string.find( string.lower( bandata.name ), filterString ) or
bandata.reason and string.find( string.lower( bandata.reason ), filterString ) or
bandata.admin and string.find( string.lower( bandata.admin ), filterString ) or
bandata.modified_admin and string.find( string.lower( bandata.modified_admin ), filterString ) )) then
--We found a valid one! .. Now for the pagination.
if #bansToSend < 17 and currentEntry >= firstEntry then
table.insert( bansToSend, bandata )
bansToSend[#bansToSend].steamID = steamID
if noFilter and #bansToSend >= 17 then break end -- If there is a filter, then don't stop the loop so we can get a "result" count.
end
currentEntry = currentEntry + 1
end
end
end
end
if not noFilter then bansToSend.count = currentEntry end
--print( "XGUI: Ban request took " .. os.clock() - perfTimer ) --Debug
-- Send bans to client via custom handling.
xgui.sendDataEvent( ply, 7, "bans", bansToSend )
end
xgui.addCmd( "getbans", bans.sendBansToUser )
--Hijack the addBan function to update XGUI's ban info.
local banfunc = ULib.addBan
ULib.addBan = function( steamid, time, reason, name, admin )
banfunc( steamid, time, reason, name, admin )
bans.processBans()
bans.unbanTimer()
end
--Hijack the unBan function to update XGUI's ban info.
local unbanfunc = ULib.unban
ULib.unban = function( steamid, admin )
unbanfunc( steamid, admin )
bans.processBans()
if timer.Exists( "xgui_unban" .. steamid ) then
timer.Remove( "xgui_unban" .. steamid )
end
end
--Create timers that will automatically perform an unban when a users ban runs out. Polls hourly.
function bans.unbanTimer()
timer.Create( "xgui_unbanTimer", 3600, 0, bans.unbanTimer )
for ID, data in pairs( ULib.bans ) do
if tonumber( data.unban ) ~= 0 then
if tonumber( data.unban ) - os.time() <= 3600 then
timer.Remove( "xgui_unban" .. ID )
timer.Create( "xgui_unban" .. ID, tonumber( data.unban ) - os.time(), 1, function() ULib.unban( ID ) end )
end
end
end
end
ulx.addToHelpManually( "Menus", "xgui fban", "<player> - Opens the add ban window, freezes the specified player, and fills out the Name/SteamID automatically. (say: !fban)" )
ulx.addToHelpManually( "Menus", "xgui xban", "<player> - Opens the add ban window and fills out Name/SteamID automatically if a player was specified. (say: !xban)" )
end
function bans.postinit()
bans.processBans()
bans.unbanTimer()
end
xgui.addSVModule( "bans", bans.init, bans.postinit )

View File

@ -1,340 +1,340 @@
--sv_groups -- by Stickly Man!
--Server-side code related to the groups menu.
local groups = {}
function groups.init()
ULib.ucl.registerAccess( "xgui_managegroups", "superadmin", "Allows managing of groups, users, and access strings via the groups tab in XGUI.", "XGUI" )
xgui.addDataType( "playermodels", player_manager.AllValidModels, "xgui_managegroups", 0, 10 )
xgui.addDataType( "teams", function() return xgui.teams end, "xgui_managegroups", 0, -20 )
xgui.addDataType( "accesses", function() return xgui.accesses end, "xgui_managegroups", 0, 5 )
xgui.addDataType( "users", function()
local temp = groups.garryUsers
table.Merge( temp, ULib.ucl.users )
return temp
end, "xgui_managegroups", 20, -10 )
function groups.setInheritance( ply, args )
if ULib.ucl.query( ply, "ulx addgroup" ) then
--Check for cycles
local group = ULib.ucl.groupInheritsFrom( args[2] )
while group do
if group == args[1] or args[1] == args[2] then
ULib.clientRPC( ply, "Derma_Message", "Cannot set inheritance! You cannot inherit from something you're inheriting to!", "XGUI NOTICE" )
return
end
group = ULib.ucl.groupInheritsFrom( group )
end
ULib.ucl.setGroupInheritance( args[1], args[2] )
end
end
xgui.addCmd( "setinheritance", groups.setInheritance )
function xgui.playerExistsByID( id )
for k, v in pairs( player.GetAll() ) do
if v:SteamID() == id or v:UniqueID() == id or ULib.splitPort( v:IPAddress() ) == id then
return v
end
end
return false
end
--Override adduser to (re)send the new user info to the players.
local tempfuncadd = ULib.ucl.addUser
ULib.ucl.addUser = function( id, allows, denies, group )
local affectedply = xgui.playerExistsByID( id )
if affectedply then groups.resetAllPlayerValues( affectedply ) end
tempfuncadd( id, allows, denies, group )
local temp = {}
temp[id] = ULib.ucl.users[id]
xgui.updateData( {}, "users", temp )
end
--Override removeuser to resend the users table to the players.
local tempfuncremove = ULib.ucl.removeUser
ULib.ucl.removeUser = function( id )
xgui.removeData( {}, "users", { id } )
local affectedply = xgui.playerExistsByID( id )
if affectedply then groups.resetAllPlayerValues( affectedply ) end
tempfuncremove( id )
end
---------------------------
--UTeam Integration Stuff--
---------------------------
function groups.createTeam( ply, args )
if ULib.ucl.query( ply, "xgui_managegroups" ) then
--Check and make sure the team doesn't exist first
local exists = false
for i, v in ipairs( xgui.teams ) do
if v.name == args[1] then
exists = true
end
end
if not exists then
local team = {}
team.name = args[1]
team.color = Color( args[2], args[3], args[4], 255 )
team.order = #xgui.teams+1
team.groups = {}
table.insert( xgui.teams, team )
groups.refreshTeams()
end
end
end
xgui.addCmd( "createTeam", groups.createTeam )
function groups.removeTeam( ply, args )
if ULib.ucl.query( ply, "xgui_managegroups" ) then
for i, v in ipairs( xgui.teams ) do
if v.name == args[1] then
for _,group in ipairs( v.groups ) do --Unassign groups in team being deleted
groups.doChangeGroupTeam( group, "" )
end
table.remove( xgui.teams, i )
groups.setTeamsOrder()
groups.refreshTeams()
break
end
end
end
end
xgui.addCmd( "removeTeam", groups.removeTeam )
function groups.changeGroupTeam( ply, args, norefresh )
if ULib.ucl.query( ply, "xgui_managegroups" ) then
groups.doChangeGroupTeam( args[1], args[2], norefresh )
end
end
xgui.addCmd( "changeGroupTeam", groups.changeGroupTeam )
function groups.doChangeGroupTeam( group, newteam, norefresh )
local resettable = {}
for _,teamdata in ipairs( xgui.teams ) do
for i,groupname in ipairs( teamdata.groups ) do
if group == groupname then --Found the previous team the group belonged to, remove it now!
table.remove( teamdata.groups, i )
--Grab old modifier info while we're here
for modifier, _ in pairs( teamdata ) do
if modifier ~= "order" and modifier ~= "index" and modifier ~= "groups" and modifier ~= "name" and modifier ~= "color" then
table.insert( resettable, modifier )
end
end
break
end
end
if teamdata.name == newteam then --If the team requested was found, then add it to the new team.
table.insert( teamdata.groups, group )
end
end
--Reset modifiers for affected players, then let UTeam set the new modifiers
groups.resetTeamValue( group, resettable, newteam=="" ) --Let the function know if the new team is unassigned
if not norefresh then groups.refreshTeams() end
end
--UTeam Parameters: If values are a table, then it specifies default, min, then max. Otherwise it just specifies a min.
--Note that the min/max values here are ABSOLUTE values, meaning values outside of this range will probably cause undesirable results.
xgui.teamDefaults = {
armor = { 0, 0, 255 },
--crouchedWalkSpeed = 0.6, --Pointless setting?
deaths = { 0, -2048, 2047 },
duckSpeed = 0.3,
frags = { 0, -2048, 2047 },
gravity = 1,
health = { 100, 1, 2.14748e+009 },
jumpPower = 200,
maxHealth = 100,
--maxSpeed = 250, --Pointless setting?
model = "scientist",
runSpeed = { 500, 1, nil },
stepSize = { 18, 0, 512 },
unDuckSpeed = 0.2,
walkSpeed = { 250, 1, nil } }
function groups.updateTeamValue( ply, args )
if ULib.ucl.query( ply, "xgui_managegroups" ) then
local modifier = args[2]
local value = tonumber( args[3] ) or args[3] --If args[3] is a number, set value as a number.
for k, v in ipairs( xgui.teams ) do
if v.name == args[1] then
if modifier == "color" then
v.color = { r=tonumber(args[3]), g=tonumber(args[4]), b=tonumber(args[5]), a=255 }
else
if value ~= "" then
--Check for out-of-bound values!
local def = xgui.teamDefaults[modifier]
if type(def) == "table" then
if def[2] and value < def[2] then value = def[2] end
if def[3] and value > def[3] then value = def[3] end
end
v[modifier] = value
else
v[modifier] = nil
--Set the players back to the original value
for _, group in ipairs( v.groups ) do
groups.resetTeamValue( group, { args[2] } )
end
end
end
--Check for order updates, only refresh the teams when args[4] flag is set to prevent multiple data sendings
if v[modifier] ~= "order" or args[4] == "true" then
groups.refreshTeams()
end
break
end
end
end
end
xgui.addCmd( "updateTeamValue", groups.updateTeamValue )
function groups.refreshTeams()
if not ULib.isSandbox() then return end --Do not perform any of the following code if UTeam is disabled.
ulx.teams = table.Copy( xgui.teams )
ulx.saveTeams() --Let ULX reprocess the teams (Empty/new teams would be lost here)
ulx.refreshTeams()
table.sort( xgui.teams, function(a, b) return a.order < b.order end ) --Sort table by order.
xgui.sendDataTable( {}, "teams" )
hook.Call( ULib.HOOK_UCLCHANGED )
--Save any teams that don't have a group assigned to it to a special file. (They'll be removed on changelevel if we don't)
local emptyteams = {}
for _, teamdata in ipairs( xgui.teams ) do
if #teamdata.groups == 0 then
table.insert( emptyteams, teamdata )
end
end
if #emptyteams > 0 then
local output = "//This file stores teams that do not have any groups assigned to it (Since ULX would discard them). Do not edit this file!\n"
output = output .. ULib.makeKeyValues( emptyteams )
ULib.fileWrite( "data/ulx/empty_teams.txt", output )
else
if ULib.fileExists( "data/ulx/empty_teams.txt" ) then
ULib.fileDelete( "data/ulx/empty_teams.txt" )
end
end
end
function groups.resetPlayerValue( ply, values )
for _, modifier in ipairs( values ) do
--Code from UTeam
local defaultvalue = xgui.teamDefaults[modifier]
if type( defaultvalue ) == "table" then defaultvalue = xgui.teamDefaults[modifier][1] end
ply[ "Set" .. modifier:sub( 1, 1 ):upper() .. modifier:sub( 2 ) ]( ply, defaultvalue )
end
end
--This function will locate all players affected by team modifier(s) being unset (or team being changed)
--and will reset any related modifiers to their defaults.
function groups.resetTeamValue( group, values, teamIsUnassigned )
for _, ply in ipairs( player.GetAll() ) do
if ply:GetUserGroup() == group then
groups.resetPlayerValue( ply, values )
if teamIsUnassigned then ply:SetTeam(1001) end --Force the player to the unassigned team
end
end
end
--Remove all UTeam values from a player (used when they change teams)
function groups.resetAllPlayerValues( ply )
for _, team in ipairs( ulx.teams ) do --Loop through each team
for _, group in ipairs( team.groups ) do --Loop through each group per team
if group == ply:GetUserGroup() then --Have we found our team associated with this players group?
local resettable = {}
for modifier, _ in pairs( team ) do --Good! Now go reset the UTeam params based on the current team.
if modifier ~= "order" and modifier ~= "index" and modifier ~= "groups" and modifier ~= "name" and modifier ~= "color" then
table.insert( resettable, modifier )
end
end
groups.resetPlayerValue( ply, resettable )
break
end
end
end
end
--Check and make sure the teams have a specified order
function groups.setTeamsOrder()
for i, v in ipairs( xgui.teams ) do
v.order = i --Assign based on their index, which should be in order set by the file
end
end
--Hijack the renameGroup and removeGroup ULib functions to properly update team information when these are called.
local tempfunc = ULib.ucl.renameGroup
ULib.ucl.renameGroup = function( orig, new )
for _, teamdata in ipairs( xgui.teams ) do
for i, groupname in ipairs( teamdata.groups ) do
if groupname == orig then
teamdata.groups[i] = new
end
break
end
end
tempfunc( orig, new )
groups.refreshTeams()
end
local otherfunc = ULib.ucl.removeGroup
ULib.ucl.removeGroup = function( name )
groups.doChangeGroupTeam( name, "", true )
otherfunc( name )
groups.refreshTeams()
xgui.sendDataTable( {}, "users" ) --Resend user information in case users were bumped to another group.
end
end
function groups.postinit()
--Get user information from Garry's users.txt
groups.garryUsers = {}
if ULib.fileExists( "settings/users.txt" ) then
for group, users in pairs ( util.KeyValuesToTable( ULib.fileRead( "settings/users.txt", true ) ) ) do
for user, steamID in pairs( users ) do
groups.garryUsers[steamID] = { name=user, group=group }
end
end
end
--Combine access data into one table.
xgui.accesses = {}
for k, v in pairs( ULib.ucl.accessStrings ) do
xgui.accesses[k] = {}
xgui.accesses[k].hStr = v
end
for k, v in pairs( ULib.ucl.accessCategories ) do
xgui.accesses[k].cat = v
end
---------------------------
--UTeam Integration Stuff--
---------------------------
--Duplicate ULX's UTeam table (required for how Megiddo stores team data within the groups data)
xgui.teams = table.Copy( ulx.teams )
--Load empty teams saved by XGUI (if any)
if ULib.fileExists( "data/ulx/empty_teams.txt" ) then
local input = ULib.fileRead( "data/ulx/empty_teams.txt" )
input = input:match( "^.-\n(.*)$" )
local emptyteams = ULib.parseKeyValues( input )
for _, teamdata in ipairs( emptyteams ) do
for k,v in pairs( teamdata ) do
teamdata[k] = tonumber( teamdata[k] ) or teamdata[k] --Ensure any number values are read as numbers and not strings
end
table.insert( xgui.teams, teamdata.order, teamdata )
end
end
groups.setTeamsOrder()
--Uteam doesn't load the shortname for playermodels, so to make it easier for the GUI, check for model paths and see if we can use a shortname instead.
for _, v in ipairs( xgui.teams ) do
if v.model then
for shortname,modelpath in pairs( player_manager.AllValidModels() ) do
if v.model == modelpath then v.model = shortname break end
end
end
end
end
xgui.addSVModule( "groups", groups.init, groups.postinit )
--sv_groups -- by Stickly Man!
--Server-side code related to the groups menu.
local groups = {}
function groups.init()
ULib.ucl.registerAccess( "xgui_managegroups", "superadmin", "Allows managing of groups, users, and access strings via the groups tab in XGUI.", "XGUI" )
xgui.addDataType( "playermodels", player_manager.AllValidModels, "xgui_managegroups", 0, 10 )
xgui.addDataType( "teams", function() return xgui.teams end, "xgui_managegroups", 0, -20 )
xgui.addDataType( "accesses", function() return xgui.accesses end, "xgui_managegroups", 0, 5 )
xgui.addDataType( "users", function()
local temp = groups.garryUsers
table.Merge( temp, ULib.ucl.users )
return temp
end, "xgui_managegroups", 20, -10 )
function groups.setInheritance( ply, args )
if ULib.ucl.query( ply, "ulx addgroup" ) then
--Check for cycles
local group = ULib.ucl.groupInheritsFrom( args[2] )
while group do
if group == args[1] or args[1] == args[2] then
ULib.clientRPC( ply, "Derma_Message", "Cannot set inheritance! You cannot inherit from something you're inheriting to!", "XGUI NOTICE" )
return
end
group = ULib.ucl.groupInheritsFrom( group )
end
ULib.ucl.setGroupInheritance( args[1], args[2] )
end
end
xgui.addCmd( "setinheritance", groups.setInheritance )
function xgui.playerExistsByID( id )
for k, v in pairs( player.GetAll() ) do
if v:SteamID() == id or v:UniqueID() == id or ULib.splitPort( v:IPAddress() ) == id then
return v
end
end
return false
end
--Override adduser to (re)send the new user info to the players.
local tempfuncadd = ULib.ucl.addUser
ULib.ucl.addUser = function( id, allows, denies, group )
local affectedply = xgui.playerExistsByID( id )
if affectedply then groups.resetAllPlayerValues( affectedply ) end
tempfuncadd( id, allows, denies, group )
local temp = {}
temp[id] = ULib.ucl.users[id]
xgui.updateData( {}, "users", temp )
end
--Override removeuser to resend the users table to the players.
local tempfuncremove = ULib.ucl.removeUser
ULib.ucl.removeUser = function( id )
xgui.removeData( {}, "users", { id } )
local affectedply = xgui.playerExistsByID( id )
if affectedply then groups.resetAllPlayerValues( affectedply ) end
tempfuncremove( id )
end
---------------------------
--UTeam Integration Stuff--
---------------------------
function groups.createTeam( ply, args )
if ULib.ucl.query( ply, "xgui_managegroups" ) then
--Check and make sure the team doesn't exist first
local exists = false
for i, v in ipairs( xgui.teams ) do
if v.name == args[1] then
exists = true
end
end
if not exists then
local team = {}
team.name = args[1]
team.color = Color( args[2], args[3], args[4], 255 )
team.order = #xgui.teams+1
team.groups = {}
table.insert( xgui.teams, team )
groups.refreshTeams()
end
end
end
xgui.addCmd( "createTeam", groups.createTeam )
function groups.removeTeam( ply, args )
if ULib.ucl.query( ply, "xgui_managegroups" ) then
for i, v in ipairs( xgui.teams ) do
if v.name == args[1] then
for _,group in ipairs( v.groups ) do --Unassign groups in team being deleted
groups.doChangeGroupTeam( group, "" )
end
table.remove( xgui.teams, i )
groups.setTeamsOrder()
groups.refreshTeams()
break
end
end
end
end
xgui.addCmd( "removeTeam", groups.removeTeam )
function groups.changeGroupTeam( ply, args, norefresh )
if ULib.ucl.query( ply, "xgui_managegroups" ) then
groups.doChangeGroupTeam( args[1], args[2], norefresh )
end
end
xgui.addCmd( "changeGroupTeam", groups.changeGroupTeam )
function groups.doChangeGroupTeam( group, newteam, norefresh )
local resettable = {}
for _,teamdata in ipairs( xgui.teams ) do
for i,groupname in ipairs( teamdata.groups ) do
if group == groupname then --Found the previous team the group belonged to, remove it now!
table.remove( teamdata.groups, i )
--Grab old modifier info while we're here
for modifier, _ in pairs( teamdata ) do
if modifier ~= "order" and modifier ~= "index" and modifier ~= "groups" and modifier ~= "name" and modifier ~= "color" then
table.insert( resettable, modifier )
end
end
break
end
end
if teamdata.name == newteam then --If the team requested was found, then add it to the new team.
table.insert( teamdata.groups, group )
end
end
--Reset modifiers for affected players, then let UTeam set the new modifiers
groups.resetTeamValue( group, resettable, newteam=="" ) --Let the function know if the new team is unassigned
if not norefresh then groups.refreshTeams() end
end
--UTeam Parameters: If values are a table, then it specifies default, min, then max. Otherwise it just specifies a min.
--Note that the min/max values here are ABSOLUTE values, meaning values outside of this range will probably cause undesirable results.
xgui.teamDefaults = {
armor = { 0, 0, 255 },
--crouchedWalkSpeed = 0.6, --Pointless setting?
deaths = { 0, -2048, 2047 },
duckSpeed = 0.3,
frags = { 0, -2048, 2047 },
gravity = 1,
health = { 100, 1, 2.14748e+009 },
jumpPower = 200,
maxHealth = 100,
--maxSpeed = 250, --Pointless setting?
model = "scientist",
runSpeed = { 500, 1, nil },
stepSize = { 18, 0, 512 },
unDuckSpeed = 0.2,
walkSpeed = { 250, 1, nil } }
function groups.updateTeamValue( ply, args )
if ULib.ucl.query( ply, "xgui_managegroups" ) then
local modifier = args[2]
local value = tonumber( args[3] ) or args[3] --If args[3] is a number, set value as a number.
for k, v in ipairs( xgui.teams ) do
if v.name == args[1] then
if modifier == "color" then
v.color = { r=tonumber(args[3]), g=tonumber(args[4]), b=tonumber(args[5]), a=255 }
else
if value ~= "" then
--Check for out-of-bound values!
local def = xgui.teamDefaults[modifier]
if type(def) == "table" then
if def[2] and value < def[2] then value = def[2] end
if def[3] and value > def[3] then value = def[3] end
end
v[modifier] = value
else
v[modifier] = nil
--Set the players back to the original value
for _, group in ipairs( v.groups ) do
groups.resetTeamValue( group, { args[2] } )
end
end
end
--Check for order updates, only refresh the teams when args[4] flag is set to prevent multiple data sendings
if v[modifier] ~= "order" or args[4] == "true" then
groups.refreshTeams()
end
break
end
end
end
end
xgui.addCmd( "updateTeamValue", groups.updateTeamValue )
function groups.refreshTeams()
if not ULib.isSandbox() then return end --Do not perform any of the following code if UTeam is disabled.
ulx.teams = table.Copy( xgui.teams )
ulx.saveTeams() --Let ULX reprocess the teams (Empty/new teams would be lost here)
ulx.refreshTeams()
table.sort( xgui.teams, function(a, b) return a.order < b.order end ) --Sort table by order.
xgui.sendDataTable( {}, "teams" )
hook.Call( ULib.HOOK_UCLCHANGED )
--Save any teams that don't have a group assigned to it to a special file. (They'll be removed on changelevel if we don't)
local emptyteams = {}
for _, teamdata in ipairs( xgui.teams ) do
if #teamdata.groups == 0 then
table.insert( emptyteams, teamdata )
end
end
if #emptyteams > 0 then
local output = "//This file stores teams that do not have any groups assigned to it (Since ULX would discard them). Do not edit this file!\n"
output = output .. ULib.makeKeyValues( emptyteams )
ULib.fileWrite( "data/ulx/empty_teams.txt", output )
else
if ULib.fileExists( "data/ulx/empty_teams.txt" ) then
ULib.fileDelete( "data/ulx/empty_teams.txt" )
end
end
end
function groups.resetPlayerValue( ply, values )
for _, modifier in ipairs( values ) do
--Code from UTeam
local defaultvalue = xgui.teamDefaults[modifier]
if type( defaultvalue ) == "table" then defaultvalue = xgui.teamDefaults[modifier][1] end
ply[ "Set" .. modifier:sub( 1, 1 ):upper() .. modifier:sub( 2 ) ]( ply, defaultvalue )
end
end
--This function will locate all players affected by team modifier(s) being unset (or team being changed)
--and will reset any related modifiers to their defaults.
function groups.resetTeamValue( group, values, teamIsUnassigned )
for _, ply in ipairs( player.GetAll() ) do
if ply:GetUserGroup() == group then
groups.resetPlayerValue( ply, values )
if teamIsUnassigned then ply:SetTeam(1001) end --Force the player to the unassigned team
end
end
end
--Remove all UTeam values from a player (used when they change teams)
function groups.resetAllPlayerValues( ply )
for _, team in ipairs( ulx.teams ) do --Loop through each team
for _, group in ipairs( team.groups ) do --Loop through each group per team
if group == ply:GetUserGroup() then --Have we found our team associated with this players group?
local resettable = {}
for modifier, _ in pairs( team ) do --Good! Now go reset the UTeam params based on the current team.
if modifier ~= "order" and modifier ~= "index" and modifier ~= "groups" and modifier ~= "name" and modifier ~= "color" then
table.insert( resettable, modifier )
end
end
groups.resetPlayerValue( ply, resettable )
break
end
end
end
end
--Check and make sure the teams have a specified order
function groups.setTeamsOrder()
for i, v in ipairs( xgui.teams ) do
v.order = i --Assign based on their index, which should be in order set by the file
end
end
--Hijack the renameGroup and removeGroup ULib functions to properly update team information when these are called.
local tempfunc = ULib.ucl.renameGroup
ULib.ucl.renameGroup = function( orig, new )
for _, teamdata in ipairs( xgui.teams ) do
for i, groupname in ipairs( teamdata.groups ) do
if groupname == orig then
teamdata.groups[i] = new
end
break
end
end
tempfunc( orig, new )
groups.refreshTeams()
end
local otherfunc = ULib.ucl.removeGroup
ULib.ucl.removeGroup = function( name )
groups.doChangeGroupTeam( name, "", true )
otherfunc( name )
groups.refreshTeams()
xgui.sendDataTable( {}, "users" ) --Resend user information in case users were bumped to another group.
end
end
function groups.postinit()
--Get user information from Garry's users.txt
groups.garryUsers = {}
if ULib.fileExists( "settings/users.txt" ) then
for group, users in pairs ( util.KeyValuesToTable( ULib.fileRead( "settings/users.txt", true ) ) ) do
for user, steamID in pairs( users ) do
groups.garryUsers[steamID] = { name=user, group=group }
end
end
end
--Combine access data into one table.
xgui.accesses = {}
for k, v in pairs( ULib.ucl.accessStrings ) do
xgui.accesses[k] = {}
xgui.accesses[k].hStr = v
end
for k, v in pairs( ULib.ucl.accessCategories ) do
xgui.accesses[k].cat = v
end
---------------------------
--UTeam Integration Stuff--
---------------------------
--Duplicate ULX's UTeam table (required for how Megiddo stores team data within the groups data)
xgui.teams = table.Copy( ulx.teams )
--Load empty teams saved by XGUI (if any)
if ULib.fileExists( "data/ulx/empty_teams.txt" ) then
local input = ULib.fileRead( "data/ulx/empty_teams.txt" )
input = input:match( "^.-\n(.*)$" )
local emptyteams = ULib.parseKeyValues( input )
for _, teamdata in ipairs( emptyteams ) do
for k,v in pairs( teamdata ) do
teamdata[k] = tonumber( teamdata[k] ) or teamdata[k] --Ensure any number values are read as numbers and not strings
end
table.insert( xgui.teams, teamdata.order, teamdata )
end
end
groups.setTeamsOrder()
--Uteam doesn't load the shortname for playermodels, so to make it easier for the GUI, check for model paths and see if we can use a shortname instead.
for _, v in ipairs( xgui.teams ) do
if v.model then
for shortname,modelpath in pairs( player_manager.AllValidModels() ) do
if v.model == modelpath then v.model = shortname break end
end
end
end
end
xgui.addSVModule( "groups", groups.init, groups.postinit )

View File

@ -1,21 +1,21 @@
--sv_maps -- by Stickly Man!
--Server-side code related to the maps menu.
local function init()
ULib.replicatedWritableCvar( "nextlevel", "rep_nextlevel", GetConVarString( "sbox_godmode" ), false, false, "ulx map" )
local function getVetoState( ply, args )
if ULib.ucl.query( ply, "ulx veto" ) then
ULib.clientRPC( ply, "xgui.updateVetoButton", ulx.timedVeto )
end
end
xgui.addCmd( "getVetoState", getVetoState )
local function updateVetoState()
for _, v in ipairs( player.GetAll() ) do
getVetoState( v )
end
end
hook.Add( "ULXVetoChanged", "XGUI_ServerCatchVeto", updateVetoState )
end
--sv_maps -- by Stickly Man!
--Server-side code related to the maps menu.
local function init()
ULib.replicatedWritableCvar( "nextlevel", "rep_nextlevel", GetConVarString( "sbox_godmode" ), false, false, "ulx map" )
local function getVetoState( ply, args )
if ULib.ucl.query( ply, "ulx veto" ) then
ULib.clientRPC( ply, "xgui.updateVetoButton", ulx.timedVeto )
end
end
xgui.addCmd( "getVetoState", getVetoState )
local function updateVetoState()
for _, v in ipairs( player.GetAll() ) do
getVetoState( v )
end
end
hook.Add( "ULXVetoChanged", "XGUI_ServerCatchVeto", updateVetoState )
end
xgui.addSVModule( "maps", init )

View File

@ -1,45 +1,45 @@
--sv_sandbox -- by Stickly Man!
--Server-side code related to the sandbox menu.
local function init()
if ULib.isSandbox() then --Only execute the following code if it's a sandbox gamemode
xgui.addDataType( "sboxlimits", function() return xgui.sboxLimits end, "xgui_gmsettings", 0, -20 )
ULib.replicatedWritableCvar( "physgun_limited", "rep_physgun_limited", GetConVarNumber( "physgun_limited" ), false, false, "xgui_gmsettings" )
ULib.replicatedWritableCvar( "sbox_noclip", "rep_sbox_noclip", GetConVarNumber( "sbox_noclip" ), false, false, "xgui_gmsettings" )
ULib.replicatedWritableCvar( "sbox_godmode", "rep_sbox_godmode", GetConVarNumber( "sbox_godmode" ), false, false, "xgui_gmsettings" )
ULib.replicatedWritableCvar( "sbox_playershurtplayers", "rep_sbox_playershurtplayers", GetConVarNumber( "sbox_playershurtplayers" ), false, false, "xgui_gmsettings" )
ULib.replicatedWritableCvar( "sbox_weapons", "rep_sbox_weapons", GetConVarNumber( "sbox_weapons" ), false, false, "xgui_gmsettings" )
ULib.replicatedWritableCvar( "sbox_persist", "rep_sbox_persist", GetConVarNumber( "sbox_persist" ), false, false, "xgui_gmsettings" )
ULib.replicatedWritableCvar( "sbox_bonemanip_misc", "rep_sbox_bonemanip_misc", GetConVarNumber( "sbox_bonemanip_misc" ), false, false, "xgui_gmsettings" )
ULib.replicatedWritableCvar( "sbox_bonemanip_npc", "rep_sbox_bonemanip_npc", GetConVarNumber( "sbox_bonemanip_npc" ), false, false, "xgui_gmsettings" )
ULib.replicatedWritableCvar( "sbox_bonemanip_player", "rep_sbox_bonemanip_player", GetConVarNumber( "sbox_bonemanip_player" ), false, false, "xgui_gmsettings" )
--Process the list of known Sandbox Cvar Limits and check if they exist
xgui.sboxLimits = {}
if ULib.isSandbox() then
local curgroup
local f = ULib.fileRead( "data/ulx/sbox_limits.txt" )
if f == nil then Msg( "XGUI ERROR: Sandbox Cvar limits file was needed but could not be found!\n" ) return end
local lines = string.Explode( "\n", f )
for i,v in ipairs( lines ) do
if v:sub( 1,1 ) ~= ";" then
if v:sub( 1,1 ) == "|" then
curgroup = table.insert( xgui.sboxLimits, {} )
xgui.sboxLimits[curgroup].title = v:sub( 2 )
else
local data = string.Explode( " ", v ) --Split Convar name from max limit
if ConVarExists( data[1] ) then
--We need to create a replicated cvar so the clients can manipulate/view them:
ULib.replicatedWritableCvar( data[1], "rep_" .. data[1], GetConVarNumber( data[1] ), false, false, "xgui_gmsettings" )
--Add to the list of cvars to send to the client
table.insert( xgui.sboxLimits[curgroup], v )
end
end
end
end
end
end
end
xgui.addSVModule( "sandbox", init )
--sv_sandbox -- by Stickly Man!
--Server-side code related to the sandbox menu.
local function init()
if ULib.isSandbox() then --Only execute the following code if it's a sandbox gamemode
xgui.addDataType( "sboxlimits", function() return xgui.sboxLimits end, "xgui_gmsettings", 0, -20 )
ULib.replicatedWritableCvar( "physgun_limited", "rep_physgun_limited", GetConVarNumber( "physgun_limited" ), false, false, "xgui_gmsettings" )
ULib.replicatedWritableCvar( "sbox_noclip", "rep_sbox_noclip", GetConVarNumber( "sbox_noclip" ), false, false, "xgui_gmsettings" )
ULib.replicatedWritableCvar( "sbox_godmode", "rep_sbox_godmode", GetConVarNumber( "sbox_godmode" ), false, false, "xgui_gmsettings" )
ULib.replicatedWritableCvar( "sbox_playershurtplayers", "rep_sbox_playershurtplayers", GetConVarNumber( "sbox_playershurtplayers" ), false, false, "xgui_gmsettings" )
ULib.replicatedWritableCvar( "sbox_weapons", "rep_sbox_weapons", GetConVarNumber( "sbox_weapons" ), false, false, "xgui_gmsettings" )
ULib.replicatedWritableCvar( "sbox_persist", "rep_sbox_persist", GetConVarNumber( "sbox_persist" ), false, false, "xgui_gmsettings" )
ULib.replicatedWritableCvar( "sbox_bonemanip_misc", "rep_sbox_bonemanip_misc", GetConVarNumber( "sbox_bonemanip_misc" ), false, false, "xgui_gmsettings" )
ULib.replicatedWritableCvar( "sbox_bonemanip_npc", "rep_sbox_bonemanip_npc", GetConVarNumber( "sbox_bonemanip_npc" ), false, false, "xgui_gmsettings" )
ULib.replicatedWritableCvar( "sbox_bonemanip_player", "rep_sbox_bonemanip_player", GetConVarNumber( "sbox_bonemanip_player" ), false, false, "xgui_gmsettings" )
--Process the list of known Sandbox Cvar Limits and check if they exist
xgui.sboxLimits = {}
if ULib.isSandbox() then
local curgroup
local f = ULib.fileRead( "data/ulx/sbox_limits.txt" )
if f == nil then Msg( "XGUI ERROR: Sandbox Cvar limits file was needed but could not be found!\n" ) return end
local lines = string.Explode( "\n", f )
for i,v in ipairs( lines ) do
if v:sub( 1,1 ) ~= ";" then
if v:sub( 1,1 ) == "|" then
curgroup = table.insert( xgui.sboxLimits, {} )
xgui.sboxLimits[curgroup].title = v:sub( 2 )
else
local data = string.Explode( " ", v ) --Split Convar name from max limit
if ConVarExists( data[1] ) then
--We need to create a replicated cvar so the clients can manipulate/view them:
ULib.replicatedWritableCvar( data[1], "rep_" .. data[1], GetConVarNumber( data[1] ), false, false, "xgui_gmsettings" )
--Add to the list of cvars to send to the client
table.insert( xgui.sboxLimits[curgroup], v )
end
end
end
end
end
end
end
xgui.addSVModule( "sandbox", init )

View File

@ -1,287 +1,287 @@
--sv_groups -- by Stickly Man!
--Server-side code related to the settings menu.
local settings = {}
function settings.init()
ULib.ucl.registerAccess( "xgui_gmsettings", "superadmin", "Allows changing of gamemode-specific settings on the settings tab in XGUI.", "XGUI" )
ULib.ucl.registerAccess( "xgui_svsettings", "superadmin", "Allows changing of server and ULX-specific settings on the settings tab in XGUI.", "XGUI" )
xgui.addDataType( "gimps", function() return ulx.gimpSays end, "xgui_svsettings", 0, -10 )
xgui.addDataType( "adverts", function() return ulx.adverts end, "xgui_svsettings", 0, -10 )
xgui.addDataType( "banreasons", function() return ulx.common_kick_reasons end, "ulx ban", 0, -10 )
xgui.addDataType( "votemaps", function() return settings.votemaps end, nil, 0, -20 )
ULib.replicatedWritableCvar( "sv_voiceenable", "rep_sv_voiceenable", GetConVarNumber( "sv_voiceenable" ), false, false, "xgui_svsettings" )
ULib.replicatedWritableCvar( "sv_alltalk", "rep_sv_alltalk", GetConVarNumber( "sv_alltalk" ), false, false, "xgui_svsettings" )
ULib.replicatedWritableCvar( "ai_disabled", "rep_ai_disabled", GetConVarNumber( "ai_disabled" ), false, false, "xgui_svsettings" )
ULib.replicatedWritableCvar( "ai_keepragdolls", "rep_ai_keepragdolls", GetConVarNumber( "ai_keepragdolls" ), false, false, "xgui_svsettings" )
ULib.replicatedWritableCvar( "ai_ignoreplayers", "rep_ai_ignoreplayers", GetConVarNumber( "ai_ignoreplayers" ), false, false, "xgui_svsettings" )
ULib.replicatedWritableCvar( "sv_gravity", "rep_sv_gravity", GetConVarNumber( "sv_gravity" ), false, false, "xgui_svsettings" )
ULib.replicatedWritableCvar( "phys_timescale", "rep_phys_timescale", GetConVarNumber( "phys_timescale" ), false, false, "xgui_svsettings" )
function settings.addGimp( ply, args )
if ULib.ucl.query( ply, "xgui_svsettings" ) then
ulx.addGimpSay( args[1] )
xgui.sendDataTable( {}, "gimps" )
settings.saveGimps()
end
end
xgui.addCmd( "addGimp", settings.addGimp )
function settings.removeGimp( ply, args )
if ULib.ucl.query( ply, "xgui_svsettings" ) then
for a, b in ipairs( ulx.gimpSays ) do
if b == args[1] then
table.remove( ulx.gimpSays, a )
xgui.sendDataTable( {}, "gimps" )
settings.saveGimps()
return nil
end
end
end
end
xgui.addCmd( "removeGimp", settings.removeGimp )
function settings.saveGimps()
local orig_file = ULib.fileRead( "data/ulx/gimps.txt" )
local comment = xgui.getCommentHeader( orig_file )
local new_file = comment
for i, gimpSay in ipairs( ulx.gimpSays ) do
new_file = new_file .. gimpSay .. "\n"
end
ULib.fileWrite( "data/ulx/gimps.txt", new_file )
end
function settings.addBanReason( ply, args )
if ULib.ucl.query( ply, "xgui_svsettings" ) then
ulx.addKickReason( args[1] )
xgui.sendDataTable( {}, "banreasons" )
settings.saveBanReasons()
end
end
xgui.addCmd( "addBanReason", settings.addBanReason )
function settings.removeBanReason( ply, args )
if ULib.ucl.query( ply, "xgui_svsettings" ) then
for a, b in ipairs( ulx.common_kick_reasons ) do
if b == args[1] then
table.remove( ulx.common_kick_reasons, a )
xgui.sendDataTable( {}, "banreasons" )
settings.saveBanReasons()
return nil
end
end
end
end
xgui.addCmd( "removeBanReason", settings.removeBanReason )
function settings.saveBanReasons()
local orig_file = ULib.fileRead( "data/ulx/banreasons.txt" )
local comment = xgui.getCommentHeader( orig_file )
local new_file = comment
for i, banReason in ipairs( ulx.common_kick_reasons ) do
new_file = new_file .. banReason .. "\n"
end
ULib.fileWrite( "data/ulx/banreasons.txt", new_file )
end
--[1]Message, [2]Delay, [3]GroupName/number, [4]Red, [5]Green, [6]Blue, [7]Length, [8]Hold
function settings.addAdvert( ply, args )
if ULib.ucl.query( ply, "xgui_svsettings" ) then
if args[3] == "<No Group>" then args[3] = nil end
local color = { r = tonumber( args[4] ), g = tonumber( args[5] ), b = tonumber( args[6] ), a = 255 } or nil
ulx.addAdvert( args[1], tonumber( args[2] ), args[3], color, tonumber( args[7] ) )
if args[8] ~= "hold" then
xgui.sendDataTable( {}, "adverts" )
settings.saveAdverts()
end
end
end
xgui.addCmd( "addAdvert", settings.addAdvert )
--[1]Old GroupType, [2]Old GroupName, [3]Old Number (order in group)
--[4]New Message, [5]New Repeat, [6]New Red, [7]New Green, [8]New Blue, [9]New Length
function settings.updateAdvert( ply, args )
if ULib.ucl.query( ply, "xgui_svsettings" ) then
local group = ( args[1] == "number" ) and tonumber( args[2] ) or args[2]
local number = tonumber( args[3] )
local advert = ulx.adverts[group][number]
advert.message = args[4]
advert.rpt = tonumber( args[5] )
advert.color = { a=255, r=tonumber( args[6] ), g=tonumber( args[7] ), b=tonumber( args[8] ) }
advert.len = tonumber( args[9] )
xgui.sendDataTable( {}, "adverts" )
settings.saveAdverts()
end
end
xgui.addCmd( "updateAdvert", settings.updateAdvert )
--[1]Old GroupType, [2]Old GroupName, [3]Old Number, [4]New Number
function settings.moveAdvert( ply, args )
if ULib.ucl.query( ply, "xgui_svsettings" ) then
local group = ( args[1] == "number" ) and tonumber( args[2] ) or args[2]
local number = tonumber( args[3] )
local advert = ulx.adverts[group][number]
table.remove( ulx.adverts[group], args[3] )
table.insert( ulx.adverts[group], args[4], advert )
xgui.sendDataTable( {}, "adverts" )
settings.saveAdverts()
end
end
xgui.addCmd( "moveAdvert", settings.moveAdvert )
function settings.renameAdvertGroup( ply, args )
if ULib.ucl.query( ply, "xgui_svsettings" ) then
local old = args[1]
local new = args[2]
if ulx.adverts[old] then
for k, v in pairs( ulx.adverts[old] ) do
ulx.addAdvert( v.message, v.rpt, new, v.color, v.len )
end
settings.removeAdvertGroup( ply, { old, type( k ) } )
end
end
end
xgui.addCmd( "renameAdvertGroup", settings.renameAdvertGroup )
--[1]GroupName, [2]Number, [3]GroupType, [4]"Ignore"
function settings.removeAdvert( ply, args, hold )
if ULib.ucl.query( ply, "xgui_svsettings" ) then
if args[4] == "hold" then hold = true end
local group = ( args[3] == "number" ) and tonumber( args[1] ) or args[1]
local number = tonumber( args[2] )
if number == #ulx.adverts[group] then
ulx.adverts[group].removed_last = true
end
table.remove( ulx.adverts[group], number )
if #ulx.adverts[group] == 0 then --Remove the existing group if no other adverts exist
ulx.adverts[group] = nil
timer.Remove( "ULXAdvert" .. type( group ) .. group )
end
if not hold then
xgui.sendDataTable( {}, "adverts" )
settings.saveAdverts()
end
end
end
xgui.addCmd( "removeAdvert", settings.removeAdvert )
function settings.removeAdvertGroup( ply, args, hold )
if ULib.ucl.query( ply, "xgui_svsettings" ) then
local group = ( args[2] == "number" ) and tonumber( args[1] ) or args[1]
for i=#ulx.adverts[group],1,-1 do
settings.removeAdvert( ply, { group, i, args[2] }, true )
end
if not hold then
xgui.sendDataTable( {}, "adverts" )
settings.saveAdverts()
end
end
end
xgui.addCmd( "removeAdvertGroup", settings.removeAdvertGroup )
function settings.saveAdverts()
local orig_file = ULib.fileRead( "data/ulx/adverts.txt" )
local comment = xgui.getCommentHeader( orig_file )
local new_file = comment
for group_name, group_data in pairs( ulx.adverts ) do
local output = ""
for i, data in ipairs( group_data ) do
if not data.len then -- Must be a tsay advert
output = output .. string.format( '{\n\t"text" %q\n\t"red" %q\n\t"green" %q\n\t"blue" %q\n\t"time" %q\n}\n',
data.message, data.color.r, data.color.g, data.color.b, data.rpt )
else -- Must be a csay advert
output = output .. string.format( '{\n\t"text" %q\n\t"red" %q\n\t"green" %q\n\t"blue" %q\n\t"time_on_screen" %q\n\t"time" %q\n}\n',
data.message, data.color.r, data.color.g, data.color.b, data.len, data.rpt )
end
end
if type( group_name ) ~= "number" then
output = string.format( "%q\n{\n\t%s}\n", group_name, output:gsub( "\n", "\n\t" ) )
end
new_file = new_file .. output
end
ULib.fileWrite( "data/ulx/adverts.txt", new_file )
end
function settings.addVotemaps( ply, args )
if ULib.ucl.query( ply, "xgui_svsettings" ) then
for _, votemap in ipairs( args ) do
table.insert( ulx.votemaps, votemap )
end
end
settings.saveVotemaps( GetConVar( "ulx_votemapMapmode" ):GetInt() )
xgui.sendDataTable( {}, "votemaps" )
end
xgui.addCmd( "addVotemaps", settings.addVotemaps )
function settings.removeVotemaps( ply, args )
if ULib.ucl.query( ply, "xgui_svsettings" ) then
for _, votemap in ipairs( args ) do
for i, map in ipairs( ulx.votemaps ) do
if map == votemap then
table.remove( ulx.votemaps, i )
break
end
end
end
end
settings.saveVotemaps( GetConVar( "ulx_votemapMapmode" ):GetInt() )
xgui.sendDataTable( {}, "votemaps" )
end
xgui.addCmd( "removeVotemaps", settings.removeVotemaps )
function settings.updatevotemaps() --Populates a table of votemaps that gets sent to the admins.
settings.votemaps = {}
for _, v in ipairs( ulx.votemaps ) do
table.insert( settings.votemaps, v )
end
end
function settings.saveVotemaps( mapmode )
local orig_file = ULib.fileRead( "data/ulx/votemaps.txt" )
local comment = xgui.getCommentHeader( orig_file )
local new_file = comment
if mapmode == 1 then --Use all maps EXCEPT what's specified in votemaps.txt
for _, map in ipairs( ulx.maps ) do
if not table.HasValue( ulx.votemaps, map ) then
new_file = new_file .. map .. "\n"
end
end
elseif mapmode == 2 then --Use only the maps specified in votemaps.txt
for _, map in ipairs( ulx.votemaps ) do
new_file = new_file .. map .. "\n"
end
else
Msg( "XGUI: Could not save votemaps- Invalid or nonexistent ulx_votemapMapmode cvar!\n" )
return
end
ULib.fileWrite( "data/ulx/votemaps.txt", new_file )
settings.updatevotemaps()
end
end
function settings.postinit()
settings.updatevotemaps()
xgui.sendDataTable( {}, "adverts" )
xgui.sendDataTable( {}, "votemaps" )
local function votemapCvarUpdate( sv_cvar, cl_cvar, ply, old_val, new_val )
if cl_cvar == "ulx_votemapmapmode" then
settings.saveVotemaps( tonumber( new_val ) )
end
end
hook.Add( "ULibReplicatedCvarChanged", "XGUI_CatchVotemapCvarUpdate", votemapCvarUpdate )
end
xgui.addSVModule( "settings", settings.init, settings.postinit )
--sv_groups -- by Stickly Man!
--Server-side code related to the settings menu.
local settings = {}
function settings.init()
ULib.ucl.registerAccess( "xgui_gmsettings", "superadmin", "Allows changing of gamemode-specific settings on the settings tab in XGUI.", "XGUI" )
ULib.ucl.registerAccess( "xgui_svsettings", "superadmin", "Allows changing of server and ULX-specific settings on the settings tab in XGUI.", "XGUI" )
xgui.addDataType( "gimps", function() return ulx.gimpSays end, "xgui_svsettings", 0, -10 )
xgui.addDataType( "adverts", function() return ulx.adverts end, "xgui_svsettings", 0, -10 )
xgui.addDataType( "banreasons", function() return ulx.common_kick_reasons end, "ulx ban", 0, -10 )
xgui.addDataType( "votemaps", function() return settings.votemaps end, nil, 0, -20 )
ULib.replicatedWritableCvar( "sv_voiceenable", "rep_sv_voiceenable", GetConVarNumber( "sv_voiceenable" ), false, false, "xgui_svsettings" )
ULib.replicatedWritableCvar( "sv_alltalk", "rep_sv_alltalk", GetConVarNumber( "sv_alltalk" ), false, false, "xgui_svsettings" )
ULib.replicatedWritableCvar( "ai_disabled", "rep_ai_disabled", GetConVarNumber( "ai_disabled" ), false, false, "xgui_svsettings" )
ULib.replicatedWritableCvar( "ai_keepragdolls", "rep_ai_keepragdolls", GetConVarNumber( "ai_keepragdolls" ), false, false, "xgui_svsettings" )
ULib.replicatedWritableCvar( "ai_ignoreplayers", "rep_ai_ignoreplayers", GetConVarNumber( "ai_ignoreplayers" ), false, false, "xgui_svsettings" )
ULib.replicatedWritableCvar( "sv_gravity", "rep_sv_gravity", GetConVarNumber( "sv_gravity" ), false, false, "xgui_svsettings" )
ULib.replicatedWritableCvar( "phys_timescale", "rep_phys_timescale", GetConVarNumber( "phys_timescale" ), false, false, "xgui_svsettings" )
function settings.addGimp( ply, args )
if ULib.ucl.query( ply, "xgui_svsettings" ) then
ulx.addGimpSay( args[1] )
xgui.sendDataTable( {}, "gimps" )
settings.saveGimps()
end
end
xgui.addCmd( "addGimp", settings.addGimp )
function settings.removeGimp( ply, args )
if ULib.ucl.query( ply, "xgui_svsettings" ) then
for a, b in ipairs( ulx.gimpSays ) do
if b == args[1] then
table.remove( ulx.gimpSays, a )
xgui.sendDataTable( {}, "gimps" )
settings.saveGimps()
return nil
end
end
end
end
xgui.addCmd( "removeGimp", settings.removeGimp )
function settings.saveGimps()
local orig_file = ULib.fileRead( "data/ulx/gimps.txt" )
local comment = xgui.getCommentHeader( orig_file )
local new_file = comment
for i, gimpSay in ipairs( ulx.gimpSays ) do
new_file = new_file .. gimpSay .. "\n"
end
ULib.fileWrite( "data/ulx/gimps.txt", new_file )
end
function settings.addBanReason( ply, args )
if ULib.ucl.query( ply, "xgui_svsettings" ) then
ulx.addKickReason( args[1] )
xgui.sendDataTable( {}, "banreasons" )
settings.saveBanReasons()
end
end
xgui.addCmd( "addBanReason", settings.addBanReason )
function settings.removeBanReason( ply, args )
if ULib.ucl.query( ply, "xgui_svsettings" ) then
for a, b in ipairs( ulx.common_kick_reasons ) do
if b == args[1] then
table.remove( ulx.common_kick_reasons, a )
xgui.sendDataTable( {}, "banreasons" )
settings.saveBanReasons()
return nil
end
end
end
end
xgui.addCmd( "removeBanReason", settings.removeBanReason )
function settings.saveBanReasons()
local orig_file = ULib.fileRead( "data/ulx/banreasons.txt" )
local comment = xgui.getCommentHeader( orig_file )
local new_file = comment
for i, banReason in ipairs( ulx.common_kick_reasons ) do
new_file = new_file .. banReason .. "\n"
end
ULib.fileWrite( "data/ulx/banreasons.txt", new_file )
end
--[1]Message, [2]Delay, [3]GroupName/number, [4]Red, [5]Green, [6]Blue, [7]Length, [8]Hold
function settings.addAdvert( ply, args )
if ULib.ucl.query( ply, "xgui_svsettings" ) then
if args[3] == "<No Group>" then args[3] = nil end
local color = { r = tonumber( args[4] ), g = tonumber( args[5] ), b = tonumber( args[6] ), a = 255 } or nil
ulx.addAdvert( args[1], tonumber( args[2] ), args[3], color, tonumber( args[7] ) )
if args[8] ~= "hold" then
xgui.sendDataTable( {}, "adverts" )
settings.saveAdverts()
end
end
end
xgui.addCmd( "addAdvert", settings.addAdvert )
--[1]Old GroupType, [2]Old GroupName, [3]Old Number (order in group)
--[4]New Message, [5]New Repeat, [6]New Red, [7]New Green, [8]New Blue, [9]New Length
function settings.updateAdvert( ply, args )
if ULib.ucl.query( ply, "xgui_svsettings" ) then
local group = ( args[1] == "number" ) and tonumber( args[2] ) or args[2]
local number = tonumber( args[3] )
local advert = ulx.adverts[group][number]
advert.message = args[4]
advert.rpt = tonumber( args[5] )
advert.color = { a=255, r=tonumber( args[6] ), g=tonumber( args[7] ), b=tonumber( args[8] ) }
advert.len = tonumber( args[9] )
xgui.sendDataTable( {}, "adverts" )
settings.saveAdverts()
end
end
xgui.addCmd( "updateAdvert", settings.updateAdvert )
--[1]Old GroupType, [2]Old GroupName, [3]Old Number, [4]New Number
function settings.moveAdvert( ply, args )
if ULib.ucl.query( ply, "xgui_svsettings" ) then
local group = ( args[1] == "number" ) and tonumber( args[2] ) or args[2]
local number = tonumber( args[3] )
local advert = ulx.adverts[group][number]
table.remove( ulx.adverts[group], args[3] )
table.insert( ulx.adverts[group], args[4], advert )
xgui.sendDataTable( {}, "adverts" )
settings.saveAdverts()
end
end
xgui.addCmd( "moveAdvert", settings.moveAdvert )
function settings.renameAdvertGroup( ply, args )
if ULib.ucl.query( ply, "xgui_svsettings" ) then
local old = args[1]
local new = args[2]
if ulx.adverts[old] then
for k, v in pairs( ulx.adverts[old] ) do
ulx.addAdvert( v.message, v.rpt, new, v.color, v.len )
end
settings.removeAdvertGroup( ply, { old, type( k ) } )
end
end
end
xgui.addCmd( "renameAdvertGroup", settings.renameAdvertGroup )
--[1]GroupName, [2]Number, [3]GroupType, [4]"Ignore"
function settings.removeAdvert( ply, args, hold )
if ULib.ucl.query( ply, "xgui_svsettings" ) then
if args[4] == "hold" then hold = true end
local group = ( args[3] == "number" ) and tonumber( args[1] ) or args[1]
local number = tonumber( args[2] )
if number == #ulx.adverts[group] then
ulx.adverts[group].removed_last = true
end
table.remove( ulx.adverts[group], number )
if #ulx.adverts[group] == 0 then --Remove the existing group if no other adverts exist
ulx.adverts[group] = nil
timer.Remove( "ULXAdvert" .. type( group ) .. group )
end
if not hold then
xgui.sendDataTable( {}, "adverts" )
settings.saveAdverts()
end
end
end
xgui.addCmd( "removeAdvert", settings.removeAdvert )
function settings.removeAdvertGroup( ply, args, hold )
if ULib.ucl.query( ply, "xgui_svsettings" ) then
local group = ( args[2] == "number" ) and tonumber( args[1] ) or args[1]
for i=#ulx.adverts[group],1,-1 do
settings.removeAdvert( ply, { group, i, args[2] }, true )
end
if not hold then
xgui.sendDataTable( {}, "adverts" )
settings.saveAdverts()
end
end
end
xgui.addCmd( "removeAdvertGroup", settings.removeAdvertGroup )
function settings.saveAdverts()
local orig_file = ULib.fileRead( "data/ulx/adverts.txt" )
local comment = xgui.getCommentHeader( orig_file )
local new_file = comment
for group_name, group_data in pairs( ulx.adverts ) do
local output = ""
for i, data in ipairs( group_data ) do
if not data.len then -- Must be a tsay advert
output = output .. string.format( '{\n\t"text" %q\n\t"red" %q\n\t"green" %q\n\t"blue" %q\n\t"time" %q\n}\n',
data.message, data.color.r, data.color.g, data.color.b, data.rpt )
else -- Must be a csay advert
output = output .. string.format( '{\n\t"text" %q\n\t"red" %q\n\t"green" %q\n\t"blue" %q\n\t"time_on_screen" %q\n\t"time" %q\n}\n',
data.message, data.color.r, data.color.g, data.color.b, data.len, data.rpt )
end
end
if type( group_name ) ~= "number" then
output = string.format( "%q\n{\n\t%s}\n", group_name, output:gsub( "\n", "\n\t" ) )
end
new_file = new_file .. output
end
ULib.fileWrite( "data/ulx/adverts.txt", new_file )
end
function settings.addVotemaps( ply, args )
if ULib.ucl.query( ply, "xgui_svsettings" ) then
for _, votemap in ipairs( args ) do
table.insert( ulx.votemaps, votemap )
end
end
settings.saveVotemaps( GetConVar( "ulx_votemapMapmode" ):GetInt() )
xgui.sendDataTable( {}, "votemaps" )
end
xgui.addCmd( "addVotemaps", settings.addVotemaps )
function settings.removeVotemaps( ply, args )
if ULib.ucl.query( ply, "xgui_svsettings" ) then
for _, votemap in ipairs( args ) do
for i, map in ipairs( ulx.votemaps ) do
if map == votemap then
table.remove( ulx.votemaps, i )
break
end
end
end
end
settings.saveVotemaps( GetConVar( "ulx_votemapMapmode" ):GetInt() )
xgui.sendDataTable( {}, "votemaps" )
end
xgui.addCmd( "removeVotemaps", settings.removeVotemaps )
function settings.updatevotemaps() --Populates a table of votemaps that gets sent to the admins.
settings.votemaps = {}
for _, v in ipairs( ulx.votemaps ) do
table.insert( settings.votemaps, v )
end
end
function settings.saveVotemaps( mapmode )
local orig_file = ULib.fileRead( "data/ulx/votemaps.txt" )
local comment = xgui.getCommentHeader( orig_file )
local new_file = comment
if mapmode == 1 then --Use all maps EXCEPT what's specified in votemaps.txt
for _, map in ipairs( ulx.maps ) do
if not table.HasValue( ulx.votemaps, map ) then
new_file = new_file .. map .. "\n"
end
end
elseif mapmode == 2 then --Use only the maps specified in votemaps.txt
for _, map in ipairs( ulx.votemaps ) do
new_file = new_file .. map .. "\n"
end
else
Msg( "XGUI: Could not save votemaps- Invalid or nonexistent ulx_votemapMapmode cvar!\n" )
return
end
ULib.fileWrite( "data/ulx/votemaps.txt", new_file )
settings.updatevotemaps()
end
end
function settings.postinit()
settings.updatevotemaps()
xgui.sendDataTable( {}, "adverts" )
xgui.sendDataTable( {}, "votemaps" )
local function votemapCvarUpdate( sv_cvar, cl_cvar, ply, old_val, new_val )
if cl_cvar == "ulx_votemapmapmode" then
settings.saveVotemaps( tonumber( new_val ) )
end
end
hook.Add( "ULibReplicatedCvarChanged", "XGUI_CatchVotemapCvarUpdate", votemapCvarUpdate )
end
xgui.addSVModule( "settings", settings.init, settings.postinit )

View File

@ -1,36 +1,36 @@
--Settings module v2 for ULX GUI -- by Stickly Man!
--This bit of code is the base for holding the various settings modules.
local settings = xlib.makepanel{ parent=xgui.null }
local autorefreshTab
if xgui.settings_tabs != nil then autorefreshTab = xgui.settings_tabs:GetActiveTab() end
xgui.settings_tabs = xlib.makepropertysheet{ x=-5, y=6, w=600, h=368, parent=settings, offloadparent=xgui.null }
function xgui.settings_tabs:SetActiveTab( active, ignoreAnim )
if ( self.m_pActiveTab == active ) then return end
if ( self.m_pActiveTab ) then
if not ignoreAnim then
xlib.addToAnimQueue( "pnlFade", { panelOut=self.m_pActiveTab:GetPanel(), panelIn=active:GetPanel() } )
else
--Run this when module permissions have changed.
xlib.addToAnimQueue( "pnlFade", { panelOut=nil, panelIn=active:GetPanel() }, 0 )
end
xlib.animQueue_start()
end
self.m_pActiveTab = active
self:InvalidateLayout()
end
local func = xgui.settings_tabs.PerformLayout
xgui.settings_tabs.PerformLayout = function( self )
func( self )
self.tabScroller:SetPos( 10, 0 )
self.tabScroller:SetWide( 555 ) --Make the tabs smaller to accommodate for the X button at the top-right corner.
end
if autorefreshTab != nil then
xgui.settings_tabs:SetActiveTab( autorefreshTab, true )
end
--Settings module v2 for ULX GUI -- by Stickly Man!
--This bit of code is the base for holding the various settings modules.
local settings = xlib.makepanel{ parent=xgui.null }
local autorefreshTab
if xgui.settings_tabs != nil then autorefreshTab = xgui.settings_tabs:GetActiveTab() end
xgui.settings_tabs = xlib.makepropertysheet{ x=-5, y=6, w=600, h=368, parent=settings, offloadparent=xgui.null }
function xgui.settings_tabs:SetActiveTab( active, ignoreAnim )
if ( self.m_pActiveTab == active ) then return end
if ( self.m_pActiveTab ) then
if not ignoreAnim then
xlib.addToAnimQueue( "pnlFade", { panelOut=self.m_pActiveTab:GetPanel(), panelIn=active:GetPanel() } )
else
--Run this when module permissions have changed.
xlib.addToAnimQueue( "pnlFade", { panelOut=nil, panelIn=active:GetPanel() }, 0 )
end
xlib.animQueue_start()
end
self.m_pActiveTab = active
self:InvalidateLayout()
end
local func = xgui.settings_tabs.PerformLayout
xgui.settings_tabs.PerformLayout = function( self )
func( self )
self.tabScroller:SetPos( 10, 0 )
self.tabScroller:SetWide( 555 ) --Make the tabs smaller to accommodate for the X button at the top-right corner.
end
if autorefreshTab != nil then
xgui.settings_tabs:SetActiveTab( autorefreshTab, true )
end
xgui.addModule( "Settings", settings, "icon16/wrench.png" )

View File

@ -1,321 +1,321 @@
--Client settings module for ULX GUI -- by Stickly Man!
--A settings module for modifing XGUI-based settings, and allows for modules to add clientside setting here.
local client = xlib.makepanel{ parent=xgui.null }
client.panel = xlib.makepanel{ x=160, y=5, w=425, h=322, parent=client }
client.catList = xlib.makelistview{ x=5, y=5, w=150, h=302, parent=client }
client.catList:AddColumn( "Clientside Settings" )
client.catList.Columns[1].DoClick = function() end
client.catList.OnRowSelected = function( self, LineID, Line )
local nPanel = xgui.modules.submodule[Line:GetValue(2)].panel
if nPanel ~= client.curPanel then
nPanel:SetZPos( 0 )
xlib.addToAnimQueue( "pnlSlide", { panel=nPanel, startx=-435, starty=0, endx=0, endy=0, setvisible=true } )
if client.curPanel then
client.curPanel:SetZPos( -1 )
xlib.addToAnimQueue( client.curPanel.SetVisible, client.curPanel, false )
end
xlib.animQueue_start()
client.curPanel = nPanel
else
xlib.addToAnimQueue( "pnlSlide", { panel=nPanel, startx=0, starty=0, endx=-435, endy=0, setvisible=false } )
self:ClearSelection()
client.curPanel = nil
xlib.animQueue_start()
end
if nPanel.onOpen then nPanel.onOpen() end --If the panel has it, call a function when it's opened
end
xlib.makebutton{ x=5, y=307, w=150, label="Save Clientside Settings", parent=client }.DoClick=function()
xgui.saveClientSettings()
end
function xgui.openClientModule( name )
name = string.lower( name )
for i = 1, #xgui.modules.submodule do
local module = xgui.modules.submodule[i]
if module.mtype == "client" and string.lower(module.name) == name then
if module.panel ~= client.curPanel then
client.catList:ClearSelection()
for i=1, #client.catList.Lines do
local line = client.catList.Lines[i]
if string.lower(line:GetColumnText(1)) == name then
client.catList:SelectItem( line )
break
end
end
end
break
end
end
end
--Process modular settings
function client.processModules()
client.catList:Clear()
for i, module in ipairs( xgui.modules.submodule ) do
if module.mtype == "client" and ( not module.access or LocalPlayer():query( module.access ) ) then
local x,y = module.panel:GetSize()
if x == y and y == 0 then module.panel:SetSize( 425, 327 ) end
module.panel:SetParent( client.panel )
local line = client.catList:AddLine( module.name, i )
if ( module.panel == client.curPanel ) then
client.curPanel = nil
client.catList:SelectItem( line )
else
module.panel:SetVisible( false )
end
end
end
client.catList:SortByColumn( 1, false )
end
client.processModules()
xgui.hookEvent( "onProcessModules", nil, client.processModules, "xguiProcessModules" )
xgui.addSettingModule( "Client", client, "icon16/layout_content.png" )
--------------------General Clientside Module--------------------
local genpnl = xlib.makepanel{ parent=xgui.null }
genpnl.pickupplayers = xlib.makecheckbox{ x=10, y=10, w=150, label="Enable picking up players with physgun (for yourself)", convar="cl_pickupplayers", parent=genpnl }
function genpnl.processModules()
genpnl.pickupplayers:SetDisabled( not LocalPlayer():query( "ulx physgunplayer" ) )
end
xgui.hookEvent( "onProcessModules", nil, genpnl.processModules, "clientGeneralProcessModules" )
xgui.addSubModule( "General Settings", genpnl, nil, "client" )
--------------------XGUI Clientside Module--------------------
local xguipnl = xlib.makepanel{ parent=xgui.null }
xlib.makebutton{ x=10, y=10, w=150, label="Refresh XGUI Modules", parent=xguipnl }.DoClick=function()
xgui.processModules()
end
local databutton = xlib.makebutton{ x=10, y=30, w=150, label="Refresh Server Data", parent=xguipnl }
databutton.DoClick=function( self )
if xgui.offlineMode then
self:SetDisabled( true )
RunConsoleCommand( "_xgui", "getInstalled" )
timer.Simple( 10, function() self:SetDisabled( false ) end )
else
if xgui.isInstalled then --We can't be in offline mode to do this
self:SetDisabled( true )
RunConsoleCommand( "xgui", "refreshdata" )
timer.Simple( 10, function() self:SetDisabled( false ) end )
end
end
end
xlib.makelabel{ x=10, y=55, label="Animation transition time:", parent=xguipnl }
xlib.makeslider{ x=10, y=70, w=150, label="<--->", max=2, value=xgui.settings.animTime, decimal=2, parent=xguipnl }.OnValueChanged = function( self, val )
local testval = math.Clamp( tonumber( val ), 0, 2 )
if testval ~= tonumber( val ) then self:SetValue( testval ) end
xgui.settings.animTime = tonumber( val )
end
xlib.makecheckbox{ x=10, y=97, w=150, label="Show Startup Messages", value=xgui.settings.showLoadMsgs, parent=xguipnl }.OnChange = function( self, bVal )
xgui.settings.showLoadMsgs = bVal
end
xlib.makelabel{ x=10, y=120, label="Infobar color:", parent=xguipnl }
xlib.makecolorpicker{ x=10, y=135, color=xgui.settings.infoColor, addalpha=true, alphamodetwo=true, parent=xguipnl }.OnChangeImmediate = function( self, color )
xgui.settings.infoColor = color
end
----------------
--SKIN MANAGER--
----------------
xlib.makelabel{ x=10, y=273, label="Derma Theme:", parent=xguipnl }
xguipnl.skinselect = xlib.makecombobox{ x=10, y=290, w=150, parent=xguipnl }
if not derma.SkinList[xgui.settings.skin] then
xgui.settings.skin = "Default"
end
xguipnl.skinselect:SetText( derma.SkinList[xgui.settings.skin].PrintName )
xgui.base.refreshSkin = true
xguipnl.skinselect.OnSelect = function( self, index, value, data )
xgui.settings.skin = data
xgui.base:SetSkin( data )
end
for skin, skindata in pairs( derma.SkinList ) do
xguipnl.skinselect:AddChoice( skindata.PrintName, skin )
end
----------------
--TAB ORDERING--
----------------
xguipnl.mainorder = xlib.makelistview{ x=175, y=10, w=115, h=110, parent=xguipnl }
xguipnl.mainorder:AddColumn( "Main Modules" )
xguipnl.mainorder.OnRowSelected = function( self, LineID, Line )
xguipnl.upbtnM:SetDisabled( LineID <= 1 )
xguipnl.downbtnM:SetDisabled( LineID >= #xgui.settings.moduleOrder )
end
xguipnl.updateMainOrder = function()
local selected = xguipnl.mainorder:GetSelectedLine() and xguipnl.mainorder:GetSelected()[1]:GetColumnText(1)
xguipnl.mainorder:Clear()
for i, v in ipairs( xgui.settings.moduleOrder ) do
local found = false
for _, tab in pairs( xgui.modules.tab ) do
if tab.name == v then
found = true
break
end
end
if found then
local l = xguipnl.mainorder:AddLine( v )
if v == selected then xguipnl.mainorder:SelectItem( l ) end
else
table.remove( xgui.settings.moduleOrder, i )
end
end
end
xgui.hookEvent( "onProcessModules", nil, xguipnl.updateMainOrder, "clientXGUIUpdateTabOrder" )
xguipnl.upbtnM = xlib.makebutton{ x=250, y=120, w=20, icon="icon16/bullet_arrow_up.png", centericon=true, disabled=true, parent=xguipnl }
xguipnl.upbtnM.DoClick = function( self )
self:SetDisabled( true )
local i = xguipnl.mainorder:GetSelectedLine()
table.insert( xgui.settings.moduleOrder, i-1, xgui.settings.moduleOrder[i] )
table.remove( xgui.settings.moduleOrder, i+1 )
xgui.processModules()
end
xguipnl.downbtnM = xlib.makebutton{ x=270, y=120, w=20, icon="icon16/bullet_arrow_down.png", centericon=true, disabled=true, parent=xguipnl }
xguipnl.downbtnM.DoClick = function( self )
self:SetDisabled( true )
local i = xguipnl.mainorder:GetSelectedLine()
table.insert( xgui.settings.moduleOrder, i+2, xgui.settings.moduleOrder[i] )
table.remove( xgui.settings.moduleOrder, i )
xgui.processModules()
end
xguipnl.settingorder = xlib.makelistview{ x=300, y=10, w=115, h=110, parent=xguipnl }
xguipnl.settingorder:AddColumn( "Setting Modules" )
xguipnl.settingorder.OnRowSelected = function( self, LineID, Line )
xguipnl.upbtnS:SetDisabled( LineID <= 1 )
xguipnl.downbtnS:SetDisabled( LineID >= #xgui.settings.settingOrder )
end
xguipnl.updateSettingOrder = function()
local selected = xguipnl.settingorder:GetSelectedLine() and xguipnl.settingorder:GetSelected()[1]:GetColumnText(1)
xguipnl.settingorder:Clear()
for i, v in ipairs( xgui.settings.settingOrder ) do
local found = false
for _, tab in pairs( xgui.modules.setting ) do
if tab.name == v then
found = true
break
end
end
if found then
local l = xguipnl.settingorder:AddLine( v )
if v == selected then xguipnl.settingorder:SelectItem( l ) end
else
table.remove( xgui.settings.settingOrder, i )
end
end
end
xgui.hookEvent( "onProcessModules", nil, xguipnl.updateSettingOrder, "clientXGUIUpdateSettingOrder" )
xguipnl.upbtnS = xlib.makebutton{ x=395, y=120, w=20, icon="icon16/bullet_arrow_up.png", centericon=true, disabled=true, parent=xguipnl }
xguipnl.upbtnS.DoClick = function( self )
self:SetDisabled( true )
local i = xguipnl.settingorder:GetSelectedLine()
table.insert( xgui.settings.settingOrder, i-1, xgui.settings.settingOrder[i] )
table.remove( xgui.settings.settingOrder, i+1 )
xgui.processModules()
end
xguipnl.downbtnS = xlib.makebutton{ x=375, y=120, w=20, icon="icon16/bullet_arrow_down.png", centericon=true, disabled=true, parent=xguipnl }
xguipnl.downbtnS.DoClick = function( self )
self:SetDisabled( true )
local i = xguipnl.settingorder:GetSelectedLine()
table.insert( xgui.settings.settingOrder, i+2, xgui.settings.settingOrder[i] )
table.remove( xgui.settings.settingOrder, i )
xgui.processModules()
end
--------------------
--XGUI POSITIONING--
--------------------
xlib.makelabel{ x=175, y=145, label="XGUI Positioning:", parent=xguipnl }
local pos = tonumber( xgui.settings.xguipos.pos )
xguipnl.b7 = xlib.makebutton{ x=175, y=160, w=20, disabled=pos==7, parent=xguipnl }
xguipnl.b7.DoClick = function( self ) xguipnl.updatePos( 7 ) end
xguipnl.b8 = xlib.makebutton{ x=195, y=160, w=20, icon="icon16/arrow_up.png", centericon=true, disabled=pos==8, parent=xguipnl }
xguipnl.b8.DoClick = function( self ) xguipnl.updatePos( 8 ) end
xguipnl.b9 = xlib.makebutton{ x=215, y=160, w=20, disabled=pos==9, parent=xguipnl }
xguipnl.b9.DoClick = function( self ) xguipnl.updatePos( 9 ) end
xguipnl.b4 = xlib.makebutton{ x=175, y=180, w=20, icon="icon16/arrow_left.png", centericon=true, disabled=pos==4, parent=xguipnl }
xguipnl.b4.DoClick = function( self ) xguipnl.updatePos( 4 ) end
xguipnl.b5 = xlib.makebutton{ x=195, y=180, w=20, icon="icon16/bullet_green.png", centericon=true, disabled=pos==5, parent=xguipnl }
xguipnl.b5.DoClick = function( self ) xguipnl.updatePos( 5 ) end
xguipnl.b6 = xlib.makebutton{ x=215, y=180, w=20, icon="icon16/arrow_right.png", centericon=true, disabled=pos==6, parent=xguipnl }
xguipnl.b6.DoClick = function( self ) xguipnl.updatePos( 6 ) end
xguipnl.b1 = xlib.makebutton{ x=175, y=200, w=20, disabled=pos==1, parent=xguipnl }
xguipnl.b1.DoClick = function( self ) xguipnl.updatePos( 1 ) end
xguipnl.b2 = xlib.makebutton{ x=195, y=200, w=20, icon="icon16/arrow_down.png", centericon=true, disabled=pos==2, parent=xguipnl }
xguipnl.b2.DoClick = function( self ) xguipnl.updatePos( 2 ) end
xguipnl.b3 = xlib.makebutton{ x=215, y=200, w=20, disabled=pos==3, parent=xguipnl }
xguipnl.b3.DoClick = function( self ) xguipnl.updatePos( 3 ) end
function xguipnl.updatePos( position, xoffset, yoffset, ignoreanim )
position = position or 5
xoffset = xoffset or tonumber( xgui.settings.xguipos.xoff )
yoffset = yoffset or tonumber( xgui.settings.xguipos.yoff )
xgui.settings.xguipos = { pos=position, xoff=xoffset, yoff=yoffset }
xgui.SetPos( position, xoffset, yoffset, ignoreanim )
xguipnl.b1:SetDisabled( position==1 )
xguipnl.b2:SetDisabled( position==2 )
xguipnl.b3:SetDisabled( position==3 )
xguipnl.b4:SetDisabled( position==4 )
xguipnl.b5:SetDisabled( position==5 )
xguipnl.b6:SetDisabled( position==6 )
xguipnl.b7:SetDisabled( position==7 )
xguipnl.b8:SetDisabled( position==8 )
xguipnl.b9:SetDisabled( position==9 )
end
xguipnl.xwang = xlib.makenumberwang{ x=245, y=167, w=50, min=-1000, max=1000, value=xgui.settings.xguipos.xoff, decimal=0, parent=xguipnl }
xguipnl.xwang.OnValueChanged = function( self, val )
xguipnl.updatePos( xgui.settings.xguipos.pos, tonumber( val ), xgui.settings.xguipos.yoffset, true )
end
xguipnl.xwang.OnEnter = function( self )
local val = tonumber( self:GetValue() )
if not val then val = 0 end
xguipnl.updatePos( xgui.settings.xguipos.pos, tonumber( val ), xgui.settings.xguipos.yoffset )
end
xguipnl.xwang.OnLoseFocus = function( self )
hook.Call( "OnTextEntryLoseFocus", nil, self )
self:OnEnter()
end
xlib.makelabel{ x=300, y=169, label="X Offset", parent=xguipnl }
xguipnl.ywang = xlib.makenumberwang{ x=245, y=193, w=50, min=-1000, max=1000, value=xgui.settings.xguipos.yoff, decimal=0, parent=xguipnl }
xguipnl.ywang.OnValueChanged = function( self, val )
xguipnl.updatePos( xgui.settings.xguipos.pos, xgui.settings.xguipos.xoffset, tonumber( val ), true )
end
xguipnl.ywang.OnEnter = function( self )
local val = tonumber( self:GetValue() )
if not val then val = 0 end
xguipnl.updatePos( xgui.settings.xguipos.pos, xgui.settings.xguipos.xoffset, tonumber( val ) )
end
xguipnl.ywang.OnLoseFocus = function( self )
hook.Call( "OnTextEntryLoseFocus", nil, self )
self:OnEnter()
end
xlib.makelabel{ x=300, y=195, label="Y Offset", parent=xguipnl }
-------------------------
--OPEN/CLOSE ANIMATIONS--
-------------------------
xlib.makelabel{ x=175, y=229, label="XGUI Animations:", parent=xguipnl }
xlib.makelabel{ x=175, y=247, label="On Open:", parent=xguipnl }
xguipnl.inAnim = xlib.makecombobox{ x=225, y=245, w=150, choices={ "Fade In", "Slide From Top", "Slide From Left", "Slide From Bottom", "Slide From Right" }, parent=xguipnl }
xguipnl.inAnim:ChooseOptionID( tonumber( xgui.settings.animIntype ) )
function xguipnl.inAnim:OnSelect( index, value, data )
xgui.settings.animIntype = index
end
xlib.makelabel{ x=175, y=272, label="On Close:", parent=xguipnl }
xguipnl.outAnim = xlib.makecombobox{ x=225, y=270, w=150, choices={ "Fade Out", "Slide To Top", "Slide To Left", "Slide To Bottom", "Slide To Right" }, parent=xguipnl }
xguipnl.outAnim:ChooseOptionID( tonumber( xgui.settings.animOuttype ) )
function xguipnl.outAnim:OnSelect( index, value, data )
xgui.settings.animOuttype = index
end
--Client settings module for ULX GUI -- by Stickly Man!
--A settings module for modifing XGUI-based settings, and allows for modules to add clientside setting here.
local client = xlib.makepanel{ parent=xgui.null }
client.panel = xlib.makepanel{ x=160, y=5, w=425, h=322, parent=client }
client.catList = xlib.makelistview{ x=5, y=5, w=150, h=302, parent=client }
client.catList:AddColumn( "Clientside Settings" )
client.catList.Columns[1].DoClick = function() end
client.catList.OnRowSelected = function( self, LineID, Line )
local nPanel = xgui.modules.submodule[Line:GetValue(2)].panel
if nPanel ~= client.curPanel then
nPanel:SetZPos( 0 )
xlib.addToAnimQueue( "pnlSlide", { panel=nPanel, startx=-435, starty=0, endx=0, endy=0, setvisible=true } )
if client.curPanel then
client.curPanel:SetZPos( -1 )
xlib.addToAnimQueue( client.curPanel.SetVisible, client.curPanel, false )
end
xlib.animQueue_start()
client.curPanel = nPanel
else
xlib.addToAnimQueue( "pnlSlide", { panel=nPanel, startx=0, starty=0, endx=-435, endy=0, setvisible=false } )
self:ClearSelection()
client.curPanel = nil
xlib.animQueue_start()
end
if nPanel.onOpen then nPanel.onOpen() end --If the panel has it, call a function when it's opened
end
xlib.makebutton{ x=5, y=307, w=150, label="Save Clientside Settings", parent=client }.DoClick=function()
xgui.saveClientSettings()
end
function xgui.openClientModule( name )
name = string.lower( name )
for i = 1, #xgui.modules.submodule do
local module = xgui.modules.submodule[i]
if module.mtype == "client" and string.lower(module.name) == name then
if module.panel ~= client.curPanel then
client.catList:ClearSelection()
for i=1, #client.catList.Lines do
local line = client.catList.Lines[i]
if string.lower(line:GetColumnText(1)) == name then
client.catList:SelectItem( line )
break
end
end
end
break
end
end
end
--Process modular settings
function client.processModules()
client.catList:Clear()
for i, module in ipairs( xgui.modules.submodule ) do
if module.mtype == "client" and ( not module.access or LocalPlayer():query( module.access ) ) then
local x,y = module.panel:GetSize()
if x == y and y == 0 then module.panel:SetSize( 425, 327 ) end
module.panel:SetParent( client.panel )
local line = client.catList:AddLine( module.name, i )
if ( module.panel == client.curPanel ) then
client.curPanel = nil
client.catList:SelectItem( line )
else
module.panel:SetVisible( false )
end
end
end
client.catList:SortByColumn( 1, false )
end
client.processModules()
xgui.hookEvent( "onProcessModules", nil, client.processModules, "xguiProcessModules" )
xgui.addSettingModule( "Client", client, "icon16/layout_content.png" )
--------------------General Clientside Module--------------------
local genpnl = xlib.makepanel{ parent=xgui.null }
genpnl.pickupplayers = xlib.makecheckbox{ x=10, y=10, w=150, label="Enable picking up players with physgun (for yourself)", convar="cl_pickupplayers", parent=genpnl }
function genpnl.processModules()
genpnl.pickupplayers:SetDisabled( not LocalPlayer():query( "ulx physgunplayer" ) )
end
xgui.hookEvent( "onProcessModules", nil, genpnl.processModules, "clientGeneralProcessModules" )
xgui.addSubModule( "General Settings", genpnl, nil, "client" )
--------------------XGUI Clientside Module--------------------
local xguipnl = xlib.makepanel{ parent=xgui.null }
xlib.makebutton{ x=10, y=10, w=150, label="Refresh XGUI Modules", parent=xguipnl }.DoClick=function()
xgui.processModules()
end
local databutton = xlib.makebutton{ x=10, y=30, w=150, label="Refresh Server Data", parent=xguipnl }
databutton.DoClick=function( self )
if xgui.offlineMode then
self:SetDisabled( true )
RunConsoleCommand( "_xgui", "getInstalled" )
timer.Simple( 10, function() self:SetDisabled( false ) end )
else
if xgui.isInstalled then --We can't be in offline mode to do this
self:SetDisabled( true )
RunConsoleCommand( "xgui", "refreshdata" )
timer.Simple( 10, function() self:SetDisabled( false ) end )
end
end
end
xlib.makelabel{ x=10, y=55, label="Animation transition time:", parent=xguipnl }
xlib.makeslider{ x=10, y=70, w=150, label="<--->", max=2, value=xgui.settings.animTime, decimal=2, parent=xguipnl }.OnValueChanged = function( self, val )
local testval = math.Clamp( tonumber( val ), 0, 2 )
if testval ~= tonumber( val ) then self:SetValue( testval ) end
xgui.settings.animTime = tonumber( val )
end
xlib.makecheckbox{ x=10, y=97, w=150, label="Show Startup Messages", value=xgui.settings.showLoadMsgs, parent=xguipnl }.OnChange = function( self, bVal )
xgui.settings.showLoadMsgs = bVal
end
xlib.makelabel{ x=10, y=120, label="Infobar color:", parent=xguipnl }
xlib.makecolorpicker{ x=10, y=135, color=xgui.settings.infoColor, addalpha=true, alphamodetwo=true, parent=xguipnl }.OnChangeImmediate = function( self, color )
xgui.settings.infoColor = color
end
----------------
--SKIN MANAGER--
----------------
xlib.makelabel{ x=10, y=273, label="Derma Theme:", parent=xguipnl }
xguipnl.skinselect = xlib.makecombobox{ x=10, y=290, w=150, parent=xguipnl }
if not derma.SkinList[xgui.settings.skin] then
xgui.settings.skin = "Default"
end
xguipnl.skinselect:SetText( derma.SkinList[xgui.settings.skin].PrintName )
xgui.base.refreshSkin = true
xguipnl.skinselect.OnSelect = function( self, index, value, data )
xgui.settings.skin = data
xgui.base:SetSkin( data )
end
for skin, skindata in pairs( derma.SkinList ) do
xguipnl.skinselect:AddChoice( skindata.PrintName, skin )
end
----------------
--TAB ORDERING--
----------------
xguipnl.mainorder = xlib.makelistview{ x=175, y=10, w=115, h=110, parent=xguipnl }
xguipnl.mainorder:AddColumn( "Main Modules" )
xguipnl.mainorder.OnRowSelected = function( self, LineID, Line )
xguipnl.upbtnM:SetDisabled( LineID <= 1 )
xguipnl.downbtnM:SetDisabled( LineID >= #xgui.settings.moduleOrder )
end
xguipnl.updateMainOrder = function()
local selected = xguipnl.mainorder:GetSelectedLine() and xguipnl.mainorder:GetSelected()[1]:GetColumnText(1)
xguipnl.mainorder:Clear()
for i, v in ipairs( xgui.settings.moduleOrder ) do
local found = false
for _, tab in pairs( xgui.modules.tab ) do
if tab.name == v then
found = true
break
end
end
if found then
local l = xguipnl.mainorder:AddLine( v )
if v == selected then xguipnl.mainorder:SelectItem( l ) end
else
table.remove( xgui.settings.moduleOrder, i )
end
end
end
xgui.hookEvent( "onProcessModules", nil, xguipnl.updateMainOrder, "clientXGUIUpdateTabOrder" )
xguipnl.upbtnM = xlib.makebutton{ x=250, y=120, w=20, icon="icon16/bullet_arrow_up.png", centericon=true, disabled=true, parent=xguipnl }
xguipnl.upbtnM.DoClick = function( self )
self:SetDisabled( true )
local i = xguipnl.mainorder:GetSelectedLine()
table.insert( xgui.settings.moduleOrder, i-1, xgui.settings.moduleOrder[i] )
table.remove( xgui.settings.moduleOrder, i+1 )
xgui.processModules()
end
xguipnl.downbtnM = xlib.makebutton{ x=270, y=120, w=20, icon="icon16/bullet_arrow_down.png", centericon=true, disabled=true, parent=xguipnl }
xguipnl.downbtnM.DoClick = function( self )
self:SetDisabled( true )
local i = xguipnl.mainorder:GetSelectedLine()
table.insert( xgui.settings.moduleOrder, i+2, xgui.settings.moduleOrder[i] )
table.remove( xgui.settings.moduleOrder, i )
xgui.processModules()
end
xguipnl.settingorder = xlib.makelistview{ x=300, y=10, w=115, h=110, parent=xguipnl }
xguipnl.settingorder:AddColumn( "Setting Modules" )
xguipnl.settingorder.OnRowSelected = function( self, LineID, Line )
xguipnl.upbtnS:SetDisabled( LineID <= 1 )
xguipnl.downbtnS:SetDisabled( LineID >= #xgui.settings.settingOrder )
end
xguipnl.updateSettingOrder = function()
local selected = xguipnl.settingorder:GetSelectedLine() and xguipnl.settingorder:GetSelected()[1]:GetColumnText(1)
xguipnl.settingorder:Clear()
for i, v in ipairs( xgui.settings.settingOrder ) do
local found = false
for _, tab in pairs( xgui.modules.setting ) do
if tab.name == v then
found = true
break
end
end
if found then
local l = xguipnl.settingorder:AddLine( v )
if v == selected then xguipnl.settingorder:SelectItem( l ) end
else
table.remove( xgui.settings.settingOrder, i )
end
end
end
xgui.hookEvent( "onProcessModules", nil, xguipnl.updateSettingOrder, "clientXGUIUpdateSettingOrder" )
xguipnl.upbtnS = xlib.makebutton{ x=395, y=120, w=20, icon="icon16/bullet_arrow_up.png", centericon=true, disabled=true, parent=xguipnl }
xguipnl.upbtnS.DoClick = function( self )
self:SetDisabled( true )
local i = xguipnl.settingorder:GetSelectedLine()
table.insert( xgui.settings.settingOrder, i-1, xgui.settings.settingOrder[i] )
table.remove( xgui.settings.settingOrder, i+1 )
xgui.processModules()
end
xguipnl.downbtnS = xlib.makebutton{ x=375, y=120, w=20, icon="icon16/bullet_arrow_down.png", centericon=true, disabled=true, parent=xguipnl }
xguipnl.downbtnS.DoClick = function( self )
self:SetDisabled( true )
local i = xguipnl.settingorder:GetSelectedLine()
table.insert( xgui.settings.settingOrder, i+2, xgui.settings.settingOrder[i] )
table.remove( xgui.settings.settingOrder, i )
xgui.processModules()
end
--------------------
--XGUI POSITIONING--
--------------------
xlib.makelabel{ x=175, y=145, label="XGUI Positioning:", parent=xguipnl }
local pos = tonumber( xgui.settings.xguipos.pos )
xguipnl.b7 = xlib.makebutton{ x=175, y=160, w=20, disabled=pos==7, parent=xguipnl }
xguipnl.b7.DoClick = function( self ) xguipnl.updatePos( 7 ) end
xguipnl.b8 = xlib.makebutton{ x=195, y=160, w=20, icon="icon16/arrow_up.png", centericon=true, disabled=pos==8, parent=xguipnl }
xguipnl.b8.DoClick = function( self ) xguipnl.updatePos( 8 ) end
xguipnl.b9 = xlib.makebutton{ x=215, y=160, w=20, disabled=pos==9, parent=xguipnl }
xguipnl.b9.DoClick = function( self ) xguipnl.updatePos( 9 ) end
xguipnl.b4 = xlib.makebutton{ x=175, y=180, w=20, icon="icon16/arrow_left.png", centericon=true, disabled=pos==4, parent=xguipnl }
xguipnl.b4.DoClick = function( self ) xguipnl.updatePos( 4 ) end
xguipnl.b5 = xlib.makebutton{ x=195, y=180, w=20, icon="icon16/bullet_green.png", centericon=true, disabled=pos==5, parent=xguipnl }
xguipnl.b5.DoClick = function( self ) xguipnl.updatePos( 5 ) end
xguipnl.b6 = xlib.makebutton{ x=215, y=180, w=20, icon="icon16/arrow_right.png", centericon=true, disabled=pos==6, parent=xguipnl }
xguipnl.b6.DoClick = function( self ) xguipnl.updatePos( 6 ) end
xguipnl.b1 = xlib.makebutton{ x=175, y=200, w=20, disabled=pos==1, parent=xguipnl }
xguipnl.b1.DoClick = function( self ) xguipnl.updatePos( 1 ) end
xguipnl.b2 = xlib.makebutton{ x=195, y=200, w=20, icon="icon16/arrow_down.png", centericon=true, disabled=pos==2, parent=xguipnl }
xguipnl.b2.DoClick = function( self ) xguipnl.updatePos( 2 ) end
xguipnl.b3 = xlib.makebutton{ x=215, y=200, w=20, disabled=pos==3, parent=xguipnl }
xguipnl.b3.DoClick = function( self ) xguipnl.updatePos( 3 ) end
function xguipnl.updatePos( position, xoffset, yoffset, ignoreanim )
position = position or 5
xoffset = xoffset or tonumber( xgui.settings.xguipos.xoff )
yoffset = yoffset or tonumber( xgui.settings.xguipos.yoff )
xgui.settings.xguipos = { pos=position, xoff=xoffset, yoff=yoffset }
xgui.SetPos( position, xoffset, yoffset, ignoreanim )
xguipnl.b1:SetDisabled( position==1 )
xguipnl.b2:SetDisabled( position==2 )
xguipnl.b3:SetDisabled( position==3 )
xguipnl.b4:SetDisabled( position==4 )
xguipnl.b5:SetDisabled( position==5 )
xguipnl.b6:SetDisabled( position==6 )
xguipnl.b7:SetDisabled( position==7 )
xguipnl.b8:SetDisabled( position==8 )
xguipnl.b9:SetDisabled( position==9 )
end
xguipnl.xwang = xlib.makenumberwang{ x=245, y=167, w=50, min=-1000, max=1000, value=xgui.settings.xguipos.xoff, decimal=0, parent=xguipnl }
xguipnl.xwang.OnValueChanged = function( self, val )
xguipnl.updatePos( xgui.settings.xguipos.pos, tonumber( val ), xgui.settings.xguipos.yoffset, true )
end
xguipnl.xwang.OnEnter = function( self )
local val = tonumber( self:GetValue() )
if not val then val = 0 end
xguipnl.updatePos( xgui.settings.xguipos.pos, tonumber( val ), xgui.settings.xguipos.yoffset )
end
xguipnl.xwang.OnLoseFocus = function( self )
hook.Call( "OnTextEntryLoseFocus", nil, self )
self:OnEnter()
end
xlib.makelabel{ x=300, y=169, label="X Offset", parent=xguipnl }
xguipnl.ywang = xlib.makenumberwang{ x=245, y=193, w=50, min=-1000, max=1000, value=xgui.settings.xguipos.yoff, decimal=0, parent=xguipnl }
xguipnl.ywang.OnValueChanged = function( self, val )
xguipnl.updatePos( xgui.settings.xguipos.pos, xgui.settings.xguipos.xoffset, tonumber( val ), true )
end
xguipnl.ywang.OnEnter = function( self )
local val = tonumber( self:GetValue() )
if not val then val = 0 end
xguipnl.updatePos( xgui.settings.xguipos.pos, xgui.settings.xguipos.xoffset, tonumber( val ) )
end
xguipnl.ywang.OnLoseFocus = function( self )
hook.Call( "OnTextEntryLoseFocus", nil, self )
self:OnEnter()
end
xlib.makelabel{ x=300, y=195, label="Y Offset", parent=xguipnl }
-------------------------
--OPEN/CLOSE ANIMATIONS--
-------------------------
xlib.makelabel{ x=175, y=229, label="XGUI Animations:", parent=xguipnl }
xlib.makelabel{ x=175, y=247, label="On Open:", parent=xguipnl }
xguipnl.inAnim = xlib.makecombobox{ x=225, y=245, w=150, choices={ "Fade In", "Slide From Top", "Slide From Left", "Slide From Bottom", "Slide From Right" }, parent=xguipnl }
xguipnl.inAnim:ChooseOptionID( tonumber( xgui.settings.animIntype ) )
function xguipnl.inAnim:OnSelect( index, value, data )
xgui.settings.animIntype = index
end
xlib.makelabel{ x=175, y=272, label="On Close:", parent=xguipnl }
xguipnl.outAnim = xlib.makecombobox{ x=225, y=270, w=150, choices={ "Fade Out", "Slide To Top", "Slide To Left", "Slide To Bottom", "Slide To Right" }, parent=xguipnl }
xguipnl.outAnim:ChooseOptionID( tonumber( xgui.settings.animOuttype ) )
function xguipnl.outAnim:OnSelect( index, value, data )
xgui.settings.animOuttype = index
end
xgui.addSubModule( "XGUI Settings", xguipnl, nil, "client" )

File diff suppressed because it is too large Load Diff

View File

@ -1,19 +1,19 @@
<html>
<body bgcolor=#dbdbdb>
<div style="text-align: center;">
<div style="width: 80%; margin: 0px auto; border: 10px solid #c9d6e4; background-color: #ededed; padding: 10px; font-size: 12px; font-family: Tahoma; margin-top: 30px; color: #818181; text-align: left;">
<div style="font-size: 30px; font-family: impact; width: 100%; margin-bottom: 5px;">My Server Name</div>
<br>
This Server uses the following mods:<br>
ULX, PropDefender, and VMFLoader.<br>
<br>
<h2>Rules</h2>
1. DON'T MESS WITH OTHER PLAYERS STUFF. If they want help, they'll ask!<br>
2. Don't spam.<br>
3. Have fun.<br>
<div style="width: 100%; text-align: center; margin: 10px; font-weight: bold;">- The Admins</div>
</div>
</div>
</body>
<html>
<body bgcolor=#dbdbdb>
<div style="text-align: center;">
<div style="width: 80%; margin: 0px auto; border: 10px solid #c9d6e4; background-color: #ededed; padding: 10px; font-size: 12px; font-family: Tahoma; margin-top: 30px; color: #818181; text-align: left;">
<div style="font-size: 30px; font-family: impact; width: 100%; margin-bottom: 5px;">My Server Name</div>
<br>
This Server uses the following mods:<br>
ULX, PropDefender, and VMFLoader.<br>
<br>
<h2>Rules</h2>
1. DON'T MESS WITH OTHER PLAYERS STUFF. If they want help, they'll ask!<br>
2. Don't spam.<br>
3. Have fun.<br>
<div style="width: 100%; text-align: center; margin: 10px; font-weight: bold;">- The Admins</div>
</div>
</div>
</body>
</html>

View File

@ -1,448 +1,448 @@
Title: ULX Readme
__ULX__
Version 3.70
*ULX v3.70 (released 00/00/00)*
ULX is an admin mod for GMod (<http://garrysmod.com/>).
ULX offers server admins an AMXX-style support. It allows multiple admins with different access levels on the same server.
It has features from basic kick, ban, and slay to fancier commands such as blind, freeze, voting, and more.
Visit our homepage at <http://ulyssesmod.net/>.
You can talk to us on our forums at <http://forums.ulyssesmod.net/>
Group: Author
ULX is brought to you by..
* Brett "Megiddo" Smith - Contact: <megiddo@ulyssesmod.net>
* JamminR - Contact: <jamminr@ulyssesmod.net>
* Stickly Man! - Contact: <sticklyman@ulyssesmod.net>
* MrPresident - Contact: <mrpresident@ulyssesmod.net>
Group: Requirements
ULX requires the latest ULib to be installed on the server. You can get ULib from the ULX homepage.
Group: Installation
To install ULX, simply extract the files from the archive to your garrysmod/addons folder.
When you've done this, you should have a file structure like this--
<garrysmod>/addons/ulx/lua/ULib/modules/ulx_init.lua
<garrysmod>/addons/ulx/lua/ulx/modules/fun.lua
etc..
Note that installation for dedicated servers is EXACTLY the same!
You absolutely, positively have to do a full server restart after installing the files. A simple map
change will not cut it!
Group: Usage
To use ULX, simply type in whatever command or cvar you want to use. If you're not running a listen server,
or if you want to give access to other users, you must add users.
You can either follow the standard gmod way of adding users
(http://maurits.tv/data/garrysmod/wiki/wiki.garrysmod.com/index0c18.html?title=Player_Groups), or you can add yourself directly to the
data/ulib/users.txt file. DO NOT EDIT THE ULIB FILE UNLESS YOU KNOW WHAT YOU'RE DOING.
A word about superadmins: Superadmins are considered the highest usergroup. They have access to all the
commands in ULX, override other user's immunity, and are shown otherwise hidden log messages (IE, shown
rcon commands admins are running, when they spectate others). Superadmins also have the power to give and
revoke access to commands using userallow and userdeny (though they can't use this command on eachother).
All commands are preceded by "ulx ". Type "ulx help" in a console without the quotes for help.
__To give yourself a jump start into ulx, simply remember the commands "ulx help" and "ulx menu". You can
also access the menu by saying "!menu".__
Check out the configs folder in ulx for some more goodies.
Group: Changelog
v3.70 - *(00/00/00)*
* [ADD] XGUI: Ability to set the 'nextlevel' cvar from the maps tab, if you have access to "ulx map".
* [ADD] Reason to votekick log (Thanks, CSchulz).
* [ADD] Steam ID parameter to "ulx who" to lookup users by Steam ID.
* [ADD] Cvar "ulx meChatEnabled" added to enable or disable the /me chat feature, or set it to Sandbox only.
* [ADD] XGUI: Added methods for developers to be able to open a specific client or server setting module.
* [FIX] The usual random slew of Garry-breakages (Thanks, Fuzzik).
* [FIX] Changing weapons while cloaked would keep them hidden when uncloaked. (Thanks, TheRealAyCe).
* [FIX] XGUI: Error if the default settings/users.txt file was missing.
* [FIX] Gamemode list includes workshop addons now (Thanks, jason2010).
* [FIX] "ulx ent" parameter parsing (Thanks, Zombine).
* [FIX] "ulx voteban" can now ban the user even if they disconnect after the vote starts.
* [FIX] Vote commands now work properly from server console.
* [FIX] XGUI: Numerous issues with selecting a custom Derma/GWEN skin.
* [FIX] XGUI: Clientside settings would not save if the client's data/ulx folder did not exist.
* [FIX] Improved how well ULX/XGUI files and XGUI clientside/serverside modules handle being autorefreshed.
* [FIX] ulx.addToHelpManually now checks for and removes and previously added manual help entries with the same command name.
* [FIX] XGUI: Bug where ulx_showMotd cvar would not be updated properly when changed by someone else.
* [FIX] XGUI: Minor performance exploit involving serverside ban sorting. (Thanks, TomatoCo).
* [FIX] Exploit involving gmod filesystem mounting. (Thanks, Willox).
* [FIX] Minor issue where opposite commands might not be run due to case sensitivity.
* [FIX] Duplicate help entries due to autorefresh and overriding commands. (Thanks iSnipeu).
* [FIX] Ragdolled players were getting removed on map cleanup.
* [FIX] XGUI: Server error in some cases when sorting bans by Unban Date after a new ban has been added.
* [CHANGE] MOTD now uses DHTML (Awesomium framework).
* [CHANGE] Hook calls to match ULib's new format.
* [CHANGE] ULX convar updates will now append an entry to data/config.txt if it is not defined in the file. Previously, these changes would not be saved.
* [CHANGE] XGUI: No longer autoexecutes skins to ensure they have been installed.
* [CHANGE] XGUI: Added "name" parameter to xgui.hookEvent to prevent event duplication. (Aids with autorefresh, is backwards compatible with old XGUI modules)
* [CHANGE] XGUI: Modules that no longer exist will be removed from the customizable sort order.
* [CHANGE] XGUI: Sliders for arguments on the Cmds tab with a small min/max delta (e.g. from 0 to 1) will now allow up to 2 decimal places, if the arg does not have cmds.round flag.
v3.62 - *(03/09/15)*
* [ADD] "ulx return" to return target to previous location they were in before a teleport command was used (Thanks for the idea, ludalex).
* [ADD] Networked variables for gimp, mute, and gag (Thanks iSnipeu).
* [ADD] XGUI: Added more sorting and filtering options for bans.
* [ADD] "ulx stopvote" to stop a vote currently in progress (Thanks, LuaTenshi).
* [ADD] XGUI: Added "onClose" event for modules that need it. (Suggested by arduinium).
* [FIX] Ban reason and the person who started the voteban is now reported in "ulx voteban" bans (Thanks iSnipeu).
* [FIX] An API change causing an error to be thrown at the end of "ulx maul" (Thanks Decicus).
* [FIX] NULL entity error after votekick on a player that left the server, now sends message stating that votekicked player already left.
* [FIX] PlayerDeath hook errors in certain gamemodes where invalid entites are sent as the killer or victim. (Thanks Mechanical Mind).
* [FIX] JailTP command now saves last player position, now works with "ulx return". (Thanks jakej78b).
* [FIX] XGUI: Slider label widths were extra large due to a slight change in default numslider behavior. (Thanks Fuzzik).
* [FIX] Garry's Mod update caused users to be banned faster than expected, log then incorrectly stated that (Console) was banned.
* [FIX] Garry's Mod update prevented votebans from working.
* [FIX] Garry's Mod update caused server crash when kicking/banning yourself via chat command.
* [FIX] Bug with spectate and respawning (Thanks Sjokomelk).
* [FIX] Bug when changing weapons while cloaked, weapons would stay invisible after uncloaking. (Thanks Z0mb1n3).
* [FIX] Could not assign BOTs to groups via ulx adduserid or XGUI. (Thanks RhapsodySL).
* [FIX] Fixed bug where XGUI would not start on dev branch of Garry's Mod. Changed to init on ULib.HOOK_LOCALPLAYERREADY instead of ULib.HOOK_UCLAUTH.
* [FIX] MOTD not enabled message would display for all players instead of the player who tried to open the motd. (Thanks TheClonker).
* [CHANGE] "PlayerSay" hooks are now only called serverside. (Thanks NoBrainCZ).
* [CHANGE] Logging now prints how long a user took to join the server.
* [CHANGE] XGUI: Updated cvarlist for sandbox and wiremod limits.
* [CHANGE] XGUI: Many Ban menu improvements. Entire banlist is no longer sent on join- data subset is now requested by the client and sent from the server.
* [CHANGE] XGUI: Ban list is now paginated instead of a giant scrollable list.
* [CHANGE] XGUI: ULX Bans and other (or "Source Bans") are no longer separated.
* [CHANGE] ULX vote variable is now global so other addons can tell if a vote is in progress. ulx.doVote() also returns whether or not it actually started a vote (Thanks for the ideas, arduinium).
* [CHANGE] XGUI: Changed gamemode dropdown on maps tab to honor player/group restrictions. (Thanks chaos12135).
v3.61 - *(08/30/13)*
* [ADD] cl_pickupplayers (defaults to 1) to allow an admin to disable the ability to pickup players (so they don't do it on accident). Done in collaboration with FPtje.
* [ADD] %curmap% and %steamid% variables in "ulx showMotd" URL for custom-served MOTDs (Thanks Mors-Quaedam).
* [ADD] XGUI: Bans are now searchable. (Thanks to iSnipeu for the code contribution!)
* [FIX] "#" (Pound signs) removing content in ulx asay (Thanks bener180).
* [FIX] Reserved slot mode 3 not kicking the shortest connected player as it is supposed to (Thanks monkstick).
* [FIX] No longer able to physgun frozen players (Thanks ms333).
* [FIX] XGUI: Added checks to prevent admins from being able to edit ban information past their restrictions (Thanks Zaph).
* [FIX] XGUI: Infobar text no longer displaying.
* [FIX] XGUI: Error caused when closing the fban window after the targeted player has left the server (Thanks nathan736).
* [FIX] XGUI: Issues with handling min / max number restrictions.
* [FIX] XGUI: Map icons not loading.
* [FIX] XGUI: Ban menu bugfixes.
* [CHANGE] Jail models. The jail is slightly bigger and can't be shot through anymore (Thanks Mors-Quaedam).
* [CHANGE] Updated PvP damage cvar to reflect Garry's changes (Thanks Mors-Quadam).
* [CHANGE] "ulx gag" now uses a server-side hook (much more robust).
v3.60 - *(01/27/13)*
* [ADD] "ulx jailtp" - A combination of tp <player> and jail <player> (Thanks HellFox).
* [ADD] "ulx resettodefaults" - Resets ULX and ULib config to defaults.
* [ADD] XGUI: Added ability to edit lower-level restrictions from a higher-level group.
* [CHANGE] ULX ban now supports restricting of time/string formats.
* [CHANGE] !teleport chat command is now also aliased as !tp.
* [CHANGE] XGUI: Utilizes ULib's more robust ID Targeting system.
* [CHANGE] XGUI: Controls added to utilize time/string formats and restrictions.
* [CHANGE] XGUI: No longer duplicates ULX replicated cvars (ulx_cl_) due to ULib changes. Uses the regular ulx_ cvars directly.
* [CHANGE] XGUI: Supports new values for sv_alltalk.
* [CHANGE] XGUI: A few changes to update look and feel. Matches Derma/GWEN skin colors better in some areas.
* [CHANGE] XGUI: No longer retrieves sandbox limits from the web. Included with download.
* [FIX] Garry breakages in GM13.
* [FIX] An exclusivity bug in "ulx freeze" (Thanks infinitywraith).
* [FIX] A console bug when trying to ulx teleport another player (Thanks infinitywraith).
* [FIX] "ulx gimp" not obeying chat anti-spam (Thanks ruok2bu).
* [FIX] "ulx userdeny" not logging properly in some cases.
* [FIX] An echo incorrectly going to all users for "ulx votekick" (Thanks JackYack13).
* [FIX] Module cross-contamination in end.lua (Thanks Pon-3).
* [FIX] Team vs public chat doing the opposite of what it should for logs and "/me" actions. Wonder how long ago Garry needlessly changed that API without us noticing.
* [FIX] Promotion bug after using "ulx userallow" on a regular user. (Thanks JackYack13).
* [FIX] Server crash when jail is placed inside trigger_remove brush. (Thanks HellFox).
* [FIX] XGUI: Changed startup code to initialize faster, handle strange server load scenarios better.
* [FIX] XGUI: BoolArgs in the Cmds tab now obey restrictions.
v3.54 - *(04/27/12)*
* [FIX] XGUI: Hard crash with the os.date function when bans have an extremely long unban time.
v3.53 - *(01/01/12)*
* [FIX] Garry breakages.
v3.52 - *(09/22/11)*
* [ADD] Support for "time strings" in ulx ban and ulx banid. EG, "5w4d3h2" would ban for 5 weeks, 4 days, 3 hours, 2 minutes (Thanks lavacano201014).
* [ADD] XGUI: New customization options-- You can now change XGUI's position and the open/close animations.
* [ADD] XGUI: Double-click a player on the commands tab to execute the command with the parameters on the right.
* [ADD] XLIB: Additional layout for xlib.makecolorpicker with alphabar.
* [FIX] No longer able to make a player invincible by freezing then mauling.
* [FIX] XGUI: Selected svsetting/clsetting modules no longer close when XGUI modules get reprocessed.
* [FIX] XGUI: Error caused when a bans unban time was changed, causing the ban to expire.
* [FIX] XGUI: Adding details to SBans would throw an error on server, wouldn't refresh on clients properly.
* [FIX] XGUI: Expired bans were not removing themselves from the client lists.
* [FIX] XGUI: Somehow managed to duplicate the entire bans module code within the same file. X|
* [FIX] XGUI: Rare error causing votemap settings to not load properly (which was previously fixable after a mapchange)
* [FIX] XGUI: Non-harmful Lua error occuring during data transfer when running XGUI on a non-sandbox game mode. (Thanks Synergy Connections!)
* [FIX] XGUI: The button for scrolling through tab names of settings modules (if there were too many) was being obscured by the close button. (Thanks [eVo]Lead4u!)
* [FIX] XGUI: UTeam information would be lost when changing UTeam settings on non sandbox-derived gamemodes
* [FIX] XGUI: DNumberWangs on color panels being extra long (due to garry update?)
* [FIX] XGUI: ULib team data not sent after making changes to teams.
* [FIX] XGUI: Users in the groups tab were not being updated when a group was removed and users in that group were moved to a new group.
* [FIX] XGUI: Issue where a group's team wasn't getting unassigned when the unassigned team had no more groups associated with it.
* [FIX] XGUI: Players' UTeam parameters update properly when their group is changed by addons that call ULib's addUser/removeUser functions directly.
* [FIX] XGUI: In the groups menu, when selecting a player and clicking "change", the list of groups was not in inherited-based order.
* [FIX] XGUI: Users table was pointlessly being sent when a groups inheritance was changed.
* [CHANGE] "ulx gag" uses a slightly more robust method of gagging now.
* [CHANGE] Using file.Append for logging now that it's available to us.
* [CHANGE] XGUI: "XGUI module" is now the "Clientside Settings Module", which contains the XGUI settings.
* [CHANGE] XGUI: Optimized sending of ULib users data-- Only sends the data when it needs to (no longer on UCLChanged), and when changed, only sends the updated info.
* [CHANGE] XGUI: Optimized client-side processing of users data, how often it updated, and lowered priority of users data processing.
* [CHANGE] XGUI: Added a data chunksize to users to help alleviate some major lag issues on servers with large users lists.
* [CHANGE] XGUI: The Groups module shows some useful stuff in the event that it wasn't able to grab data from the server.
* [CHANGE] XGUI: Modifying UTeam settings is now disabled on non sandbox-derived gamemodes.
* [CHANGE] XGUI: Serverside xgui.removeData much more more robust.
* [REMOVE] XGUI: xgui_oldcheck.lua (No longer checks for a pre-svn version of XGUI installed.)
v3.51 - *(05/14/11)*
* [FIX] XGUI: Votemaps and kick/ban reasons not getting refreshed properly.
* [FIX] XGUI: Ban menu was incorrectly visible to everyone.
v3.50 - *(05/13/11)*
* [ADD] Autocomplete to ulx playsound.
* [ADD] Hook, "ULXLoaded".
* [ADD] Documentation for new keywords to "ulx help". Keywords for target self, target group, target picker, and negate.
* [ADD] Ability to specify a URL in "ulx showMotd" to show a URL.
* [ADD] ulx removeuserid, userallowid, userdenyid.
* [ADD] Integrated UTeam.
* [ADD] Colored action echoes.
* [ADD] ulx logJoinLeaveEchoes to echo player steamid's to admins when players join and leave.
* [FIX] Problem when ulx votemapMintime or votemapWaittime was more than 59 minutes (Thanks Stickly Man!).
* [FIX] Improved error checking on "ulx ent" (Thanks Python1320).
* [FIX] Error when running "ulx whip" from dedicated console (Thanks AtomicSpark).
* [FIX] Can no longer use "ulx motd" if the server has motd disabled, made it so you can change "ulx showMotd" on-the-fly (Thanks AtomicSpark).
* [FIX] "ulx groupallow" error when used from outside the server console (Thanks AtomicSpark).
* [FIX] ULX done loading hook removal on listen servers (Thanks Stickly Man!).
* [FIX] Fixed "ulx unspectate" not working in DarkRP (Thanks Ayran).
* [FIX] Fixed "ulx unragdoll" not working in DarkRP (Thanks Ayran).
* [FIX] Fixed an issue with dying while frozen (Thanks Stickly Man!).
* [FIX] Can't repeatedly slay someone anymore.
* [FIX] Can't attempt to ban a bot anymore (Thanks Stickly Man!).
* [FIX] Asay being printed to console twice if the console used the command.
* [FIX] Ragdolling a player with the Eli Vance model no longer throws an error (Thanks Insano-Man).
* [FIX] Admin approval box timing out and not allowing any more votes to be taken (Thanks Rambomst).
* [FIX] Various garry breakages.
* [FIX] A bug with ulx whip where a timer would never be removed if the player left during whipping.
* [FIX] Can no longer ragdoll a dead person (Thanks RiftNinja).
* [FIX] Jails are much more robust (if you get out, it puts you back, and you can't get physgunned out) (Thanks RiftNinja).
* [FIX] Can no longer slay frozen players (Thanks RiftNinja).
* [FIX] Can no longer whip or slap frozen players, and freezing a whipped player stops the whip (Thanks Stickly Man!).
* [CHANGE] Usermanagement commands, ulx who, and various other functions to better take advantage of the new UCL system
* [CHANGE] ulx ignite no longer spreads. Spreading messed up UPS protection.
* [CHANGE] Lots of various accessibility changes for XGUI.
* [CHANGE] Lowered priority of gimp check, you can now ungimp yourself while gimped.
* [CHANGE] Replaced cvar implementation for XGUI. Uses replicated cvars now for easy menu access.
* [CHANGE] Errors now show in red.
* [CHANGE] "ulx ent" can now be undone using garry's undo system.
* [CHANGE] Dropped requirement to read usermanagementhelp before using those commands.
* [CHANGE] Player isn't allowed to suicide while in a lot of the different ulx-states now.
* [CHANGE] Lots of changes to support new command system.
* [CHANGE] Moved config system to data folder, separated out some of the configs to separate files.
* [CHANGE] Shortened maul time considerably.
* [CHANGE] Added ability to get the return of ulx luarun by using '=', IE "ulx luarun =2+2" (also handles tables!).
* [CHANGE] Added uid to debuginfo.
* [CHANGE] tsay and csay now log who used the command.
* [CHANGE] Can specify '-1' to ulx logSpawnsEcho to disable echoing to dedicated console.
* [CHANGE] ulx adduser will use your existing id you're currently authed by if it exists, instead of always using steam id.
* [REMOVE] ulx ghost, I was never happy with it.
* [REMOVE] Old menus, uses XGUI now.
v3.40 - *(06/20/09)*
* [ADD] Alltalk to the admin menu
* [FIX] Umsgs being sent too early in certain circumstances.
* [FIX] The .ini files not loading properly on listen servers.
* [FIX] Problems introduced by garry's changes in handling concommands
* [FIX] Changed ULX_README.TXT file to point to proper instruction link for editing Gmod default users.
* [FIX] Removed a patch for a garrysmod autocomplete bug that's now fixed.
* [FIX] Maps not being sent correctly to the client.
* [FIX] Can't create a vote twice anymore.
* [FIX] A bug with loading the map list on listen server hosts.
* [FIX] An unfreeze bug with the ulx jail walls.
* [FIX] A caps bug with ulx adduserid.
* [FIX] You can now unragdoll yourself even if a third party addon removes the ragdoll.
* [FIX] Various formatting issues with ulx ban and ulx banid.
* [CHANGE] ulx ragdoll and unragdoll now preserve angle and velocity.
* [CHANGE] motdfile cvar now defaults to ulx_motd.txt. Sick of forcefully overriding other mod's motds. Renamed our motd to match.
* [CHANGE] ulx slap, whip, hp to further prevent being "stuck in ground".
* [CHANGE] Menus are derma'tized.
* [CHANGE] Updated how it handles svn version information.
v3.31 - *(06/08/08)*
* [ADD] ulx adduserid - Add a user by steam id (ie STEAM_0:1:1234...) (Does not actually verify user validity) (Thanks Mr.President)
* [FIX] Garry's 1/29/08 update breaking MOTD.
* [FIX] Links not working on MOTD.
* [FIX] Bug where you'd be stuck if someone disconnected while you were spectating them.
* [FIX] TF2 motd showing by default.
* [FIX] The usual assortment of small fixes not worth mentioning.
* [CHANGE] Unignite help command changed to be more standardized with other help.
* [CHANGE] Help indicates that multiple users can now also be command targeted using an asterisk "*". (ULib change added this)
* [CHANGE] Miscellaneous spelling corrections.
* [CHANGE] ulx chattime so that if your chat doesn't go through it is not counted towards your time.
* [CHANGE] Can no longer set hp or armor to less than 0.
* [REMOVE] ulx mingekick, useless now.
v3.30 - *(01/26/08)*
* [ADD] ulx strip - Strips player(s) weapons
* [ADD] ulx armor - Sets player(s) armor
* [ADD] We now log NPC spawns
* [ADD] ulx unignite - Unignites individual player, <all> players, or <everything> Any entity/player on fire.
* [FIX] ulx ban requiring a reason.
* [FIX] Added some more sanity checks to ulx maul.
* [FIX] The usual assortment of small fixes not worth mentioning.
* [FIX] ulx usermanagementhelp erroring out.
* [FIX] .ini's not loading for listen servers.
* [FIX] Case sensitivity problem with addgroup and removegroup.
* [FIX] ulx ent incorrectly requiring at least one flag.
* [FIX] All command inputs involving numbers should now be sanitized.
* [FIX] Autocomplete send in a multi-layer authentication environment (probably doesn't affect anyone)
* [CHANGE] The 0-255 scale for "ulx cloak" has been reversed.
* [CHANGE] Model paths in logging is now standardized (linux notation, single slashes)
* [CHANGE] The user initiating a silent logged command still sees the echo, even if they don't have access to see it normally.
* [CHANGE] Various misc things for new engine compatibility (IE, ulx clientmenu got a major change and other menu changes)
* [CHANGE] Removed all timers dealing with initialization and now rely on flags from the client. This makes the ULX initialization much more dependable.
* [CHANGE] Name change watching is now taken care of by ULib.
* [CHANGE] Converted all calls from ULib.consoleCommand( "exec ..." ) to ULib.execFile() to avoid running into the block on "exec" without our module.
v3.20 - *(09/23/07)*
* [ADD] ulx send - Allows admin to transport a player to another player (no more goto then bring!)
* [ADD] ulx maul - Maul a player with fast zombies
* [ADD] ulx gag - Silence individual player's microphone/sound input on voice enabled servers.
* [ADD] New module system. It's now easier than ever to add, remove, or change ULX commands!
* [ADD] ulx.addToMenu(). Use this function if you want a module to add something to any of the ULX menu.
* [ADD] ulx debuginfo. Use this function to get information to give us when you're asking support questions.
* [ADD] Votes now have a background behind them.
* [ADD] ulx voteEcho. A config option to enable echo of votes. Does not apply to votemap.
* [ADD] Maps menu now has option for gamemode.
* [ADD] Ban menu to view and remove bans.
* [ADD] ulx removeruser, addgroup, removegroup, groupallow, groupdeny, usermanagementhelp
* [FIX] ulx whip - No longer allows multiple whip sessions of an individual player at same time.
* [FIX] ulx adduser - You no longer have to reconnect to get given access.
* [FIX] Vastly improved ulx send, goto, and teleport. You should never get stuck in anything ever again.
* [FIX] Various initialization functions trying to access a disconnected player
* [FIX] Vastly improved reserved slots
* [FIX] Can't spawn junk or suicide while frozen now.
* [FIX] Coming out of spectate keeps god mode (if the player was given god with "ulx god")
* [FIX] Can't use "ulx hp" to give 100000+ hp anymore (crashes players with default HUD).
* [FIX] If you're authed twice, you won't get duplicates in autocomplete
* [FIX] ulx votemapmapmode and votemapaddmap not working
* [CHANGE] /me <action> can now be used in ulx asay/chat @ admin chat. "@ /me bashes spammer over the head"
* [CHANGE] Commands that used player:spawn (ragdoll,spectate, more) now return player to health/armor they had previously.
* [CHANGE] ulx teleport. Can now teleport a player to where you are looking. Logs it if a player is specified.
* [CHANGE] You can now specify entire directories in ulx addForcedDownload
* [CHANGE] A few internal changes to the ULX base to compliment the new ULib UCL access string system
* [CHANGE] ULX echoes come after the command now.
* [CHANGE] Configs are now under /cfg/* instead of /lua/ulx/configs/*
* [CHANGE] bring goto and teleport now zero out your velocity after moving you.
* [CHANGE] bring and goto can now still move you when you would get stuck if you're noclipped.
* [CHANGE] A hidden echo that is still shown to admins is now clearly labeled as such.
* [CHANGE] ulx cexec now takes multiple targets. (This was the intended behavior)
* [CHANGE] Lots of minor tweaks that would take too long to list. ;)
* [CHANGE] All say commands require spaces after them except the "@" commands. (IE, "!slapbob" no longer slaps bob)
* [CHANGE] Access to physgun players now uses the access string "ulx physgunplayer"
* [CHANGE] Access to reserved slots now uses the access string "ulx reservedslots"
* [CHANGE] Complete rewrite of advert system. You probably won't notice any difference (except hostname fix), but the code is leaner and meaner.
* [CHANGE] No interval option for ulx whip anymore, too easy to abuse.
* [CHANGE] Menus now use derma
* [CHANGE] The ULX configs should now really and truly load after the default configs
* [CHANGE] Votemap, votekick, voteban now all require the approval of the admin that started the vote if it wins.
* [CHANGE] Voteban can now receive a parameter for ban time.
* [CHANGE] ulx map can now receive gamemode for a parameter.
* [CHANGE] You can now use newlines in adverts.
* [CHANGE] Dropped requirement of being at least an opper for userallow/deny
* [CHANGE] ulx who has an updated format that includes custom groups.
* [CHANGE] ulx help is now categorized. (Reserved slots, teleportation, chat, etc )
* [CHANGE] ulx thetime can now only be used once every minute (server wide)
v3.11 - *(06/19/07)*
* [FIX] ulx vote. No longer public, people can't vote more than once, won't continue to hog the binds.
* [FIX] rslots will now set rslots on dedicated server start
* [FIX] Bring/goto getting you stuck in player sometimes.
* [FIX] Can't use vehicles from inside a jail now.
* [CHANGE] bring and goto now place teleporting player behind target
* [CHANGE] Upped votemapMinvotes to 3 (was 2).
* [CHANGE] Player physgun now only works in sandbox, lower admins can't physgun immune admins, freezes player while held.
* [CHANGE] Unblocked custom groups from ulx adduser.
v3.10 - *(05/05/07)*
* [ADD] Admins with slap access can move players now.
* [ADD] Chat anti-spam
* [ADD] ulx addForcedDownload for configs
* [ADD] Per-gamemode config folder
* [ADD] Voting! ulx vote, ulx votemap2, ulx voteban, ulx votekick
* [ADD] Maps menu
* [ADD] Lots more features to logging, like object spawn logging.
* [ADD] Reserved slots
* [FIX] Lots of minor insignificant bugs
* [FIX] Jail issues
* [FIX] Logging player connect on dedicated server issues
* [FIX] Now takes advantage of fixed umsgs. Fixes rare crash.
* [FIX] Can now psay immune players
* [FIX] Minor bugs in logs
* [CHANGE] Logs will now wrap to new date if server's on past midnight
* [CHANGE] You can now use the admin menu in gamemodes derived from sandbox.
* [CHANGE] Cleaned up now obsolete GetTable() calls
* [CHANGE] Motd is now driven by lua. Much easier to deal with, fixes many problems.
* [CHANGE] Can now use sv_cheats 1/0 from ulx rcon (dodges block)
* [CHANGE] ulx lua_run is now ulx luarun to dodge another block
* [CHANGE] Now in addon format
* [CHANGE] ulx ignite will now last until you die and can spread.
* [CHANGE] Global toolmode deny doesn't affect admins.
v3.02 - *(01/10/07)*
* [CHANGE] Admin menu won't spam console so bad now
* [FIX] Some more command crossbreeding issues (IE ragdolling jailed player)
* [FIX] Teleport commands able to put someone in a wall. This is still possible, but much less likely.
* [ADD] Motd manipulation. Auto-shows on startup and !motd
* [ADD] toolallow, tooldeny, tooluserallow, tooluserdeny. Works fine, but is EXPERIMENTAL!
v3.01 - *(01/07/07)*
* [ADD] ulx whip
* [ADD] ulx adduser
* [ADD] ulx userallow - Allow users access to commands
* [ADD] ulx userdeny - Deny users access to commands
* [ADD] ulx bring - Bring a user to you
* [ADD] ulx goto - Goto a user
* [ADD] ulx teleport - Teleport to where you're looking
* [ADD] IRC-style "/me" command
* [FIX] You can't use the adminmenu outside sandbox now
* [FIX] Vastly improved "ulx jail"
* [FIX] Improved "ulx ragdoll"
* [FIX] pvp damage checkbox in adminmenu not working.
* [FIX] Ban button
* [FIX] Ban not kicking users
* [FIX] Blinded users being able to see through the camera tool
* [FIX] Admin menu not showing values on a dedicated server
* [FIX] Admin menu checkboxes (which are now buttons!)
* [FIX] Ulx commands much more usable from dedicated server console.
* [FIX] Dedicated server spam (IsListenServerHost)
* [FIX] Uncloak works properly now
* [FIX] Various problems using commands on users in vehicles. (Thanks Jalit)
v3.0 - *(01/01/07)*
* Initial version for GM10
Group: Credits
Thanks to everyone in #ulysses in irc.gamesurge.net for not giving up on me :)
A big thanks to JamminR for listening to me ramble on and for giving the project fresh insights.
Group: License
This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 License.
To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/3.0/ or send a letter to
Creative Commons
543 Howard Street
5th Floor
San Francisco, California 94105
USA
Title: ULX Readme
__ULX__
Version 3.70
*ULX v3.70 (released 00/00/00)*
ULX is an admin mod for GMod (<http://garrysmod.com/>).
ULX offers server admins an AMXX-style support. It allows multiple admins with different access levels on the same server.
It has features from basic kick, ban, and slay to fancier commands such as blind, freeze, voting, and more.
Visit our homepage at <http://ulyssesmod.net/>.
You can talk to us on our forums at <http://forums.ulyssesmod.net/>
Group: Author
ULX is brought to you by..
* Brett "Megiddo" Smith - Contact: <megiddo@ulyssesmod.net>
* JamminR - Contact: <jamminr@ulyssesmod.net>
* Stickly Man! - Contact: <sticklyman@ulyssesmod.net>
* MrPresident - Contact: <mrpresident@ulyssesmod.net>
Group: Requirements
ULX requires the latest ULib to be installed on the server. You can get ULib from the ULX homepage.
Group: Installation
To install ULX, simply extract the files from the archive to your garrysmod/addons folder.
When you've done this, you should have a file structure like this--
<garrysmod>/addons/ulx/lua/ULib/modules/ulx_init.lua
<garrysmod>/addons/ulx/lua/ulx/modules/fun.lua
etc..
Note that installation for dedicated servers is EXACTLY the same!
You absolutely, positively have to do a full server restart after installing the files. A simple map
change will not cut it!
Group: Usage
To use ULX, simply type in whatever command or cvar you want to use. If you're not running a listen server,
or if you want to give access to other users, you must add users.
You can either follow the standard gmod way of adding users
(http://maurits.tv/data/garrysmod/wiki/wiki.garrysmod.com/index0c18.html?title=Player_Groups), or you can add yourself directly to the
data/ulib/users.txt file. DO NOT EDIT THE ULIB FILE UNLESS YOU KNOW WHAT YOU'RE DOING.
A word about superadmins: Superadmins are considered the highest usergroup. They have access to all the
commands in ULX, override other user's immunity, and are shown otherwise hidden log messages (IE, shown
rcon commands admins are running, when they spectate others). Superadmins also have the power to give and
revoke access to commands using userallow and userdeny (though they can't use this command on eachother).
All commands are preceded by "ulx ". Type "ulx help" in a console without the quotes for help.
__To give yourself a jump start into ulx, simply remember the commands "ulx help" and "ulx menu". You can
also access the menu by saying "!menu".__
Check out the configs folder in ulx for some more goodies.
Group: Changelog
v3.70 - *(00/00/00)*
* [ADD] XGUI: Ability to set the 'nextlevel' cvar from the maps tab, if you have access to "ulx map".
* [ADD] Reason to votekick log (Thanks, CSchulz).
* [ADD] Steam ID parameter to "ulx who" to lookup users by Steam ID.
* [ADD] Cvar "ulx meChatEnabled" added to enable or disable the /me chat feature, or set it to Sandbox only.
* [ADD] XGUI: Added methods for developers to be able to open a specific client or server setting module.
* [FIX] The usual random slew of Garry-breakages (Thanks, Fuzzik).
* [FIX] Changing weapons while cloaked would keep them hidden when uncloaked. (Thanks, TheRealAyCe).
* [FIX] XGUI: Error if the default settings/users.txt file was missing.
* [FIX] Gamemode list includes workshop addons now (Thanks, jason2010).
* [FIX] "ulx ent" parameter parsing (Thanks, Zombine).
* [FIX] "ulx voteban" can now ban the user even if they disconnect after the vote starts.
* [FIX] Vote commands now work properly from server console.
* [FIX] XGUI: Numerous issues with selecting a custom Derma/GWEN skin.
* [FIX] XGUI: Clientside settings would not save if the client's data/ulx folder did not exist.
* [FIX] Improved how well ULX/XGUI files and XGUI clientside/serverside modules handle being autorefreshed.
* [FIX] ulx.addToHelpManually now checks for and removes and previously added manual help entries with the same command name.
* [FIX] XGUI: Bug where ulx_showMotd cvar would not be updated properly when changed by someone else.
* [FIX] XGUI: Minor performance exploit involving serverside ban sorting. (Thanks, TomatoCo).
* [FIX] Exploit involving gmod filesystem mounting. (Thanks, Willox).
* [FIX] Minor issue where opposite commands might not be run due to case sensitivity.
* [FIX] Duplicate help entries due to autorefresh and overriding commands. (Thanks iSnipeu).
* [FIX] Ragdolled players were getting removed on map cleanup.
* [FIX] XGUI: Server error in some cases when sorting bans by Unban Date after a new ban has been added.
* [CHANGE] MOTD now uses DHTML (Awesomium framework).
* [CHANGE] Hook calls to match ULib's new format.
* [CHANGE] ULX convar updates will now append an entry to data/config.txt if it is not defined in the file. Previously, these changes would not be saved.
* [CHANGE] XGUI: No longer autoexecutes skins to ensure they have been installed.
* [CHANGE] XGUI: Added "name" parameter to xgui.hookEvent to prevent event duplication. (Aids with autorefresh, is backwards compatible with old XGUI modules)
* [CHANGE] XGUI: Modules that no longer exist will be removed from the customizable sort order.
* [CHANGE] XGUI: Sliders for arguments on the Cmds tab with a small min/max delta (e.g. from 0 to 1) will now allow up to 2 decimal places, if the arg does not have cmds.round flag.
v3.62 - *(03/09/15)*
* [ADD] "ulx return" to return target to previous location they were in before a teleport command was used (Thanks for the idea, ludalex).
* [ADD] Networked variables for gimp, mute, and gag (Thanks iSnipeu).
* [ADD] XGUI: Added more sorting and filtering options for bans.
* [ADD] "ulx stopvote" to stop a vote currently in progress (Thanks, LuaTenshi).
* [ADD] XGUI: Added "onClose" event for modules that need it. (Suggested by arduinium).
* [FIX] Ban reason and the person who started the voteban is now reported in "ulx voteban" bans (Thanks iSnipeu).
* [FIX] An API change causing an error to be thrown at the end of "ulx maul" (Thanks Decicus).
* [FIX] NULL entity error after votekick on a player that left the server, now sends message stating that votekicked player already left.
* [FIX] PlayerDeath hook errors in certain gamemodes where invalid entites are sent as the killer or victim. (Thanks Mechanical Mind).
* [FIX] JailTP command now saves last player position, now works with "ulx return". (Thanks jakej78b).
* [FIX] XGUI: Slider label widths were extra large due to a slight change in default numslider behavior. (Thanks Fuzzik).
* [FIX] Garry's Mod update caused users to be banned faster than expected, log then incorrectly stated that (Console) was banned.
* [FIX] Garry's Mod update prevented votebans from working.
* [FIX] Garry's Mod update caused server crash when kicking/banning yourself via chat command.
* [FIX] Bug with spectate and respawning (Thanks Sjokomelk).
* [FIX] Bug when changing weapons while cloaked, weapons would stay invisible after uncloaking. (Thanks Z0mb1n3).
* [FIX] Could not assign BOTs to groups via ulx adduserid or XGUI. (Thanks RhapsodySL).
* [FIX] Fixed bug where XGUI would not start on dev branch of Garry's Mod. Changed to init on ULib.HOOK_LOCALPLAYERREADY instead of ULib.HOOK_UCLAUTH.
* [FIX] MOTD not enabled message would display for all players instead of the player who tried to open the motd. (Thanks TheClonker).
* [CHANGE] "PlayerSay" hooks are now only called serverside. (Thanks NoBrainCZ).
* [CHANGE] Logging now prints how long a user took to join the server.
* [CHANGE] XGUI: Updated cvarlist for sandbox and wiremod limits.
* [CHANGE] XGUI: Many Ban menu improvements. Entire banlist is no longer sent on join- data subset is now requested by the client and sent from the server.
* [CHANGE] XGUI: Ban list is now paginated instead of a giant scrollable list.
* [CHANGE] XGUI: ULX Bans and other (or "Source Bans") are no longer separated.
* [CHANGE] ULX vote variable is now global so other addons can tell if a vote is in progress. ulx.doVote() also returns whether or not it actually started a vote (Thanks for the ideas, arduinium).
* [CHANGE] XGUI: Changed gamemode dropdown on maps tab to honor player/group restrictions. (Thanks chaos12135).
v3.61 - *(08/30/13)*
* [ADD] cl_pickupplayers (defaults to 1) to allow an admin to disable the ability to pickup players (so they don't do it on accident). Done in collaboration with FPtje.
* [ADD] %curmap% and %steamid% variables in "ulx showMotd" URL for custom-served MOTDs (Thanks Mors-Quaedam).
* [ADD] XGUI: Bans are now searchable. (Thanks to iSnipeu for the code contribution!)
* [FIX] "#" (Pound signs) removing content in ulx asay (Thanks bener180).
* [FIX] Reserved slot mode 3 not kicking the shortest connected player as it is supposed to (Thanks monkstick).
* [FIX] No longer able to physgun frozen players (Thanks ms333).
* [FIX] XGUI: Added checks to prevent admins from being able to edit ban information past their restrictions (Thanks Zaph).
* [FIX] XGUI: Infobar text no longer displaying.
* [FIX] XGUI: Error caused when closing the fban window after the targeted player has left the server (Thanks nathan736).
* [FIX] XGUI: Issues with handling min / max number restrictions.
* [FIX] XGUI: Map icons not loading.
* [FIX] XGUI: Ban menu bugfixes.
* [CHANGE] Jail models. The jail is slightly bigger and can't be shot through anymore (Thanks Mors-Quaedam).
* [CHANGE] Updated PvP damage cvar to reflect Garry's changes (Thanks Mors-Quadam).
* [CHANGE] "ulx gag" now uses a server-side hook (much more robust).
v3.60 - *(01/27/13)*
* [ADD] "ulx jailtp" - A combination of tp <player> and jail <player> (Thanks HellFox).
* [ADD] "ulx resettodefaults" - Resets ULX and ULib config to defaults.
* [ADD] XGUI: Added ability to edit lower-level restrictions from a higher-level group.
* [CHANGE] ULX ban now supports restricting of time/string formats.
* [CHANGE] !teleport chat command is now also aliased as !tp.
* [CHANGE] XGUI: Utilizes ULib's more robust ID Targeting system.
* [CHANGE] XGUI: Controls added to utilize time/string formats and restrictions.
* [CHANGE] XGUI: No longer duplicates ULX replicated cvars (ulx_cl_) due to ULib changes. Uses the regular ulx_ cvars directly.
* [CHANGE] XGUI: Supports new values for sv_alltalk.
* [CHANGE] XGUI: A few changes to update look and feel. Matches Derma/GWEN skin colors better in some areas.
* [CHANGE] XGUI: No longer retrieves sandbox limits from the web. Included with download.
* [FIX] Garry breakages in GM13.
* [FIX] An exclusivity bug in "ulx freeze" (Thanks infinitywraith).
* [FIX] A console bug when trying to ulx teleport another player (Thanks infinitywraith).
* [FIX] "ulx gimp" not obeying chat anti-spam (Thanks ruok2bu).
* [FIX] "ulx userdeny" not logging properly in some cases.
* [FIX] An echo incorrectly going to all users for "ulx votekick" (Thanks JackYack13).
* [FIX] Module cross-contamination in end.lua (Thanks Pon-3).
* [FIX] Team vs public chat doing the opposite of what it should for logs and "/me" actions. Wonder how long ago Garry needlessly changed that API without us noticing.
* [FIX] Promotion bug after using "ulx userallow" on a regular user. (Thanks JackYack13).
* [FIX] Server crash when jail is placed inside trigger_remove brush. (Thanks HellFox).
* [FIX] XGUI: Changed startup code to initialize faster, handle strange server load scenarios better.
* [FIX] XGUI: BoolArgs in the Cmds tab now obey restrictions.
v3.54 - *(04/27/12)*
* [FIX] XGUI: Hard crash with the os.date function when bans have an extremely long unban time.
v3.53 - *(01/01/12)*
* [FIX] Garry breakages.
v3.52 - *(09/22/11)*
* [ADD] Support for "time strings" in ulx ban and ulx banid. EG, "5w4d3h2" would ban for 5 weeks, 4 days, 3 hours, 2 minutes (Thanks lavacano201014).
* [ADD] XGUI: New customization options-- You can now change XGUI's position and the open/close animations.
* [ADD] XGUI: Double-click a player on the commands tab to execute the command with the parameters on the right.
* [ADD] XLIB: Additional layout for xlib.makecolorpicker with alphabar.
* [FIX] No longer able to make a player invincible by freezing then mauling.
* [FIX] XGUI: Selected svsetting/clsetting modules no longer close when XGUI modules get reprocessed.
* [FIX] XGUI: Error caused when a bans unban time was changed, causing the ban to expire.
* [FIX] XGUI: Adding details to SBans would throw an error on server, wouldn't refresh on clients properly.
* [FIX] XGUI: Expired bans were not removing themselves from the client lists.
* [FIX] XGUI: Somehow managed to duplicate the entire bans module code within the same file. X|
* [FIX] XGUI: Rare error causing votemap settings to not load properly (which was previously fixable after a mapchange)
* [FIX] XGUI: Non-harmful Lua error occuring during data transfer when running XGUI on a non-sandbox game mode. (Thanks Synergy Connections!)
* [FIX] XGUI: The button for scrolling through tab names of settings modules (if there were too many) was being obscured by the close button. (Thanks [eVo]Lead4u!)
* [FIX] XGUI: UTeam information would be lost when changing UTeam settings on non sandbox-derived gamemodes
* [FIX] XGUI: DNumberWangs on color panels being extra long (due to garry update?)
* [FIX] XGUI: ULib team data not sent after making changes to teams.
* [FIX] XGUI: Users in the groups tab were not being updated when a group was removed and users in that group were moved to a new group.
* [FIX] XGUI: Issue where a group's team wasn't getting unassigned when the unassigned team had no more groups associated with it.
* [FIX] XGUI: Players' UTeam parameters update properly when their group is changed by addons that call ULib's addUser/removeUser functions directly.
* [FIX] XGUI: In the groups menu, when selecting a player and clicking "change", the list of groups was not in inherited-based order.
* [FIX] XGUI: Users table was pointlessly being sent when a groups inheritance was changed.
* [CHANGE] "ulx gag" uses a slightly more robust method of gagging now.
* [CHANGE] Using file.Append for logging now that it's available to us.
* [CHANGE] XGUI: "XGUI module" is now the "Clientside Settings Module", which contains the XGUI settings.
* [CHANGE] XGUI: Optimized sending of ULib users data-- Only sends the data when it needs to (no longer on UCLChanged), and when changed, only sends the updated info.
* [CHANGE] XGUI: Optimized client-side processing of users data, how often it updated, and lowered priority of users data processing.
* [CHANGE] XGUI: Added a data chunksize to users to help alleviate some major lag issues on servers with large users lists.
* [CHANGE] XGUI: The Groups module shows some useful stuff in the event that it wasn't able to grab data from the server.
* [CHANGE] XGUI: Modifying UTeam settings is now disabled on non sandbox-derived gamemodes.
* [CHANGE] XGUI: Serverside xgui.removeData much more more robust.
* [REMOVE] XGUI: xgui_oldcheck.lua (No longer checks for a pre-svn version of XGUI installed.)
v3.51 - *(05/14/11)*
* [FIX] XGUI: Votemaps and kick/ban reasons not getting refreshed properly.
* [FIX] XGUI: Ban menu was incorrectly visible to everyone.
v3.50 - *(05/13/11)*
* [ADD] Autocomplete to ulx playsound.
* [ADD] Hook, "ULXLoaded".
* [ADD] Documentation for new keywords to "ulx help". Keywords for target self, target group, target picker, and negate.
* [ADD] Ability to specify a URL in "ulx showMotd" to show a URL.
* [ADD] ulx removeuserid, userallowid, userdenyid.
* [ADD] Integrated UTeam.
* [ADD] Colored action echoes.
* [ADD] ulx logJoinLeaveEchoes to echo player steamid's to admins when players join and leave.
* [FIX] Problem when ulx votemapMintime or votemapWaittime was more than 59 minutes (Thanks Stickly Man!).
* [FIX] Improved error checking on "ulx ent" (Thanks Python1320).
* [FIX] Error when running "ulx whip" from dedicated console (Thanks AtomicSpark).
* [FIX] Can no longer use "ulx motd" if the server has motd disabled, made it so you can change "ulx showMotd" on-the-fly (Thanks AtomicSpark).
* [FIX] "ulx groupallow" error when used from outside the server console (Thanks AtomicSpark).
* [FIX] ULX done loading hook removal on listen servers (Thanks Stickly Man!).
* [FIX] Fixed "ulx unspectate" not working in DarkRP (Thanks Ayran).
* [FIX] Fixed "ulx unragdoll" not working in DarkRP (Thanks Ayran).
* [FIX] Fixed an issue with dying while frozen (Thanks Stickly Man!).
* [FIX] Can't repeatedly slay someone anymore.
* [FIX] Can't attempt to ban a bot anymore (Thanks Stickly Man!).
* [FIX] Asay being printed to console twice if the console used the command.
* [FIX] Ragdolling a player with the Eli Vance model no longer throws an error (Thanks Insano-Man).
* [FIX] Admin approval box timing out and not allowing any more votes to be taken (Thanks Rambomst).
* [FIX] Various garry breakages.
* [FIX] A bug with ulx whip where a timer would never be removed if the player left during whipping.
* [FIX] Can no longer ragdoll a dead person (Thanks RiftNinja).
* [FIX] Jails are much more robust (if you get out, it puts you back, and you can't get physgunned out) (Thanks RiftNinja).
* [FIX] Can no longer slay frozen players (Thanks RiftNinja).
* [FIX] Can no longer whip or slap frozen players, and freezing a whipped player stops the whip (Thanks Stickly Man!).
* [CHANGE] Usermanagement commands, ulx who, and various other functions to better take advantage of the new UCL system
* [CHANGE] ulx ignite no longer spreads. Spreading messed up UPS protection.
* [CHANGE] Lots of various accessibility changes for XGUI.
* [CHANGE] Lowered priority of gimp check, you can now ungimp yourself while gimped.
* [CHANGE] Replaced cvar implementation for XGUI. Uses replicated cvars now for easy menu access.
* [CHANGE] Errors now show in red.
* [CHANGE] "ulx ent" can now be undone using garry's undo system.
* [CHANGE] Dropped requirement to read usermanagementhelp before using those commands.
* [CHANGE] Player isn't allowed to suicide while in a lot of the different ulx-states now.
* [CHANGE] Lots of changes to support new command system.
* [CHANGE] Moved config system to data folder, separated out some of the configs to separate files.
* [CHANGE] Shortened maul time considerably.
* [CHANGE] Added ability to get the return of ulx luarun by using '=', IE "ulx luarun =2+2" (also handles tables!).
* [CHANGE] Added uid to debuginfo.
* [CHANGE] tsay and csay now log who used the command.
* [CHANGE] Can specify '-1' to ulx logSpawnsEcho to disable echoing to dedicated console.
* [CHANGE] ulx adduser will use your existing id you're currently authed by if it exists, instead of always using steam id.
* [REMOVE] ulx ghost, I was never happy with it.
* [REMOVE] Old menus, uses XGUI now.
v3.40 - *(06/20/09)*
* [ADD] Alltalk to the admin menu
* [FIX] Umsgs being sent too early in certain circumstances.
* [FIX] The .ini files not loading properly on listen servers.
* [FIX] Problems introduced by garry's changes in handling concommands
* [FIX] Changed ULX_README.TXT file to point to proper instruction link for editing Gmod default users.
* [FIX] Removed a patch for a garrysmod autocomplete bug that's now fixed.
* [FIX] Maps not being sent correctly to the client.
* [FIX] Can't create a vote twice anymore.
* [FIX] A bug with loading the map list on listen server hosts.
* [FIX] An unfreeze bug with the ulx jail walls.
* [FIX] A caps bug with ulx adduserid.
* [FIX] You can now unragdoll yourself even if a third party addon removes the ragdoll.
* [FIX] Various formatting issues with ulx ban and ulx banid.
* [CHANGE] ulx ragdoll and unragdoll now preserve angle and velocity.
* [CHANGE] motdfile cvar now defaults to ulx_motd.txt. Sick of forcefully overriding other mod's motds. Renamed our motd to match.
* [CHANGE] ulx slap, whip, hp to further prevent being "stuck in ground".
* [CHANGE] Menus are derma'tized.
* [CHANGE] Updated how it handles svn version information.
v3.31 - *(06/08/08)*
* [ADD] ulx adduserid - Add a user by steam id (ie STEAM_0:1:1234...) (Does not actually verify user validity) (Thanks Mr.President)
* [FIX] Garry's 1/29/08 update breaking MOTD.
* [FIX] Links not working on MOTD.
* [FIX] Bug where you'd be stuck if someone disconnected while you were spectating them.
* [FIX] TF2 motd showing by default.
* [FIX] The usual assortment of small fixes not worth mentioning.
* [CHANGE] Unignite help command changed to be more standardized with other help.
* [CHANGE] Help indicates that multiple users can now also be command targeted using an asterisk "*". (ULib change added this)
* [CHANGE] Miscellaneous spelling corrections.
* [CHANGE] ulx chattime so that if your chat doesn't go through it is not counted towards your time.
* [CHANGE] Can no longer set hp or armor to less than 0.
* [REMOVE] ulx mingekick, useless now.
v3.30 - *(01/26/08)*
* [ADD] ulx strip - Strips player(s) weapons
* [ADD] ulx armor - Sets player(s) armor
* [ADD] We now log NPC spawns
* [ADD] ulx unignite - Unignites individual player, <all> players, or <everything> Any entity/player on fire.
* [FIX] ulx ban requiring a reason.
* [FIX] Added some more sanity checks to ulx maul.
* [FIX] The usual assortment of small fixes not worth mentioning.
* [FIX] ulx usermanagementhelp erroring out.
* [FIX] .ini's not loading for listen servers.
* [FIX] Case sensitivity problem with addgroup and removegroup.
* [FIX] ulx ent incorrectly requiring at least one flag.
* [FIX] All command inputs involving numbers should now be sanitized.
* [FIX] Autocomplete send in a multi-layer authentication environment (probably doesn't affect anyone)
* [CHANGE] The 0-255 scale for "ulx cloak" has been reversed.
* [CHANGE] Model paths in logging is now standardized (linux notation, single slashes)
* [CHANGE] The user initiating a silent logged command still sees the echo, even if they don't have access to see it normally.
* [CHANGE] Various misc things for new engine compatibility (IE, ulx clientmenu got a major change and other menu changes)
* [CHANGE] Removed all timers dealing with initialization and now rely on flags from the client. This makes the ULX initialization much more dependable.
* [CHANGE] Name change watching is now taken care of by ULib.
* [CHANGE] Converted all calls from ULib.consoleCommand( "exec ..." ) to ULib.execFile() to avoid running into the block on "exec" without our module.
v3.20 - *(09/23/07)*
* [ADD] ulx send - Allows admin to transport a player to another player (no more goto then bring!)
* [ADD] ulx maul - Maul a player with fast zombies
* [ADD] ulx gag - Silence individual player's microphone/sound input on voice enabled servers.
* [ADD] New module system. It's now easier than ever to add, remove, or change ULX commands!
* [ADD] ulx.addToMenu(). Use this function if you want a module to add something to any of the ULX menu.
* [ADD] ulx debuginfo. Use this function to get information to give us when you're asking support questions.
* [ADD] Votes now have a background behind them.
* [ADD] ulx voteEcho. A config option to enable echo of votes. Does not apply to votemap.
* [ADD] Maps menu now has option for gamemode.
* [ADD] Ban menu to view and remove bans.
* [ADD] ulx removeruser, addgroup, removegroup, groupallow, groupdeny, usermanagementhelp
* [FIX] ulx whip - No longer allows multiple whip sessions of an individual player at same time.
* [FIX] ulx adduser - You no longer have to reconnect to get given access.
* [FIX] Vastly improved ulx send, goto, and teleport. You should never get stuck in anything ever again.
* [FIX] Various initialization functions trying to access a disconnected player
* [FIX] Vastly improved reserved slots
* [FIX] Can't spawn junk or suicide while frozen now.
* [FIX] Coming out of spectate keeps god mode (if the player was given god with "ulx god")
* [FIX] Can't use "ulx hp" to give 100000+ hp anymore (crashes players with default HUD).
* [FIX] If you're authed twice, you won't get duplicates in autocomplete
* [FIX] ulx votemapmapmode and votemapaddmap not working
* [CHANGE] /me <action> can now be used in ulx asay/chat @ admin chat. "@ /me bashes spammer over the head"
* [CHANGE] Commands that used player:spawn (ragdoll,spectate, more) now return player to health/armor they had previously.
* [CHANGE] ulx teleport. Can now teleport a player to where you are looking. Logs it if a player is specified.
* [CHANGE] You can now specify entire directories in ulx addForcedDownload
* [CHANGE] A few internal changes to the ULX base to compliment the new ULib UCL access string system
* [CHANGE] ULX echoes come after the command now.
* [CHANGE] Configs are now under /cfg/* instead of /lua/ulx/configs/*
* [CHANGE] bring goto and teleport now zero out your velocity after moving you.
* [CHANGE] bring and goto can now still move you when you would get stuck if you're noclipped.
* [CHANGE] A hidden echo that is still shown to admins is now clearly labeled as such.
* [CHANGE] ulx cexec now takes multiple targets. (This was the intended behavior)
* [CHANGE] Lots of minor tweaks that would take too long to list. ;)
* [CHANGE] All say commands require spaces after them except the "@" commands. (IE, "!slapbob" no longer slaps bob)
* [CHANGE] Access to physgun players now uses the access string "ulx physgunplayer"
* [CHANGE] Access to reserved slots now uses the access string "ulx reservedslots"
* [CHANGE] Complete rewrite of advert system. You probably won't notice any difference (except hostname fix), but the code is leaner and meaner.
* [CHANGE] No interval option for ulx whip anymore, too easy to abuse.
* [CHANGE] Menus now use derma
* [CHANGE] The ULX configs should now really and truly load after the default configs
* [CHANGE] Votemap, votekick, voteban now all require the approval of the admin that started the vote if it wins.
* [CHANGE] Voteban can now receive a parameter for ban time.
* [CHANGE] ulx map can now receive gamemode for a parameter.
* [CHANGE] You can now use newlines in adverts.
* [CHANGE] Dropped requirement of being at least an opper for userallow/deny
* [CHANGE] ulx who has an updated format that includes custom groups.
* [CHANGE] ulx help is now categorized. (Reserved slots, teleportation, chat, etc )
* [CHANGE] ulx thetime can now only be used once every minute (server wide)
v3.11 - *(06/19/07)*
* [FIX] ulx vote. No longer public, people can't vote more than once, won't continue to hog the binds.
* [FIX] rslots will now set rslots on dedicated server start
* [FIX] Bring/goto getting you stuck in player sometimes.
* [FIX] Can't use vehicles from inside a jail now.
* [CHANGE] bring and goto now place teleporting player behind target
* [CHANGE] Upped votemapMinvotes to 3 (was 2).
* [CHANGE] Player physgun now only works in sandbox, lower admins can't physgun immune admins, freezes player while held.
* [CHANGE] Unblocked custom groups from ulx adduser.
v3.10 - *(05/05/07)*
* [ADD] Admins with slap access can move players now.
* [ADD] Chat anti-spam
* [ADD] ulx addForcedDownload for configs
* [ADD] Per-gamemode config folder
* [ADD] Voting! ulx vote, ulx votemap2, ulx voteban, ulx votekick
* [ADD] Maps menu
* [ADD] Lots more features to logging, like object spawn logging.
* [ADD] Reserved slots
* [FIX] Lots of minor insignificant bugs
* [FIX] Jail issues
* [FIX] Logging player connect on dedicated server issues
* [FIX] Now takes advantage of fixed umsgs. Fixes rare crash.
* [FIX] Can now psay immune players
* [FIX] Minor bugs in logs
* [CHANGE] Logs will now wrap to new date if server's on past midnight
* [CHANGE] You can now use the admin menu in gamemodes derived from sandbox.
* [CHANGE] Cleaned up now obsolete GetTable() calls
* [CHANGE] Motd is now driven by lua. Much easier to deal with, fixes many problems.
* [CHANGE] Can now use sv_cheats 1/0 from ulx rcon (dodges block)
* [CHANGE] ulx lua_run is now ulx luarun to dodge another block
* [CHANGE] Now in addon format
* [CHANGE] ulx ignite will now last until you die and can spread.
* [CHANGE] Global toolmode deny doesn't affect admins.
v3.02 - *(01/10/07)*
* [CHANGE] Admin menu won't spam console so bad now
* [FIX] Some more command crossbreeding issues (IE ragdolling jailed player)
* [FIX] Teleport commands able to put someone in a wall. This is still possible, but much less likely.
* [ADD] Motd manipulation. Auto-shows on startup and !motd
* [ADD] toolallow, tooldeny, tooluserallow, tooluserdeny. Works fine, but is EXPERIMENTAL!
v3.01 - *(01/07/07)*
* [ADD] ulx whip
* [ADD] ulx adduser
* [ADD] ulx userallow - Allow users access to commands
* [ADD] ulx userdeny - Deny users access to commands
* [ADD] ulx bring - Bring a user to you
* [ADD] ulx goto - Goto a user
* [ADD] ulx teleport - Teleport to where you're looking
* [ADD] IRC-style "/me" command
* [FIX] You can't use the adminmenu outside sandbox now
* [FIX] Vastly improved "ulx jail"
* [FIX] Improved "ulx ragdoll"
* [FIX] pvp damage checkbox in adminmenu not working.
* [FIX] Ban button
* [FIX] Ban not kicking users
* [FIX] Blinded users being able to see through the camera tool
* [FIX] Admin menu not showing values on a dedicated server
* [FIX] Admin menu checkboxes (which are now buttons!)
* [FIX] Ulx commands much more usable from dedicated server console.
* [FIX] Dedicated server spam (IsListenServerHost)
* [FIX] Uncloak works properly now
* [FIX] Various problems using commands on users in vehicles. (Thanks Jalit)
v3.0 - *(01/01/07)*
* Initial version for GM10
Group: Credits
Thanks to everyone in #ulysses in irc.gamesurge.net for not giving up on me :)
A big thanks to JamminR for listening to me ramble on and for giving the project fresh insights.
Group: License
This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 License.
To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/3.0/ or send a letter to
Creative Commons
543 Howard Street
5th Floor
San Francisco, California 94105
USA