mirror of
https://github.com/NanoAi/gm_pug.git
synced 2025-03-04 03:03:15 -05:00
Initial Commit
This commit is contained in:
commit
70986191dc
5
.vscode/settings.json
vendored
Normal file
5
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"Lua.diagnostics.disable": [
|
||||
"undefined-global"
|
||||
]
|
||||
}
|
5
lua/autorun/server/sv_pug_init.lua
Normal file
5
lua/autorun/server/sv_pug_init.lua
Normal file
@ -0,0 +1,5 @@
|
||||
hook.Add("InitPostEntity", "PUG_Startup", function()
|
||||
timer.Simple(0, function()
|
||||
include("pug/sv_pug.lua")
|
||||
end)
|
||||
end)
|
200
lua/pug/modules/ghosting.lua
Normal file
200
lua/pug/modules/ghosting.lua
Normal file
@ -0,0 +1,200 @@
|
||||
local PUG = PUG
|
||||
local hook, timer = hook, timer
|
||||
local u = PUG.util
|
||||
|
||||
hook.Add("PUG_SetCollisionGroup", "PUGCollision", function( ent, group )
|
||||
local isGroupNone = ( group == COLLISION_GROUP_NONE )
|
||||
local checkEnt = ( ent.PUGBadEnt and not PUG:isGoodEnt( ent ) )
|
||||
if isGroupNone and checkEnt and ( not ent.PUGFrozen ) then
|
||||
return COLLISION_GROUP_INTERACTIVE
|
||||
end
|
||||
end)
|
||||
|
||||
hook.Add("PUG_EnableMotion", "PUGCollision", function( ent, _, bool )
|
||||
if bool and ent.PUGBadEnt then
|
||||
ent:SetCollisionGroup( COLLISION_GROUP_INTERACTIVE )
|
||||
end
|
||||
end)
|
||||
|
||||
local function isTrap( ent )
|
||||
local check = false
|
||||
|
||||
local isVehicle = u.isVehicle( ent )
|
||||
local center = ent:LocalToWorld( ent:OBBCenter() )
|
||||
local bRadius = ent:BoundingRadius()
|
||||
|
||||
for _,v in next, ents.FindInSphere( center, bRadius ) do
|
||||
local isLivingPlayer = ( v:IsPlayer() and v:Alive() )
|
||||
|
||||
if isVehicle then
|
||||
if isLivingPlayer then
|
||||
-- Check if the distance between the sphere centers is less
|
||||
-- than the sum of their radius.
|
||||
local vCenter = v:LocalToWorld( v:OBBCenter() )
|
||||
if center:Distance( vCenter ) < v:BoundingRadius() then
|
||||
check = v
|
||||
end
|
||||
end
|
||||
else
|
||||
if isLivingPlayer then
|
||||
local pos = v:GetPos()
|
||||
local trace = { start = pos, endpos = pos, filter = v }
|
||||
local tr = util.TraceEntity( trace, v )
|
||||
|
||||
if tr.Entity == ent then
|
||||
check = v
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if check then break end
|
||||
end
|
||||
|
||||
return check and true or false
|
||||
end
|
||||
|
||||
function PUG:Ghost( ent, ghost )
|
||||
if not ghost then
|
||||
self:UnGhost( ent )
|
||||
return
|
||||
end
|
||||
|
||||
if ent.jailWall then return end
|
||||
if not ent.PUGBadEnt then return end
|
||||
|
||||
ent.FPPAntiSpamIsGhosted = nil -- Override FPP Ghosting.
|
||||
ent.PUGGhost = ent.PUGGhost or {}
|
||||
ent.PUGGhost.collision = ent:GetCollisionGroup()
|
||||
|
||||
-- If and old collision group was set get it.
|
||||
if ent.OldCollisionGroup then -- FPP Compatibility
|
||||
ent.PUGGhost.collision = ent.OldCollisionGroup
|
||||
end
|
||||
|
||||
if ent.DPP_oldCollision then -- DPP Compatibility
|
||||
ent.PUGGhost.collision = ent.DPP_oldCollision
|
||||
end
|
||||
|
||||
ent.OldCollisionGroup = nil
|
||||
ent.DPP_oldCollision = nil
|
||||
ent.PUGGhosted = true
|
||||
|
||||
timer.Simple(0, function()
|
||||
if not IsValid( ent ) then return end
|
||||
|
||||
if not ent.PUGGhost.colour then
|
||||
ent.PUGGhost.colour = ent:GetColor()
|
||||
|
||||
-- Compatibility with other Ghosting
|
||||
if ent.OldColor then
|
||||
ent.PUGGhost.colour = ent.OldColor
|
||||
end
|
||||
|
||||
if ent.__DPPColor then
|
||||
ent.PUGGhost.colour = ent.__DPPColor
|
||||
end
|
||||
|
||||
ent.OldColor = nil
|
||||
ent.__DPPColor = nil
|
||||
end
|
||||
|
||||
ent:SetColor( Color(4, 20, 36, 250) )
|
||||
end)
|
||||
|
||||
ent.PUGGhost.render = ent:GetRenderMode()
|
||||
ent:SetRenderMode( RENDERMODE_TRANSALPHA )
|
||||
ent:DrawShadow( false )
|
||||
|
||||
if noCollide then
|
||||
ent:SetCollisionGroup( COLLISION_GROUP_WORLD )
|
||||
else
|
||||
ent:SetCollisionGroup( COLLISION_GROUP_DEBRIS_TRIGGER )
|
||||
end
|
||||
|
||||
do -- Fix magic surfing
|
||||
local phys = ent:GetPhysicsObject()
|
||||
if IsValid(phys) then
|
||||
phys:EnableCollisions( false )
|
||||
timer.Simple(0, function()
|
||||
if IsValid(phys) then
|
||||
phys:EnableCollisions( true )
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
ent:CollisionRulesChanged()
|
||||
end
|
||||
|
||||
function PUG:UnGhost( ent )
|
||||
if not ent.PUGGhosted then return end
|
||||
|
||||
local trap = isTrap(ent)
|
||||
local moving = u.entityIsMoving(ent)
|
||||
|
||||
if not ( trap or moving ) then
|
||||
u.entityForceDrop( ent )
|
||||
|
||||
ent.APG_Ghosted = false
|
||||
ent:DrawShadow( true )
|
||||
|
||||
ent:SetRenderMode( ent.APG_oldRenderMode or RENDERMODE_NORMAL )
|
||||
ent:SetColor( ent.APG_oldColor or Color( 255, 255, 255, 255) )
|
||||
ent.APG_oldColor = false
|
||||
|
||||
local newCollisionGroup = COLLISION_GROUP_INTERACTIVE
|
||||
|
||||
if PUG:isGoodEnt( ent ) then
|
||||
newCollisionGroup = ent.PUGGhost.collision
|
||||
else
|
||||
if ent.PUGGhost.collision == COLLISION_GROUP_WORLD then
|
||||
newCollisionGroup = COLLISION_GROUP_WORLD
|
||||
else
|
||||
if ent.PUGFrozen then
|
||||
newCollisionGroup = COLLISION_GROUP_NONE
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ent:SetCollisionGroup( newCollisionGroup )
|
||||
ent:CollisionRulesChanged()
|
||||
return true
|
||||
else
|
||||
--FIXME: Add notification here!
|
||||
print("A prop has tried to unghost, but couldn't!")
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
hook.Add("PUG_PostPhysgunPickup", "PUGGhosting", function(_, ent, canPickup)
|
||||
u.addJob(function()
|
||||
if not canPickup then return end
|
||||
if IsValid( ent ) then
|
||||
PUG:Ghost( ent, true )
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
hook.Add("PhysgunDrop", "PUGGhosting", function(_, ent)
|
||||
timer.Simple(0.5, function()
|
||||
u.addJob(function()
|
||||
if u.isEntityHeld(ent) then return end
|
||||
if IsValid( ent ) then
|
||||
PUG:Ghost( ent, false )
|
||||
end
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
|
||||
return {
|
||||
hooks = {
|
||||
["PUG_SetCollisionGroup"] = "PUGCollision",
|
||||
["PUG_EnableMotion"] = "PUGCollision",
|
||||
["PUG_PostPhysgunPickup"] = "PUGGhosting",
|
||||
["PhysgunDrop"] = "PUGGhosting",
|
||||
},
|
||||
settings = {
|
||||
["GhostColour"] = "4 20 36 250",
|
||||
["GhostsNoCollide"] = "0",
|
||||
}
|
||||
}
|
0
lua/pug/modules/lagdetection.lua
Normal file
0
lua/pug/modules/lagdetection.lua
Normal file
0
lua/pug/modules/physgun.lua
Normal file
0
lua/pug/modules/physgun.lua
Normal file
0
lua/pug/modules/sleepyphys.lua
Normal file
0
lua/pug/modules/sleepyphys.lua
Normal file
0
lua/pug/modules/stacks.lua
Normal file
0
lua/pug/modules/stacks.lua
Normal file
0
lua/pug/modules/tools.lua
Normal file
0
lua/pug/modules/tools.lua
Normal file
234
lua/pug/sv_pug.lua
Normal file
234
lua/pug/sv_pug.lua
Normal file
@ -0,0 +1,234 @@
|
||||
local hook, timer = hook, timer
|
||||
PUG = PUG or {}
|
||||
|
||||
PUG.util = include("pug/sv_pug_util.lua")
|
||||
local u = PUG.util
|
||||
|
||||
-- TODO: Make badEnts & goodEnts use a Database.
|
||||
local badEnts = {
|
||||
["prop_physics"] = true,
|
||||
["prop_ragdoll"] = true,
|
||||
["sent_deployableballoons"] = true,
|
||||
["sent_streamradio"] = true,
|
||||
["gmod_button"] = true,
|
||||
["gmod_hoverball"] = true,
|
||||
["gmod_thruster"] = true,
|
||||
["gmod_wheel"] = true,
|
||||
["gmod_poly"] = true,
|
||||
["keypad"] = true,
|
||||
}
|
||||
|
||||
local goodEnts = {
|
||||
["gmod_hands"] = true,
|
||||
}
|
||||
|
||||
function PUG:isBadEnt( ent )
|
||||
if type(ent) ~= "Entity" then return false end
|
||||
|
||||
if not IsValid(ent) then
|
||||
return false
|
||||
end
|
||||
|
||||
if ent.jailWall then
|
||||
return false
|
||||
end
|
||||
|
||||
if ent:GetPersistent() then
|
||||
return false
|
||||
end
|
||||
|
||||
if ent == Entity(0) or ent:IsWorld() then
|
||||
return false
|
||||
end
|
||||
|
||||
if badEnts[ ent:GetClass() ] == true then
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
function PUG:isGoodEnt( ent )
|
||||
if type(ent) ~= "Entity" then return false end
|
||||
|
||||
if goodEnts[ ent:GetClass() ] == true then
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
function PUG:addBadEnt( class )
|
||||
local typeResult = type(class)
|
||||
assert(typeResult == "string", "string expected got " .. typeResult)
|
||||
badEnts[class] = true
|
||||
end
|
||||
|
||||
function PUG:addGoodEnt( class )
|
||||
local typeResult = type(class)
|
||||
assert(typeResult == "string", "string expected got " .. typeResult)
|
||||
goodEnts[class] = true
|
||||
end
|
||||
|
||||
do
|
||||
local GM = GM or GAMEMODE
|
||||
|
||||
PUG._PhysgunPickup = PUG._PhysgunPickup or GM.PhysgunPickup
|
||||
function GM:PhysgunPickup(ply, ent)
|
||||
local canPickup = PUG._PhysgunPickup(self, ply, ent)
|
||||
hook.Run( "PUG_PostPhysgunPickup", ply, ent, canPickup )
|
||||
return canPickup
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local ENT = FindMetaTable("Entity")
|
||||
|
||||
PUG._SetCollisionGroup = PUG._SetCollisionGroup or ENT.SetCollisionGroup
|
||||
PUG._SetModelScale = PUG._SetModelScale or ENT.SetModelScale
|
||||
PUG._SetColor = PUG._SetColor or ENT.SetColor
|
||||
PUG._SetPos = PUG._SetPos or ENT.SetPos
|
||||
|
||||
function ENT:SetCollisionGroup( group )
|
||||
local getHook = hook.Run("PUG_SetCollisionGroup", self, group)
|
||||
group = getHook or group
|
||||
|
||||
if getHook ~= true then
|
||||
PUG._SetCollisionGroup( self, group )
|
||||
end
|
||||
end
|
||||
|
||||
--NOTE: Fix SetColor behaving unpredictably + legacy support.
|
||||
function ENT:SetColor( color, ... )
|
||||
local r, g, b, a
|
||||
|
||||
if type(color) == "number" then
|
||||
r = color
|
||||
g = select(1, ...) or 255
|
||||
b = select(2, ...) or 255
|
||||
a = select(3, ...) or 255
|
||||
color = Color(r, g, b, a)
|
||||
elseif type(color) == "table" and not IsColor(color) then
|
||||
r = color.r or 255
|
||||
g = color.g or 255
|
||||
b = color.b or 255
|
||||
a = color.a or 255
|
||||
color = Color(r, g, b, a)
|
||||
end
|
||||
|
||||
if not IsColor(color) then
|
||||
local emsg = "Invalid color passed to SetColor! This error "
|
||||
emsg = emsg .. "prevents stuff from turning purple/pink."
|
||||
ErrorNoHalt( emsg )
|
||||
else
|
||||
PUG._SetColor( self, color )
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:SetPos( pos )
|
||||
PUG._SetPos( self, pos )
|
||||
timer.Simple(0, function()
|
||||
hook.Run( "PUG_PostSetPos", self, pos )
|
||||
end)
|
||||
end
|
||||
|
||||
--NOTE: Fix engine crash resulting from entities being resized.
|
||||
--REF: https://github.com/Facepunch/garrysmod-issues/issues/3547
|
||||
|
||||
function ENT:SetModelScale( scale, deltaTime )
|
||||
PUG._SetModelScale( self, scale, deltaTime )
|
||||
|
||||
local min, max = self:OBBMins(), self:OBBMaxs()
|
||||
if min:Distance(max) > 12000 then
|
||||
PUG._SetModelScale( self, 1, 0 )
|
||||
FixInvalidPhysicsObject( self )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local PhysObj = FindMetaTable( "PhysObj" )
|
||||
PUG._EnableMotion = PUG._EnableMotion or PhysObj.EnableMotion
|
||||
|
||||
function PhysObj:EnableMotion( bool )
|
||||
local ent = self:GetEntity()
|
||||
local hookRun = { hook.Run( "PUG_EnableMotion", ent, self, bool ) }
|
||||
|
||||
if hookRun[1] == true then return end
|
||||
bool = hookRun[2] or bool
|
||||
ent.PUGFrozen = (not bool)
|
||||
|
||||
return PUG._EnableMotion( self, bool )
|
||||
end
|
||||
end
|
||||
|
||||
local function getBadEnt( ent )
|
||||
if PUG:isBadEnt( ent ) then
|
||||
ent.PUGBadEnt = true
|
||||
return true
|
||||
else
|
||||
if ent.PUGBadEnt then
|
||||
ent.PUGBadEnt = nil
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
hook.Add("OnEntityCreated", "PUG_EntityCreated", function( ent )
|
||||
getBadEnt( ent )
|
||||
timer.Simple(0, function()
|
||||
hook.Run( "PUG_isBadEnt", ent, getBadEnt(ent) )
|
||||
end)
|
||||
end)
|
||||
|
||||
hook.Add("PUG_PostPhysgunPickup", "main", function( ply, ent, canPickup )
|
||||
if not canPickup then return end
|
||||
u.entityForceDrop( ent )
|
||||
u.addEntityHolder( ent, ply )
|
||||
ent.PUGPicked = true
|
||||
end)
|
||||
|
||||
hook.Add("PhysgunDrop", "PUG_PhysgunDrop", function( ply, ent )
|
||||
ent.PUGPicked = false
|
||||
u.removeEntityHolder( ent, ply )
|
||||
end)
|
||||
|
||||
local function applyPlayerHack( ply )
|
||||
timer.Simple(0, function()
|
||||
local phys = ply:GetPhysicsObject()
|
||||
if IsValid(phys) then
|
||||
phys:EnableMotion(false)
|
||||
phys:Sleep()
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
--FIXME: Check if "PlayerInitialSpawn" is also needed.
|
||||
hook.Add("PlayerInitialSpawn", "PUG_PlayerSpawn", applyPlayerHack)
|
||||
hook.Add("PlayerSpawn", "PUG_PlayerSpawn", applyPlayerHack)
|
||||
|
||||
hook.Add("EntityTakeDamage", "PUG_DamageControl", function(target, dmg)
|
||||
if type(target) ~= "Player" then
|
||||
return
|
||||
end
|
||||
|
||||
local ent = dmg:GetInflictor()
|
||||
local damageType = dmg:GetDamageType()
|
||||
|
||||
if ent.PUGBadEnt then
|
||||
return true
|
||||
else
|
||||
if IsValid( ent ) then
|
||||
if PUG:isGoodEnt( ent ) or ent:IsWeapon() then
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if damageType == DMG_CRUSH or damageType == DMG_VEHICLE then
|
||||
return true
|
||||
end
|
||||
end)
|
||||
|
||||
-- NOTE: Now that the base is setup, load the modules!
|
||||
include("pug/sv_pug_loader.lua")
|
102
lua/pug/sv_pug_loader.lua
Normal file
102
lua/pug/sv_pug_loader.lua
Normal file
@ -0,0 +1,102 @@
|
||||
PUG.modules = PUG.modules or {}
|
||||
|
||||
local file, hook, timer = file, hook, timer
|
||||
local path = "pug/modules/"
|
||||
local modules = file.Find( path .. "*.lua", "LUA" )
|
||||
local u = PUG.util
|
||||
|
||||
|
||||
for _, fileName in next, modules do
|
||||
if fileName then
|
||||
local niceName = string.gsub(fileName, "%.lua", "")
|
||||
PUG.modules[ niceName ] = {
|
||||
enabled = false,
|
||||
path = path .. fileName,
|
||||
data = {}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
function PUG:load( moduleName )
|
||||
local module = self.modules[ moduleName ]
|
||||
if module then
|
||||
module.data = include( module.path )
|
||||
module.enabled = true
|
||||
end
|
||||
end
|
||||
|
||||
function PUG:unLoad( moduleName )
|
||||
local module = self.modules[ moduleName ]
|
||||
if module and next(module.data) == 1 then
|
||||
local data = module.data
|
||||
|
||||
for _, hookID in next, data.hooks do
|
||||
hook.Remove(hookID)
|
||||
end
|
||||
|
||||
for _, timerID in next, data.timers do
|
||||
timer.Remove(timerID)
|
||||
end
|
||||
|
||||
module.enabled = false
|
||||
else
|
||||
local emsg = "The module " .. moduleName .. " doesn't exist or is "
|
||||
emsg = emsg .. "invalid."
|
||||
ErrorNoHalt( emsg )
|
||||
end
|
||||
end
|
||||
|
||||
local function writeData()
|
||||
local json = util.TableToJSON( PUG.modules, true )
|
||||
file.Write( "pugsettings.txt", json )
|
||||
end
|
||||
|
||||
function PUG:saveConfig()
|
||||
local readFile = file.Read( "pugsettings.txt", "DATA" )
|
||||
|
||||
if ( not readFile ) or ( readFile == "" ) then
|
||||
writeData()
|
||||
else
|
||||
local data = util.JSONToTable( readFile )
|
||||
|
||||
if not data then
|
||||
local emsg = "Your custom settings have not been loaded "
|
||||
emsg = emsg .. "because you have a misconfigured settings file! "
|
||||
emsg = emsg .. "The default settings were used instead!"
|
||||
ErrorNoHalt( emsg )
|
||||
return
|
||||
end
|
||||
|
||||
-- Look for saved data that does not actually exist and remove it.
|
||||
-- k is Key, v is Value, _ is Dropped/Unused.
|
||||
|
||||
for k, _ in next, data do
|
||||
if not self.modules[ k ] then
|
||||
data[ k ] = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Update data with new modules if new modules were added.
|
||||
for k, v in next, self.modules do
|
||||
if not data[ k ] then
|
||||
data[ k ] = v
|
||||
end
|
||||
end
|
||||
|
||||
for k, v in next, data do
|
||||
if v.enabled then
|
||||
self:load(k)
|
||||
print("[PUGLoader] ", k, " has been loaded!")
|
||||
end
|
||||
end
|
||||
|
||||
self.modules = data
|
||||
|
||||
timer.Simple(0, function()
|
||||
writeData()
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
PUG:saveConfig()
|
||||
print("PUG has hopped onto your server! Your physics are safe with PUG.")
|
102
lua/pug/sv_pug_util.lua
Normal file
102
lua/pug/sv_pug_util.lua
Normal file
@ -0,0 +1,102 @@
|
||||
local util = {}
|
||||
|
||||
function util.safeSetCollisionGroup( ent, group, pObj )
|
||||
if ent:IsPlayerHolding() then return end
|
||||
if ent.APG_Ghosted then return end
|
||||
if pObj then pObj:Sleep() end
|
||||
ent:SetCollisionGroup( group )
|
||||
ent:CollisionRulesChanged()
|
||||
end
|
||||
|
||||
function util.isVehicle( ent, basic )
|
||||
if not IsValid( ent ) then return false end
|
||||
|
||||
if ent:IsVehicle() then return true end
|
||||
if string.find( ent:GetClass(), "vehicle" ) then return true end
|
||||
|
||||
if basic then return false end
|
||||
|
||||
local parent = ent:GetParent()
|
||||
return util.isVehicle( parent, true )
|
||||
end
|
||||
|
||||
function util.callOnConstraints( ent, callback )
|
||||
local constrained = constraint.GetAllConstrainedEntities( ent )
|
||||
for _, child in next, constrained do
|
||||
if IsValid( child ) and child ~= ent then
|
||||
callback( child )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function util.entityForceDrop( ent )
|
||||
DropEntityIfHeld( ent )
|
||||
ent:ForcePlayerDrop()
|
||||
|
||||
if type(ent.PUGHolding) ~= "table" then return end
|
||||
|
||||
for _, ply in next, ent.PUGHolding do
|
||||
if ply and IsValid(ply) then
|
||||
ply:ConCommand("-attack")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function util.entityIsMoving( ent )
|
||||
if type( ent ) ~= "Entity" then return end
|
||||
if not IsValid( ent ) then return end
|
||||
|
||||
local zero = Vector(0,0,0)
|
||||
local phys = ent:GetPhysicsObject()
|
||||
|
||||
if IsValid(phys) then
|
||||
local vel = phys:GetVelocity():Distance(zero)
|
||||
return ( vel > 0.15 ), vel
|
||||
else
|
||||
return false, nil
|
||||
end
|
||||
end
|
||||
|
||||
function util.isEntityHeld( ent )
|
||||
if type(ent.PUGHolding) ~= "table" then
|
||||
return false
|
||||
end
|
||||
return ( next( ent.PUGHolding ) ~= nil )
|
||||
end
|
||||
|
||||
function util.addEntityHolder( ent, ply )
|
||||
local steamID = ply:SteamID()
|
||||
ent.PUGHolding = ent.PUGHolding or {}
|
||||
ent.PUGHolding[steamID] = ply
|
||||
end
|
||||
|
||||
function util.removeEntityHolder( ent, ply )
|
||||
local steamID = ply:SteamID()
|
||||
ent.PUGHolding = ent.PUGHolding or {}
|
||||
ent.PUGHolding[steamID] = nil
|
||||
end
|
||||
|
||||
do
|
||||
local jobs = {}
|
||||
|
||||
function util.addJob( callback )
|
||||
assert(type(callback) == "function", "The callback must be a function!")
|
||||
local index = #jobs + 1
|
||||
jobs[ index ] = callback
|
||||
end
|
||||
|
||||
function util.removeJob( index )
|
||||
jobs[ index ] = nil
|
||||
end
|
||||
|
||||
hook.Add("Tick", "PUG_JobProcessor", function()
|
||||
local index = #jobs
|
||||
local job = jobs[ index ]
|
||||
if job then
|
||||
job()
|
||||
util.removeJob( index )
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
return util
|
Loading…
Reference in New Issue
Block a user