Compare commits

...

6 Commits

Author SHA1 Message Date
SticklyMan
aa414f3fa8 Update ulib.build 2024-03-24 11:40:09 -06:00
Brett Smith
cc8b5583bc Update ulib.build 2017-03-21 15:37:41 -05:00
Brett Smith
422c622f1b Update ulib.build 2017-03-19 13:43:48 -05:00
Brett Smith
9d7b86247a Update ulib.build 2016-05-22 14:37:38 -04:00
Brett Smith
0e95556a1f Update ulib.build 2016-02-15 19:36:42 -05:00
Nayruden
f07605ed1e gh-pages cleanup 2015-11-19 21:29:24 -06:00
31 changed files with 1 additions and 8453 deletions

View File

@ -1,12 +0,0 @@
#!/bin/sh
#
# .git/hooks/post-commit
#
if [ -a .commit ]
then
rm .commit
echo `date '+%s'` > ulib.build
git add ulib.build
git commit --amend -C HEAD --no-verify
fi

View File

@ -1,7 +0,0 @@
#!/bin/sh
#
# .git/hooks/pre-commit
#
# Prevent infinite loops on post-commit
touch .commit

View File

@ -1,280 +0,0 @@
Title: ULib Readme
*ULib v2.52d (released 00/00/00)*
ULib is a developer library for GMod 10 (<http://garrysmod.com/>).
ULib provides such features as universal physics, user access lists, and much, much more!
Visit our homepage at <http://ulyssesmod.net/>.
You can talk to us on our forums at <http://forums.ulyssesmod.net/>.
Group: Author
ULib 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
ULib requires a working copy of the latest garrysmod, and that's it!
Group: Installation
To install ULib, 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/ulib/lua/ULib/init.lua
<garrysmod>/addons/ulib/lua/ULib/server/util.lua
<garrysmod>/addons/ulib/lua/autorun/ulib_init.lua
<garrysmod>/addons/ulib/data/ULib/users.txt
etc..
Please note that installation is the same on dedicated servers.
You absolutely, positively have to do a full server restart after installing the files. A simple map
change will not cut it!
Group: Usage
Server admins do not "use" ULib, they simply enjoy the benefits it has to offer.
After installing ULib correctly, scripts that take advantage of ULib will take care of the rest.
Rest easy!
Group: Changelog
v2.60 - *(00/00/00)*
* [ADD] ULib.ucl.getUserInfoFromID for getting user info from an ID.
* [ADD] CAMI support.
* [ADD] "noMount" parameter to file-related APIs.
* [ADD] ULibGetUser(s)CustomKeyword hooks (Thanks, LuaTenshi).
* [ADD] ULib.getVersion().
* [FIX] The usual random slew of Garry-breakages (Thanks, Fuzzik).
* [FIX] An assumption regarding player authentication that led to a player's group being reset to user sometimes.
* [FIX] Garry API change for ULib.findinDir (Thanks, ascentechit).
* [FIX] Workaround for Garry's odd handling of bot UIDs (Thanks, BurgerLUA).
* [FIX] Improved how well ULib files handle being autorefreshed.
* [FIX] Exploitable console command could potentially cause errors and/or crashes.
* [FIX] Exploit involving file system mounting.
* [CHANGE] Hook system. It's much faster (CPU-wise) and a little easier to use now (Many thanks for input from darkjacky and Divran).
v2.52 - *(03/09/15)*
* [ADD] Admin parameter to ULib.unban for overriding purposes (Thanks for the idea, MStruntze).
* [ADD] A list of players is presented when a target string for getUser matches more than one player (Thanks, RhapsodySL).
* [FIX] ULib.ucl.registerAccess not allowing an access tag to be registered to no groups.
* [FIX] Several incorrect file I/O calls (Thanks, Q4-Bi).
* [FIX] Hook priority being messed up for parent hook when hooks are called recursively (Thanks, NoBrainCZ).
* [FIX] Some fiddly-bits with group case-sensitivity (Thanks, BryanFlannery).
* [CHANGE] hook.Run to match Garry's changes.
* [CHANGE] ULib.HOOK_LOCALPLAYERREADY is now called on InitPostEntity instead of OnEntCreate.
v2.51 - *(08/30/13)*
* [FIX] ULib.ucl.userAllow not working on disconnected players (Thanks, JackYack13).
* [FIX] Issue with setting groups with capitals in the group name (Thanks, FPtje!).
* [FIX] Calling SetUserGroup not passing on information to clients (Thanks, Bo98).
* [FIX] Garry's File I/O bugs by wrapping all his I/O.
* [FIX] A user group lower casing that no longer belonged in the code (Thanks, iamalexer).
* [FIX] Some issues with casing in ULib commands (Thanks, TheSpy7).
* [FIX] Invalid time restrictions throwing an error (Thanks, Scratch).
* [FIX] A problem with targeting in single player (Effected XGUI. Thanks, bender180).
* [FIX] A problem with self-target restrictions breaking commands under certain conditions (Thanks, iSnipeu).
* [FIX] A bug with being able to update replicated variables after running a listen server and then joining another server.
* [REMOVED] Temp garry-patch for reading from the data directory that appears to be fixed now.
v2.50 - *(01/27/13)*
* [ADD] ULib.pcallError -- Does what global PCallError used to do before it was removed.
* [ADD] Shows reasons to kicked person upon kick or ban (Thanks FPtje!).
* [ADD] Operator to target only a specific group, ignoring inheritance ('#').
* [ADD] Operator to target a specific id ('$').
* [ADD] ULib.namedQueueFunctionCall to allow scripts to create their own queues separate of the main one.
* [ADD] The ability to have aliased chat commands.
* [FIX] The usual assortment of garry breakages.
* [FIX] Changed away from our custom implementation of datastream to use Garry's new net library.
* [FIX] Error with returning from invisibility when the player has no weapons (Thanks HellFox).
* [FIX] "ULibCommandCalled" hook not being called on chat commands (Thanks Adult).
* [CHANGE] Replicated cvars aren't actually relying on source replication anymore since Garry broke it (but they function the same).
* [CHANGE] Lots of changes to match GM13.
* [CHANGE] NumArg now allows for time string format.
* [CHANGE] Hook library to match garry's. hook.isInHook was removed, no longer able to support with garry's changes.
v2.42 - *(01/01/12)*
* [FIX] Garry breakages.
v2.41 - *(09/22/11)*
* [ADD] ULib.ucl.getUserRegisteredID.
* [ADD] ULib.stringTimeToSeconds (Thanks lavacano201014).
* [FIX] Now properly kicks users who are banned while joining (Thanks Willdy).
v2.40 - *(05/13/11)*
* [ADD] ULib.tsayColor and Ulib.tsayError
* [ADD] Replicated cvars. Nearly a direct port from the UPS implementation, with a few improvements.
* [ADD] queueFunctionCall, ported from UPS.
* [ADD] Player:GetUserGroup().
* [ADD] Player:CheckGroup(), ability to check if a user in a group via inheritance.
* [ADD] ULib.getPlyByUID().
* [ADD] ULib.clientRPC(), send massive amounts of data to a client with ease.
* [ADD] Upgrade script.
* [ADD] hook.getCurrentHooks(), returns all currently processing hooks.
* [ADD] hook.isInHook( name ), returns if you're in the specified hook or not.
* [ADD] ULib.splitPort(), ULib.isValidSteamID(), ULib.isValidIP().
* [ADD] ULib.backupFile().
* [ADD] ULib.throwBadArg(), useful for argument checking.
* [ADD] ULib.checkArg(), useful for argument checking.
* [ADD] ULib.getPicker(), returns a user directly in front of another user.
* [ADD] Utilities for table inheritance.
* [ADD] New 'translation' command system that acts as a wrapper between a user and lua.
* [ADD] New (and very different) command system.
* [ADD] Lots of new hooks.
* [ADD] Support for gatekeeper in ULib.kick.
* [ADD] Our own optimized version of datastream, since garry's implementation is always broken.
* [ADD] ULib.getAllReadyPlayers(), useful for sending usermessages to everyone.
* [ADD] Basic spam detection system for ULib commands.
* [FIX] ULib.filesInDir, was completely broken.
* [FIX] The usual assortment of garry breakages.
* [FIX] Some case-sensitive issues with the ULib add-command functions.
* [FIX] Attempting to delete misc_registered.txt when it didn't exist.
* [FIX] ULib.splitArgs now really properly handles escaped quotes and now unescapes them.
* [FIX] Concommands created by ULib removing empty args.
* [FIX] Overflowing command buffer when executing large config files.
* [FIX] Optimized various functions to support up to 4000 bans (at least!).
* [FIX] Bug where reloading ban information when a temp ban had less than a minute left made the ban permanent.
* [FIX] Bug where ULib was reading in bad characters from source bans (Thanks edk141).
* [CHANGE] Chat hooks are now a high priority due to other aggressive admin mods overriding ULX.
* [CHANGE] Rewrote UCL entirely. The upgrade script should take care of bringing over old data into the new system.
* [CHANGE] Added the ability to have access tags for each access string. These allow the accesses to have customizable behavior.
* [CHANGE] Access tags now have comments attached to them (for the "what is it?" among us).
* [CHANGE] Added lots of keywords (and keyword negation!) to ULib.getUsers and ULib.getUser.
* [CHANGE] Invisible gets rid of shadows now.
* [CHANGE] Garry's hook table spec is now more closely followed. (Thanks aVoN!)
* [CHANGE] Moved the hook changes to the shared portion so clients can use the enhanced hooks as well.
* [CHANGE] Updated the hooks file to match garry's recent changes. Also increased efficiency in hooks (faster than garry's!)
* [CHANGE] Slaps now do a view punch as well.
* [CHANGE] Allow nil access on ULib.addSayCommand so that you can create a command you always have access to.
* [CHANGE] ULib.ucl.query always returns true when a nil access string is passed in.
* [REMOVE] Ability to have passwords in UCL, don't think it worked anymore and it was never really used.
* [REMOVE] Immunity no longer exists, since the new UCL has a much better method of doing the same thing.
* [REMOVE] Some hooks due to garry breakage.
* [REMOVE] Chat sounds on tsay, engine no longer makes sounds so neither should tsay.
v2.30 - *(06/20/09)*
* [FIX] Umsgs being sent too early in certain circumstances.
* [FIX] Some issues garry introduced in the Jan09 update regarding player initialization.
* [FIX] ParseKeyValues not unescaping backslashes.
* [CHANGE] Rewrote splitArgs and parseKeyValues.
* [CHANGE] misc_registered.txt now self-destructs on missing or empty groups.txt.
* [CHANGE] All gamemode.Call refs to hook.Call, thanks aVoN!
* [CHANGE] SetUserGroup now REMOVES any other groups and sets an exclusive group. Sorry about this, but this is for the better.
v2.21 - *(06/08/08)*
* [ADD] Support for client/server-side only modules.
* [FIX] Bug in ULib.tsay that would incorrectly print to console if the target player was disconnecting.
* [FIX] Makes sure that prop protectors don't take ownership of props using physgun reload while a prop is unmovable.
* [CHANGE] ULib.getUsers now returns multiple users on an asterisk "*" when enable_keywords is true. "<ALL>" can still be used. (Thanks Kyzer)
v2.20 - *(01/26/08)*
* [ADD] ULib now has three shiny new hooks to let you know about client initialization and a new hook to signal a player name change.
* [FIX] A possible bug in the physics helpers.
* [CHANGE] Various things to bring ULib into new engine compatibility.
* [CHANGE] Removed all timers dealing with initialization and now rely on flags from the client. This makes the ULib initialization much more dependable.
* [CHANGE] Converted all calls from ULib.consoleCommand( "exec ..." ) to ULib.execFile() to avoid running into the block on "exec" without our module.
* [REMOVE] Removing the module for now, might re-appear in the next version
v2.10 - *(09/23/07)*
* [ADD] New hook library. Completely backwards compatible, but can now do priorities. (Server-side only)
* [ADD] ULib.parseKeyValues, ULib.makeKeyValues
* [ADD] ULib.getSpawnInfo, ULib.Spawn - Enhanced Spawn... will replace original health/armor when called if getSpawnInfo called first.
* [ADD] READDED hexing system to get around garry's ConCommand() blocks. So much is now blocked that it's interferring with normal ULX operations.
* [ADD] Our server module again. This time with only console-executing abilities. This is because garry has blocked much of what we need. Source is included.
* [ADD] Custom ban list to store temp bans and additional ban info. Permanent bans are still stored in banned_user.cfg, and the two lists are synchronized.
* [FIX] Can now query players from client side.
* [FIX] An exploit in DisallowDelete() that allowed players to still remove the props
* [FIX] Various initialization functions trying to access a disconnected player
* [FIX] ULib.csay() sending umsgs to invalid players.
* [FIX] UCL by clantag not working.
* [CHANGE] Big changes in ucl.query() and concommand functions. Probably won't be backwards compatible.
* [CHANGE] UCL now uses our new keyvalues functions. It should be backwards compatible with your old data, but we make no promises. If you're having trouble with it, try starting from scratch.
* [CHANGE] ULib.tsay has a wait parameter to send on next frame
* [CHANGE] subconcommands are now case insensitive
* [CHANGE] Csay's now have fade.
* [CHANGE] DisallowSpawning() now implements SpawnObject. For example, people can't sit and precache props while in the ulx jail.
* [CHANGE] Say commands are now case insensitive and default to needing a space between command and arg (can flag to use old behavior though)
* [CHANGE] ULib.ban, and ULib.kickban now accept additional information and pass data to ULib.addBan.
* [CHANGE] Immunity is now an access string instead of a group
* [CHANGE] Overcoming immunity is no longer bound to superadmins
* [CHANGE] Increased performance of UCL.
* [REMOVED] The vgui panels, derma is the vgui of choice now.
v2.05 - *(06/19/07)*
* [ADD] ply:SetUserGroup() -- Thanks aVoN!
* [ADD] ply:DisallowVehicles( bool )
* [FIX] A timer error in UCL, was messing up scoreboard sometimes.
* [FIX] Security hole where exploiters could gain superadmin access
* [CHANGE] You can assign allow/denies to the default user group, "user" now. (IE, allow guests to slap)
* [CHANGE] DisallowSpawning now disallows tools that can spawn things.
* [REMOVED] Old settings/users.txt stuff, handled by SetUserGroup now
v2.04 - *(05/05/07)*
* [ADD] ULib.isSandbox
* [ADD] Player/ent hooks DisallowMoving, DisallowDeleting, DisallowSpawning, DisallowNoclip
* [ADD] Some vgui libs (URoundButton, URoundMenu)
* [FIX] Double printing in console.
* [CHANGE] Implemented garry's "proper" way of including c-side files.
* [CHANGE] Implemented client side UCL
* [CHANGE] Now in addon format
* [CHANGE] Slapping noclipped players will take them out of noclip to prevent them flying very far out of the world
* [CHANGE] Improved the umsg send/receive functions
* [REMOVED] Hexing system to get around garry's ConCommand() blocks. Very little is blocked now.
* [REMOVED] Dll, MOTD functionality is handled by ULX now.
v2.03 - *(01/10/07)*
* [ADD] ULib module, has functions for motd, concommands, and downloading files. SOURCE CODE!
* [FIX] Player slap after dead problem.
v2.02 - *(01/07/07)*
* [ADD] New system for giving files to clients. Strips comments and puts them in a separate folder.
* [FIX] Autocompletes aren't handled so hackishly now. This should fix some occasional errors.
* [FIX] Lots of general fixes.
v2.01 - *(01/02/07)*
* [FIX] Importing from garry's default user file.
* [FIX] All users receiving "you do not have access" message.
v2.0 - *(01/01/07)*
* Initial version for GM10
Group: Developers
To all developers, I sincerely hope you enjoy what ULib has to offer!
If you have any suggestions, comments, or complaints, please tell us at <http://forums.ulyssesmod.net/>.
If you want an overview of what's in ULib, please visit the documentation at <http://ulyssesmod.net/docs/>.
If you find any bugs, you can report them at <https://github.com/Nayruden/Ulysses/issues>.
All ULib's functions are kept in the table "ULib" to prevent conflicts.
Revisions are kept in the function/variable documentation. If you don't see revisions listed, it hasn't changed since v2.0
If you write a script taking advantage of ULib, stick the init script inside ULib/modules. ULib will load your script after
ULib loads, and will send it to and load it on clients as well.
Some important quirks developers should know about --
* autocomplete - You have to define the autocomplete on the client, so if you pass a string for autocomplete to ULib.concommand,
it will assume you mean a client function. There's also a delay in the sending of these to the client.
Group: Credits
Thanks to JamminR, who is always there to offer help and advice to those who need it.
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

View File

@ -1,14 +0,0 @@
{
"title" : "ULib",
"type" : "ServerContent",
"tags" : [ "fun", "build" ],
"ignore" :
[
"addon.txt",
".editorconfig",
".git*",
"ulib.build",
"ULib_readme.txt"
],
"workshopid": 557962238
}

View File

@ -1,11 +0,0 @@
"AddonInfo"
{
"name" "ULib"
"version" "2.52d"
"up_date" "00/00/00"
"author_name" "Team Ulysses"
"author_email" "teamulysses@ulyssesmod.net"
"author_url" "http://www.ulyssesmod.net/"
"info" "Lua Library"
"override" "0"
}

View File

@ -1,6 +0,0 @@
-- Short and sweet
if SERVER then
include( "ulib/init.lua" )
else
include( "ulib/cl_init.lua" )
end

View File

@ -1,63 +0,0 @@
ULib = ULib or {} -- Init table
include( "ulib/shared/defines.lua" )
include( "ulib/shared/misc.lua" )
include( "ulib/shared/util.lua" )
include( "ulib/shared/hook.lua" )
include( "ulib/shared/tables.lua" )
include( "ulib/client/commands.lua" )
include( "ulib/shared/messages.lua" )
include( "ulib/shared/player.lua" )
include( "ulib/client/cl_util.lua" )
include( "ulib/client/draw.lua" )
include( "ulib/shared/commands.lua" )
include( "ulib/shared/sh_ucl.lua" )
include( "ulib/shared/cami_global.lua" )
include( "ulib/shared/cami_ulib.lua" )
Msg( string.format( "You are running ULib version %.2f.\n", ULib.VERSION ) )
--Shared modules
local files = file.Find( "ulib/modules/*.lua", "LUA" )
if #files > 0 then
for _, file in ipairs( files ) do
Msg( "[ULIB] Loading SHARED module: " .. file .. "\n" )
include( "ulib/modules/" .. file )
end
end
--Client modules
local files = file.Find( "ulib/modules/client/*.lua", "LUA" )
if #files > 0 then
for _, file in ipairs( files ) do
Msg( "[ULIB] Loading CLIENT module: " .. file .. "\n" )
include( "ulib/modules/client/" .. file )
end
end
local needs_auth = {}
local function onEntCreated( ent )
if ent:IsPlayer() and needs_auth[ ent:UserID() ] then
hook.Call( ULib.HOOK_UCLAUTH, _, ent ) -- Because otherwise the server might call this before the player is created
needs_auth[ ent:UserID() ] = nil
end
end
hook.Add( "OnEntityCreated", "ULibPlayerAuthCheck", onEntCreated, HOOK_MONITOR_HIGH ) -- Listen for player creations
local function onInitPostEntity()
if LocalPlayer():IsValid() then
hook.Call( ULib.HOOK_LOCALPLAYERREADY, _, LocalPlayer() )
RunConsoleCommand( "ulib_cl_ready" )
end
end
hook.Add( "InitPostEntity", "ULibLocalPlayerReady", onInitPostEntity, HOOK_MONITOR_HIGH ) -- Flag server when LocalPlayer() should be valid
-- We're trying to make sure that the player auths after the player object is created, this function is part of that check
function authPlayerIfReady( ply, userid )
if ply and ply:IsValid() then
hook.Call( ULib.HOOK_UCLAUTH, _, ply ) -- Call hook
else
needs_auth[ userid ] = true
end
end

View File

@ -1,186 +0,0 @@
--[[
Title: Utilities
Some client-side utilties
]]
local function ULibRPC()
local fn_string = net.ReadString()
local args = net.ReadTable()
local fn = ULib.findVar( fn_string )
if type( fn ) ~= "function" then return error( "Received bad RPC, invalid function (" .. tostring( fn_string ) .. ")!" ) end
-- Since the table length operator can't always be trusted if there are holes in it, find the length by ourself
local max = 0
for k, v in pairs( args ) do
local n = tonumber( k )
if n and n > max then
max = n
end
end
fn( unpack( args, 1, max ) )
end
net.Receive( "URPC", ULibRPC )
function ULib.getVersionData( build, workshop )
ULib.build = build
ULib.usingWorkshop = workshop
end
--[[
Function: getVersion
TODO
]]
function ULib.getVersion()
local versionStr
local build = ULib.build
local usingWorkshop = ULib.usingWorkshop
if ULib.RELEASE then
versionStr = string.format( "v%.02f", ULib.VERSION )
elseif usingWorkshop then
versionStr = string.format( "v%.02fw", ULib.VERSION )
elseif build then -- It's not release and it's not workshop
versionStr = string.format( "v%.02fd (%s)", ULib.VERSION, os.date( "%x", build ) )
else -- Not sure what this version is, but it's not a release
versionStr = string.format( "v%.02fd", ULib.VERSION )
end
return versionStr, ULib.VERSION, build, usingWorkshop
end
--[[
Function: umsgRcv
Receive a umsg sent by ULib.umsgSend
Parameters:
um - The user message object
Returns:
The variable from the umsg.
]]
function ULib.umsgRcv( um, control )
local tv = control or um:ReadChar()
local ret -- Our return value
if tv == ULib.TYPE_STRING then
ret = um:ReadString()
elseif tv == ULib.TYPE_FLOAT then
ret = um:ReadFloat()
elseif tv == ULib.TYPE_SHORT then
ret = um:ReadShort()
elseif tv == ULib.TYPE_LONG then
ret = um:ReadLong()
elseif tv == ULib.TYPE_BOOLEAN then
ret = um:ReadBool()
elseif tv == ULib.TYPE_ENTITY then
ret = um:ReadEntity()
elseif tv == ULib.TYPE_VECTOR then
ret = um:ReadVector()
elseif tv == ULib.TYPE_ANGLE then
ret = um:ReadAngle()
elseif tv == ULib.TYPE_CHAR then
ret = um:ReadChar()
elseif tv == ULib.TYPE_TABLE_BEGIN then
ret = {}
while true do -- Yes an infite loop. We have a break inside.
local key = ULib.umsgRcv( um )
if key == nil then break end -- Here's our break
ret[ key ] = ULib.umsgRcv( um )
end
elseif tv == ULib.TYPE_TABLE_END then
return nil
elseif tv == ULib.TYPE_NIL then
return nil
else
ULib.error( "Unknown type passed to umsgRcv - " .. tv )
end
return ret
end
-- This will play sounds client side
local function rcvSound( um )
local str = um:ReadString()
if not ULib.fileExists( "sound/" .. str ) then
Msg( "[LC ULib ERROR] Received invalid sound\n" )
return
end
if LocalPlayer():IsValid() then
LocalPlayer():EmitSound( Sound( str ) )
end
end
usermessage.Hook( "ulib_sound", rcvSound )
local cvarinfo = {} -- Stores the client cvar object indexed by name of the server cvar
local reversecvar = {} -- Stores the name of server cvars indexed by the client cvar
-- When our client side cvar is changed, notify the server to change it's cvar too.
local function clCvarChanged( cl_cvar, oldvalue, newvalue )
if not reversecvar[ cl_cvar ] then -- Error
return
elseif reversecvar[ cl_cvar ].ignore then -- ignore
reversecvar[ cl_cvar ].ignore = nil
return
end
local sv_cvar = reversecvar[ cl_cvar ].sv_cvar
RunConsoleCommand( "ulib_update_cvar", sv_cvar, newvalue )
end
-- This is the counterpart to <replicatedWithWritableCvar>. See that function for more info. We also add callbacks from here.
local function readCvar( um )
local sv_cvar = um:ReadString()
local cl_cvar = um:ReadString()
local default_value = um:ReadString()
local current_value = um:ReadString()
cvarinfo[ sv_cvar ] = GetConVar( cl_cvar ) or CreateClientConVar( cl_cvar, default_value, false, false ) -- Make sure it's created one way or another (second case is most common)
reversecvar[ cl_cvar ] = { sv_cvar=sv_cvar }
ULib.queueFunctionCall( function() -- Queued to ensure we don't overload the client console
hook.Call( ULib.HOOK_REPCVARCHANGED, _, sv_cvar, cl_cvar, nil, nil, current_value )
if cvarinfo[ sv_cvar ]:GetString() ~= current_value then
reversecvar[ cl_cvar ].ignore = true -- Flag so hook doesn't do anything. Flag is removed at hook.
RunConsoleCommand( cl_cvar, current_value )
end
end )
cvars.AddChangeCallback( cl_cvar, clCvarChanged )
end
usermessage.Hook( "ulib_repWriteCvar", readCvar )
-- This is called when they've attempted to change a cvar they don't have access to.
local function changeCvar( um )
local ply = um:ReadEntity()
local cl_cvar = um:ReadString()
local oldvalue = um:ReadString()
local newvalue = um:ReadString()
local changed = oldvalue ~= newvalue
if not reversecvar[ cl_cvar ] then -- Error!
return
end
local sv_cvar = reversecvar[ cl_cvar ].sv_cvar
ULib.queueFunctionCall( function() -- Queued so we won't overload the client console and so that changes are always going to be called via the hook AFTER the initial hook is called
if changed then
hook.Call( ULib.HOOK_REPCVARCHANGED, _, sv_cvar, cl_cvar, ply, oldvalue, newvalue )
end
if GetConVarString( cl_cvar ) ~= newvalue then
reversecvar[ cl_cvar ].ignore = true -- Flag so hook doesn't do anything. Flag is removed at hook.
RunConsoleCommand( cl_cvar, newvalue)
end
end )
end
usermessage.Hook( "ulib_repChangeCvar", changeCvar )

View File

@ -1,16 +0,0 @@
--[[
Function: redirect
This redirects any command to the server.
*DO NOT CALL DIRECTLY*
Parameters:
ply - The player using the command
command - The command being used
argv - The table of arguments
]]
function ULib.redirect( ply, command, argv )
RunConsoleCommand( "_u", command, unpack( argv ) )
end

View File

@ -1,54 +0,0 @@
--[[
Title: Draw
Our client-side draw functions
]]
--[[
Function: csayDraw
Draws a csay text on the screen.
Parameters:
msg - The message to draw.
color - *(Optional, defaults to 255, 255, 255, 255)* The color of the text
duration - *(Optional, defaults to 5)* The length of the text
fade - *(Optional, defaults to 0.5)* The length of fade time
Revisions:
v2.10 - Added fade parameter
]]
function ULib.csayDraw( msg, color, duration, fade )
color = color or Color( 255, 255, 255, 255 )
duration = duration or 5
fade = fade or 0.5
local start = CurTime()
local function drawToScreen()
local alpha = 255
local dtime = CurTime() - start
if dtime > duration then -- Our time has come :'(
hook.Remove( "HUDPaint", "CSayHelperDraw" )
return
end
if fade - dtime > 0 then -- beginning fade
alpha = (fade - dtime) / fade -- 0 to 1
alpha = 1 - alpha -- Reverse
alpha = alpha * 255
end
if duration - dtime < fade then -- ending fade
alpha = (duration - dtime) / fade -- 0 to 1
alpha = alpha * 255
end
color.a = alpha
draw.DrawText( msg, "TargetID", ScrW() * 0.5, ScrH() * 0.25, color, TEXT_ALIGN_CENTER )
end
hook.Add( "HUDPaint", "CSayHelperDraw", drawToScreen )
end

View File

@ -1,100 +0,0 @@
if not ULib then
ULib = {}
-- For historical purposes
if not ULib.consoleCommand then ULib.consoleCommand = game.ConsoleCommand end
file.CreateDir( "ulib" )
Msg( "///////////////////////////////\n" )
Msg( "// Ulysses Library //\n" )
Msg( "///////////////////////////////\n" )
Msg( "// Loading... //\n" )
Msg( "// shared/defines.lua //\n" )
include( "ulib/shared/defines.lua" )
Msg( "// shared/misc.lua //\n" )
include( "ulib/shared/misc.lua" )
Msg( "// shared/util.lua //\n" )
include( "ulib/shared/util.lua" )
Msg( "// shared/hook.lua //\n" )
include( "ulib/shared/hook.lua" )
Msg( "// shared/table.lua //\n" )
include( "ulib/shared/tables.lua" )
Msg( "// shared/player.lua //\n" )
include( "ulib/shared/player.lua" )
Msg( "// server/player.lua //\n" )
include( "ulib/server/player.lua" )
Msg( "// shared/messages.lua //\n" )
include( "ulib/shared/messages.lua" )
Msg( "// shared/commands.lua //\n" )
include( "ulib/shared/commands.lua" )
Msg( "// server/concommand.lua //\n" )
include( "ulib/server/concommand.lua" )
Msg( "// server/util.lua //\n" )
include( "ulib/server/util.lua" )
Msg( "// shared/sh_ucl.lua //\n" )
include( "ulib/shared/sh_ucl.lua" )
Msg( "// server/ucl.lua //\n" )
include( "ulib/server/ucl.lua" )
Msg( "// server/phys.lua //\n" )
include( "ulib/server/phys.lua" )
Msg( "// server/player_ext.lua //\n" )
include( "server/player_ext.lua" )
Msg( "// server/entity_ext.lua //\n" )
include( "server/entity_ext.lua" )
Msg( "// shared/cami_global.lua //\n" )
include( "shared/cami_global.lua" )
Msg( "// shared/cami_ulib.lua //\n" )
include( "shared/cami_ulib.lua" )
Msg( "// Load Complete! //\n" )
Msg( "///////////////////////////////\n" )
AddCSLuaFile( "ulib/cl_init.lua" )
AddCSLuaFile( "autorun/ulib_init.lua" )
local folder = "ulib/shared"
local files = file.Find( folder .. "/" .. "*.lua", "LUA" )
for _, file in ipairs( files ) do
AddCSLuaFile( folder .. "/" .. file )
end
folder = "ulib/client"
files = file.Find( folder .. "/" .. "*.lua", "LUA" )
for _, file in ipairs( files ) do
AddCSLuaFile( folder .. "/" .. file )
end
--Shared modules
local files = file.Find( "ulib/modules/*.lua", "LUA" )
if #files > 0 then
for _, file in ipairs( files ) do
Msg( "[ULIB] Loading SHARED module: " .. file .. "\n" )
include( "ulib/modules/" .. file )
AddCSLuaFile( "ulib/modules/" .. file )
end
end
--Server modules
local files = file.Find( "ulib/modules/server/*.lua", "LUA" )
if #files > 0 then
for _, file in ipairs( files ) do
Msg( "[ULIB] Loading SERVER module: " .. file .. "\n" )
include( "ulib/modules/server/" .. file )
end
end
--Client modules
local files = file.Find( "ulib/modules/client/*.lua", "LUA" )
if #files > 0 then
for _, file in ipairs( files ) do
Msg( "[ULIB] Loading CLIENT module: " .. file .. "\n" )
AddCSLuaFile( "ulib/modules/client/" .. file )
end
end
local function clReady( ply )
ply.ulib_ready = true
hook.Call( ULib.HOOK_LOCALPLAYERREADY, _, ply )
end
concommand.Add( "ulib_cl_ready", clReady ) -- Called when the c-side player object is ready
end

View File

@ -1,3 +0,0 @@
This folder is similar to the lua/autorun folder, except all scripts in this folder are loaded after ULib.
Scripts in this directory are shared ( loaded both server and client side ).
Scripts in the client and server sub-folders are loaded only on the client and server respectively.

View File

@ -1,117 +0,0 @@
--[[
Title: Concommand Helpers
Server-side compliment of the shared commands.lua
]]
--[[
Table: sayCmds
This table holds our say commands.
]]
ULib.sayCmds = ULib.sayCmds or {}
--[[
Function: sayCmdCheck
Say callback which will check to see if there's a say command being used. *DO NOT CALL DIRECTLY*
Parameters:
ply - The player.
strText - The text.
bTeam - Team say.
Revisions:
v2.10 - Made case-insensitive
]]
local function sayCmdCheck( ply, strText, bTeam )
local match
for str, data in pairs( ULib.sayCmds ) do
local str2 = str
if strText:len() < str:len() then -- Go ahead and allow commands w/o spaces
str2 = string.Trim( str )
end
if strText:sub( 1, str2:len() ):lower() == str2 then
if not match or match:len() <= str:len() then -- Don't rematch if there's a more specific one already.
match = str
end
end
end
if match then -- We've got a winner!
local data = ULib.sayCmds[ match ]
local args = string.Trim( strText:sub( match:len() + 1 ) ) -- Strip the caller command out
local argv = ULib.splitArgs( args )
-- ULib command callback
if data.__cmd then
local return_value = hook.Call( ULib.HOOK_COMMAND_CALLED, _, ply, data.__cmd, argv )
if return_value == false then
return nil
end
end
if not ULib.ucl.query( ply, data.access ) then
ULib.tsay( ply, "You do not have access to this command, " .. ply:Nick() .. "." )
-- Print their name to intimidate them :)
return "" -- Block from appearing
end
local fn = data.fn
local hide = data.hide
ULib.pcallError( fn, ply, match:Trim(), argv, args )
if hide then return "" end
end
return nil
end
hook.Add( "PlayerSay", "ULib_saycmd", sayCmdCheck, HOOK_HIGH ) -- High-priority
--[[
Function: addSayCommand
Just like ULib's <concommand()> except that the callback is called when the command is said in chat instead of typed in the console.
Parameters:
say_cmd - A command string for says. IE: "!kick", then when someone says "!kick", it'll call the callback.
fn_call - The function to call when the command's called.
access - The access string to associate access with this say command. (IE: "ulx kick"). Remember to call <ULib.ucl.registerAccess()> if the access string isn't being used in a command.
hide_say - *(Optional, defaults to false)* If true, will hide the chat message. Use this if you don't want other people to see the command.
nospace - *(Optional, defaults to false)* If true, a space won't be required after the command "IE: !slapbob" vs "!slap bob".
Revisions:
v2.10 - Added nospace parameter, made case insensitive
v2.40 - Removed the command help parameter, now accepts nil as access (for always allowed)
]]
function ULib.addSayCommand( say_cmd, fn_call, access, hide_say, nospace )
say_cmd = string.Trim( say_cmd:lower() )
if not nospace then
say_cmd = say_cmd .. " "
end
ULib.sayCmds[ say_cmd ] = { fn=fn_call, hide=hide_say, access=access }
end
--[[
Function: removeSayCommand
Removes a say command.
Parameters:
say_cmd - The command string for says to remove.
]]
function ULib.removeSayCommand( say_cmd )
ULib.sayCmds[ say_cmd ] = nil -- Remove both forms
ULib.sayCmds[ say_cmd .. " " ] = nil
end

View File

@ -1,192 +0,0 @@
local meta = FindMetaTable( "Entity" )
-- Return if there's nothing to add on to
if not meta then return end
-- Are you a STOOL author who's angry that your tool isn't on this list?
-- Just add this to your code:
-- if ULib then table.insert( ULib.delWhiteList, "my_stool" ) end
ULib.delWhitelist = -- White list for objects that can't be deleted
{
"colour",
"material",
"paint",
"hoverball",
"emitter",
"elastic",
"hydraulic",
"muscle",
"nail",
"ballsocket",
"ballsocket_adv",
"pulley",
"rope",
"slider",
"weld",
"winch",
"balloon",
"button",
"duplicator",
"dynamite",
"keepupright",
"lamp",
"nocollide",
"thruster",
"turret",
"wheel",
"eyeposer",
"faceposer",
"statue",
"weld_ez",
"axis",
-- Properties
"gravity",
"collision",
--"keepupright", -- Already above
"persist",
}
-- Are you a STOOL author who's angry that your tool isn't on this list?
-- Just add this to your code:
-- if ULib then table.insert( ULib.moveWhiteList, "my_stool" ) end
ULib.moveWhitelist = -- White list for objects that can't be moved
{
"colour",
"material",
"paint",
"duplicator",
"eyeposer",
"faceposer",
"remover",
-- Properties
--"remover", -- Already above
"persist",
}
function meta:DisallowMoving( bool )
self.NoMoving = bool
end
function meta:DisallowDeleting( bool, callback, no_replication )
self.NoDeleting = bool
self.NoDeletingCallback = callback
self.NoReplication = no_replication
end
local function tool( ply, tr, toolmode, second )
-- In the case of the nail gun, let's check the entity they're nailing TO first.
if toolmode == "nail" and not second then
local tr2 = {}
tr2.start = tr.HitPos
tr2.endpos = tr.HitPos + ply:GetAimVector() * 16
tr2.filter = { ply, tr.Entity }
local trace = util.TraceLine( tr2 )
if trace.Entity and trace.Entity:IsValid() and not trace.Entity:IsPlayer() then
local ret = tool( ply, trace, toolmode, true )
if ret ~= nil then
return ret
end
end
end
-- In the case of the remover, we have to make sure they're not trying to right click remove one of no delete ents
if toolmode == "remover" and ply:KeyDown( IN_ATTACK2 ) and not ply:KeyDownLast( IN_ATTACK2 ) then
local ConstrainedEntities = constraint.GetAllConstrainedEntities( tr.Entity )
if ConstrainedEntities then -- If we have anything to worry about
-- Loop through all the entities in the system
for _, ent in pairs( ConstrainedEntities ) do
if ent.NoDeleting then
ULib.tsay( ply, "You cannot use a right click delete on this ent because it is constrained to a non-deleteable entity." )
return false
end
end
end
end
if tr.Entity.NoMoving then
if not table.HasValue( ULib.moveWhitelist, toolmode ) then
return false
end
end
if tr.Entity.NoDeleting then
if not table.HasValue( ULib.delWhitelist, toolmode ) then
return false
end
end
end
hook.Add( "CanTool", "ULibEntToolCheck", tool, HOOK_HIGH )
local function property( ply, propertymode, ent )
if ent.NoMoving then
if not table.HasValue( ULib.moveWhitelist, toolmode ) then
return false
end
end
if ent.NoDeleting then
if not table.HasValue( ULib.delWhitelist, toolmode ) then
return false
end
end
end
hook.Add( "CanProperty", "ULibEntPropertyCheck", property, HOOK_HIGH )
local function physgun( ply, ent )
if ent.NoMoving then return false end
end
hook.Add( "PhysgunPickup", "ULibEntPhysCheck", physgun, HOOK_HIGH )
hook.Add( "CanPlayerUnfreeze", "ULibEntUnfreezeCheck", physgun, HOOK_HIGH )
local function physgunReload( weapon, ply )
local trace = util.GetPlayerTrace( ply )
local tr = util.TraceLine( trace )
local ent = tr.Entity
if not ent or not ent:IsValid() or ent:IsWorld() then return end -- Invalid or not interested
if ent.NoMoving then return false end
end
hook.Add( "OnPhysgunReload", "ULibEntPhysReloadCheck", physgunReload, HOOK_HIGH )
local function damageCheck( ent )
if ent.NoDeleting then
-- return false
end
end
hook.Add( "EntityTakeDamage", "ULibEntDamagedCheck", damageCheck, HOOK_MONITOR_HIGH )
-- This is just in case we have some horribly programmed addon that goes rampant in deleting things
local function removedCheck( ent )
if ent.NoDeleting and not ent.NoReplication then
local class = ent:GetClass()
local pos = ent:GetPos()
local ang = ent:GetAngles()
local model = ent:GetModel()
local frozen = false
if ent:GetPhysicsObject():IsValid() and not ent:GetPhysicsObject():IsMoveable() then
frozen = true
end
local t = ent:GetTable()
ULib.queueFunctionCall( function() -- Create it next frame because 1. Old ent won't be in way and 2. We won't overflow the server while shutting down
local ent2 = ents.Create( class )
table.Merge( ent2:GetTable(), t )
ent2:SetModel( model )
ent2:SetPos( pos )
ent2:SetAngles( ang )
ent2:Spawn()
if frozen then
ent2:GetPhysicsObject():EnableMotion( false )
end
if ent2.NoDeletingCallback then
ent2.NoDeletingCallback( ent, ent2 )
end
end )
end
end
hook.Add( "EntityRemoved", "ULibEntRemovedCheck", removedCheck, HOOK_MONITOR_HIGH )

View File

@ -1,115 +0,0 @@
--[[
Title: Physics Helpers
Various functions to make dealing with the HL2 physics engine a little easier.
]]
--[[
Function: applyAccel
Parameters:
ent - The entity to apply the acceleration to
magnitude - The amount of acceleration ( Use nil if the magnitude is specified in the direction )
direction - The direction to apply the acceleration in ( if the magnitude is part of the direction, specify nil for the magnitude )
dTime - *(Optional, defaults to 1)* The time passed since the last update in seconds ( IE: 0.5 for dTime would only apply half the acceleration )
]]
function ULib.applyAccel( ent, magnitude, direction, dTime )
if dTime == nil then dTime = 1 end
if magnitude ~= nil then
direction:Normalize()
else
magnitude = 1
end
-- Times it by the time elapsed since the last update.
local accel = magnitude * dTime
-- Convert our scalar accel to a vector accel
accel = direction * accel
if ent:GetMoveType() == MOVETYPE_VPHYSICS then
-- a = f/m , so times by mass to get the force.
local force = accel * ent:GetPhysicsObject():GetMass()
ent:GetPhysicsObject():ApplyForceCenter( force )
else
ent:SetVelocity( accel ) -- As it turns out, SetVelocity() is actually SetAccel() in GM10
end
end
--[[
Function: applyForce
Parameters:
ent - The entity to apply the force to
magnitude - The amount of force ( Use nil if the magnitude is specified in the direction )
direction - The direction to apply the force in ( if the magnitude is part of the direction, specify nil for the magnitude )
dTime - *(Optional, defaults to 1)* The time passed since the last update in seconds ( IE: 0.5 for dTime would only apply half the force )
]]
function ULib.applyForce( ent, magnitude, direction, dTime )
if dTime == nil then dTime = 1 end
if magnitude ~= nil then
direction:Normalize()
else
magnitude = 1
end
-- Times it by the time elapsed since the last update.
local force = magnitude * dTime
-- Convert our scalar force to a vector force
force = direction * force
if ent:GetMoveType() == MOVETYPE_VPHYSICS then
ent:GetPhysicsObject():ApplyForceCenter( force )
else
-- Because we're not dealing with objects that have vphysics, they might not have a mass. This would cause errors, let's catch them here.
local mass = ent:GetPhysicsObject():GetMass()
if not mass then
mass = 1
Msg( "applyForce was called with a non-physics entity that doesn't have a mass. To continue calculations, we're assuming it has a mass of one. This could very well produce unrealistic looking physics!\n")
end
-- f = m*a, so divide it by mass to get the accel
local accel = force * 1/mass
ent:SetVelocity( accel ) -- As it turns out, SetVelocity() is actually SetAccel() in GM10
end
end
--[[
Function: applyAccelInCurDirection
Applies an acceleration in the entities current *velocity* direction ( not the entity's heading ). See <applyAccel>.
Basically makes the entity go faster or slower ( if a negative magnitude is passed ).
Parameters:
ent - The entity to apply the force to
magnitude - The amount of acceleration
dTime - *(Optional, defaults to 1)* The time passed since the last update in seconds ( IE: 0.5 for dTime would only apply half the acceleration )
]]
function ULib.applyAccelInCurDirection( ent, magnitude, dTime )
local direction = ent:GetVelocity( entid ):GetNormalized()
ULib.applyAccel( entid, magnitude, direction, dTime )
end
--[[
Function: applyForceInCurDirection
Applies a force in the entities current *velocity* direction ( not the entity's heading ). See <applyForce>.
Basically makes the entity go faster or slower ( if a negative magnitude is passed ).
Parameters:
ent - The entity to apply the force to
magnitude - The amount of force
dTime - *(Optional, defaults to 1)* The time passed since the last update in seconds ( IE: 0.5 for dTime would only apply half the force )
]]
function ULib.applyForceInCurDirection( ent, magnitude, dTime )
local direction = ent:GetVelocity( entid ):GetNormalized()
ULib.applyForce( entid, magnitude, direction, dTime )
end

View File

@ -1,491 +0,0 @@
--[[
Title: Player
Holds some helpful player functions.
]]
--[[
Table: slapSounds
These are the sounds used for slaps.
]]
local slapSounds = {
"physics/body/body_medium_impact_hard1.wav",
"physics/body/body_medium_impact_hard2.wav",
"physics/body/body_medium_impact_hard3.wav",
"physics/body/body_medium_impact_hard5.wav",
"physics/body/body_medium_impact_hard6.wav",
"physics/body/body_medium_impact_soft5.wav",
"physics/body/body_medium_impact_soft6.wav",
"physics/body/body_medium_impact_soft7.wav",
}
--[[
Function: slap
Slaps an entity, can be a user or any entity.
Parameters:
ent - The target ent.
damage - *(Optional, defaults to 0)* The amount of damage to inflict on the entity.
power - *(Optional, defaults to 30)* The power of the slap.
nosound - *(Optional, defaults to false)* If true, no sound will be played.
]]
function ULib.slap( ent, damage, power, nosound )
if ent:GetMoveType() == MOVETYPE_OBSERVER then return end -- Nothing we can do.
damage = damage or 0
power = power or 500
if ent:IsPlayer() then
if not ent:Alive() then
return -- Nothing we can do.
end
if ent:InVehicle() then
ent:ExitVehicle()
end
if ent:GetMoveType() == MOVETYPE_NOCLIP then
ent:SetMoveType( MOVETYPE_WALK )
end
end
if not nosound then -- Play a slap sound
local sound_num = math.random( #slapSounds ) -- Choose at random
ent:EmitSound( slapSounds[ sound_num ] )
end
local direction = Vector( math.random( 20 )-10, math.random( 20 )-10, math.random( 20 )-5 ) -- Make it random, slightly biased to go up.
ULib.applyAccel( ent, power, direction )
local angle_punch_pitch = math.Rand( -20, 20 )
local angle_punch_yaw = math.sqrt( 20*20 - angle_punch_pitch * angle_punch_pitch )
if math.random( 0, 1 ) == 1 then
angle_punch_yaw = angle_punch_yaw * -1
end
ent:ViewPunch( Angle( angle_punch_pitch, angle_punch_yaw, 0 ) )
local newHp = ent:Health() - damage
if newHp <= 0 then
if ent:IsPlayer() then
ent:Kill()
else
ent:Fire( "break", 1, 0 )
end
return
end
ent:SetHealth( newHp )
end
--[[
Function: kick
Kicks a user.
Parameters:
ply - The player to kick.
reason - *(Optional)* The reason to give for kicking.
]]
function ULib.kick( ply, reason, calling_ply )
if reason and calling_ply ~= nil then
local nick = calling_ply:IsValid() and string.format( "%s(%s)", calling_ply:Nick(), calling_ply:SteamID() ) or "Console"
ply:Kick( string.format( "Kicked by %s (%s)", nick, reason or "[ULX] Kicked from server" ) )
else
ply:Kick( reason or "[ULX] Kicked from server" )
end
end
--[[
Function: ban
Bans a user.
Parameters:
ply - The player to ban.
time - *(Optional)* The time in minutes to ban the person for, leave nil or 0 for permaban.
reason - *(Optional)* The reason for banning
admin - *(Optional)* Admin player enacting ban
Revisions:
v2.10 - Added support for custom ban list
]]
function ULib.ban( ply, time, reason, admin )
if not time or type( time ) ~= "number" then
time = 0
end
ULib.addBan( ply:SteamID(), time, reason, ply:Name(), admin )
-- Load our currently banned users so we don't overwrite them
if ULib.fileExists( "cfg/banned_user.cfg" ) then
ULib.execFile( "cfg/banned_user.cfg" )
end
end
--[[
Function: kickban
Kicks and bans a user.
Parameters:
ply - The player to ban.
time - *(Optional)* The time in minutes to ban the person for, leave nil or 0 for permaban.
reason - *(Optional)* The reason for banning
admin - *(Optional)* Admin player enacting ban
Revisions:
v2.10 - Added support for custom ban list
]]
function ULib.kickban( ply, time, reason, admin )
if not time or type( time ) ~= "number" then
time = 0
end
ULib.addBan( ply:SteamID(), time, reason, ply:Name(), admin )
-- Load our currently banned users so we don't overwrite them
if ULib.fileExists( "cfg/banned_user.cfg" ) then
ULib.execFile( "cfg/banned_user.cfg" )
end
end
--[[
Function: addBan
Helper function to store additional data about bans.
Parameters:
steamid - Banned player's steamid
time - Length of ban
reason - *(Optional)* Reason for banning
name - *(Optional)* Name of player banned
admin - *(Optional)* Admin player enacting the ban
Revisions:
2.10 - Initial
2.40 - If the steamid is connected, kicks them with the reason given
]]
function ULib.addBan( steamid, time, reason, name, admin )
local strTime = time ~= 0 and string.format( "for %s minute(s)", time ) or "permanently"
local showReason = string.format( "Banned %s: %s", strTime, reason )
local players = player.GetAll()
for i=1, #players do
if players[ i ]:SteamID() == steamid then
ULib.kick( players[ i ], showReason, admin )
end
end
-- Remove all semicolons from the reason to prevent command injection
showReason = string.gsub(showReason, ";", "")
-- This redundant kick code is to ensure they're kicked -- even if they're joining
game.ConsoleCommand( string.format( "kickid %s %s\n", steamid, showReason or "" ) )
game.ConsoleCommand( string.format( "banid %f %s kick\n", time, steamid ) )
game.ConsoleCommand( "writeid\n" )
local admin_name
if admin then
admin_name = "(Console)"
if admin:IsValid() then
admin_name = string.format( "%s(%s)", admin:Name(), admin:SteamID() )
end
end
local t = {}
if ULib.bans[ steamid ] then
t = ULib.bans[ steamid ]
t.modified_admin = admin_name
t.modified_time = os.time()
else
t.admin = admin_name
end
t.time = t.time or os.time()
if time > 0 then
t.unban = ( ( time * 60 ) + os.time() )
else
t.unban = 0
end
if reason then
t.reason = reason
end
if name then
t.name = name
end
ULib.bans[ steamid ] = t
ULib.fileWrite( ULib.BANS_FILE, ULib.makeKeyValues( ULib.bans ) )
end
--[[
Function: unban
Unbans the given steamid.
Parameters:
steamid - The steamid to unban.
admin - *(Optional)* Admin player unbanning steamid
Revisions:
v2.10 - Initial
]]
function ULib.unban( steamid, admin )
--Default banlist
if ULib.fileExists( "cfg/banned_user.cfg" ) then
ULib.execFile( "cfg/banned_user.cfg" )
end
ULib.queueFunctionCall( game.ConsoleCommand, "removeid " .. steamid .. ";writeid\n" ) -- Execute after done loading bans
--ULib banlist
ULib.bans[ steamid ] = nil
ULib.fileWrite( ULib.BANS_FILE, ULib.makeKeyValues( ULib.bans ) )
end
local function doInvis()
local players = player.GetAll()
local remove = true
for _, player in ipairs( players ) do
local t = player:GetTable()
if t.invis then
remove = false
if player:Alive() and player:GetActiveWeapon():IsValid() then
if player:GetActiveWeapon() ~= t.invis.wep then
if t.invis.wep and IsValid( t.invis.wep ) then -- If changed weapon, set the old weapon to be visible.
t.invis.wep:SetRenderMode( RENDERMODE_NORMAL )
t.invis.wep:Fire( "alpha", 255, 0 )
t.invis.wep:SetMaterial( "" )
end
t.invis.wep = player:GetActiveWeapon()
ULib.invisible( player, true, t.invis.vis )
end
end
end
end
if remove then
hook.Remove( "Think", "InvisThink" )
end
end
--[[
Function: invisible
Makes a user invisible
Parameters:
ply - The player to affect.
bool - Whether they're invisible or not
visibility - *(Optional, defaults to 0)* A number from 0 to 255 for their visibility.
Revisions:
v2.40 - Removes shadow when invisible
]]
function ULib.invisible( ply, bool, visibility )
if not ply:IsValid() then return end -- This is called on a timer so we need to verify they're still connected
if bool then
visibility = visibility or 0
ply:DrawShadow( false )
ply:SetMaterial( "models/effects/vol_light001" )
ply:SetRenderMode( RENDERMODE_TRANSALPHA )
ply:Fire( "alpha", visibility, 0 )
ply:GetTable().invis = { vis=visibility, wep=ply:GetActiveWeapon() }
if IsValid( ply:GetActiveWeapon() ) then
ply:GetActiveWeapon():SetRenderMode( RENDERMODE_TRANSALPHA )
ply:GetActiveWeapon():Fire( "alpha", visibility, 0 )
ply:GetActiveWeapon():SetMaterial( "models/effects/vol_light001" )
if ply:GetActiveWeapon():GetClass() == "gmod_tool" then
ply:DrawWorldModel( false ) -- tool gun has problems
else
ply:DrawWorldModel( true )
end
end
hook.Add( "Think", "InvisThink", doInvis )
else
ply:DrawShadow( true )
ply:SetMaterial( "" )
ply:SetRenderMode( RENDERMODE_NORMAL )
ply:Fire( "alpha", 255, 0 )
local activeWeapon = ply:GetActiveWeapon()
if IsValid( activeWeapon ) then
activeWeapon:SetRenderMode( RENDERMODE_NORMAL )
activeWeapon:Fire( "alpha", 255, 0 )
activeWeapon:SetMaterial( "" )
end
ply:GetTable().invis = nil
end
end
--[[
Function: refreshBans
Refreshes the ULib bans.
]]
function ULib.refreshBans()
local err
if not ULib.fileExists( ULib.BANS_FILE ) then
ULib.bans = {}
else
ULib.bans, err = ULib.parseKeyValues( ULib.fileRead( ULib.BANS_FILE ) )
end
if err then
Msg( "Bans file was not formatted correctly. Attempting to fix and backing up original\n" )
if err then
Msg( "Error while reading bans file was: " .. err .. "\n" )
end
Msg( "Original file was backed up to " .. ULib.backupFile( ULib.BANS_FILE ) .. "\n" )
ULib.bans = {}
end
local default_bans = ""
if ULib.fileExists( "cfg/banned_user.cfg" ) then
ULib.execFile( "cfg/banned_user.cfg" )
ULib.queueFunctionCall( game.ConsoleCommand, "writeid\n" )
default_bans = ULib.fileRead( "cfg/banned_user.cfg" )
end
--default_bans = ULib.makePatternSafe( default_bans )
default_bans = string.gsub( default_bans, "banid %d+ ", "" )
default_bans = string.Explode( "\n", default_bans:gsub( "\r", "" ) )
local ban_set = {}
for _, v in pairs( default_bans ) do
if v ~= "" then
ban_set[ v ] = true
if not ULib.bans[ v ] then
ULib.bans[ v ] = { unban = 0 }
end
end
end
for k, v in pairs( ULib.bans ) do
if type( v ) == "table" and type( k ) == "string" then
local time = ( v.unban - os.time() ) / 60
if time > 0 then
game.ConsoleCommand( string.format( "banid %f %s\n", time, k ) )
elseif math.floor( v.unban ) == 0 then -- We floor it because GM10 has floating point errors that might make it be 0.1e-20 or something dumb.
if not ban_set[ k ] then
ULib.bans[ k ] = nil
end
else
ULib.bans[ k ] = nil
end
else
Msg( "Warning: Bad ban data is being ignored, key = " .. tostring( k ) .. "\n" )
ULib.bans[ k ] = nil
end
end
-- We're queueing this because it will split the load out for VERY large ban files
ULib.queueFunctionCall( function() ULib.fileWrite( ULib.BANS_FILE, ULib.makeKeyValues( ULib.bans ) ) end )
end
ULib.pcallError( ULib.refreshBans )
--[[
Function: getSpawnInfo
Grabs and returns player information that can be used to respawn player with same health/armor as before the spawn.
Parameters:
ply - The player to grab information for.
Returns:
Updates player object to store health and armor. Has no effect unless ULib.Spawn is used later.
]]
function ULib.getSpawnInfo( player )
local result = {}
local t = {}
player.ULibSpawnInfo = t
t.health = player:Health()
t.armor = player:Armor()
if player:GetActiveWeapon():IsValid() then
t.curweapon = player:GetActiveWeapon():GetClass()
end
local weapons = player:GetWeapons()
local data = {}
for _, weapon in ipairs( weapons ) do
printname = weapon:GetClass()
data[ printname ] = {}
data[ printname ].clip1 = weapon:Clip1()
data[ printname ].clip2 = weapon:Clip2()
data[ printname ].ammo1 = player:GetAmmoCount( weapon:GetPrimaryAmmoType() )
data[ printname ].ammo2 = player:GetAmmoCount( weapon:GetSecondaryAmmoType() )
end
t.data = data
end
-- Helper function for ULib.spawn()
local function doWeapons( player, t )
if not player:IsValid() then return end -- Drat, missed 'em.
player:StripAmmo()
player:StripWeapons()
for printname, data in pairs( t.data ) do
player:Give( printname )
local weapon = player:GetWeapon( printname )
weapon:SetClip1( data.clip1 )
weapon:SetClip2( data.clip2 )
player:SetAmmo( data.ammo1, weapon:GetPrimaryAmmoType() )
player:SetAmmo( data.ammo2, weapon:GetSecondaryAmmoType() )
end
if t.curweapon then
player:SelectWeapon( t.curweapon )
end
end
--[[
Function: spawn
Enhanced spawn player. Can spawn player and return health/armor to status before the spawn. (Only IF ULib.getSpawnInfo was used previously.)
Clears previously set values that were stored from ULib.getSpawnInfo.
Parameters:
ply - The player to grab information for.
bool - If true, spawn will set player information to values stored using ULib.SpawnInfo
Returns:
Spawns player. Sets health/armor to stored defaults if ULib.getSpawnInfo was used previously. Clears SpawnInfo table afterwards.
]]
function ULib.spawn( player, bool )
player:Spawn()
if bool and player.ULibSpawnInfo then
local t = player.ULibSpawnInfo
player:SetHealth( t.health )
player:SetArmor( t.armor )
timer.Simple( 0.1, function() doWeapons( player, t ) end )
player.ULibSpawnInfo = nil
end
end

View File

@ -1,67 +0,0 @@
local meta = FindMetaTable( "Player" )
ULib.spawnWhitelist = -- Tool white list for tools that don't spawn things
{
"colour",
"material",
"paint",
"ballsocket",
"ballsocket_adv",
"weld",
"keepupright",
"nocollide",
"eyeposer",
"faceposer",
"statue",
"weld_ez",
"axis",
}
-- Return if there's nothing to add on to
if not meta then return end
function meta:DisallowNoclip( bool )
self.NoNoclip = bool
end
function meta:DisallowSpawning( bool )
self.NoSpawning = bool
end
function meta:DisallowVehicles( bool )
self.NoVehicles = bool
end
local function tool( ply, tr, toolmode )
if ply.NoSpawning then
if not table.HasValue( ULib.spawnWhitelist, toolmode ) then
return false
end
end
end
hook.Add( "CanTool", "ULibPlayerToolCheck", tool, HOOK_HIGH )
local function noclip( ply )
if ply.NoNoclip then return false end
end
hook.Add( "PlayerNoClip", "ULibNoclipCheck", noclip, HOOK_HIGH )
local function spawnblock( ply )
if ply.NoSpawning then return false end
end
hook.Add( "PlayerSpawnObject", "ULibSpawnBlock", spawnblock )
hook.Add( "PlayerSpawnEffect", "ULibSpawnBlock", spawnblock )
hook.Add( "PlayerSpawnProp", "ULibSpawnBlock", spawnblock )
hook.Add( "PlayerSpawnNPC", "ULibSpawnBlock", spawnblock )
hook.Add( "PlayerSpawnVehicle", "ULibSpawnBlock", spawnblock )
hook.Add( "PlayerSpawnRagdoll", "ULibSpawnBlock", spawnblock )
hook.Add( "PlayerSpawnSENT", "ULibSpawnBlock", spawnblock )
hook.Add( "PlayerGiveSWEP", "ULibSpawnBlock", spawnblock )
local function vehicleblock( ply, ent )
if ply.NoVehicles then
return false
end
end
hook.Add( "CanPlayerEnterVehicle", "ULibVehicleBlock", vehicleblock, HOOK_HIGH )
hook.Add( "CanDrive", "ULibVehicleDriveBlock", vehicleblock, HOOK_HIGH )

File diff suppressed because it is too large Load Diff

View File

@ -1,364 +0,0 @@
--[[
Title: Utilities
Has some useful server utilities
]]
--[[
Function: clientRPC
Think of this function as if you're calling a client function directly from the server. You state who should run it, what the name of
the function is, and then a list of parameters to pass to that function on the client. ULib handles the rest. Parameters can be any
data type that's allowed on the network and of any size. Send huge tables or strings, it's all the same, and it all works.
Parameters:
filter - The Player object, table of Player objects for who you want to send this to, nil sends to everyone.
fn - A string of the function to run on the client. Does *not* need to be in the global namespace, "myTable.myFunction" works too.
... - *Optional* The parameters to pass to the function.
Revisions:
v2.40 - Initial.
]]
function ULib.clientRPC( plys, fn, ... )
ULib.checkArg( 1, "ULib.clientRPC", {"nil","Player","table"}, plys )
ULib.checkArg( 2, "ULib.clientRPC", {"string"}, fn )
net.Start( "URPC" )
net.WriteString( fn )
net.WriteTable( {...} )
if plys then
net.Send( plys )
else
net.Broadcast()
end
end
--[[
Function: umsgSend
Makes sending umsgs a blast. You don't have to bother knowing what type you're sending, just use ULib.umsgRcv() on the client.
Note that while you can send tables with this function, you're limited by the max umsg size. If you're sending a large amount of data,
consider using <clientRPC()> instead.
Parameters:
v - The value to send.
queue - *(For use by <clientRPC()> ONLY)* A boolean of whether the messages should be queued with RPC or not.
]]
function ULib.umsgSend( v, queue )
local tv = type( v )
local function call( fn, ... )
if queue then
queueRPC( fn, ... )
else
fn( ... )
end
end
if tv == "string" then
call( umsg.Char, ULib.TYPE_STRING )
call( umsg.String, v )
elseif tv == "number" then
if math.fmod( v, 1 ) ~= 0 then -- It's a float
call( umsg.Char, ULib.TYPE_FLOAT )
call( umsg.Float, v )
else
if v <= 127 and v >= -127 then
call( umsg.Char, ULib.TYPE_CHAR )
call( umsg.Char, v )
elseif v < 32767 and v > -32768 then
call( umsg.Char, ULib.TYPE_SHORT )
call( umsg.Short, v )
else
call( umsg.Char, ULib.TYPE_LONG )
call( umsg.Long, v )
end
end
elseif tv == "boolean" then
call( umsg.Char, ULib.TYPE_BOOLEAN )
call( umsg.Bool, v )
elseif tv == "Entity" or tv == "Player" then
call( umsg.Char, ULib.TYPE_ENTITY )
call( umsg.Entity, v )
elseif tv == "Vector" then
call( umsg.Char, ULib.TYPE_VECTOR )
call( umsg.Vector, v )
elseif tv == "Angle" then
call( umsg.Char, ULib.TYPE_ANGLE )
call( umsg.Angle, v )
elseif tv == "table" then
call( umsg.Char, ULib.TYPE_TABLE_BEGIN )
for key, value in pairs( v ) do
ULib.umsgSend( key, queue )
ULib.umsgSend( value, queue )
end
call( umsg.Char, ULib.TYPE_TABLE_END )
elseif tv == "nil" then
call( umsg.Char, ULib.TYPE_NIL )
else
ULib.error( "Unknown type passed to umsgSend -- " .. tv )
end
end
--[[
Function: play3DSound
Plays a 3D sound, the further away from the point the player is, the softer the sound will be.
Parameters:
sound - The sound to play, relative to the sound folder.
vector - The point to play the sound at.
volume - *(Optional, defaults to 1)* The volume to make the sound.
pitch - *(Optional, defaults to 1)* The pitch to make the sound, 1 = normal.
]]
function ULib.play3DSound( sound, vector, volume, pitch )
volume = volume or 100
pitch = pitch or 100
local ent = ents.Create( "info_null" )
if not ent:IsValid() then return end
ent:SetPos( vector )
ent:Spawn()
ent:Activate()
ent:EmitSound( sound, volume, pitch )
end
--[[
Function: getAllReadyPlayers
Similar to player.GetAll(), except it only returns players that have ULib ready to go.
Revisions:
2.40 - Initial
]]
function ULib.getAllReadyPlayers()
local players = player.GetAll()
for i=#players, 1, -1 do
if not players[ i ].ulib_ready then
table.remove( players, i )
end
end
return players
end
--[[
Function: getVersion
TODO
]]
function ULib.getVersion()
local versionStr
local build = nil
local usingWorkshop = false
-- Get workshop information, if available
local addons = engine.GetAddons()
for i=1, #addons do
-- Ideally we'd use the "wsid" from this table
-- But, as of 19 Nov 2015, that is broken, so we'll work around it
if addons[i].file:find(tostring(ULib.WORKSHOPID)) then
usingWorkshop = true
end
end
-- If we have good build data, set it in "build"
if ULib.fileExists( "ulib.build" ) then
local buildStr = ULib.fileRead( "ulib.build" )
local buildNum = tonumber(buildStr)
-- Make sure the time is something reasonable -- between the year 2014 and 2128
if buildNum and buildNum > 1400000000 and buildNum < 5000000000 then
build = buildNum
end
end
if ULib.RELEASE then
versionStr = string.format( "v%.02f", ULib.VERSION )
elseif usingWorkshop then
versionStr = string.format( "v%.02fw", ULib.VERSION )
elseif build then -- It's not release and it's not workshop
versionStr = string.format( "v%.02fd (%s)", ULib.VERSION, os.date( "%x", build ) )
else -- Not sure what this version is, but it's not a release
versionStr = string.format( "v%.02fd", ULib.VERSION )
end
return versionStr, ULib.VERSION, build, usingWorkshop
end
local function sendVersionData( ply )
local _, _, build, workshop = ULib.getVersion()
ULib.clientRPC( ply, "ULib.getVersionData", build, workshop )
end
hook.Add( "PlayerInitialSpawn", "ULibSendVersionData", sendVersionData )
ULib.updateAvailable = false
local function ulibUpdateCheck( body, len, headers, httpCode )
if httpCode ~= 200 then
return
end
local currentBuild = tonumber(body)
if not currentBuild then return end
local _, _, myBuild = ULib.getVersion()
if myBuild < currentBuild then
ULib.updateAvailable = true
Msg( "[ULib] There is an update available\n" )
end
end
local function downloadForUlibUpdateCheck()
local _, _, myBuild, workshop = ULib.getVersion()
if not myBuild or workshop then
return
end
if ULib.RELEASE then
http.Fetch( "https://teamulysses.github.io/ulib/ulib.build", ulibUpdateCheck )
else
http.Fetch( "https://raw.githubusercontent.com/TeamUlysses/ulib/master/ulib.build", ulibUpdateCheck )
end
end
hook.Add( "Initialize", "ULibUpdateChecker", downloadForUlibUpdateCheck )
local function advertiseNewVersion( ply )
if ply:IsAdmin() and ULib.updateAvailable and not ply.UlibUpdateAdvertised then
ULib.tsay( ply, "[ULib] There is an update available" )
ply.UlibUpdateAdvertised = true
end
end
hook.Add( ULib.HOOK_UCLAUTH, "ULibAdvertiseUpdate", advertiseNewVersion )
ULib.repcvars = ULib.repcvars or {} -- This is used for <ULib.replicatedWithWritableCvar> in order to keep track of valid cvars and access info.
local repcvars = ULib.repcvars
local repCvarServerChanged
--[[
Function: replicatedWritableCvar
This function is mainly intended for use with the menus. This function is very similar to creating a replicated cvar with one caveat:
This function also creates a cvar on the client that can be modified and will be sent back to the server.
Parameters:
sv_cvar - The string of server side cvar.
cl_cvar - The string of the client side cvar. *THIS MUST BE DIFFERENT FROM THE sv_cvar VALUE IF YOU'RE PIGGY BACKING AN EXISTING REPLICATED CVAR (like sv_gravity)*.
default_value - The string of the default value for the cvar.
save - Boolean of whether or not the value is persistent across map changes.
This uses garry's way, which has lots of issues. We recommend you watch the cvar for changes and handle saving yourself.
notify - Boolean of whether or not value changes are announced on the server
access - The string of the access required for a client to actually change the value.
Returns:
The server-side cvar object.
Revisions:
v2.40 - Initial.
v2.50 - Changed to not depend on the replicated cvars themselves due to Garry-breakage.
]]
function ULib.replicatedWritableCvar( sv_cvar, cl_cvar, default_value, save, notify, access )
sv_cvar = sv_cvar:lower()
cl_cvar = cl_cvar:lower()
local flags = 0
if save then
flags = flags + FCVAR_ARCHIVE
end
if notify then
flags = flags + FCVAR_NOTIFY
end
local cvar_obj = GetConVar( sv_cvar ) or CreateConVar( sv_cvar, default_value, flags )
umsg.Start( "ulib_repWriteCvar" ) -- Send to everyone connected
umsg.String( sv_cvar )
umsg.String( cl_cvar )
umsg.String( default_value )
umsg.String( cvar_obj:GetString() )
umsg.End()
repcvars[ sv_cvar ] = { access=access, default=default_value, cl_cvar=cl_cvar, cvar_obj=cvar_obj }
cvars.AddChangeCallback( sv_cvar, repCvarServerChanged )
hook.Call( ULib.HOOK_REPCVARCHANGED, _, sv_cvar, cl_cvar, nil, nil, cvar_obj:GetString() )
return cvar_obj
end
local function repCvarOnJoin( ply )
for sv_cvar, v in pairs( repcvars ) do
umsg.Start( "ulib_repWriteCvar", ply )
umsg.String( sv_cvar )
umsg.String( v.cl_cvar )
umsg.String( v.default )
umsg.String( v.cvar_obj:GetString() )
umsg.End()
end
end
hook.Add( ULib.HOOK_LOCALPLAYERREADY, "ULibSendCvars", repCvarOnJoin )
local function clientChangeCvar( ply, command, argv )
local sv_cvar = argv[ 1 ]
local newvalue = argv[ 2 ]
if not sv_cvar or not newvalue or not repcvars[ sv_cvar:lower() ] then -- Bad value, ignore
return
end
sv_cvar = sv_cvar:lower()
cvar_obj = repcvars[ sv_cvar ].cvar_obj
local oldvalue = cvar_obj:GetString()
if oldvalue == newvalue then return end -- Agreement
local access = repcvars[ sv_cvar ].access
if not ply:query( access ) then
ULib.tsayError( ply, "You do not have access to this cvar (" .. sv_cvar .. "), " .. ply:Nick() .. "." )
umsg.Start( "ulib_repChangeCvar", ply )
umsg.Entity( ply )
umsg.String( repcvars[ sv_cvar ].cl_cvar )
umsg.String( oldvalue )
umsg.String( oldvalue ) -- No change
umsg.End()
return
end
repcvars[ sv_cvar ].ignore = ply -- Flag other hook not to go off. Flag will be removed at hook.
RunConsoleCommand( sv_cvar, newvalue )
hook.Call( ULib.HOOK_REPCVARCHANGED, _, sv_cvar, repcvars[ sv_cvar ].cl_cvar, ply, oldvalue, newvalue )
end
concommand.Add( "ulib_update_cvar", clientChangeCvar, nil, nil, FCVAR_SERVER_CAN_EXECUTE )
-- Adding FCVAR_SERVER_CAN_EXECUTE above prevents an odd bug where if a user hosts a listen server, this command gets registered,
-- but when they join another server they can't change any replicated cvars.
repCvarServerChanged = function( sv_cvar, oldvalue, newvalue )
if not repcvars[ sv_cvar ] then -- Bad value or we need to ignore it
return
end
umsg.Start( "ulib_repChangeCvar" ) -- Tell clients to reset to new value
umsg.Entity( repcvars[ sv_cvar ].ignore or Entity( 0 ) )
umsg.String( repcvars[ sv_cvar ].cl_cvar )
umsg.String( oldvalue )
umsg.String( newvalue )
umsg.End()
if repcvars[ sv_cvar ].ignore then
repcvars[ sv_cvar ].ignore = nil
else
hook.Call( ULib.HOOK_REPCVARCHANGED, _, sv_cvar, repcvars[ sv_cvar ].cl_cvar, Entity( 0 ), oldvalue, newvalue )
end
end

View File

@ -1,524 +0,0 @@
--[[
CAMI - Common Admin Mod Interface.
Makes admin mods intercompatible and provides an abstract privilege interface
for third party addons.
IMPORTANT: This is a draft script. It is very much WIP.
Follows the specification on this page:
https://docs.google.com/document/d/1QIRVcAgZfAYf1aBl_dNV_ewR6P25wze2KmUVzlbFgMI
Structures:
CAMI_USERGROUP, defines the charactaristics of a usergroup:
{
Name
string
The name of the usergroup
Inherits
string
The name of the usergroup this usergroup inherits from
}
CAMI_PRIVILEGE, defines the charactaristics of a privilege:
{
Name
string
The name of the privilege
MinAccess
string
One of the following three: user/admin/superadmin
HasAccess
function(
privilege :: CAMI_PRIVILEGE,
actor :: Player,
target :: Player
) :: bool
optional
Function that decides whether a player can execute this privilege,
optionally on another player (target).
}
]]
-- Version number in YearMonthDay format.
local version = 20150902.1
if CAMI and CAMI.Version >= version then return end
CAMI = CAMI or {}
CAMI.Version = version
--[[
usergroups
Contains the registered CAMI_USERGROUP usergroup structures.
Indexed by usergroup name.
]]
local usergroups = CAMI.GetUsergroups and CAMI.GetUsergroups() or {
user = {
Name = "user",
Inherits = "user"
},
admin = {
Name = "admin",
Inherits = "user"
},
superadmin = {
Name = "superadmin",
Inherits = "admin"
}
}
--[[
privileges
Contains the registered CAMI_PRIVILEGE privilege structures.
Indexed by privilege name.
]]
local privileges = CAMI.GetPrivileges and CAMI.GetPrivileges() or {}
--[[
CAMI.RegisterUsergroup
Registers a usergroup with CAMI.
Parameters:
usergroup
CAMI_USERGROUP
(see CAMI_USERGROUP structure)
source
any
Identifier for your own admin mod. Can be anything.
Use this to make sure CAMI.RegisterUsergroup function and the
CAMI.OnUsergroupRegistered hook don't cause an infinite loop
Return value:
CAMI_USERGROUP
The usergroup given as argument.
]]
function CAMI.RegisterUsergroup(usergroup, source)
usergroups[usergroup.Name] = usergroup
hook.Call("CAMI.OnUsergroupRegistered", nil, usergroup, source)
return usergroup
end
--[[
CAMI.UnregisterUsergroup
Unregisters a usergroup from CAMI. This will call a hook that will notify
all other admin mods of the removal.
Call only when the usergroup is to be permanently removed.
Parameters:
usergroupName
string
The name of the usergroup.
source
any
Identifier for your own admin mod. Can be anything.
Use this to make sure CAMI.UnregisterUsergroup function and the
CAMI.OnUsergroupUnregistered hook don't cause an infinite loop
Return value:
bool
Whether the unregistering succeeded.
]]
function CAMI.UnregisterUsergroup(usergroupName, source)
if not usergroups[usergroupName] then return false end
local usergroup = usergroups[usergroupName]
usergroups[usergroupName] = nil
hook.Call("CAMI.OnUsergroupUnregistered", nil, usergroup, source)
return true
end
--[[
CAMI.GetUsergroups
Retrieves all registered usergroups.
Return value:
Table of CAMI_USERGROUP, indexed by their names.
]]
function CAMI.GetUsergroups()
return usergroups
end
--[[
CAMI.GetUsergroup
Receives information about a usergroup.
Return value:
CAMI_USERGROUP
Returns nil when the usergroup does not exist.
]]
function CAMI.GetUsergroup(usergroupName)
return usergroups[usergroupName]
end
--[[
CAMI.UsergroupInherits
Returns true when usergroupName1 inherits usergroupName2.
Note that usergroupName1 does not need to be a direct child.
Every usergroup trivially inherits itself.
Parameters:
usergroupName1
string
The name of the usergroup that is queried.
usergroupName2
string
The name of the usergroup of which is queried whether usergroupName1
inherits from.
Return value:
bool
Whether usergroupName1 inherits usergroupName2.
]]
function CAMI.UsergroupInherits(usergroupName1, usergroupName2)
repeat
if usergroupName1 == usergroupName2 then return true end
usergroupName1 = usergroups[usergroupName1] and
usergroups[usergroupName1].Inherits or
usergroupName1
until not usergroups[usergroupName1] or
usergroups[usergroupName1].Inherits == usergroupName1
-- One can only be sure the usergroup inherits from user if the
-- usergroup isn't registered.
return usergroupName1 == usergroupName2 or usergroupName2 == "user"
end
--[[
CAMI.InheritanceRoot
All usergroups must eventually inherit either user, admin or superadmin.
Regardless of what inheritance mechism an admin may or may not have, this
always applies.
This method always returns either user, admin or superadmin, based on what
usergroups eventually inherit.
Parameters:
usergroupName
string
The name of the usergroup of which the root of inheritance is
requested
Return value:
string
The name of the root usergroup (either user, admin or superadmin)
]]
function CAMI.InheritanceRoot(usergroupName)
if not usergroups[usergroupName] then return end
local inherits = usergroups[usergroupName].Inherits
while inherits ~= usergroups[usergroupName].Inherits do
usergroupName = usergroups[usergroupName].Inherits
end
return usergroupName
end
--[[
CAMI.RegisterPrivilege
Registers a privilege with CAMI.
Note: do NOT register all your admin mod's privileges with this function!
This function is for third party addons to register privileges
with admin mods, not for admin mods sharing the privileges amongst one
another.
Parameters:
privilege
CAMI_PRIVILEGE
See CAMI_PRIVILEGE structure.
Return value:
CAMI_PRIVILEGE
The privilege given as argument.
]]
function CAMI.RegisterPrivilege(privilege)
privileges[privilege.Name] = privilege
hook.Call("CAMI.OnPrivilegeRegistered", nil, privilege)
return privilege
end
--[[
CAMI.UnregisterPrivilege
Unregisters a privilege from CAMI. This will call a hook that will notify
all other admin mods of the removal.
Call only when the privilege is to be permanently removed.
Parameters:
privilegeName
string
The name of the privilege.
Return value:
bool
Whether the unregistering succeeded.
]]
function CAMI.UnregisterPrivilege(privilegeName)
if not privileges[privilegeName] then return false end
local privilege = privileges[privilegeName]
privileges[privilegeName] = nil
hook.Call("CAMI.OnPrivilegeUnregistered", nil, privilege)
return true
end
--[[
CAMI.GetPrivileges
Retrieves all registered privileges.
Return value:
Table of CAMI_PRIVILEGE, indexed by their names.
]]
function CAMI.GetPrivileges()
return privileges
end
--[[
CAMI.GetPrivilege
Receives information about a privilege.
Return value:
CAMI_PRIVILEGE when the privilege exists.
nil when the privilege does not exist.
]]
function CAMI.GetPrivilege(privilegeName)
return privileges[privilegeName]
end
--[[
CAMI.PlayerHasAccess
Queries whether a certain player has the right to perform a certain action.
Note: this function does NOT return an immediate result!
The result is in the callback!
Parameters:
actorPly
Player
The player of which is requested whether they have the privilege.
privilegeName
string
The name of the privilege.
callback
function(bool, string)
This function will be called with the answer. The bool signifies the
yes or no answer as to whether the player is allowed. The string
will optionally give a reason.
targetPly
Optional.
The player on which the privilege is executed.
extraInfoTbl
Optional.
Table containing extra information.
Officially supported members:
Fallback
string
Either of user/admin/superadmin. When no admin mod replies,
the decision is based on the admin status of the user.
Defaults to admin if not given.
IgnoreImmunity
bool
Ignore any immunity mechanisms an admin mod might have.
CommandArguments
table
Extra arguments that were given to the privilege command.
Return value:
None, the answer is given in the callback function in order to allow
for the admin mod to perform e.g. a database lookup.
]]
-- Default access handler
local defaultAccessHandler = {["CAMI.PlayerHasAccess"] =
function(_, actorPly, privilegeName, callback, _, extraInfoTbl)
-- The server always has access in the fallback
if not IsValid(actorPly) then return callback(true, "Fallback.") end
local priv = privileges[privilegeName]
local fallback = extraInfoTbl and (
not extraInfoTbl.Fallback and actorPly:IsAdmin() or
extraInfoTbl.Fallback == "user" and true or
extraInfoTbl.Fallback == "admin" and actorPly:IsAdmin() or
extraInfoTbl.Fallback == "superadmin" and actorPly:IsSuperAdmin())
if not priv then return callback(fallback, "Fallback.") end
callback(
priv.MinAccess == "user" or
priv.MinAccess == "admin" and actorPly:IsAdmin() or
priv.MinAccess == "superadmin" and actorPly:IsSuperAdmin()
, "Fallback.")
end,
["CAMI.SteamIDHasAccess"] =
function(_, _, _, callback)
callback(false, "No information available.")
end
}
function CAMI.PlayerHasAccess(actorPly, privilegeName, callback, targetPly,
extraInfoTbl)
hook.Call("CAMI.PlayerHasAccess", defaultAccessHandler, actorPly,
privilegeName, callback, targetPly, extraInfoTbl)
end
--[[
CAMI.GetPlayersWithAccess
Finds the list of currently joined players who have the right to perform a
certain action.
NOTE: this function will NOT return an immediate result!
The result is in the callback!
Parameters:
privilegeName
string
The name of the privilege.
callback
function(players)
This function will be called with the list of players with access.
targetPly
Optional.
The player on which the privilege is executed.
extraInfoTbl
Optional.
Table containing extra information.
Officially supported members:
Fallback
string
Either of user/admin/superadmin. When no admin mod replies,
the decision is based on the admin status of the user.
Defaults to admin if not given.
IgnoreImmunity
bool
Ignore any immunity mechanisms an admin mod might have.
CommandArguments
table
Extra arguments that were given to the privilege command.
]]
function CAMI.GetPlayersWithAccess(privilegeName, callback, targetPly,
extraInfoTbl)
local allowedPlys = {}
local allPlys = player.GetAll()
local countdown = #allPlys
local function onResult(ply, hasAccess, _)
countdown = countdown - 1
if hasAccess then table.insert(allowedPlys, ply) end
if countdown == 0 then callback(allowedPlys) end
end
for _, ply in pairs(allPlys) do
CAMI.PlayerHasAccess(ply, privilegeName,
function(...) onResult(ply, ...) end,
targetPly, extraInfoTbl)
end
end
--[[
CAMI.SteamIDHasAccess
Queries whether a player with a steam ID has the right to perform a certain
action.
Note: the player does not need to be in the server for this to
work.
Note: this function does NOT return an immediate result!
The result is in the callback!
Parameters:
actorSteam
Player
The SteamID of the player of which is requested whether they have
the privilege.
privilegeName
string
The name of the privilege.
callback
function(bool, string)
This function will be called with the answer. The bool signifies the
yes or no answer as to whether the player is allowed. The string
will optionally give a reason.
targetSteam
Optional.
The SteamID of the player on which the privilege is executed.
extraInfoTbl
Optional.
Table containing extra information.
Officially supported members:
IgnoreImmunity
bool
Ignore any immunity mechanisms an admin mod might have.
CommandArguments
table
Extra arguments that were given to the privilege command.
Return value:
None, the answer is given in the callback function in order to allow
for the admin mod to perform e.g. a database lookup.
]]
function CAMI.SteamIDHasAccess(actorSteam, privilegeName, callback,
targetSteam, extraInfoTbl)
hook.Call("CAMI.SteamIDHasAccess", defaultAccessHandler, actorSteam,
privilegeName, callback, targetSteam, extraInfoTbl)
end
--[[
CAMI.SignalUserGroupChanged
Signify that your admin mod has changed the usergroup of a player. This
function communicates to other admin mods what it thinks the usergroup
of a player should be.
Listen to the hook to receive the usergroup changes of other admin mods.
Parameters:
ply
Player
The player for which the usergroup is changed
old
string
The previous usergroup of the player.
new
string
The new usergroup of the player.
source
any
Identifier for your own admin mod. Can be anything.
]]
function CAMI.SignalUserGroupChanged(ply, old, new, source)
hook.Call("CAMI.PlayerUsergroupChanged", nil, ply, old, new, source)
end
--[[
CAMI.SignalSteamIDUserGroupChanged
Signify that your admin mod has changed the usergroup of a disconnected
player. This communicates to other admin mods what it thinks the usergroup
of a player should be.
Listen to the hook to receive the usergroup changes of other admin mods.
Parameters:
ply
string
The steam ID of the player for which the usergroup is changed
old
string
The previous usergroup of the player.
new
string
The new usergroup of the player.
source
any
Identifier for your own admin mod. Can be anything.
]]
function CAMI.SignalSteamIDUserGroupChanged(steamId, old, new, source)
hook.Call("CAMI.SteamIDUsergroupChanged", nil, steamId, old, new, source)
end

View File

@ -1,106 +0,0 @@
--[[
File: CAMI
Implements CAMI version "20150902.1".
The CAMI API is designed by Falco "FPtje" Peijnenburg, but this source code
remains under the same licensing as the rest of ULib.
To update the shared FPtje CAMI logic, run the following in the
appropriate directory...
: wget https://raw.githubusercontent.com/glua/CAMI/master/sh_cami.lua -O cami_global.lua
]]
CAMI.ULX_TOKEN = "ULX"
local function playerHasAccess( actorPly, priv, callback, targetPly, extra )
local priv = priv:lower()
local result = ULib.ucl.query( actorPly, priv, true )
if result ~= nil then
callback(result)
return true
end
end
hook.Add( "CAMI.PlayerHasAccess", "ULXCamiPlayerHasAccess", playerHasAccess )
local function steamIDHasAccess( steamid, priv, callback, targetPly, extra )
local priv = priv:lower()
steamid = steamid:upper()
if not ULib.isValidSteamID( steamid ) then return end
local connectedPly = ULib.getPlyByID( steamid )
if connectedPly then return playerHasAccess( connectedPly, priv, callback, targetPly, extra ) end
-- ULib currently doesn't support looking up permissions for users that aren't connected. Maybe in the future?
end
hook.Add( "CAMI.SteamIDHasAccess", "ULXCamiSteamidHasAccess", steamIDHasAccess )
-- Registering/deleting groups on client not necessary for ULib since we pass
-- that data around from the server
if CLIENT then return end
local function onGroupRegistered( camiGroup, originToken )
-- Ignore if ULX is the source, or if we receive bad data from another addon
if originToken == CAMI.ULX_TOKEN then return end
if ULib.findInTable( {"superadmin", "admin", "user"}, camiGroup.Name ) then return end
if not ULib.ucl.groups[ camiGroup.Name ] then
ULib.ucl.addGroup( camiGroup.Name, nil, camiGroup.Inherits, true )
else
ULib.ucl.setGroupInheritance( camiGroup.Name, camiGroup.Inherits )
end
end
hook.Add( "CAMI.OnUsergroupRegistered", "ULXCamiGroupRegistered", onGroupRegistered )
local function onGroupRemoved( camiGroup, originToken )
-- Ignore if ULX is the source, or if we receive bad data from another addon
if originToken == CAMI.ULX_TOKEN then return end
if ULib.findInTable( {"superadmin", "admin", "user"}, camiGroup.Name ) then return end
ULib.ucl.removeGroup( camiGroup.Name, true )
end
hook.Add( "CAMI.OnUsergroupUnregistered", "ULXCamiGroupRemoved", onGroupRemoved )
local function onUsersGroupChanged( ply, oldGroup, newGroup, originToken )
if originToken == CAMI.ULX_TOKEN then return end
local id = ULib.ucl.getUserRegisteredID( ply )
if not id then id = ply:SteamID() end
if newGroup == ULib.ACCESS_ALL then
ULib.ucl.removeUser( id, true )
else
if not ULib.ucl.groups[ newGroup ] then -- Just in case we were never notified of this group
local camiGroup = CAMI.GetUsergroup(usergroupName)
local inherits = camiGroup and camiGroup.Inherits
ULib.ucl.addGroup( newGroup, nil, inherits, true )
end
ULib.ucl.addUser( id, nil, nil, newGroup, true )
end
end
hook.Add( "CAMI.PlayerUsergroupChanged", "ULXCamiUsersGroupChanged", onUsersGroupChanged )
local function onPrivilegeRegistered( camiPriv )
local priv = camiPriv.Name:lower()
ULib.ucl.registerAccess( priv, camiPriv.MinAccess, "A privilege from CAMI", "CAMI" )
end
hook.Add( "CAMI.OnPrivilegeRegistered", "ULXCamiPrivilegeRegistered", onPrivilegeRegistered )
-- Register anything already loaded
for _, camiPriv in pairs(CAMI.GetPrivileges()) do
onPrivilegeRegistered( camiPriv )
end
for name, camiGroup in pairs(CAMI.GetUsergroups()) do
onGroupRegistered( camiGroup )
end
-- End register anything already loaded
-- Register ULib things into CAMI
for name, data in pairs(ULib.ucl.groups) do
if not ULib.findInTable( {"superadmin", "admin", "user"}, name ) then
CAMI.RegisterUsergroup( {Name=name, Inherits=(data.inherit_from or "user")}, CAMI.ULX_TOKEN )
end
end
-- End register ULib things into CAMI

File diff suppressed because it is too large Load Diff

View File

@ -1,287 +0,0 @@
--[[
Title: Defines
Holds some defines used on both client and server.
]]
ULib = ULib or {}
ULib.RELEASE = false -- Don't access these two directlly, use ULib.getVersion()
ULib.VERSION = 2.52
ULib.ACCESS_ALL = "user"
ULib.ACCESS_OPERATOR = "operator"
ULib.ACCESS_ADMIN = "admin"
ULib.ACCESS_SUPERADMIN = "superadmin"
ULib.DEFAULT_ACCESS = ULib.ACCESS_ALL
ULib.DEFAULT_TSAY_COLOR = Color( 151, 211, 255 ) -- Found by using MS Paint
--[[
Section: Umsg Helpers
These are ids for the ULib umsg functions, so the client knows what they're getting.
]]
ULib.TYPE_ANGLE = 1
ULib.TYPE_BOOLEAN = 2
ULib.TYPE_CHAR = 3
ULib.TYPE_ENTITY = 4
ULib.TYPE_FLOAT = 5
ULib.TYPE_LONG = 6
ULib.TYPE_SHORT = 7
ULib.TYPE_STRING = 8
ULib.TYPE_VECTOR = 9
-- These following aren't actually datatypes, we handle them ourselves
ULib.TYPE_TABLE_BEGIN = 10
ULib.TYPE_TABLE_END = 11
ULib.TYPE_NIL = 12
ULib.RPC_UMSG_NAME = "URPC"
ULib.TYPE_SIZE = {
[ULib.TYPE_ANGLE] = 12, -- 3 floats
[ULib.TYPE_BOOLEAN] = 1,
[ULib.TYPE_CHAR] = 1,
[ULib.TYPE_ENTITY] = 4, -- Found through trial and error
[ULib.TYPE_FLOAT] = 4,
[ULib.TYPE_LONG] = 4,
[ULib.TYPE_SHORT] = 2,
[ULib.TYPE_VECTOR] = 12, -- 3 floats
[ULib.TYPE_NIL] = 0, -- Not technically a type but we handle it anyways
}
ULib.MAX_UMSG_BYTES = 255
--[[
Section: Hooks
These are the hooks that ULib has created that other modders are free to make use of.
]]
--[[
Hook: UCLAuthed
Called *on both server and client* when a player has been (re)authenticated by UCL. Called for ALL players, regardless of access.
Parameters passed to callback:
ply - The player that got (re)authenticated.
Revisions:
v2.40 - Initial
]]
ULib.HOOK_UCLAUTH = "UCLAuthed"
--[[
Hook: UCLChanged
Called *on both server and client* when anything in ULib.ucl.users, ULib.ucl.authed, or ULib.ucl.groups changes. No parameters are passed to callbacks.
Revisions:
v2.40 - Initial
]]
ULib.HOOK_UCLCHANGED = "UCLChanged"
--[[
Hook: ULibReplicatedCvarChanged
Called *on both client and server* when a replicated cvar changes or is created.
Parameters passed to callback:
sv_cvar - The name of the server-side cvar.
cl_cvar - The name of the client-side cvar.
ply - The player changing the cvar or nil on initial value.
old_value - The previous value of the cvar, nil if this call is to set the initial value.
new_value - The new value of the cvar.
Revisions:
v2.40 - Initial
v2.50 - Removed nil on client side restriction.
]]
ULib.HOOK_REPCVARCHANGED = "ULibReplicatedCvarChanged"
--[[
Hook: ULibLocalPlayerReady
Called *on both client and server* when a player entity is created. (can now run commands). Only works for local
player on the client side.
Parameters passed to callback:
ply - The player that's ready (local player on client side).
Revisions:
v2.40 - Initial
]]
ULib.HOOK_LOCALPLAYERREADY = "ULibLocalPlayerReady"
--[[
Hook: ULibCommandCalled
Called *on server* whenever a ULib command is run, return false to override and not allow, true to stop executing callbacks and allow.
Parameters passed to callback:
ply - The player attempting to execute the command.
commandName - The command that's being executed.
args - The table of args for the command.
Revisions:
v2.40 - Initial
]]
ULib.HOOK_COMMAND_CALLED = "ULibCommandCalled"
--[[
Hook: ULibPlayerTarget
Called whenever one player is about to target another player. Called *BEFORE* any other validation
takes place. Return false and error message to disallow target completely, return true to
override any other validation logic and allow the target to take place, return a player to force
the target to be the specified player.
Parameters passed to callback:
ply - The player attempting to execute the command.
commandName - The command that's being executed.
target - The proposed target of the command before any other validation logic takes place.
Revisions:
v2.40 - Initial
]]
ULib.HOOK_PLAYER_TARGET = "ULibPlayerTarget"
--[[
Hook: ULibPlayerTargets
Called whenever one player is about to target another set of players. Called *BEFORE* any other validation
takes place. Return false and error message to disallow target completely, return true to
override any other validation logic and allow the target to take place, return a table of players to force
the targets to be the specified players.
Parameters passed to callback:
ply - The player attempting to execute the command.
commandName - The command that's being executed.
targets - The proposed targets of the command before any other validation logic takes place.
Revisions:
v2.40 - Initial
]]
ULib.HOOK_PLAYER_TARGETS = "ULibPlayerTargets" -- Exactly the same as the above but used when the player is using a command that can target multiple players.
--[[
Hook: ULibPostTranslatedCommand
*Server hook*. Called after a translated command (ULib.cmds.TranslatedCommand) has been successfully
verified. This hook directly follows the callback for the command itself.
Parameters passed to callback:
ply - The player that executed the command.
commandName - The command that's being executed.
translated_args - A table of the translated arguments, as passed into the callback function itself.
Revisions:
v2.40 - Initial
]]
ULib.HOOK_POST_TRANSLATED_COMMAND = "ULibPostTranslatedCommand"
--[[
Hook: ULibPlayerNameChanged
Called within one second of a player changing their name.
Parameters passed to callback:
ply - The player that changed names.
oldName - The player's old name, before the change.
newName - The player's new name, after the change.
Revisions:
v2.40 - Initial
]]
ULib.HOOK_PLAYER_NAME_CHANGED = "ULibPlayerNameChanged"
--[[
Hook: ULibGetUsersCustomKeyword
Called during ULib.getUsers when considering a target string for keywords.
This could be used to create a new, custom keyword for targetting users who
have been connected for less than five minutes, for example.
Return nil or a table of player objects to add to the target list.
Parameters passed to callback:
target - A string chunk of a possibly larger target list to operate on.
ply - The player doing the targetting, not always specified (can be nil).
Revisions:
v2.60 - Initial
]]
ULib.HOOK_GETUSERS_CUSTOM_KEYWORD = "ULibGetUsersCustomKeyword"
--[[
Hook: ULibGetUserCustomKeyword
Called during ULib.getUser when considering a target string for keywords.
This could be used to create a new, custom keyword for always targetting a
specific connected steamid, for example. Or, to target the shortest connected
player.
Return nil or a player object.
Parameters passed to callback:
target - A string target.
ply - The player doing the targetting, not always specified (can be nil).
Revisions:
v2.60 - Initial
]]
ULib.HOOK_GETUSER_CUSTOM_KEYWORD = "ULibGetUserCustomKeyword"
ULib.WORKSHOPID = 557962238
--[[
Section: UCL Helpers
These defines are server-only, to help with UCL.
]]
if SERVER then
ULib.UCL_LOAD_DEFAULT = true -- Set this to false to ignore the SetUserGroup() call.
ULib.UCL_USERS = "data/ulib/users.txt"
ULib.UCL_GROUPS = "data/ulib/groups.txt"
ULib.UCL_REGISTERED = "data/ulib/misc_registered.txt" -- Holds access strings that ULib has already registered
ULib.BANS_FILE = "data/ulib/bans.txt"
ULib.DEFAULT_GRANT_ACCESS = { allow={}, deny={}, guest=true }
hook.Add( "Initialize", "ULibCheckFileInit", function()
if ULib.fileExists( ULib.UCL_REGISTERED ) and ULib.fileExists( "addons/ulib/data/" .. ULib.UCL_GROUPS ) and ULib.fileRead( ULib.UCL_GROUPS ) == ULib.fileRead( "addons/ulib/data/" .. ULib.UCL_GROUPS ) then
-- File has been reset, delete registered
ULib.fileDelete( ULib.UCL_REGISTERED )
end
end)
end
--[[
Section: Net pooled strings
These defines are server-only, to help with the networking library.
]]
if SERVER then
util.AddNetworkString( "URPC" )
end

View File

@ -1,252 +0,0 @@
local gmod = gmod
local pairs = pairs
local isfunction = isfunction
local isstring = isstring
local isnumber = isnumber
local math = math
local IsValid = IsValid
--[[
local concommand = concommand
local print = print
local PrintTable = PrintTable
local tostring = tostring
local assert = assert
local table = table--]]
HOOK_MONITOR_HIGH = -2
HOOK_HIGH = -1
HOOK_NORMAL = 0
HOOK_LOW = 1
HOOK_MONITOR_LOW = 2
if hook.GetULibTable then return end -- Prevent autorefresh reloading this file
-- Grab all previous hooks from the pre-existing hook module.
local OldHooks = hook.GetTable()
module( "hook" )
local Hooks = {}
local BackwardsHooks = {} -- A table fully to garry's spec for aVoN
--
-- For access to the Hooks table.. for some reason.
--
function GetTable() return BackwardsHooks end
function GetULibTable() return Hooks end
--
-- Add a hook
--
function Add( event_name, name, func, priority )
priority = priority or 0
if ( !isfunction( func ) ) then return end
if ( !isstring( event_name ) ) then return end
if ( !isnumber( priority ) ) then return end
priority = math.floor( priority )
if ( priority < -2 ) then priority = -2 end -- math.Clamp may not have been defined yet
if ( priority > 2 ) then priority = 2 end
Remove( event_name, name ) -- This keeps the event name unique, even among the priorities
if (Hooks[ event_name ] == nil) then
Hooks[ event_name ] = {[-2]={}, [-1]={}, [0]={}, [1]={}, [2]={}}
BackwardsHooks[ event_name ] = {}
end
Hooks[ event_name ][ priority ][ name ] = { fn=func, isstring=isstring( name ) }
BackwardsHooks[ event_name ][ name ] = func -- Keep the classic style too so we won't break anything
end
--
-- Remove a hook
--
function Remove( event_name, name )
if ( !isstring( event_name ) ) then return end
if ( !Hooks[ event_name ] ) then return end
for i=-2, 2 do
Hooks[ event_name ][ i ][ name ] = nil
end
BackwardsHooks[ event_name ][ name ] = nil
end
--
-- Run a hook (this replaces Call)
--
function Run( name, ... )
return Call( name, gmod and gmod.GetGamemode() or nil, ... )
end
--
-- Called by the engine
--
function Call( name, gm, ... )
--
-- Run hooks
--
local HookTable = Hooks[ name ]
if ( HookTable != nil ) then
for i=-2, 2 do
for k, v in pairs( HookTable[ i ] ) do
if ( v.isstring ) then
--
-- If it's a string, it's cool
--
local a, b, c, d, e, f = v.fn( ... )
if ( a != nil && i > -2 && i < 2 ) then
return a, b, c, d, e, f
end
else
--
-- If the key isn't a string - we assume it to be an entity
-- Or panel, or something else that IsValid works on.
--
if ( IsValid( k ) ) then
--
-- If the object is valid - pass it as the first argument (self)
--
local a, b, c, d, e, f = v.fn( k, ... )
if ( a != nil && i > -2 && i < 2 ) then
return a, b, c, d, e, f
end
else
--
-- If the object has become invalid - remove it
--
HookTable[ i ][ k ] = nil
end
end
end
end
end
--
-- Call the gamemode function
--
if ( !gm ) then return end
local GamemodeFunction = gm[ name ]
if ( GamemodeFunction == nil ) then return end
return GamemodeFunction( gm, ... )
end
-- Bring in all the old hooks
for event_name, t in pairs( OldHooks ) do
for name, func in pairs( t ) do
Add( event_name, name, func )
end
end
--[[
local failed = false
local shouldFail = false
local i, t
-- Since the correctness of this file is so important, we've made a little test suite
local function appendGenerator( n )
return function( t )
table.insert( t, n )
end
end
local function returnRange()
return 1, 2, 3, 4, 5, 6, 7, 8
end
local function noop()
end
local function err()
if shouldFail then
error( "this error is normal!" )
else
error( "this error is bad!" )
failed = true
end
end
local function doTests( ply, cmd, argv )
print( "Being run on client: " .. tostring( CLIENT ) )
-- First make sure there's no return value leakage...
Add( "LeakageA", "a", returnRange )
t = { Call( "LeakageA", _ ) }
assert( #t == 6 )
for k, v in pairs( t ) do
assert( k == v )
end
Add( "LeakageB", "a", noop )
t = { Call( "LeakageB", _ ) }
assert( #t == 0 )
-- Now let's make sure errors are handled correctly...
shouldFail = true
Add( "ErrCheck", "a", noop )
--Add( "ErrCheck", "b", err )
Add( "ErrCheck", "c", noop )
Add( "ErrCheck", "d", returnRange )
t = { Call( "ErrCheck", _ ) }
assert( #t == 6 )
--assert( #Hooks.ErrCheck == 3 and Hooks.ErrCheck[4] == nil ) -- Should have been reduced so that the 'b' got removed
shouldFail = false
t = { Call( "ErrCheck", _ ) }
assert( #t == 6 )
-- Check for override
Add( "ErrCheck", "d", noop, 1 ) -- Different priority, same name should still override
t = { Call( "ErrCheck", _ ) }
assert( #t == 0 )
-- Check for order and readonly'ness...
Add( "Order", "n20a", returnRange, -2 )
Add( "Order", "a", appendGenerator( 3 ) )
Add( "Order", "n20a", appendGenerator( 1 ), -2 )
Add( "Order", "n10a", appendGenerator( 2 ), -1 )
Add( "Order", "10a", appendGenerator( 4 ), 1 )
Add( "Order", "20a", returnRange, 2 )
Add( "Order", "20aa", appendGenerator( 5 ), 2 )
t = {}
Call( "Order", _, t )
print( t, #t )
PrintTable( t )
assert( #t == 5 )
for k, v in pairs( t ) do
assert( k == v )
end
Add( "Test", "AAA", function () print( "AAA" ) Remove( "Test", "AAA" ) end )
Add( "Test", "BBB", function () print( "BBB" ) end)
Run( "Test" )
Run( "Test" )
if failed then
print( "Tests failed!" )
else
print( "All tests passed!" )
end
end
concommand.Add( "run_hook_tests", doTests )--]]

View File

@ -1,285 +0,0 @@
--[[
Title: Messages
Handles messaging like logging, debug, etc.
]]
--[[
Function: tsay
Prints a message in talk say as well as in the user's consoles.
Parameters:
ply - The player to print to, set to nil to send to everyone. (Ignores this param if called on client)
msg - The message to print.
wait - *(Optional, defaults to false)* Wait one frame before posting. (Useful to use from things like chat hooks)
wasValid - *(INTERNAL USE ONLY)* This is flagged on waiting if the player *was* valid.
Revisions:
v2.10 - Initial
]]
function ULib.tsay( ply, msg, wait, wasValid )
ULib.checkArg( 1, "ULib.tsay", {"nil","Player","Entity"}, ply )
ULib.checkArg( 2, "ULib.tsay", "string", msg )
ULib.checkArg( 3, "ULib.tsay", {"nil","boolean"}, wait )
if wait then ULib.namedQueueFunctionCall( "ULibChats", ULib.tsay, ply, msg, false, ply and ply:IsValid() ) return end -- Call next frame
if SERVER and ply and not ply:IsValid() then -- Server console
if wasValid then -- This means we had a valid player that left, so do nothing
return
end
Msg( msg .. "\n" )
return
end
if CLIENT then
LocalPlayer():ChatPrint( msg )
return
end
if ply then
ply:ChatPrint( msg )
else
local players = player.GetAll()
for _, player in ipairs( players ) do
player:ChatPrint( msg )
end
end
end
local serverConsole = {} -- Used in the function below to identify the server console (internal use)
local function tsayColorCallback( ply, ... )
if CLIENT then
chat.AddText( ... )
return
end
if ply and ply ~= serverConsole and not ply:IsValid() then return end -- Player must have left the server
local args = { ... }
if ply == serverConsole then
for i=2, #args, 2 do
Msg( args[ i ] )
end
Msg( "\n" );
return
end
local current_chunk = { size = 0 }
local chunks = { current_chunk }
local max_chunk_size = 240
while #args > 0 do
local arg = table.remove( args, 1 )
local typ = type( arg )
local arg_size = typ == "table" and 4 or #arg + 2 -- Include null in strings, bool in both
if typ == "string" and current_chunk.size + arg_size > max_chunk_size then -- Split a large string up into multiple messages
local substr = arg:sub( 1, math.max( 1, max_chunk_size - current_chunk.size - 2 ) )
if #substr > 0 then
table.insert( current_chunk, substr )
end
table.insert( args, 1, arg:sub( #substr + 1) )
current_chunk = { size = 0 }
table.insert( chunks, current_chunk )
else
if current_chunk.size + arg_size > max_chunk_size then
current_chunk = { size = 0 }
table.insert( chunks, current_chunk )
end
current_chunk.size = current_chunk.size + arg_size
table.insert( current_chunk, arg )
end
end
for chunk_num=1, #chunks do
local chunk = chunks[ chunk_num ]
umsg.Start( "tsayc", ply )
umsg.Bool( chunk_num == #chunks )
umsg.Char( #chunk )
for i=1, #chunk do
local arg = chunk[ i ]
if type( arg ) == "string" then
umsg.Bool( true )
umsg.String( arg )
else
umsg.Bool( false )
umsg.Char( arg.r - 128 )
umsg.Char( arg.g - 128 )
umsg.Char( arg.b - 128 )
end
end
umsg.End()
end
end
if CLIENT then
local accumulator = {}
local function tsayColorHook( um )
local last = um:ReadBool()
local argn = um:ReadChar()
for i=1, argn do
if um:ReadBool() then
table.insert( accumulator, um:ReadString() )
else
table.insert( accumulator, Color( um:ReadChar() + 128, um:ReadChar() + 128, um:ReadChar() + 128) )
end
end
if last then
chat.AddText( unpack( accumulator ) )
accumulator = {}
end
end
usermessage.Hook( "tsayc", tsayColorHook )
end
--[[
Function: tsayColor
Prints a tsay message in color!
Parameters:
ply - The player to print to, set to nil to send to everyone. (Ignores this param if called on client)
wait - *(Optional, defaults to false)* Wait one frame before posting. (Useful to use from things like chat hooks)
... - color arg and text arg ad infinitum, color needs to come before the text it's coloring.
Revisions:
v2.40 - Initial.
]]
function ULib.tsayColor( ply, wait, ... )
if SERVER and ply and not ply:IsValid() then ply = serverConsole end -- Mark as server
if wait then ULib.namedQueueFunctionCall( "ULibChats", tsayColorCallback, ply, ... ) return end -- Call next frame
tsayColorCallback( ply, ... )
end
--[[
Function: tsayError
Just like tsay, but prints the string in red
Parameters:
ply - The player to print to, set to nil to send to everyone. (Ignores this param if called on client)
msg - The message to print.
wait - *(Optional, defaults to false)* Wait one frame before posting. (Useful to use from things like chat hooks)
Revisions:
v2.40 - Initial.
]]
function ULib.tsayError( ply, msg, wait )
return ULib.tsayColor( ply, wait, Color( 255, 140, 39 ), msg )
end
--[[
Function: csay
Prints a message in center of the screen as well as in the user's consoles.
Parameters:
ply - The player to print to, set to nil to send to everyone. (Ignores this param if called on client)
msg - The message to print.
color - *(Optional)* The amount of red to use for the text.
duration - *(Optional)* The amount of time to show the text.
fade - *(Optional, defaults to 0.5)* The length of fade time
Revisions:
v2.10 - Added fade parameter. Fixed it sending the message multiple times.
v2.40 - Changed to use clientRPC.
]]
function ULib.csay( ply, msg, color, duration, fade )
if CLIENT then
ULib.csayDraw( msg, color, duration )
Msg( msg .. "\n" )
return
end
ULib.clientRPC( ply, "ULib.csayDraw", msg, color, duration, fade )
ULib.console( ply, msg )
end
--[[
Function: console
Prints a message in the user's consoles.
Parameters:
ply - The player to print to, set to nil to send to everyone. (Ignores this param if called on client)
msg - The message to print.
]]
function ULib.console( ply, msg )
if CLIENT or (ply and not ply:IsValid()) then
Msg( msg .. "\n" )
return
end
if ply then
ply:PrintMessage( HUD_PRINTCONSOLE, msg .. "\n" )
else
local players = player.GetAll()
for _, player in ipairs( players ) do
player:PrintMessage( HUD_PRINTCONSOLE, msg .. "\n" )
end
end
end
--[[
Function: error
Gives an error to console.
Parameters:
s - The string to use as the error message
]]
function ULib.error( s )
if CLIENT then
Msg( "[LC ULIB ERROR] " .. s .. "\n" )
else
Msg( "[LS ULIB ERROR] " .. s .. "\n" )
end
end
--[[
Function: debugFunctionCall
Prints a function call, very useful for debugging.
Parameters:
name - The name of the function called.
... - all arguments to the function.
Revisions:
v2.40 - Now uses print instead of Msg, since Msg seems to have a low max length.
Changed how the variable length params work so you can pass nil followed by more params
]]
function ULib.debugFunctionCall( name, ... )
local args = { ... }
print( "Function '" .. name .. "' called. Parameters:" )
for i=1, #args do
local value = ULib.serialize( args[ i ] )
print( "[PARAMETER " .. i .. "]: Type=" .. type( args[ i ] ) .. "\tValue=(" .. value .. ")" )
end
end

View File

@ -1,904 +0,0 @@
--[[
Title: Miscellaneous
Some utility functions. Unlike the functions in util.lua, this file only holds non-HL2 specific functions.
]]
--[[
Function: explode
Split a string by a separator.
Parameters:
separator - The separator string.
str - A string.
limit - *(Optional)* Max number of elements in the table
Returns:
A table of str split by separator, nil and error message otherwise.
Revisions:
v2.10 - Initial (dragged over from a GM9 archive though)
]]
function ULib.explode( separator, str, limit )
local t = {}
local curpos = 1
while true do -- We have a break in the loop
local newpos, endpos = str:find( separator, curpos ) -- find the next separator in the string
if newpos ~= nil then -- if found then..
table.insert( t, str:sub( curpos, newpos - 1 ) ) -- Save it in our table.
curpos = endpos + 1 -- save just after where we found it for searching next time.
else
if limit and #t > limit then
return t -- Reached limit
end
table.insert( t, str:sub( curpos ) ) -- Save what's left in our array.
break
end
end
return t
end
--[[
Function: stripComments
Strips comments from a string
Parameters:
str - The string to stip comments from
comment - The comment string. If it's found, whatever comes after it on that line is ignored. ( IE: "//" )
blockcommentbeg - *(Optional)* The block comment begin string. ( IE: "/<star>" )
blockcommentend - *(Optional, must be specified if above parameter is)* The block comment end string. ( IE: "<star>/" )
Returns:
The string with the comments stripped, nil and error otherwise.
Revisions:
v2.02 - Fixed block comments in more complicated files.
]]
function ULib.stripComments( str, comment, blockcommentbeg, blockcommentend )
if blockcommentbeg and string.sub( blockcommentbeg, 1, string.len( comment ) ) == comment then -- If the first of the block comment is the linecomment ( IE: --[[ and -- ).
string.gsub( str, ULib.makePatternSafe( comment ) .. "[%S \t]*", function ( match )
if string.sub( match, 1, string.len( blockcommentbeg ) ) == blockcommentbeg then
return "" -- No substitution, this is a block comment.
end
str = string.gsub( str, ULib.makePatternSafe( match ), "", 1 )
return ""
end )
str = string.gsub( str, ULib.makePatternSafe( blockcommentbeg ) .. ".-" .. ULib.makePatternSafe( blockcommentend ), "" )
else -- Doesn't need special processing.
str = string.gsub( str, ULib.makePatternSafe( comment ) .. "[%S \t]*", "" )
if blockcommentbeg and blockcommentend then
str = string.gsub( str, ULib.makePatternSafe( blockcommentbeg ) .. ".-" .. ULib.makePatternSafe( blockcommentend ), "" )
end
end
return str
end
--[[
Function: makePatternSafe
Makes a string safe for pattern usage, like in string.gsub(). Basically replaces all keywords with % and keyword.
Parameters:
str - The string to make pattern safe
Returns:
The pattern safe string
]]
function ULib.makePatternSafe( str )
return str:gsub( "([%(%)%.%%%+%-%*%?%[%]%^%$])", "%%%1" )
end
--[[
Function: stripQuotes
Trims leading and tailing quotes from a string
Parameters:
s - The string to strip
Returns:
The stripped string
]]
function ULib.stripQuotes( s )
return s:gsub( "^%s*[\"]*(.-)[\"]*%s*$", "%1" )
end
--[[
Function: unescapeBackslash
Converts '\\' to '\'
Parameters:
s - The string to convert
Returns:
The converted string
]]
function ULib.unescapeBackslash( s )
return s:gsub( "\\\\", "\\" )
end
--[[
Function: splitPort
Parameters:
ipAndPort - An IP address in the form xxx.xxx.xxx.xxx:xxxx
Returns:
The IP as the first return value, the port as the second return value
Revisions:
v2.40 - Initial.
]]
function ULib.splitPort( ipAndPort )
return unpack( ULib.explode( ":", ipAndPort ) )
end
--[[
Function: splitArgs
This is similar to string.Explode( " ", str ) except that it will also obey quotation marks.
Parameters:
args - The string to split from
start_token - The string character to start a string with.
end_token - The string character to end a string with.
Returns:
A table containing the individual arguments and a boolean stating whether or not mismatched quotes were found.
Example:
:ULib.splitArgs( "This is a \"Cool sentence to\" make \"split up\"" )
returns...
:{ "This", "is", "a", "Cool sentence to", "make", "split up" }
Notes:
* Mismatched quotes will result in having the last quote grouping the remaining input into
one argument.
* Arguments outside of quotes are trimmed (via string.Trim), while what's inside quotes is not
trimmed at all.
Revisions:
v2.10 - Can now handle tabs and trims strings before returning.
v2.30 - Rewrite. Can now properly handle escaped quotes. New param, ignore_mismatch.
v2.40 - Rewrite. Much more stable and predictable now. Removed ignore_mismatch param. As
far as I can tell, it now matches the source engine's split arg behavior exactly. Also
accepts tokens to consider a string.
]]
function ULib.splitArgs( args, start_token, end_token )
args = args:Trim()
local argv = {}
local curpos = 1 -- Our current position within the string
local in_quote = false -- Is the text we're currently processing in a quote?
start_token = start_token or "\""
end_token = end_token or "\""
local args_len = args:len()
while in_quote or curpos <= args_len do
local quotepos = args:find( in_quote and end_token or start_token, curpos, true )
-- The string up to the quote, the whole string if no quote was found
local prefix = args:sub( curpos, (quotepos or 0) - 1 )
if not in_quote then
local trimmed = prefix:Trim()
if trimmed ~= "" then -- Something to be had from this...
local t = ULib.explode( "%s+", trimmed )
table.Add( argv, t )
end
else
table.insert( argv, prefix )
end
-- If a quote was found, reduce our position and note our state
if quotepos ~= nil then
curpos = quotepos + 1
in_quote = not in_quote
else -- Otherwise we've processed the whole string now
break
end
end
return argv, in_quote
end
--[[
Function: parseKeyValues
Parses a keyvalue formatted string into a table.
Parameters:
str - The string to parse.
convert - *(Optional, defaults to false)* Setting this to true will convert garry's keyvalues to a better form. This has two effects.
First, it will remove the "Out"{} wrapper. Second, it will convert any keys that equate to a number to a number.
Returns:
The table, nil and error otherwise. *If you find you're missing information from the table, the file format might be incorrect.*
Example format:
:test
:{
: "howdy" "bbq"
:
: foo
: {
: "bar" "false"
: }
:
:}
Revisions:
v2.10 - Initial (but tastefully stolen from a GM9 version)
v2.30 - Rewrite. Much more robust and properly unescapes backslashes now.
v2.40 - Properly handles escaped quotes now.
]]
function ULib.parseKeyValues( str, convert )
local lines = ULib.explode( "\r?\n", str )
local parent_tables = {} -- Traces our way to root
local current_table = {}
local is_insert_last_op = false
for i, line in ipairs( lines ) do
local tmp_string = string.char( 01, 02, 03 ) -- Replacement
local tokens = ULib.splitArgs( (line:gsub( "\\\"", tmp_string )) )
for i, token in ipairs( tokens ) do
tokens[ i ] = ULib.unescapeBackslash( token ):gsub( tmp_string, "\"" )
end
local num_tokens = #tokens
if num_tokens == 1 then
local token = tokens[ 1 ]
if token == "{" then
local new_table = {}
if is_insert_last_op then
current_table[ table.remove( current_table ) ] = new_table
else
table.insert( current_table, new_table )
end
is_insert_last_op = false
table.insert( parent_tables, current_table )
current_table = new_table
elseif token == "}" then
is_insert_last_op = false
current_table = table.remove( parent_tables )
if current_table == nil then
return nil, "Mismatched recursive tables on line " .. i
end
else
is_insert_last_op = true
table.insert( current_table, tokens[ 1 ] )
end
elseif num_tokens == 2 then
is_insert_last_op = false
if convert and tonumber( tokens[ 1 ] ) then
tokens[ 1 ] = tonumber( tokens[ 1 ] )
end
current_table[ tokens[ 1 ] ] = tokens[ 2 ]
elseif num_tokens > 2 then
return nil, "Bad input on line " .. i
end
end
if #parent_tables ~= 0 then
return nil, "Mismatched recursive tables"
end
if convert and table.Count( current_table ) == 1 and
type( current_table.Out ) == "table" then -- If we caught a stupid garry-wrapper
current_table = current_table.Out
end
return current_table
end
--[[
Function: makeKeyValues
Makes a key values string from a table.
Parameters:
t - The table to make the keyvalues from. This can only contain tables, numbers, and strings.
tab - *Only for internal use*, this helps make inline tables look better.
completed - A list of table values that have already been parsed, this is *only for internal use* to make sure we don't hit an infinite loop.
Returns:
The string, nil and error otherwise.
Notes:
If you use numbers as keys in the table, just the values will be used.
Example table format:
:{ test = { howdy = "bbq", foo = { bar = "false" } } }
Example return format:
:test
:{
: "howdy" "bbq"
:
: foo
: {
: "bar" "false"
: }
:
:}
Revisions:
v2.10 - Initial (but tastefully stolen from a GM9 version)
v2.40 - Increased performance for insanely high table counts.
]]
function ULib.makeKeyValues( t, tab, completed )
ULib.checkArg( 1, "ULib.makeKeyValues", "table", t )
tab = tab or ""
completed = completed or {}
if completed[ t ] then return "" end -- We've already done this table.
completed[ t ] = true
local str = ""
for k, v in pairs( t ) do
str = str .. tab
if type( k ) ~= "number" then
str = string.format( "%s%q\t", str, tostring( k ) )
end
if type( v ) == "table" then
str = string.format( "%s\n%s{\n%s%s}\n", str, tab, ULib.makeKeyValues( v, tab .. "\t", completed ), tab )
elseif type( v ) == "string" then
str = string.format( "%s%q\n", str, v )
else
str = str .. tostring( v ) .. "\n"
end
end
return str
end
--[[
Function: toBool
Converts a bool, nil, string, or number to a bool
Parameters:
x - The string or number
Returns:
The bool
Revisions:
v2.10 - Initial.
v2.40 - Added ability to convert nils and bools.
]]
function ULib.toBool( x )
if type( x ) == "boolean" then return x end
if x == nil then return false end
if tonumber( x ) ~= nil then
x = math.Round( tonumber( x ) )
if x == 0 then
return false
else
return true
end
end
x = x:lower()
if x == "t" or x == "true" or x == "yes" or x == "y" then
return true
else
return false
end
end
--[[
Function: findVar
Given a string, find a var starting from the global namespace. This will correctly parse tables. IE, "ULib.serialize".
Parameters:
var - The variable you wish to find
Returns:
The variable or nil
Revisions:
v2.40 - Removed dependency on gmod functions.
]]
function ULib.findVar( var )
if not var then error( "Nil param passed to ULib.findVar", 2 ) end
local loc = ULib.explode( "%.", var )
local x = _G
for _, v in ipairs( loc ) do
x = x[ v ]
if not x then return end
end
return x
end
--[[
Function: throwBadArg
Throws an error similar to the lua "bad argument #x to <fn_name> (<type> expected, got <type>).
Parameters:
argnum - *(Optional)* The argument number that was bad.
fnName - *(Optional)* The name of the function being called.
expected - *(Optional)* The string of the type you expected.
data - *(Optional)* The actual data you got.
throwLevel - *(Optional, defaults to 3)* How many levels up to throw the error.
Returns:
Never returns, throws an error
Revisions:
v2.40 - Initial.
]]
function ULib.throwBadArg( argnum, fnName, expected, data, throwLevel )
throwLevel = throwLevel or 3
local str = "bad argument"
if argnum then
str = str .. " #" .. tostring( argnum )
end
if fnName then
str = str .. " to " .. fnName
end
if expected or data then
str = str .. " ("
if expected then
str = str .. expected .. " expected"
end
if expected and data then
str = str .. ", "
end
if data then
str = str .. "got " .. type( data )
end
str = str .. ")"
end
error( str, throwLevel )
end
--[[
Function: checkArg
Checks to see if an arg matches what is expected, if not, calls throwBadArg().
Parameters:
argnum - *(Optional)* The argument number you're.
fnName - *(Optional)* The name of the function being called.
expected - The string of the type you expect or a table of types you expect.
data - The actual data you got.
throwLevel - *(Optional, defaults to 4)* How many levels up to throw the error.
Returns:
Never returns if the data is bad, throws an error. Otherwise returns nil.
Revisions:
v2.40 - Initial.
]]
function ULib.checkArg( argnum, fnName, expected, data, throwLevel )
throwLevel = throwLevel or 4
if type( expected ) == "string" then
if type( data ) == expected then
return
else
return ULib.throwBadArg( argnum, fnName, expected, data, throwLevel )
end
else
if table.HasValue( expected, type( data ) ) then
return
else
return ULib.throwBadArg( argnum, fnName, table.concat( expected, "," ), data, throwLevel )
end
end
end
--[[
Function: isValidSteamID
Checks to see if a given string is a valid steamid.
Parameters:
steamid - The string of the supposed steamid.
Returns:
True if it's valid, false if not.
Revisions:
v2.40 - Initial.
]]
function ULib.isValidSteamID( steamid )
return steamid:match( "^STEAM_%d:%d:%d+$" ) ~= nil
end
--[[
Function: isValidIP
Checks to see if a given string is a valid IPv4 address.
Parameters:
ip - The string of the supposed ip.
Returns:
True if it's valid, false if not.
Revisions:
v2.40 - Initial.
]]
function ULib.isValidIP( ip )
if ip:find( "^%d%d?%d?%.%d%d?%d?%.%d%d?%d?%.%d%d?%d?$" ) then
return true
else
return false
end
end
--[[
Function: removeCommentHeader
Removes a comment header.
Parameters:
data - The string to remove the comment from.
comment_char - The comment char.
Returns:
Data without the comment header.
Revisions:
v2.40 - Initial.
]]
function ULib.removeCommentHeader( data, comment_char )
comment_char = comment_char or ";"
local lines = ULib.explode( "\r?\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 not_comment = table.concat( lines, "\n", end_comment_line + 1 )
return not_comment:Trim()
end
--[[
Function: stringTimeToSeconds
Converts a string containing time information to seconds.
Parameters:
str - The time string. Defaults to minutes, "h" is for hours, "d" is for days, "w" is for weeks.
Returns:
The number of minutes represented by the string or nil if it's unable to parse the string.
Revisions:
v2.41 - Initial
v2.43 - Added year parameter
]]
function ULib.stringTimeToSeconds( str )
if str == nil or type( str ) == "number" then
return str
end
str = str:gsub( " ", "" )
local minutes = 0
local keycode_location = str:find( "%a" )
while keycode_location do
local keycode = str:sub( keycode_location, keycode_location )
local num = tonumber( str:sub( 1, keycode_location - 1 ) )
if not num then
return nil
end
local multiplier
if keycode == "h" then
multiplier = 60
elseif keycode == "d" then
multiplier = 60 * 24
elseif keycode == "w" then
multiplier = 60 * 24 * 7
elseif keycode == "y" then
multiplier = 60 * 24 * 365
else
return nil
end
str = str:sub( keycode_location + 1 )
keycode_location = str:find( "%a" )
minutes = minutes + num * multiplier
end
local num = 0
if str ~= "" then
num = tonumber( str )
end
if num == nil then
return nil
end
return minutes + num
end
--[[
Section: Inheritance
]]
--[[
Function: inheritsFrom
Creates a psudeo-inheritance for lua. It will search for variables that do
not exist in derived 'classes' in the parent 'classes', among other things
explained below.
Parameters:
baseClass - The class to derive from. This value *must* either be nil
or a class created using <inheritsFrom()>.
Returns:
The table of the derived class.
Revisions:
v2.40 - Initial.
Notes:
* Adapted with improvements from a lua-users inheritance tutorial
<http://lua-users.org/wiki/InheritanceTutorial>.
* Create using Class:create( ... ) or Class( ... ) (equivalent).
* Whatever's passed in the '...', above, is passed to
derived_class:instantiate(). This allows for a 'constructor'.
See Also:
* <root_class>
* <root_class:create>
* <root_class:class>
* <root_class:superClass>
* <root_class:instantiate>
* <root_class:isa>
Example:
:b = inherits_from( nil )
:function b:instantiate( ... )
: print( "base", unpack( arg ) )
:end
:
:d = inherits_from( b )
:function d:instantiate( ... )
: print( "derived", unpack( arg ) )
:end
:
:b1 = b( "should be base" )
:d1 = d( "should be derived" )
:print( "d1 is d?", d1:isa( d ), "is b?", d1:isa( b ) )
:print( "b1 is d?", b1:isa( d ), "is b?", b1:isa( b ) )
Output:
:base should be base
:derived should be derived
:d1 is d? true is b? true
:b1 is d? false is b? true
]]
function inheritsFrom( base_class )
local new_class = {}
-- The meta-table for INSTANCES (IE, created with Class:create())
local instance_mt = { __index = new_class, class=new_class, base_class=base_class }
-- The meta-table for the root_class (this will only ever have one table associated with it)
local class_mt = table.Copy( instance_mt ) -- Only a few differences so copy
class_mt.__index = base_class or root_class -- Use base or our special meta-base
class_mt.__call = root_class.call -- Set up call alias
class_mt.class = new_class -- Set up alias to ourself
class_mt.instance_mt = instance_mt -- Need this for root_class:create()
setmetatable( new_class, class_mt )
return new_class
end
--[[
Table: root_class
This is a local table that holds our functions that we want *all* classes
to have.
]]
root_class = {}
--[[
Function: root_class:call
This is a utility function used by the metatable __call to resolve Class( ... ) to Class:create( ... ).
Parameters:
parent_table - The table of the caller.
... - Extra construction parameters, passed to Class:instantiate.
Returns:
The 'class instance'.
Revisions:
v2.40 - Initial.
]]
function root_class.call( parent_table, ... )
return parent_table:class():create( ... )
end
--[[
Function: root_class:create
This is used to create new 'class instances'.
Parameters:
... - Extra construction parameters, passed to Class:instantiate.
Revisions:
v2.40 - Initial.
]]
function root_class:create( ... )
local newinst = {}
setmetatable( newinst, getmetatable( self ).instance_mt )
newinst:instantiate( ... ) -- 'Constructor'
return newinst
end
-- Return the class object of the instance
function root_class:class()
return getmetatable( self ).class
end
-- Return the super class object of the instance
function root_class:superClass()
base_class = getmetatable( self ).base_class
return base_class ~= root_class and base_class or nil -- Nil if root class
end
-- We need to make sure this func exists, but can be overridden
function root_class:instantiate()
end
-- Return true if the caller is an instance of theClass
function root_class:isa( target_class )
local cur_class = self:class()
while cur_class and not b_isa do
if cur_class == target_class then
return true
else
cur_class = cur_class:superClass()
end
end
return false
end
function isClass( obj )
return type( obj ) == "table" and type( obj.isa ) == "function" and obj:isa( root_class )
end
-- This wonderful bit of following code will make sure that no rogue coder can screw us up by changing the value of '_'
_ = nil -- Make sure we're starting out right.
local meta = getmetatable( _G ) or {}
if type( meta ) == "boolean" then return end -- Metatable is protected, so we aren't able to run this code without erroring.
local old__newindex = meta.__newindex
setmetatable( _G, meta )
function meta.__newindex( t, k, v )
if k == "_" then
-- If you care enough to fix bad scripts uncomment this following line.
-- error( "attempt to modify global variable '_'", 2 )
return
end
if old__newindex then
old__newindex( t, k, v )
else
rawset( t, k, v )
end
end

View File

@ -1,344 +0,0 @@
--[[
Title: Player
Has useful player-related functions.
]]
--[[
Function: getPicker
Gets the player directly in front of the specified player
Parameters:
ply - The player to look for another player in front of.
radius - *(Optional, defaults to 30)* How narrow to make our checks for players in front of us.
Returns:
The player most directly in front of us if one exists with the given constraints, otherwise nil.
Revisions:
v2.40 - Initial.
]]
function ULib.getPicker( ply, radius )
radius = radius or 30
local trace = util.GetPlayerTrace( ply )
local trace_results = util.TraceLine( trace )
if not trace_results.Entity:IsValid() or not trace_results.Entity:IsPlayer() then
-- Try finding a best choice
local best_choice
local best_choice_diff
local pos = ply:GetPos()
local ang = ply:GetAimVector():Angle()
local players = player.GetAll()
for _, player in ipairs( players ) do
if player ~= ply then
local vec_diff = player:GetPos() - Vector( 0, 0, 16 ) - pos
local newang = vec_diff:Angle()
local diff = math.abs( math.NormalizeAngle( newang.pitch - ang.pitch ) ) + math.abs( math.NormalizeAngle( newang.yaw - ang.yaw ) )
if not best_choice_diff or diff < best_choice_diff then
best_choice_diff = diff
best_choice = player
end
end
end
if not best_choice or best_choice_diff > radius then
return -- Give up
else
return best_choice
end
else
return trace_results.Entity
end
end
local Player = FindMetaTable( "Player" )
local checkIndexes = { Player.UniqueID, function( ply ) if CLIENT then return "" end local ip = ULib.splitPort( ply:IPAddress() ) return ip end, Player.SteamID, Player.UserID }
--[[
Function: getPlyByID
Finds a user identified by the given ID.
Parameters:
id - The ID to try to match against connected players. Can be a unique id, ip address, steam id, or user id.
Returns:
The player matching the id given or nil if none match.
Revisions:
v2.50 - Initial.
]]
function ULib.getPlyByID( id )
id = id:upper()
local players = player.GetAll()
for _, indexFn in ipairs( checkIndexes ) do
for _, ply in ipairs( players ) do
if tostring( indexFn( ply ) ) == id then
return ply
end
end
end
return nil
end
--[[
Function: getUniqueIDForPly
Finds a unique ID for a player, suitable for use in getUsers or getUser to uniquely identify the given player.
Parameters:
ply - The player we want an ID for
Returns:
The id for the player or nil if none are unique.
Revisions:
v2.50 - Initial.
v2.51 - Added exception for single player since it's handled differently on client and server.
]]
function ULib.getUniqueIDForPlayer( ply )
if game.SinglePlayer() then
return "1"
end
local players = player.GetAll()
for _, indexFn in ipairs( checkIndexes ) do
local id = indexFn( ply )
if ULib.getUser( "$" .. id, true ) == ply then
return id
end
end
return nil
end
--[[
Function: getUsers
Finds users matching an identifier.
Parameters:
target - A string of what you'd like to target. Accepts a comma separated list.
enable_keywords - *(Optional, defaults to false)* If true, the keywords "*" for all players, "^" for self,
"@" for picker (person in front of you), "#<group>" for those inside a specific group,
"%<group>" for users inside a group (counting inheritance), and "$<id>" for users matching a
particular ID will be activated.
Any of these can be negated with "!" before it. IE, "!^" targets everyone but yourself.
ply - *(Optional)* Player needing getUsers, this is necessary for some of the keywords.
Returns:
A table of players (false and message if none found).
Revisions:
v2.40 - Rewrite, added more keywords, removed immunity.
v2.50 - Added "#" and '$' keywords, removed special exception for "%user" (replaced by "#user").
]]
function ULib.getUsers( target, enable_keywords, ply )
local players = player.GetAll()
-- First, do a full name match in case someone's trying to exploit our target system
for _, player in ipairs( players ) do
if target:lower() == player:Nick():lower() then
return { player }
end
end
-- Okay, now onto the show!
local targetPlys = {}
local pieces = ULib.explode( ",", target )
for _, piece in ipairs( pieces ) do
piece = piece:Trim()
if piece ~= "" then
local keywordMatch = false
if enable_keywords then
local tmpTargets = {}
local negate = false
if piece:sub( 1, 1 ) == "!" and piece:len() > 1 then
negate = true
piece = piece:sub( 2 )
end
if piece:sub( 1, 1 ) == "$" then
local player = ULib.getPlyByID( piece:sub( 2 ) )
if player then
table.insert( tmpTargets, player )
end
elseif piece == "*" then -- All!
table.Add( tmpTargets, players )
elseif piece == "^" then -- Self!
if ply then
if ply:IsValid() then
table.insert( tmpTargets, ply )
elseif not negate then
return false, "You cannot target yourself from console!"
end
end
elseif piece == "@" then
if IsValid( ply ) then
local player = ULib.getPicker( ply )
if player then
table.insert( tmpTargets, player )
end
end
elseif piece:sub( 1, 1 ) == "#" and ULib.ucl.groups[ piece:sub( 2 ) ] then
local group = piece:sub( 2 )
for _, player in ipairs( players ) do
if player:GetUserGroup() == group then
table.insert( tmpTargets, player )
end
end
elseif piece:sub( 1, 1 ) == "%" and ULib.ucl.groups[ piece:sub( 2 ) ] then
local group = piece:sub( 2 )
for _, player in ipairs( players ) do
if player:CheckGroup( group ) then
table.insert( tmpTargets, player )
end
end
else
local tblForHook = hook.Run( ULib.HOOK_GETUSERS_CUSTOM_KEYWORD, piece, ply )
if tblForHook then
table.Add( tmpTargets, tblForHook )
end
end
if negate then
for _, player in ipairs( players ) do
if not table.HasValue( tmpTargets, player ) then
keywordMatch = true
table.insert( targetPlys, player )
end
end
else
if #tmpTargets > 0 then
keywordMatch = true
table.Add( targetPlys, tmpTargets )
end
end
end
if not keywordMatch then
for _, player in ipairs( players ) do
if player:Nick():lower():find( piece:lower(), 1, true ) then -- No patterns
table.insert( targetPlys, player )
end
end
end
end
end
-- Now remove duplicates
local finalTable = {}
for _, player in ipairs( targetPlys ) do
if not table.HasValue( finalTable, player ) then
table.insert( finalTable, player )
end
end
if #finalTable < 1 then
return false, "No target found or target has immunity!"
end
return finalTable
end
--[[
Function: getUser
Finds a user matching an identifier.
Parameters:
target - A string of the user you'd like to target. IE, a partial player name.
enable_keywords - *(Optional, defaults to false)* If true, the keywords "^" for self, "@" for picker (person in
front of you), and "$<id>" will be activated.
ply - *(Optional)* Player needing getUsers, this is necessary to use keywords.
Returns:
The resulting player target, false and message if no user found.
Revisions:
v2.40 - Rewrite, added keywords, removed immunity.
v2.50 - Added "$" keyword.
]]
function ULib.getUser( target, enable_keywords, ply )
local players = player.GetAll()
target = target:lower()
local plyMatches = {}
if enable_keywords and target:sub( 1, 1 ) == "$" then
possibleId = target:sub( 2 )
table.insert( plyMatches, ULib.getPlyByID( possibleId ) )
end
-- First, do a full name match in case someone's trying to exploit our target system
for _, player in ipairs( players ) do
if target == player:Nick():lower() then
if #plyMatches == 0 then
return player
else
return false, "Found multiple targets! Please choose a better string for the target. (EG, the whole name)"
end
end
end
if enable_keywords then
if target == "^" and ply then
if ply:IsValid() then
return ply
else
return false, "You cannot target yourself from console!"
end
elseif IsValid( ply ) and target == "@" then
local player = ULib.getPicker( ply )
if not player then
return false, "No player found in the picker"
else
return player
end
else
local player = hook.Run( ULib.HOOK_GETUSER_CUSTOM_KEYWORD, target, ply )
if player then return player end
end
end
for _, player in ipairs( players ) do
if player:Nick():lower():find( target, 1, true ) then -- No patterns
table.insert( plyMatches, player )
end
end
if #plyMatches == 0 then
return false, "No target found or target has immunity!"
elseif #plyMatches > 1 then
local str = plyMatches[ 1 ]:Nick()
for i=2, #plyMatches do
str = str .. ", " .. plyMatches[ i ]:Nick()
end
return false, "Found multiple targets: " .. str .. ". Please choose a better string for the target. (EG, the whole name)"
end
return plyMatches[ 1 ]
end

View File

@ -1,303 +0,0 @@
--[[
Title: Shared UCL
Shared UCL stuff.
]]
--[[
Table: ucl
Holds all of the ucl variables and functions
]]
ULib.ucl = ULib.ucl or {}
local ucl = ULib.ucl -- Make it easier for us to refer to
-- Setup!
ucl.groups = ucl.groups or {} -- Stores allows, inheritance, and custom addon info keyed by group name
ucl.users = ucl.users or {} -- Stores allows, denies, group, and last seen name keyed by user id (steamid, ip, whatever)
ucl.authed = ucl.authed or {} -- alias to ucl.users subtable for player if they have an entry, otherwise a "guest" entry. Keyed by uniqueid.
-- End setup
--[[
Function: ucl.query
This function is used to see if a user has access to a command.
Parameters:
ply - The player to check access for
access - The access string to check for. (IE "ulx slap", doesn't have to be a command though). If nil is passed in, this always
returns true.
hide - *(Optional, defaults to false)* Normally, a listen host is automatically given access to everything.
Set this to true if you want to treat the listen host as a normal user. (Will be denied commands that no one has access to)
Returns:
A bool signifying whether or not they have access.
Revisions:
v2.40 - Rewrite.
]]
function ucl.query( ply, access, hide )
if SERVER and (not ply:IsValid() or (not hide and ply:IsListenServerHost())) then return true end -- Grant full access to server host.
if access == nil then return true end
-- if ply:IsBot() then return false end -- Bots have no access!
access = access:lower()
local unique_id = ply:UniqueID()
if CLIENT and game.SinglePlayer() then
unique_id = "1" -- Fix garry's bug
end
if not ucl.authed[ unique_id ] then return error( "[ULIB] Unauthed player" ) end -- Sanity check
local playerInfo = ucl.authed[ unique_id ]
-- First check the player's info
if table.HasValue( playerInfo.deny, access ) then return false end -- Deny overrides all else
if table.HasValue( playerInfo.allow, access ) then return true end
if playerInfo.allow[ access ] then return true, playerInfo.allow[ access ] end -- Access tag
-- Now move onto groups and group inheritance
local group = ply:GetUserGroup()
while group do -- While group is not nil
local groupInfo = ucl.groups[ group ]
if not groupInfo then return error( "[ULib] Player " .. ply:Nick() .. " has an invalid group (" .. group .. "), aborting. Please be careful when modifying the ULib files!" ) end
if table.HasValue( groupInfo.allow, access ) then return true end
if groupInfo.allow[ access ] then return true, groupInfo.allow[ access ] end
group = ucl.groupInheritsFrom( group )
end
-- No specific instruction, assume they don't have access.
return nil
end
--[[
Function: ucl.groupInheritsFrom
This function is used to see if a specified group is inheriting from another
Parameters:
group - The group to check inheritance on. Must be a valid group.
Returns:
The group this group is inheriting from or nil (everything implicity inherits from "user", "user" inherits from nil).
Revisions:
v2.40 - Initial.
]]
function ucl.groupInheritsFrom( group )
if not ucl.groups[ group ] then return false end
if group == ULib.ACCESS_ALL then -- Force this to inherit from nil
return nil
elseif not ucl.groups[ group ].inherit_from or ucl.groups[ group ].inherit_from == "" then
return ULib.ACCESS_ALL
else
return ucl.groups[ group ].inherit_from
end
end
--[[
Function: ucl.getInheritanceTree
This function returns a tree-like structure representing the group inheritance architecture.
Returns:
The inheritance tree.
Example return:
:PrintTable( ULib.ucl.getInheritanceTree() )
:user:
: trusted:
: members:
: thedumbones:
: admin:
: superadmin:
: serverowner:
: clanowner:
: respected:
Revisions:
v2.40 - Initial
]]
function ucl.getInheritanceTree()
local inherits = { [ULib.ACCESS_ALL]={} }
local find = { [ULib.ACCESS_ALL]=inherits[ULib.ACCESS_ALL] }
for group, _ in pairs( ucl.groups ) do
if group ~= ULib.ACCESS_ALL then
local inherits_from = ucl.groupInheritsFrom( group )
if not inherits_from then inherits_from = ULib.ACCESS_ALL end
find[ inherits_from ] = find[ inherits_from ] or {} -- Use index if it exists, otherwise create one for this group
find[ group ] = find[ group ] or {} -- If someone's created our index, use it. Otherwise, create one.
find[ inherits_from ][ group ] = find[ group ]
end
end
return inherits
end
--[[
Function: ucl.getGroupCanTarget
Gets what a group is allowed to target in the UCL.
Parameters:
group - A string of the group name. (IE: superadmin)
Returns:
The string of who they're allowed to target (IE: !%admin) or nil if there's no restriction.
Revisions:
v2.40 - Initial.
]]
function ucl.getGroupCanTarget( group )
ULib.checkArg( 1, "ULib.ucl.getGroupCanTarget", "string", group )
if not ucl.groups[ group ] then return error( "Group does not exist (" .. group .. ")", 2 ) end
return ucl.groups[ group ].can_target
end
-- Client init stuff
if CLIENT then
function ucl.initClientUCL( authed, groups )
ucl.authed = authed
ucl.groups = groups
end
end
------------------
--//Meta hooks//--
------------------
local meta = FindMetaTable( "Player" )
if not meta then return end
--[[
Function: Player:query
This is an alias of ULib.ucl.query()
]]
function meta:query( access, hide )
return ULib.ucl.query( self, access, hide )
end
local origIsAdmin = meta.IsAdmin
--[[
Function: Player:IsAdmin
Overwrite garry's IsAdmin function to check for membership in admin group. This is so if group "serverowner"
inherits from superadmin, this function will still return true when checking on a member belonging to the
"serverowner" group.
Returns:
True is the user belongs in the admin group directly or indirectly, false otherwise.
Revisions:
v2.40 - Rewrite.
]]
function meta:IsAdmin()
if ucl.groups[ ULib.ACCESS_ADMIN ] then
return self:CheckGroup( ULib.ACCESS_ADMIN )
else -- Group doesn't exist, fall back on garry's method
origIsAdmin( self )
end
end
local origIsSuperAdmin = meta.IsSuperAdmin
--[[
Function: Player:IsSuperAdmin
Overwrite garry's IsSuperAdmin function to check for membership in admin group. This is so if group "serverowner"
inherits from superadmin, this function will still return true when checking on a member belonging to the
"serverowner" group.
Returns:
True is the user belongs in the superadmin group directly or indirectly, false otherwise.
Revisions:
v2.40 - Rewrite.
]]
function meta:IsSuperAdmin()
if ucl.groups[ ULib.ACCESS_SUPERADMIN ] then
return self:CheckGroup( ULib.ACCESS_SUPERADMIN )
else -- Group doesn't exist, fall back on garry's method
origIsSuperAdmin( self )
end
end
--[[
Function: Player:GetUserGroup
This should have been included with garrysmod by default, so ULib is creating it for us.
Returns:
The group the player belongs to.
Revisions:
v2.40 - Initial.
]]
function meta:GetUserGroup()
if not self:IsValid() then return "" end -- Not a valid player
local uid = self:UniqueID()
if CLIENT and game.SinglePlayer() then
uid = "1" -- Fix garry's bug
end
if not ucl.authed[ uid ] then return "" end
return ucl.authed[ uid ].group or "user"
end
--[[
Function: Player:CheckGroup
This function is similar to IsUserGroup(), but this one checks the UCL group chain as well.
For example, if a user is in group "owner" which inherits from "superadmin", this function
will return true if you check the user against "superadmin", where IsUserGroup() wouldn't.
Parameters:
group - The group you want to check a player's membership in.
Returns:
A boolean stating whether they have membership in the group or not.
Revisions:
v2.40 - Initial.
]]
function meta:CheckGroup( group_check )
if not ucl.groups[ group_check ] then return false end
local group = self:GetUserGroup()
while group do
if group == group_check then return true end
group = ucl.groupInheritsFrom( group )
end
return false
end

View File

@ -1,163 +0,0 @@
--[[
Title: Tables
Some table helpers.
]]
-- Based off "RecursiveReadOnlyTables" by VeLoSo (http://lua-users.org/wiki/RecursiveReadOnlyTables)
-- cache the metatables of all existing read-only tables,
-- so our functions can get to them, but user code can't
local metatable_cache = setmetatable( {}, { __mode = "k" } )
local function make_getter( real_table )
local function getter( dummy, key )
local ret = real_table[ key ]
if type( ret ) == "table" and not metatable_cache[ ret ] then
ret = ULib.makeReadOnly( ret )
end
return ret
end
return getter
end
local function setter()
ULib.error( "Attempt to modify read-only table!" )
end
local function make_pairs( real_table )
local function pairs()
local key, value, cur_key = nil, nil, nil
local function nexter() -- both args dummy
key, value = next( real_table, cur_key )
cur_key = key
if type( key ) == "table" and not metatable_cache[ key ] then
key = ULib.makeReadOnly( key )
end
if type( value ) == "table" and not metatable_cache[ value ] then
value = ULib.makeReadOnly( value )
end
return key, value
end
return nexter -- values 2 and 3 dummy
end
return pairs
end
--[[
Function: makeReadOnly
Makes a table and all recursive tables read-only
Parameters:
t - The table to make read-only
Returns:
The table read-only'fied
]]
function ULib.makeReadOnly( t )
local new={}
local mt={
__metatable = "read only table",
__index = make_getter( t ),
__newindex = setter,
__pairs = make_pairs( t ),
__type = "read-only table" }
setmetatable( new, mt )
metatable_cache[ new ] = mt
return new
end
--[[
Function: ropairs
The equivalent of "pairs" for a readonly table, since "pairs" won't work.
Parameters:
t - The table
]]
function ULib.ropairs( t )
local mt = metatable_cache[ t ]
if mt==nil then
ULib.error( "bad argument #1 to 'ropairs' (read-only table expected, got " .. type(t) .. ")" )
end
return mt.__pairs()
end
--[[
Function: findInTable
Finds a value in a table. As opposed to table.HasValue(), this function will *only* check numeric keys, and will return a number of where the value is.
Parameters:
t - The table to check
check - The value to check if it exists in t. Can be any type.
init - *(Optional, defaults to 1)* The value to start from.
last - *(Optional, defaults to the length of the table)* The value to end at.
recursive - *(Optional, default to false)* If true, it will check any subtables it comes across.
Returns:
The number of the key where check resides, false if none is found. If init > last it returns false as well.
]]
function ULib.findInTable( t, check, init, last, recursive )
init = init or 1
last = last or #t
if init > last then return false end
for i=init, last do
if t[ i ] == check then return i end
if type( t[ i ] ) == "table" and recursive then return ULib.findInTable( v, check, 1, recursive ) end
end
return false
end
--[[
Function: matrixTable
Splits a table into a number of given columns. Does not change original table.
Parameters:
t - The table to split
columns, The number of columns to create
Returns:
The new table with the column being the first key and the row being the second key.
Revisions:
v2.10 - Initial
]]
function ULib.matrixTable( t, columns )
local baserows = math.floor( #t / columns )
local remainder = math.fmod( #t, columns )
local nt = {} -- New table after we process
local curn = 1 -- What value to grab next from our old table
for i=1, columns do
local numtograb = baserows
if i <= remainder then
numtograb = baserows + 1
end
nt[ i ] = {}
for n=0, numtograb - 1 do
table.insert( nt[ i ], t[ curn + n ] )
end
curn = curn + numtograb
end
return nt
end

View File

@ -1,577 +0,0 @@
--[[
Title: Utilities
Some utility functions. Unlike the functions in misc.lua, this file only holds HL2 specific functions.
]]
local dataFolder = "data"
--[[
Function: fileExists
Checks for the existence of a file by path.
Parameters:
f - The path to check, rooted at the garry's mod root directory.
noMount - *(Optional)* If true, will not look in mounted directories.
Returns:
True if the file exists, false otherwise.
Revisions:
v2.51 - Initial revision (tired of Garry changing his API all the time).
v2.70 - Added noMount parameter to *only* look in mod directory.
]]
function ULib.fileExists( f, noMount )
if noMount then return file.Exists( f, "MOD" ) end
local isDataFolder = f:lower():sub( 1, dataFolder:len() ) ~= dataFolder
fWoData = f:sub( dataFolder:len() + 2 ) -- +2 removes path seperator
return file.Exists( f, "GAME" ) or (isDataFolder and file.Exists( fWoData, "DATA" ))
end
--[[
Function: fileRead
Reads a file and returns the contents. This function is not very forgiving on providing oddly formatted filepaths.
Parameters:
f - The file to read, rooted at the garrysmod directory.
noMount - *(Optional)* If true, will not look in mounted directories.
Returns:
The file contents or nil if the file does not exist.
Revisions:
v2.51 - Initial revision (tired of Garry changing his API all the time).
v2.70 - Added noMount parameter to *only* look in mod directory.
]]
function ULib.fileRead( f, noMount )
local existsWoMount = ULib.fileExists( f, true )
if noMount then
if not existsWoMount then
return nil
end
return file.Read( f, "MOD" )
end
local isDataFolder = f:lower():sub( 1, dataFolder:len() ) == dataFolder
fWoData = f:sub( dataFolder:len() + 2 ) -- +2 removes path seperator
if not existsWoMount and not ULib.fileExists( f ) then
return nil
end
if not isDataFolder then
return file.Read( f, "GAME" )
else
-- We want to prefer any data files at the root, but allow for mounted directories
if existsWoMount then
return file.Read( fWoData, "DATA" )
else
return file.Read( f, "GAME" )
end
end
end
--[[
Function: fileWrite
Writes file content.
Parameters:
f - The file path to write to, rooted at the garrysmod directory.
content - The content to write.
Revisions:
v2.51 - Initial revision (tired of Garry changing his API all the time).
]]
function ULib.fileWrite( f, content )
local isDataFolder = f:lower():sub( 1, dataFolder:len() ) == dataFolder
fWoData = f:sub( dataFolder:len() + 2 ) -- +2 removes path seperator
if not isDataFolder then return nil end
file.Write( fWoData, content )
end
--[[
Function: fileAppend
Append to file content.
Parameters:
f - The file path to append to, rooted at the garrysmod directory.
content - The content to append.
Revisions:
v2.51 - Initial revision (tired of Garry changing his API all the time).
]]
function ULib.fileAppend( f, content )
local isDataFolder = f:lower():sub( 1, dataFolder:len() ) == dataFolder
fWoData = f:sub( dataFolder:len() + 2 ) -- +2 removes path seperator
if not isDataFolder then return nil end
file.Append( fWoData, content )
end
--[[
Function: fileCreateDir
Create a directory.
Parameters:
f - The directory path to create, rooted at the garrysmod directory.
Revisions:
v2.51 - Initial revision (tired of Garry changing his API all the time).
]]
function ULib.fileCreateDir( f )
local isDataFolder = f:lower():sub( 1, dataFolder:len() ) == dataFolder
fWoData = f:sub( dataFolder:len() + 2 ) -- +2 removes path seperator
if not isDataFolder then return nil end
file.CreateDir( fWoData )
end
--[[
Function: fileDelete
Delete file contents.
Parameters:
f - The file path to delete, rooted at the garrysmod directory.
Revisions:
v2.51 - Initial revision (tired of Garry changing his API all the time).
]]
function ULib.fileDelete( f )
local isDataFolder = f:lower():sub( 1, dataFolder:len() ) == dataFolder
fWoData = f:sub( dataFolder:len() + 2 ) -- +2 removes path seperator
if not isDataFolder then return nil end
file.Delete( fWoData )
end
--[[
Function: fileIsDir
Is file a directory?
Parameters:
f - The file path to check, rooted at the garrysmod directory.
noMount - *(Optional)* If true, will not look in mounted directories.
Returns:
True if dir, false otherwise.
Revisions:
v2.51 - Initial revision (tired of Garry changing his API all the time).
v2.70 - Added noMount parameter to *only* look in mod directory.
]]
function ULib.fileIsDir( f, noMount )
if not noMount then
return file.IsDir( f, "GAME" )
else
return file.IsDir( f, "MOD" )
end
end
--[[
Function: execFile
Executes a file on the console. Use this instead of the "exec" command when the config lies outside the cfg folder.
Parameters:
f - The file, relative to the garrysmod folder.
queueName - The queue name to ULib.namedQueueFunctionCall to use.
noMount - *(Optional)* If true, will not look in mounted directories.
Revisions:
v2.40 - No longer strips comments, removed ability to execute on players.
v2.50 - Added option to conform to Garry's API changes and queueName to specify queue name to use.
v2.51 - Removed option parameter.
v2.70 - Added noMount parameter to *only* look in mod directory.
]]
function ULib.execFile( f, queueName, noMount )
if not ULib.fileExists( f, noMount ) then
ULib.error( "Called execFile with invalid file! " .. f )
return
end
ULib.execString( ULib.fileRead( f, noMount ), queueName )
end
--[[
Function: execString
Just like <execFile>, except acts on newline-delimited strings.
Parameters:
f - The string.
queueName - The queue name to ULib.namedQueueFunctionCall to use.
Revisions:
v2.40 - Initial.
v2.50 - Added queueName to specify queue name to use. Removed ability to execute on players.
]]
function ULib.execString( f, queueName )
local lines = string.Explode( "\n", f )
local buffer = ""
local buffer_lines = 0
local exec = "exec "
for _, line in ipairs( lines ) do
line = string.Trim( line )
if line:lower():sub( 1, exec:len() ) == exec then
local dummy, dummy, cfg = line:lower():find( "^exec%s+([%w%.]+)%s*/?/?.*$")
if not cfg:find( ".cfg", 1, true ) then cfg = cfg .. ".cfg" end -- Add it if it's not there
ULib.execFile( "cfg/" .. cfg, queueName )
elseif line ~= "" then
buffer = buffer .. line .. "\n"
buffer_lines = buffer_lines + 1
if buffer_lines >= 10 then
ULib.namedQueueFunctionCall( queueName, ULib.consoleCommand, buffer )
buffer_lines = 0
buffer = ""
end
end
end
if buffer_lines > 0 then
ULib.namedQueueFunctionCall( queueName, ULib.consoleCommand, buffer )
end
end
--[[
Function: serialize
Serializes a variable. It basically converts a variable into a runnable code string. It works correctly with inline tables.
Parameters:
v - The variable you wish to serialize
Returns:
The string of the serialized variable
Revisions:
v2.40 - Can now serialize entities and players
]]
function ULib.serialize( v )
local t = type( v )
local str
if t == "string" then
str = string.format( "%q", v )
elseif t == "boolean" or t == "number" then
str = tostring( v )
elseif t == "table" then
str = table.ToString( v )
elseif t == "Vector" then
str = "Vector(" .. v.x .. "," .. v.y .. "," .. v.z .. ")"
elseif t == "Angle" then
str = "Angle(" .. v.pitch .. "," .. v.yaw .. "," .. v.roll .. ")"
elseif t == "Player" then
str = tostring( v )
elseif t == "Entity" then
str = tostring( v )
elseif t == "nil" then
str = "nil"
else
ULib.error( "Passed an invalid parameter to serialize! (type: " .. t .. ")" )
return
end
return str
end
--[[
Function: isSandbox
Returns true if the current gamemode is sandbox or is derived from sandbox.
]]
function ULib.isSandbox()
return GAMEMODE.IsSandboxDerived
end
local function insertResult( files, result, relDir )
if not relDir then
table.insert( files, result )
else
table.insert( files, relDir .. "/" .. result )
end
end
--[[
Function: filesInDir
Returns files in directory.
Parameters:
dir - The dir to look for files in.
recurse - *(Optional, defaults to false)* If true, searches directories recursively.
noMount - *(Optional)* If true, will not look in mounted directories.
root - *INTERNAL USE ONLY* This helps with recursive functions.
Revisions:
v2.10 - Initial (But dragged over from GM9 archive).
v2.40 - Fixed (was completely broken).
v2.50 - Now assumes paths relative to base folder.
v2.60 - Fix for Garry API-changes
v2.70 - Added noMount parameter to *only* look in mod directory.
]]
function ULib.filesInDir( dir, recurse, noMount, root )
if not ULib.fileIsDir( dir ) then
return nil
end
local files = {}
local relDir
if root then
relDir = dir:gsub( root .. "[\\/]", "" )
end
root = root or dir
local resultFiles, resultFolders = file.Find( dir .. "/*", not noMount and "GAME" or "MOD" )
for i=1, #resultFiles do
insertResult( files, resultFiles[ i ], relDir )
end
for i=1, #resultFolders do
if recurse then
files = table.Add( files, ULib.filesInDir( dir .. "/" .. resultFolders[ i ], recurse, noMount, root ) )
else
insertResult( files, resultFolders[ i ], relDir )
end
end
return files
end
-- Helper function for <queueFunctionCall()>
local stacks = {}
local function onThink()
local remove = true
for queueName, stack in pairs( stacks ) do
local num = #stack
if num > 0 then
remove = false
local b, e = pcall( stack[ 1 ].fn, unpack( stack[ 1 ], 1, stack[ 1 ].n ) )
if not b then
ErrorNoHalt( "ULib queue error: " .. tostring( e ) .. "\n" )
end
table.remove( stack, 1 ) -- Remove the first inserted item. This is FIFO
end
end
if remove then
hook.Remove( "Think", "ULibQueueThink" )
end
end
--[[
Function: queueFunctionCall
Adds a function call to the queue to be called. Guaranteed to be called sometime after the current frame. Very handy
when you need to delay a call for some reason. Uses a think hook, but it's only hooked when there's stuff in the queue.
Parameters:
fn - The function to call
... - *(Optional)* The parameters to pass to the function
Revisions:
v2.40 - Initial (But dragged over from UPS).
]]
function ULib.queueFunctionCall( fn, ... )
if type( fn ) ~= "function" then
error( "queueFunctionCall received a bad function", 2 )
return
end
ULib.namedQueueFunctionCall( "defaultQueueName", fn, ... )
end
--[[
Function: namedQueueFunctionCall
Exactly like <queueFunctionCall()>, but allows for separately running queues to exist.
Parameters:
queueName - The unique name of the queue (the queue group)
fn - The function to call
... - *(Optional)* The parameters to pass to the function
Revisions:
v2.50 - Initial.
]]
function ULib.namedQueueFunctionCall( queueName, fn, ... )
queueName = queueName or "defaultQueueName"
if type( fn ) ~= "function" then
error( "queueFunctionCall received a bad function", 2 )
return
end
stacks[ queueName ] = stacks[ queueName ] or {}
table.insert( stacks[ queueName ], { fn=fn, n=select( "#", ... ), ... } )
hook.Add( "Think", "ULibQueueThink", onThink, HOOK_MONITOR_HIGH )
end
--[[
Function: backupFile
Copies a file to a backup file. If a backup file already exists, makes incrementing numbered backup files.
Parameters:
f - The file to backup, rooted in the garrysmod directory.
Returns:
The pathname of the file it was backed up to.
Revisions:
v2.40 - Initial.
]]
function ULib.backupFile( f )
local contents = ULib.fileRead( f )
local filename = f:GetFileFromFilename():sub( 1, -5 ) -- Remove '.txt'
local folder = f:GetPathFromFilename()
local num = 1
local targetPath = folder .. filename .. "_backup.txt"
while ULib.fileExists( targetPath ) do
num = num + 1
targetPath = folder .. filename .. "_backup" .. num .. ".txt"
end
-- We now have a filename that doesn't yet exist!
ULib.fileWrite( targetPath, contents )
return targetPath
end
--[[
Function: nameCheck
Checks all players' names at regular intervals to detect name changes. Calls ULibPlayerNameChanged if the name changed. *DO NOT CALL DIRECTLY*
Revisions:
2.20 - Initial
]]
function ULib.nameCheck()
local players = player.GetAll()
for _, ply in ipairs( players ) do
if not ply.ULibLastKnownName then ply.ULibLastKnownName = ply:Nick() end
if ply.ULibLastKnownName ~= ply:Nick() then
hook.Call( ULib.HOOK_PLAYER_NAME_CHANGED, nil, ply, ply.ULibLastKnownName, ply:Nick() )
ply.ULibLastKnownName = ply:Nick()
end
end
end
timer.Create( "ULibNameCheck", 1, 0, ULib.nameCheck )
--[[
Function: getPlyByUID
Parameters:
uid - The uid to lookup.
Returns:
The player that has the specified unique id, nil if none exists.
Revisions:
v2.40 - Initial.
]]
function ULib.getPlyByUID( uid )
local players = player.GetAll()
for _, ply in ipairs( players ) do
if ply:UniqueID() == uid then
return ply
end
end
return nil
end
--[[
Function: pcallError
An adaptation of a function that used to exist before GM13, allows you to
call functions safely and print errors (if it errors).
Parameters:
... - Arguments to pass to the function
Returns:
The same thing regular pcall returns
Revisions:
v2.50 - Initial.
]]
function ULib.pcallError( ... )
local returns = { pcall( ... ) }
if not returns[ 1 ] then -- The status flag
ErrorNoHalt( returns[ 2 ] ) -- The error message
end
return unpack( returns )
end

View File

@ -1 +1 @@
1447990092
1711250860