Initial commit

This commit is contained in:
pavetr
2026-03-15 14:54:49 +03:00
commit 64f8029c06
4027 changed files with 254888 additions and 0 deletions

View File

@@ -0,0 +1,93 @@
FPP = FPP or {}
--Make buddies if not there
sql.Query("CREATE TABLE IF NOT EXISTS FPP_Buddies('steamid' TEXT NOT NULL, 'name' TEXT NOT NULL, 'physgun' INTEGER NOT NULL, 'gravgun' INTEGER NOT NULL, 'toolgun' INTEGER NOT NULL, 'playeruse' INTEGER NOT NULL, 'entitydamage' INTEGER NOT NULL, PRIMARY KEY('steamid'));")
FPP.Buddies = {}
function FPP.LoadBuddies()
local data = sql.Query("SELECT * FROM FPP_Buddies")
for _, v in ipairs(data or {}) do
FPP.Buddies[v.steamid] = {name = v.name, physgun = v.physgun, gravgun = v.gravgun, toolgun = v.toolgun, playeruse = v.playeruse, entitydamage = v.entitydamage} --Put all the buddies in the table
for _, ply in ipairs(player.GetAll()) do --If the buddies are in the server then add them serverside
if ply:SteamID() == v.steamid then
-- update the name
sql.Query("UPDATE FPP_Buddies SET name = " .. sql.SQLStr(ply:Nick()) .. " WHERE steamid = " .. sql.SQLStr(v.steamid) .. ";")
FPP.Buddies[v.steamid].name = ply:Nick()
RunConsoleCommand("FPP_SetBuddy", ply:UserID(), v.physgun, v.gravgun, v.toolgun, v.playeruse, v.entitydamage)
end
end
end
end
hook.Add("InitPostEntity", "FPP_Start", FPP.LoadBuddies)
function FPP.SaveBuddy(SteamID, Name, Type, value)
if Type == "remove" then
FPP.Buddies[SteamID] = nil
sql.Query("DELETE FROM FPP_Buddies WHERE steamid = " .. sql.SQLStr(SteamID) .. ";")
for _, v in ipairs(player.GetAll()) do
if v:SteamID() == SteamID then
RunConsoleCommand("FPP_SetBuddy", v:UserID(), "0", "0", "0", "0", "0")
end
end
return
end
FPP.Buddies[SteamID] = FPP.Buddies[SteamID] or {name = Name, physgun = 0, gravgun = 0, toolgun = 0, playeruse = 0, entitydamage = 0} -- Create if not there
FPP.Buddies[SteamID][Type] = value
local data = sql.Query("SELECT * FROM FPP_Buddies WHERE steamid = " .. sql.SQLStr(SteamID) .. ";")
if data then
sql.Query("UPDATE FPP_Buddies SET " .. Type .. " = " .. value .. " WHERE steamid = " .. sql.SQLStr(SteamID) .. ";")
-- Oi! update the name!
sql.Query("UPDATE FPP_Buddies SET name = " .. sql.SQLStr(Name) .. " WHERE steamid = " .. sql.SQLStr(SteamID) .. ";")
FPP.Buddies[SteamID].name = Name
else
sql.Query("INSERT INTO FPP_Buddies VALUES(" .. sql.SQLStr(SteamID) .. ", " .. sql.SQLStr(Name) .. ", " .. FPP.Buddies[SteamID].physgun .. ", " .. FPP.Buddies[SteamID].gravgun .. ", " .. FPP.Buddies[SteamID].toolgun .. ", " .. FPP.Buddies[SteamID].playeruse .. ", " .. FPP.Buddies[SteamID].entitydamage .. ");")
end
--Let the server know of your changes
for _, v in ipairs(player.GetAll()) do
if v:SteamID() == SteamID then -- If the person you're adding is in the server then add him serverside
RunConsoleCommand("FPP_SetBuddy", v:UserID(), FPP.Buddies[SteamID].physgun, FPP.Buddies[SteamID].gravgun, FPP.Buddies[SteamID].toolgun, FPP.Buddies[SteamID].playeruse, FPP.Buddies[SteamID].entitydamage)
--Don't break because there can be people(bots actually) with the same steam ID
end
end
local ShouldRemove = true -- Remove the buddy if he isn't buddy in anything anymore
for _, v in pairs(FPP.Buddies[SteamID]) do
if v == 1 or v == "1" then
ShouldRemove = false
break
end
end
if ShouldRemove then -- If everything = 0 then he's not your friend anymore
FPP.Buddies[SteamID] = nil
sql.Query("DELETE FROM FPP_Buddies WHERE steamid = " .. sql.SQLStr(SteamID) .. ";")
for _, v in ipairs(player.GetAll()) do
if v:SteamID() == SteamID then
RunConsoleCommand("FPP_SetBuddy", v:UserID(), "0", "0", "0", "0", "0")
end
end
end
end
function FPP.NewBuddy(um)
local ply = um:ReadEntity()
if not IsValid(ply) or not ply:IsPlayer() then return end
local SteamID = ply:SteamID()
local data = sql.Query("SELECT * FROM FPP_Buddies")
for _, v in ipairs(data or {}) do
-- make the player buddy if they're in your buddies list
if v.steamid ~= SteamID then continue end
RunConsoleCommand("FPP_SetBuddy", ply:UserID(), v.physgun, v.gravgun, v.toolgun, v.playeruse, v.entitydamage)
-- update the name
sql.Query("UPDATE FPP_Buddies SET name = " .. sql.SQLStr(ply:Nick()) .. " WHERE steamid = " .. sql.SQLStr(SteamID) .. ";")
FPP.Buddies[SteamID] = FPP.Buddies[SteamID] or {}
FPP.Buddies[SteamID].name = ply:Nick()
end
end
usermessage.Hook("FPP_CheckBuddy", FPP.NewBuddy)

View File

@@ -0,0 +1,221 @@
FPP = FPP or {}
surface.CreateFont("TabLarge", {
size = 17,
weight = 700,
antialias = true,
shadow = false,
font = "Trebuchet MS"})
hook.Add("CanTool", "FPP_CL_CanTool", function(ply, trace, tool) -- Prevent client from SEEING his toolgun shoot while it doesn't shoot serverside.
if IsValid(trace.Entity) and not FPP.canTouchEnt(trace.Entity, "Toolgun") then
return false
end
end)
-- This looks weird, but whenever a client touches an ent he can't touch, without the code it'll look like he picked it up. WITH the code it really looks like he can't
-- besides, when the client CAN pick up a prop, it also looks like he can.
hook.Add("PhysgunPickup", "FPP_CL_PhysgunPickup", function(ply, ent)
if not FPP.canTouchEnt(ent, "Physgun") then
return false
end
end)
-- Makes sure the client doesn't think they can punt props
hook.Add("GravGunPunt", "FPP_CL_GravGunPunt", function(ply, ent)
if tobool(FPP.Settings.FPP_GRAVGUN1.noshooting) then return false end
if IsValid(ent) and not FPP.canTouchEnt(ent, "Gravgun") then
return false
end
end)
local surface_SetFont = surface.SetFont
local surface_GetTextSize = surface.GetTextSize
local surface_SetDrawColor = surface.SetDrawColor
local draw_SimpleText = draw.SimpleText
local draw_DrawText = draw.DrawText
local draw_RoundedBox = draw.RoundedBox
local HUDNote_c = 0
local HUDNote_i = 1
local HUDNotes = {}
-- Notify ripped off the Sandbox notify, changed to my likings
function FPP.AddNotify(str, type, total_time)
local tab = {}
tab.text = str
tab.recv = SysTime()
tab.velx = 0
tab.vely = -5
tab.time_visible = total_time ~= 0 and total_time or 6
surface_SetFont( "TabLarge" )
local w = surface_GetTextSize( str )
tab.x = ScrW() / 2 + w * 0.5 + (ScrW() / 20)
tab.y = ScrH()
tab.a = 255
if type then
tab.type = true
else
tab.type = false
end
table.insert( HUDNotes, tab )
HUDNote_c = HUDNote_c + 1
HUDNote_i = HUDNote_i + 1
local ply = LocalPlayer()
if not IsValid(ply) then return end -- I honestly got this error
ply:EmitSound("npc/turret_floor/click1.wav", 10, 100)
end
usermessage.Hook("FPP_Notify", function(u) FPP.AddNotify(u:ReadString(), u:ReadBool(), u:ReadFloat()) end)
local function DrawNotice(k, v, i)
local H = ScrH() / 1024
local x = v.x - 75 * H
local y = v.y - 20 * H - 2
surface_SetFont( "TabLarge" )
local w, h = surface_GetTextSize( v.text )
w = w
h = h + 10
if v.type then
draw_RoundedBox(4, x - w - h + 16, y - 8, w + h, h, Color(30, 100, 30, v.a * 0.4))
else
draw_RoundedBox(4, x - w - h + 16, y - 8, w + h, h, Color(100, 30, 30, v.a * 0.4))
end
-- Draw Icon
surface_SetDrawColor( 255, 255, 255, v.a )
draw_SimpleText(v.text, "TabLarge", x + 1, y + 1, Color(0, 0, 0, v.a * 0.8), TEXT_ALIGN_RIGHT)
draw_SimpleText(v.text, "TabLarge", x - 1, y - 1, Color(0, 0, 0, v.a * 0.5), TEXT_ALIGN_RIGHT)
draw_SimpleText(v.text, "TabLarge", x + 1, y - 1, Color(0, 0, 0, v.a * 0.6), TEXT_ALIGN_RIGHT)
draw_SimpleText(v.text, "TabLarge", x - 1, y + 1, Color(0, 0, 0, v.a * 0.6), TEXT_ALIGN_RIGHT)
draw_SimpleText(v.text, "TabLarge", x, y, Color(255, 255, 255, v.a), TEXT_ALIGN_RIGHT)
local ideal_y = ScrH() - (HUDNote_c - i) * h
local ideal_x = ScrW() / 2 + w * 0.5 + (ScrW() / 20)
local timeleft = v.time_visible - (SysTime() - v.recv)
-- Cartoon style about to go thing
if (timeleft < 0.5) then
ideal_x = ScrW() / 2 + w * 0.5 + 200
end
-- Gone!
if (timeleft < 0.2) then
ideal_y = ScrH() + 50
end
local spd = RealFrameTime() * 15
v.y = v.y + v.vely * spd
v.x = v.x + v.velx * spd
local dist = ideal_y - v.y
v.vely = v.vely + dist * spd * 1
if (math.abs(dist) < 2 and math.abs(v.vely) < 0.1) then
v.vely = 0
end
dist = ideal_x - v.x
v.velx = v.velx + dist * spd * 1
if math.abs(dist) < 2 and math.abs(v.velx) < 0.1 then
v.velx = 0
end
-- Friction.. kind of FPS independant.
v.velx = v.velx * (0.95 - RealFrameTime() * 8)
v.vely = v.vely * (0.95 - RealFrameTime() * 8)
end
local weaponClassTouchTypes = {
["weapon_physgun"] = "Physgun",
["weapon_physcannon"] = "Gravgun",
["gmod_tool"] = "Toolgun",
}
local function FilterEntityTable(eyepos, t)
local filtered = nil
local filteredDistance = nil
for i, ent in ipairs(t) do
local class = ent:GetClass()
if ent:IsWeapon() or ent:IsPlayer() or class == "viewmodel" then continue end
-- Get the entity that is closest by
local distance = ent:NearestPoint(eyepos):DistToSqr(eyepos)
if filtered == nil or distance < filteredDistance then
filtered = ent
filteredDistance = distance
end
end
return filtered
end
local boxBackground = Color(0, 0, 0, 110)
local canTouchTextColor = Color(0, 255, 0, 255)
local cannotTouchTextColor = Color(255, 0, 0, 255)
local function HUDPaint()
local i = 0
for k, v in pairs(HUDNotes) do
if v ~= 0 then
i = i + 1
DrawNotice(k, v, i)
end
end
for k, v in pairs(HUDNotes) do
if v ~= 0 and v.recv + v.time_visible < SysTime() then
HUDNotes[ k ] = 0
HUDNote_c = HUDNote_c - 1
if (HUDNote_c == 0) then HUDNotes = {} end
end
end
if FPP.getPrivateSetting("HideOwner") then return end
--Show the owner:
local ply = LocalPlayer()
local eyepos = ply:EyePos()
local LAEnt2 = ents.FindAlongRay(eyepos, eyepos + EyeAngles():Forward() * 16384)
local LAEnt = FilterEntityTable(eyepos, LAEnt2)
if not IsValid(LAEnt) then return end
-- Prevent being able to see ownership through walls
local eyeTrace = ply:GetEyeTrace()
-- GetEyeTrace can return nil before InitPostEntity
if eyeTrace == nil then return end
if eyeTrace.HitPos:DistToSqr(eyeTrace.StartPos) < LAEnt:NearestPoint(eyeTrace.StartPos):DistToSqr(eyeTrace.StartPos) then return end
local weapon = ply:GetActiveWeapon()
local class = weapon:IsValid() and weapon:GetClass() or ""
local touchType = weaponClassTouchTypes[class] or "EntityDamage"
local reason = FPP.entGetTouchReason(LAEnt, touchType)
if not reason then return end
local originalOwner = LAEnt:GetNW2String("FPP_OriginalOwner")
originalOwner = originalOwner ~= "" and (" (previous owner: %s)"):format(originalOwner) or ""
reason = reason .. originalOwner
surface_SetFont("Default")
local w,h = surface_GetTextSize(reason)
local col = FPP.canTouchEnt(LAEnt, touchType) and canTouchTextColor or cannotTouchTextColor
local scrH = ScrH()
draw_RoundedBox(4, 0, scrH / 2 - h - 2, w + 10, 20, boxBackground)
draw_DrawText(reason, "Default", 5, scrH / 2 - h, col, 0)
surface_SetDrawColor(255, 255, 255, 255)
end
hook.Add("HUDPaint", "FPP_HUDPaint", HUDPaint)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,93 @@
FPP = FPP or {}
FPP.entOwners = FPP.entOwners or {}
FPP.entTouchability = FPP.entTouchability or {}
FPP.entTouchReasons = FPP.entTouchReasons or {}
local touchTypes = {
Physgun = 1,
Gravgun = 2,
Toolgun = 4,
PlayerUse = 8,
EntityDamage = 16
}
local reasonSize = 4 -- bits
local reasons = {
[1] = "owner", -- you can't touch other people's props
[2] = "world",
[3] = "disconnected",
[4] = "blocked",
[5] = "constrained",
[6] = "buddy",
[7] = "shared",
[8] = "player", -- you can't pick up players
}
local function receiveTouchData(len)
repeat
local entIndex = net.ReadUInt(13)
local ownerIndex = net.ReadUInt(8)
local touchability = net.ReadUInt(5)
local reason = net.ReadUInt(20)
if ownerIndex == 255 then
ownerIndex = -1
end
FPP.entOwners[entIndex] = ownerIndex
FPP.entTouchability[entIndex] = touchability
FPP.entTouchReasons[entIndex] = reason
until net.ReadBit() == 1
end
net.Receive("FPP_TouchabilityData", receiveTouchData)
function FPP.entGetOwner(ent)
local idx = FPP.entOwners[ent:EntIndex()]
ent.FPPOwner = idx and Entity(idx) or nil
return ent.FPPOwner
end
function FPP.canTouchEnt(ent, touchType)
ent.FPPCanTouch = FPP.entTouchability[ent:EntIndex()]
if not touchType or not ent.FPPCanTouch then
return ent.FPPCanTouch
end
return bit.bor(ent.FPPCanTouch, touchTypes[touchType]) == ent.FPPCanTouch
end
local touchTypeMultiplier = {
["Physgun"] = 0,
["Gravgun"] = 1,
["Toolgun"] = 2,
["PlayerUse"] = 3,
["EntityDamage"] = 4
}
function FPP.entGetTouchReason(ent, touchType)
local idx = FPP.entTouchReasons[ent:EntIndex()] or 0
ent.FPPCanTouchWhy = idx
if not touchType then return idx end
local maxReasonValue = 15
-- 1111 shifted to the right touch type
local touchTypeMask = bit.lshift(maxReasonValue, reasonSize * touchTypeMultiplier[touchType])
-- Extract reason for touch type from reason number
local touchTypeReason = bit.band(idx, touchTypeMask)
-- Shift it back to the right
local reasonNr = bit.rshift(touchTypeReason, reasonSize * touchTypeMultiplier[touchType])
local reason = reasons[reasonNr]
local owner = ent:CPPIGetOwner()
if reasonNr == 1 then -- convert owner to the actual player
return not isnumber(owner) and IsValid(owner) and owner:Nick() or "Unknown player"
elseif reasonNr == 6 then
return "Buddy (" .. (IsValid(owner) and owner:Nick() or "Unknown player") .. ")"
end
return reason
end

View File

@@ -0,0 +1,219 @@
FPP = FPP or {}
FPP.AntiSpam = FPP.AntiSpam or {}
function FPP.AntiSpam.GhostFreeze(ent, phys)
ent:SetRenderMode(RENDERMODE_TRANSCOLOR)
ent:DrawShadow(false)
ent.FPPOldColor = ent.FPPOldColor or ent:GetColor()
ent.StartPos = ent:GetPos()
ent:SetColor(Color(ent.FPPOldColor.r, ent.FPPOldColor.g, ent.FPPOldColor.b, ent.FPPOldColor.a - 155))
ent:SetCollisionGroup(COLLISION_GROUP_WORLD)
ent.CollisionGroup = COLLISION_GROUP_WORLD
ent.FPPAntiSpamMotionEnabled = phys:IsMoveable()
phys:EnableMotion(false)
ent.FPPAntiSpamIsGhosted = true
end
function FPP.UnGhost(ply, ent)
if ent.FPPAntiSpamIsGhosted then
ent.FPPAntiSpamIsGhosted = nil
ent:DrawShadow(true)
if ent.OldCollisionGroup then ent:SetCollisionGroup(ent.OldCollisionGroup) ent.OldCollisionGroup = nil end
if ent.FPPOldColor then
ent:SetColor(Color(ent.FPPOldColor.r, ent.FPPOldColor.g, ent.FPPOldColor.b, ent.FPPOldColor.a))
end
ent.FPPOldColor = nil
ent:SetCollisionGroup(COLLISION_GROUP_NONE)
ent.CollisionGroup = COLLISION_GROUP_NONE
local phys = ent:GetPhysicsObject()
if phys:IsValid() then
phys:EnableMotion(ent.FPPAntiSpamMotionEnabled)
end
end
end
local blacklist = {
["gmod_wire_indicator"] = true,
["phys_constraint"] = true
}
function FPP.AntiSpam.CreateEntity(ply, ent, IsDuplicate)
if not tobool(FPP.Settings.FPP_ANTISPAM1.toggle) then return end
local phys = ent:GetPhysicsObject()
if not phys:IsValid() then return end
local entTable = ent:GetTable()
-- Some spawn methods trigger different paths to this function, causing an
-- entity to be counted multiple times for antispam.
if entTable.FPPCountedForAntispam then return end
entTable.FPPCountedForAntispam = true
local shouldRegister = hook.Call("FPP_ShouldRegisterAntiSpam", nil, ply, ent, IsDuplicate)
if shouldRegister == false then return end
local class = ent:GetClass()
-- I power by ten because the volume of a prop can vary between 65 and like a few billion
if tobool(FPP.Settings.FPP_ANTISPAM1.bigpropantispam) and phys:GetVolume() and phys:GetVolume() > math.pow(10, FPP.Settings.FPP_ANTISPAM1.bigpropsize) and not string.find(class, "constraint") and not string.find(class, "hinge")
and not string.find(class, "magnet") and not string.find(class, "collision") and not blacklist[class] then
ply.FPPAntispamBigProp = ply.FPPAntispamBigProp or 0
ply.FPPAntiSpamLastBigProp = ply.FPPAntiSpamLastBigProp or 0
if not IsDuplicate then
ply.FPPAntispamBigProp = ply.FPPAntispamBigProp + 1
end
local curTime = CurTime()
local spawningBlockedUntil =
ply.FPPAntiSpamLastBigProp + ply.FPPAntispamBigProp * FPP.Settings.FPP_ANTISPAM1.bigpropwait
if curTime < spawningBlockedUntil then
-- The current attempt would have been blocked until
-- spawningBlockedUntil. The next attempt will add up to that time.
-- The wait time is thus the time the user should wait before the
-- next attempt.
local waitTime = spawningBlockedUntil + FPP.Settings.FPP_ANTISPAM1.bigpropwait - curTime
FPP.Notify(
ply,
"Please wait " .. math.Round(waitTime, 2) .. " Seconds before spawning a big prop again",
false,
waitTime
)
ent:Remove()
return
end
if not IsDuplicate then
ply.FPPAntiSpamLastBigProp = curTime
-- Spawning succeeded, reset big prop count to 0
ply.FPPAntispamBigProp = 0
end
local waitTime = FPP.Settings.FPP_ANTISPAM1.bigpropwait
FPP.AntiSpam.GhostFreeze(ent, phys)
FPP.Notify(
ply,
"Your prop is ghosted because it is too big. Interract with it to unghost it.",
true,
waitTime
)
return
end
if not IsDuplicate and not blacklist[class] then
ply.FPPAntiSpamCount = (ply.FPPAntiSpamCount or 0) + 1
local time = math.Max(1, FPP.Settings.FPP_ANTISPAM1.smallpropdowngradecount)
timer.Simple(ply.FPPAntiSpamCount / time, function()
if IsValid(ply) then
ply.FPPAntiSpamCount = ply.FPPAntiSpamCount - 1
end
end)
if ply.FPPAntiSpamCount >= FPP.Settings.FPP_ANTISPAM1.smallpropghostlimit and ply.FPPAntiSpamCount <= FPP.Settings.FPP_ANTISPAM1.smallpropdenylimit
and not ent:IsVehicle() --[[Vehicles don't like being ghosted, they tend to crash the server]] then
FPP.AntiSpam.GhostFreeze(ent, phys)
FPP.Notify(ply, "Your prop is ghosted for antispam, interract with it to unghost it.", true)
return
elseif ply.FPPAntiSpamCount > FPP.Settings.FPP_ANTISPAM1.smallpropdenylimit then
ent:Remove()
FPP.Notify(ply, "Prop removed due to spam", false)
return
end
end
end
function FPP.AntiSpam.DuplicatorSpam(ply)
if not tobool(FPP.Settings.FPP_ANTISPAM1.toggle) then return true end
if FPP.Settings.FPP_ANTISPAM1.duplicatorlimit == 0 then return true end
ply.FPPAntiSpamLastDuplicate = ply.FPPAntiSpamLastDuplicate or 0
ply.FPPAntiSpamLastDuplicate = ply.FPPAntiSpamLastDuplicate + 1
timer.Simple(ply.FPPAntiSpamLastDuplicate / FPP.Settings.FPP_ANTISPAM1.duplicatorlimit, function() if IsValid(ply) then ply.FPPAntiSpamLastDuplicate = ply.FPPAntiSpamLastDuplicate - 1 end end)
if ply.FPPAntiSpamLastDuplicate >= FPP.Settings.FPP_ANTISPAM1.duplicatorlimit then
FPP.Notify(ply, "Can't duplicate due to spam", false)
return false
end
return true
end
local function IsEmpty(ent)
local mins, maxs = ent:LocalToWorld(ent:OBBMins( )), ent:LocalToWorld(ent:OBBMaxs( ))
local tr = {}
tr.start = mins
tr.endpos = maxs
local ignore = player.GetAll()
table.insert(ignore, ent)
tr.filter = ignore
local trace = util.TraceLine(tr)
return trace.Entity
end
local function e2AntiMinge()
if not wire_expression2_funcs then return end
local e2func = wire_expression2_funcs["applyForce(e:v)"]
if not e2func or not e2func[3] then return end
local applyForce = e2func[3]
e2func[3] = function(self, args, ...)
if not tobool(FPP.Settings.FPP_GLOBALSETTINGS1.antie2minge) then return applyForce(self, args, ...) end
local arg_2_1 = args[2][1]
local ent
-- In some earlier versions of wiremod, args[2][1] is a function, which
-- can be called to get the target entity.
if isfunction(arg_2_1) then
ent = args[2][1](self, args[2])
else
-- In later versions, the first argument is the entity
ent = args[1]
end
if not IsValid(ent) or ent:CPPIGetOwner() ~= self.player then return end
-- No check for whether the entity has already been no collided with players
-- because while it would help performance,
-- it would make it possible to get around this with constrained ents
local ConstrainedEnts = constraint.GetAllConstrainedEntities(ent)
if ConstrainedEnts then -- Includes original entity
for _, v in pairs(ConstrainedEnts) do
if IsValid(v) then
v:SetCollisionGroup(COLLISION_GROUP_WEAPON)
end
end
end
return applyForce(self, args, ...)
end
end
hook.Add("InitPostEntity", "FPP.InitializeAntiMinge", function()
local backupPropSpawn = DoPlayerEntitySpawn
function DoPlayerEntitySpawn(ply, ...)
local ent = backupPropSpawn(ply, ...)
if not tobool(FPP.Settings.FPP_ANTISPAM1.antispawninprop) then return ent end
local PropInProp = IsEmpty(ent)
if not IsValid(PropInProp) then return ent end
local pos = PropInProp:NearestPoint(ply:EyePos()) + ply:GetAimVector() * -1 * ent:BoundingRadius()
ent:SetPos(pos)
return ent
end
e2AntiMinge()
end)
e2AntiMinge()
--More crash preventing:
local function antiragdollcrash(ply)
local pos = ply:GetEyeTraceNoCursor().HitPos
for _, v in ipairs(ents.FindInSphere(pos, 30)) do
if v:GetClass() == "func_door" then
FPP.Notify(ply, "Can't spawn a ragdoll near doors", false)
return false
end
end
end
hook.Add("PlayerSpawnRagdoll", "FPP.AntiSpam.AntiCrash", antiragdollcrash)

View File

@@ -0,0 +1,721 @@
FPP = FPP or {}
FPP.DisconnectedPlayers = FPP.DisconnectedPlayers or {}
local PLAYER = FindMetaTable("Player")
local ENTITY = FindMetaTable("Entity")
--[[-------------------------------------------------------------------------
Checks if a model is blocked
---------------------------------------------------------------------------]]
function FPP.IsBlockedModel(model)
if model == "" or not FPP.Settings or not FPP.Settings.FPP_BLOCKMODELSETTINGS1 or
not tobool(FPP.Settings.FPP_BLOCKMODELSETTINGS1.toggle)
or not FPP.BlockedModels or not model then return end
model = string.lower(model or "")
model = string.Replace(model, "\\", "/")
model = string.gsub(model, "[\\/]+", "/")
if string.find(model, "../", 1, true) then
return true, "The model path goes up in the folder tree."
end
local found = FPP.BlockedModels[model]
if tobool(FPP.Settings.FPP_BLOCKMODELSETTINGS1.iswhitelist) and not found then
-- Prop is not in the white list
return true, "The model of this entity is not in the white list!"
elseif not tobool(FPP.Settings.FPP_BLOCKMODELSETTINGS1.iswhitelist) and found then
-- prop is in the black list
return true, "The model of this entity is in the black list!"
end
return false
end
--[[-------------------------------------------------------------------------
Prevents spawning a prop or effect when its model is blocked
---------------------------------------------------------------------------]]
local function propSpawn(ply, model)
local blocked, msg = FPP.IsBlockedModel(model)
if blocked then
FPP.Notify(ply, msg, false)
return false
end
end
hook.Add("PlayerSpawnObject", "FPP_SpawnEffect", propSpawn) -- prevents precaching
hook.Add("PlayerSpawnProp", "FPP_SpawnProp", propSpawn) -- PlayerSpawnObject isn't always called
hook.Add("PlayerSpawnEffect", "FPP_SpawnEffect", propSpawn)
hook.Add("PlayerSpawnRagdoll", "FPP_SpawnEffect", propSpawn)
--[[-------------------------------------------------------------------------
Setting owner when someone spawns something
---------------------------------------------------------------------------]]
if cleanup then
FPP.oldcleanup = FPP.oldcleanup or cleanup.Add
function cleanup.Add(ply, Type, ent)
if not IsValid(ply) or not IsValid(ent) then return FPP.oldcleanup(ply, Type, ent) end
--Set the owner of the entity
ent:CPPISetOwner(ply)
if not tobool(FPP.Settings.FPP_BLOCKMODELSETTINGS1.propsonly) then
local model = ent.GetModel and ent:GetModel()
local blocked, msg = FPP.IsBlockedModel(model)
if blocked then
FPP.Notify(ply, msg, false)
ent:Remove()
return
end
end
if FPP.AntiSpam and Type ~= "constraints" and Type ~= "stacks" and Type ~= "AdvDupe2" and (not AdvDupe2 or not AdvDupe2.SpawningEntity) and (not ent.IsVehicle or not ent:IsVehicle()) then
FPP.AntiSpam.CreateEntity(ply, ent, Type == "duplicates")
end
if ent:GetClass() == "gmod_wire_expression2" then
ent:SetCollisionGroup(COLLISION_GROUP_WEAPON)
end
return FPP.oldcleanup(ply, Type, ent)
end
end
if PLAYER.AddCount then
FPP.oldcount = FPP.oldcount or PLAYER.AddCount
function PLAYER:AddCount(Type, ent)
if not IsValid(self) or not IsValid(ent) then return FPP.oldcount(self, Type, ent) end
--Set the owner of the entity
ent:CPPISetOwner(self)
return FPP.oldcount(self, Type, ent)
end
end
local entSetCreator = ENTITY.SetCreator
if entSetCreator then
function ENTITY:SetCreator(ply)
self:CPPISetOwner(ply)
entSetCreator(self, ply)
end
end
if undo then
local AddEntity, SetPlayer, Finish = undo.AddEntity, undo.SetPlayer, undo.Finish
local Undo = {}
local UndoPlayer
function undo.AddEntity(ent, ...)
if not isbool(ent) and IsValid(ent) then table.insert(Undo, ent) end
AddEntity(ent, ...)
end
function undo.SetPlayer(ply, ...)
UndoPlayer = ply
SetPlayer(ply, ...)
end
function undo.Finish(...)
if IsValid(UndoPlayer) then
for _, v in pairs(Undo) do
v:CPPISetOwner(UndoPlayer)
end
end
Undo = {}
UndoPlayer = nil
Finish(...)
end
end
hook.Add("PlayerSpawnedSWEP", "FPP.Spawn.SWEP", function(ply, ent)
ent:CPPISetOwner(ply)
end)
hook.Add("PlayerSpawnedSENT", "FPP.Spawn.SENT", function(ply, ent)
ent:CPPISetOwner(ply)
end)
--------------------------------------------------------------------------------------
--The protecting itself
--------------------------------------------------------------------------------------
FPP.Protect = {}
--Physgun Pickup
function FPP.Protect.PhysgunPickup(ply, ent)
if not tobool(FPP.Settings.FPP_PHYSGUN1.toggle) then if FPP.UnGhost then FPP.UnGhost(ply, ent) end return end
if not ent:IsValid() then return end
local cantouch
local skipReturn = false
if isfunction(ent.PhysgunPickup) then
cantouch = ent:PhysgunPickup(ply, ent)
-- Do not return the value, the gamemode will do this
-- Allows other hooks to run
skipReturn = true
elseif ent.PhysgunPickup ~= nil then
cantouch = ent.PhysgunPickup
else
cantouch = not ent:IsPlayer() and FPP.plyCanTouchEnt(ply, ent, "Physgun")
skipReturn = ent:IsPlayer()
end
if cantouch and FPP.UnGhost then FPP.UnGhost(ply, ent) end
if not cantouch and not skipReturn then return false end
end
hook.Add("PhysgunPickup", "FPP.Protect.PhysgunPickup", FPP.Protect.PhysgunPickup)
--Physgun reload
function FPP.Protect.PhysgunReload(weapon, ply)
if not tobool(FPP.Settings.FPP_PHYSGUN1.reloadprotection) then return end
local ent = ply:GetEyeTrace().Entity
if not IsValid(ent) then return end
local cantouch
local skipReturn = false
if isfunction(ent.OnPhysgunReload) then
cantouch = ent:OnPhysgunReload(ply, ent)
-- Do not return the value, the gamemode will do this
-- Allows other hooks to run
skipReturn = true
elseif ent.OnPhysgunReload ~= nil then
cantouch = ent.OnPhysgunReload
else
cantouch = not ent:IsPlayer() and FPP.plyCanTouchEnt(ply, ent, "Physgun")
end
if cantouch and FPP.UnGhost then FPP.UnGhost(ply, ent) end
if not cantouch and not skipReturn then return false end
-- Double reload breaks when returning true here
end
hook.Add("OnPhysgunReload", "FPP.Protect.PhysgunReload", FPP.Protect.PhysgunReload)
function FPP.PhysgunFreeze(weapon, phys, ent, ply)
if isfunction(ent.OnPhysgunFreeze) then
local val = ent:OnPhysgunFreeze(weapon, phys, ent, ply)
-- Do not return the value, the gamemode will do this
if val ~= nil then return end
elseif ent.OnPhysgunFreeze ~= nil then
return ent.OnPhysgunFreeze
end
end
hook.Add("OnPhysgunFreeze", "FPP.Protect.PhysgunFreeze", FPP.PhysgunFreeze)
--Gravgun pickup
function FPP.Protect.GravGunPickup(ply, ent)
if not tobool(FPP.Settings.FPP_GRAVGUN1.toggle) then return end
if not IsValid(ent) then return end -- You don't want a cross when looking at the floor while holding right mouse
if ent:IsPlayer() then return end
local cantouch
if isfunction(ent.GravGunPickup) then
cantouch = ent:GravGunPickup(ply, ent)
elseif ent.GravGunPickup ~= nil then
cantouch = ent.GravGunPickup
else
cantouch = not ent:IsPlayer() and FPP.plyCanTouchEnt(ply, ent, "Gravgun")
end
if cantouch and FPP.UnGhost then FPP.UnGhost(ply, ent) end
if cantouch == false then DropEntityIfHeld(ent) end
end
hook.Add("GravGunOnPickedUp", "FPP.Protect.GravGunPickup", FPP.Protect.GravGunPickup)
function FPP.Protect.CanGravGunPickup(ply, ent)
if not tobool(FPP.Settings.FPP_GRAVGUN1.toggle) or not IsValid(ent) then return end
if isfunction(ent.GravGunPickup) then
-- Function name different than gamemode's (GravGunPickup vs GravGunPickupAllowed)
-- Override FPP's behavior when implemented
local val = ent:GravGunPickup(ply, ent)
if val ~= nil then
if val == false then return false end
return
end
elseif ent.GravGunPickup ~= nil then
if ent.GravGunPickup == false then return false end
return
end
local cantouch = FPP.plyCanTouchEnt(ply, ent, "Gravgun")
if cantouch == false then return false end
end
hook.Add("GravGunPickupAllowed", "FPP.Protect.CanGravGunPickup", FPP.Protect.CanGravGunPickup)
--Gravgun punting
function FPP.Protect.GravGunPunt(ply, ent)
if tobool(FPP.Settings.FPP_GRAVGUN1.noshooting) then DropEntityIfHeld(ent) return false end
-- Do not reason further if gravgun protection is disabled.
if not tobool(FPP.Settings.FPP_GRAVGUN1.toggle) then return end
if not IsValid(ent) then DropEntityIfHeld(ent) return end
local cantouch
local skipReturn = false
if isfunction(ent.GravGunPunt) then
cantouch = ent:GravGunPunt(ply, ent)
-- Do not return the value, the gamemode will do this
-- Allows other hooks to run
skipReturn = true
elseif ent.GravGunPunt ~= nil then
cantouch = ent.GravGunPunt
else
cantouch = not ent:IsPlayer() and FPP.plyCanTouchEnt(ply, ent, "Gravgun")
end
if cantouch and FPP.UnGhost then FPP.UnGhost(ply, ent) end
if not cantouch then DropEntityIfHeld(ent) end
if not cantouch and not skipReturn then return false end
end
hook.Add("GravGunPunt", "FPP.Protect.GravGunPunt", FPP.Protect.GravGunPunt)
--PlayerUse
function FPP.Protect.PlayerUse(ply, ent)
if not tobool(FPP.Settings.FPP_PLAYERUSE1.toggle) then return end
if not IsValid(ent) then return end
local cantouch
local skipReturn = false
if isfunction(ent.PlayerUse) then
cantouch = ent:PlayerUse(ply, ent)
-- Do not return the value, the gamemode will do this
-- Allows other hooks to run
skipReturn = true
elseif ent.PlayerUse ~= nil then
cantouch = ent.PlayerUse
else
cantouch = not ent:IsPlayer() and FPP.plyCanTouchEnt(ply, ent, "PlayerUse")
end
if cantouch and FPP.UnGhost then FPP.UnGhost(ply, ent) end
if not cantouch and not skipReturn then return false end
end
hook.Add("PlayerUse", "FPP.Protect.PlayerUse", FPP.Protect.PlayerUse)
--EntityDamage
function FPP.Protect.EntityDamage(ent, dmginfo)
if not IsValid(ent) then return end
local inflictor = dmginfo:GetInflictor()
local attacker = dmginfo:GetAttacker()
local amount = dmginfo:GetDamage()
if isfunction(ent.EntityDamage) then
local val = ent:EntityDamage(ent, inflictor, attacker, amount, dmginfo)
-- Do not return the value, the gamemode will do this
if val ~= nil then return end
elseif ent.EntityDamage ~= nil then
return ent.EntityDamage
end
if not tobool(FPP.Settings.FPP_ENTITYDAMAGE1.toggle) then return end
-- Don't do anything about players
if ent:IsPlayer() then return end
if not attacker:IsPlayer() then
if not tobool(FPP.Settings.FPP_ENTITYDAMAGE1.protectpropdamage) then return end
local attackerOwner = attacker:CPPIGetOwner()
local entOwner = ent:CPPIGetOwner()
if IsValid(attackerOwner) and IsValid(entOwner) then
local cantouch = FPP.plyCanTouchEnt(attackerOwner, ent, "EntityDamage")
if not cantouch then
dmginfo:SetDamage(0)
ent.FPPAntiDamageWorld = ent.FPPAntiDamageWorld or 0
ent.FPPAntiDamageWorld = ent.FPPAntiDamageWorld + 1
timer.Simple(1, function()
if not ent.FPPAntiDamageWorld then return end
ent.FPPAntiDamageWorld = ent.FPPAntiDamageWorld - 1
if ent.FPPAntiDamageWorld == 0 then
ent.FPPAntiDamageWorld = nil
end
end)
end
return
end
if attacker == game.GetWorld() and ent.FPPAntiDamageWorld then
dmginfo:SetDamage(0)
end
return
end
local cantouch = FPP.plyCanTouchEnt(attacker, ent, "EntityDamage")
if not cantouch then dmginfo:SetDamage(0) end
end
hook.Add("EntityTakeDamage", "FPP.Protect.EntityTakeDamage", FPP.Protect.EntityDamage)
--Toolgun
--for advanced duplicator, you can't use the IsWeapon function
local allweapons = {
["weapon_crowbar"] = true,
["weapon_physgun"] = true,
["weapon_physcannon"] = true,
["weapon_pistol"] = true,
["weapon_stunstick"] = true,
["weapon_357"] = true,
["weapon_smg1"] = true,
["weapon_ar2"] = true,
["weapon_shotgun"] = true,
["weapon_crossbow"] = true,
["weapon_frag"] = true,
["weapon_rpg"] = true,
["gmod_camera"] = true,
["gmod_tool"] = true,
["weapon_bugbait"] = true}
timer.Simple(5, function()
for _, v in ipairs(weapons.GetList()) do
if v.ClassName then allweapons[string.lower(v.ClassName or "")] = true end
end
end)
local invalidToolData = {
["model"] = {
"*",
"\\"
},
["material"] = {
"*",
"\\",
" ",
"effects/highfive_red",
"pp/copy",
".v",
"skybox/"
},
["sound"] = {
"?",
" "
},
["soundname"] = {
" ",
"?"
},
["tracer"] = {
"dof_node"
},
["door_class"] = {
"env_laser"
},
-- Limit wheel torque
["rx"] = 360,
["ry"] = 360,
["rz"] = 360
}
invalidToolData.override = invalidToolData.material
invalidToolData.rope_material = invalidToolData.material
function FPP.Protect.CanTool(ply, trace, tool, ENT)
local ignoreGeneralRestrictTool = false
local SteamID = ply:SteamID()
FPP.RestrictedToolsPlayers = FPP.RestrictedToolsPlayers or {}
if FPP.RestrictedToolsPlayers[tool] and FPP.RestrictedToolsPlayers[tool][SteamID] ~= nil then--Player specific
if FPP.RestrictedToolsPlayers[tool][SteamID] == false then
FPP.Notify(ply, "Toolgun restricted for you!", false)
return false
elseif FPP.RestrictedToolsPlayers[tool][SteamID] == true then
ignoreGeneralRestrictTool = true --If someone is allowed, then he's allowed even though he's not admin, so don't check for further restrictions
end
end
if not ignoreGeneralRestrictTool then
local Group = FPP.Groups[FPP.GroupMembers[SteamID]] or FPP.Groups[ply:GetUserGroup()] or FPP.Groups.default -- What group is the player in. If not in a special group, then he's in default group
local CanGroup = true
if Group and ((Group.allowdefault and table.HasValue(Group.tools, tool)) or -- If the tool is on the BLACKLIST or
(not Group.allowdefault and not table.HasValue(Group.tools, tool))) then -- If the tool is NOT on the WHITELIST
CanGroup = false
end
if FPP.RestrictedTools[tool] then
if tonumber(FPP.RestrictedTools[tool].admin) == 1 and not ply:IsAdmin() then
FPP.Notify(ply, "Toolgun restricted! Admin only!", false)
return false
elseif tonumber(FPP.RestrictedTools[tool].admin) == 2 and not ply:IsSuperAdmin() then
FPP.Notify(ply, "Toolgun restricted! Superadmin only!", false)
return false
elseif (tonumber(FPP.RestrictedTools[tool].admin) == 1 and ply:IsAdmin()) or (tonumber(FPP.RestrictedTools[tool].admin) == 2 and ply:IsSuperAdmin()) then
CanGroup = true -- If the person is not in the BUT has admin access, he should be able to use the tool
end
if FPP.RestrictedTools[tool]["team"] and #FPP.RestrictedTools[tool]["team"] > 0 and not table.HasValue(FPP.RestrictedTools[tool]["team"], ply:Team()) then
FPP.Notify(ply, "Toolgun restricted! incorrect team!", false)
return false
end
end
if not CanGroup then
FPP.Notify(ply, "Toolgun restricted! incorrect group!", false)
return false
end
end
-- Anti server crash
if IsValid(ply:GetActiveWeapon()) and ply:GetActiveWeapon().GetToolObject and ply:GetActiveWeapon():GetToolObject() then
local toolObj = ply:GetActiveWeapon():GetToolObject()
for t, block in pairs(invalidToolData) do
local clientInfo = string.lower(toolObj:GetClientInfo(t) or "")
-- Check for number limits
if isnumber(block) then
local num = tonumber(clientInfo) or 0
if num > block or num < -block then
FPP.Notify(ply, "The client settings of the tool are invalid!", false)
return false
end
continue
end
for _, item in pairs(block) do
if string.find(clientInfo, item, 1, true) then
FPP.Notify(ply, "The client settings of the tool are invalid!", false)
return false
end
end
end
end
local ent = IsEntity(ENT) and ENT or trace and trace.Entity
if IsEntity(ent) and isfunction(ent.CanTool) and ent:GetClass() ~= "gmod_cameraprop" and ent:GetClass() ~= "gmod_rtcameraprop" then
local val = ent:CanTool(ply, trace, tool, ENT)
-- Do not return the value, the gamemode will do this
if val ~= nil then return end
elseif IsEntity(ent) and ent.CanTool ~= nil and ent:GetClass() ~= "gmod_cameraprop" and ent:GetClass() ~= "gmod_rtcameraprop" then
return ent.CanTool
end
if tobool(FPP.Settings.FPP_TOOLGUN1.toggle) and IsValid(ent) then
local cantouch = FPP.plyCanTouchEnt(ply, ent, "Toolgun")
if not cantouch then return false end
end
if tool ~= "adv_duplicator" and tool ~= "duplicator" and tool ~= "advdupe2" then return end
if not ENT and not FPP.AntiSpam.DuplicatorSpam(ply) then return false end
local EntTable =
(tool == "adv_duplicator" and ply:GetActiveWeapon():GetToolObject().Entities) or
(tool == "advdupe2" and ply.AdvDupe2 and ply.AdvDupe2.Entities) or
(tool == "duplicator" and ply.CurrentDupe and ply.CurrentDupe.Entities)
if not EntTable then return end
for k, v in pairs(EntTable) do
local lowerClass = string.lower(v.Class)
if tobool(FPP.Settings.FPP_TOOLGUN1.duplicatenoweapons) and
(not ply:IsAdmin() or (ply:IsAdmin() and not tobool(FPP.Settings.FPP_TOOLGUN1.spawnadmincanweapon))) and
(allweapons[lowerClass] or string.find(lowerClass, "ai_") == 1 or string.find(lowerClass, "item_ammo_") == 1) then
FPP.Notify(ply, "Duplicating blocked entity " .. lowerClass, false)
EntTable[k] = nil
end
if tobool(FPP.Settings.FPP_TOOLGUN1.duplicatorprotect) and (not ply:IsAdmin() or (ply:IsAdmin() and not tobool(FPP.Settings.FPP_TOOLGUN1.spawnadmincanblocked))) then
local setspawning = tobool(FPP.Settings.FPP_TOOLGUN1.spawniswhitelist)
if not tobool(FPP.Settings.FPP_TOOLGUN1.spawniswhitelist) and FPP.Blocked.Spawning1[lowerClass] then
FPP.Notify(ply, "Duplicating blocked entity " .. lowerClass, false)
EntTable[k] = nil
end
-- if the whitelist is on you can't spawn it unless it's found
if tobool(FPP.Settings.FPP_TOOLGUN1.spawniswhitelist) and FPP.Blocked.Spawning1[lowerClass] then
setspawning = false
end
if setspawning then
FPP.Notify(ply, "Duplicating blocked entity " .. lowerClass, false)
EntTable[k] = nil
end
end
end
return
end
hook.Add("CanTool", "FPP.Protect.CanTool", FPP.Protect.CanTool)
function FPP.Protect.CanEditVariable(ent, ply, key, varVal, editTbl)
if not tobool(FPP.Settings.FPP_TOOLGUN1.toggle) then return true end
local val = FPP.Protect.CanProperty(ply, "editentity", ent)
if val ~= nil then return val end
end
hook.Add("CanEditVariable", "FPP.Protect.CanEditVariable", FPP.Protect.CanEditVariable)
function FPP.Protect.CanProperty(ply, property, ent)
-- Use Toolgun because I'm way too lazy to make a new type
if not tobool(FPP.Settings.FPP_TOOLGUN1.toggle) then return true end
local cantouch = FPP.plyCanTouchEnt(ply, ent, "Toolgun")
if not cantouch then return false end
end
hook.Add("CanProperty", "FPP.Protect.CanProperty", FPP.Protect.CanProperty)
function FPP.Protect.CanDrive(ply, ent)
-- Use Toolgun because I'm way too lazy to make a new type
if not tobool(FPP.Settings.FPP_TOOLGUN1.toggle) then return true end
local cantouch = FPP.plyCanTouchEnt(ply, ent, "Toolgun")
if not cantouch then return false end
end
hook.Add("CanDrive", "FPP.Protect.CanDrive", FPP.Protect.CanDrive)
local function freezeDisconnected(ply)
local SteamID = ply:SteamID()
for _, ent in ipairs(ents.GetAll()) do
local physObj = ent:GetPhysicsObject()
if ent.FPPOwnerID ~= SteamID or ent:GetPersistent() or not physObj:IsValid() then continue end
physObj:EnableMotion(false)
end
end
--Player disconnect, not part of the Protect table.
function FPP.PlayerDisconnect(ply)
if not IsValid(ply) then return end
local SteamID = ply:SteamID()
FPP.DisconnectedPlayers[SteamID] = true
if tobool(FPP.Settings.FPP_GLOBALSETTINGS1.freezedisconnected) then
freezeDisconnected(ply)
end
if ply.FPPFallbackOwner then
-- FPP.DisconnectedOriginalOwners = FPP.DisconnectedOriginalOwners or {}
-- FPP.DisconnectedOriginalOwners[SteamID] = {props = {}}
local fallback = player.GetBySteamID(ply.FPPFallbackOwner)
for _, v in ipairs(ents.GetAll()) do
if v.FPPOwnerID ~= SteamID or v:GetPersistent() then continue end
v.FPPFallbackOwner = ply.FPPFallbackOwner
if IsValid(fallback) then
v:CPPISetOwner(fallback)
end
-- table.insert(FPP.DisconnectedOriginalOwners[SteamID].props, v)
-- Only set when not set already
-- this prevents the original owner being set again
-- when the fallback hands their props over to a second
-- (or third, or nth) fallback
if v:GetNW2String("FPP_OriginalOwner", "") == "" then
v:SetNW2String("FPP_OriginalOwner", SteamID)
end
end
-- Create disconnect timer if fallback is not in server
-- ownership is transferred immediately when fallback is in server
if IsValid(fallback) then
return
end
end
if not tobool(FPP.Settings.FPP_GLOBALSETTINGS1.cleanupdisconnected) or
not FPP.Settings.FPP_GLOBALSETTINGS1.cleanupdisconnectedtime then
return
end
if ply:IsAdmin() and not tobool(FPP.Settings.FPP_GLOBALSETTINGS1.cleanupadmin) then return end
timer.Simple(FPP.Settings.FPP_GLOBALSETTINGS1.cleanupdisconnectedtime, function()
if not tobool(FPP.Settings.FPP_GLOBALSETTINGS1.cleanupdisconnected) then return end -- Settings can change in time.
for _, v in ipairs(player.GetAll()) do
if v:SteamID() == SteamID then
return
end
end
for _, v in ipairs(ents.GetAll()) do
if v.FPPOwnerID ~= SteamID or v:GetPersistent() then continue end
v:Remove()
end
FPP.DisconnectedPlayers[SteamID] = nil -- Player out of the Disconnect table
end)
end
hook.Add("PlayerDisconnected", "FPP.PlayerDisconnect", FPP.PlayerDisconnect)
-- PlayerInitialspawn, the props he had left before will now be theirs again
function FPP.PlayerInitialSpawn(ply)
local RP = RecipientFilter()
local SteamID = ply:SteamID()
timer.Simple(5, function()
if not IsValid(ply) then return end
RP:AddAllPlayers()
RP:RemovePlayer(ply)
umsg.Start("FPP_CheckBuddy", RP) --Message everyone that a new player has joined
umsg.Entity(ply)
umsg.End()
end)
local entities = {}
if FPP.DisconnectedPlayers[SteamID] then -- Check if the player has rejoined within the auto remove time
for _, v in ipairs(ents.GetAll()) do
if (v.FPPOwnerID == SteamID or v.FPPFallbackOwner == SteamID or v:GetNW2String("FPP_OriginalOwner") == SteamID) then
v.FPPFallbackOwner = nil
v:CPPISetOwner(ply)
table.insert(entities, v)
if v:GetNW2String("FPP_OriginalOwner") == SteamID then
v:SetNW2String("FPP_OriginalOwner", "")
end
end
end
end
local plys = {}
for _, v in ipairs(player.GetAll()) do if v ~= ply then table.insert(plys, v) end end
timer.Create("FPP_recalculate_cantouch_" .. ply:UserID(), 0, 1, function()
FPP.recalculateCanTouch({ply}, ents.GetAll())
end)
end
hook.Add("PlayerInitialSpawn", "FPP.PlayerInitialSpawn", FPP.PlayerInitialSpawn)
local backup = ENTITY.FireBullets
local blockedEffects = {
["particleeffect"] = true,
["smoke"] = true,
["vortdispel"] = true,
["helicoptermegabomb"] = true,
}
function ENTITY:FireBullets(bullet, ...)
if not bullet.TracerName then return backup(self, bullet, ...) end
if blockedEffects[string.lower(bullet.TracerName)] then
bullet.TracerName = ""
end
return backup(self, bullet, ...)
end
-- Hydraulic exploit workaround
-- One should not be able to constrain doors to anything
local canConstrain = constraint.CanConstrain
local disallowedConstraints = {
["prop_door_rotating"] = true,
["func_door"] = true,
["func_breakable_surf"] = true
}
function constraint.CanConstrain(ent, bone)
if IsValid(ent) and disallowedConstraints[string.lower(ent:GetClass())] then return false end
return canConstrain(ent, bone)
end

View File

@@ -0,0 +1,75 @@
--[[
DEFAULT BLOCKED MODELS
THIS FILE WILL ONLY BE RUN ONCE, WHICH IS THE FIRST TIME YOU INSTALL FPP, OR AFTER THE SQLITE DATABASE IS RESET
DO NOT EDIT THIS FILE AS IT IS USELESS
GO TO Q>UTILITIES>FPP ADMIN SETTINGS > BLOCKED MODEL OPTIONS TO ADD OR REMOVE BLOCKED MODELS
--]]
FPP = FPP or {}
local defaultblocked = {
"models/cranes/crane_frame.mdl",
"models/items/item_item_crate.mdl",
"models/props/cs_militia/silo_01.mdl",
"models/props/cs_office/microwave.mdl",
"models/props/de_train/biohazardtank.mdl",
"models/props_buildings/building_002a.mdl",
"models/props_buildings/collapsedbuilding01a.mdl",
"models/props_buildings/project_building01.mdl",
"models/props_buildings/project_building02.mdl",
"models/props_buildings/project_building03.mdl",
"models/props_buildings/project_destroyedbuildings01.mdl",
"models/props_buildings/row_church_fullscale.mdl",
"models/props_buildings/row_corner_1_fullscale.mdl",
"models/props_buildings/row_res_1_fullscale.mdl",
"models/props_buildings/row_res_2_ascend_fullscale.mdl",
"models/props_buildings/row_res_2_fullscale.mdl",
"models/props_c17/consolebox01a.mdl",
"models/props_c17/oildrum001_explosive.mdl",
"models/props_c17/paper01.mdl",
"models/props_c17/trappropeller_engine.mdl",
"models/props_canal/canal_bridge01.mdl",
"models/props_canal/canal_bridge02.mdl",
"models/props_canal/canal_bridge03a.mdl",
"models/props_canal/canal_bridge03b.mdl",
"models/props_combine/combine_citadel001.mdl",
"models/props_combine/combine_mine01.mdl",
"models/props_combine/combinetrain01.mdl",
"models/props_combine/combinetrain02a.mdl",
"models/props_combine/combinetrain02b.mdl",
"models/props_combine/prison01.mdl",
"models/props_combine/prison01b.mdl",
"models/props_combine/prison01c.mdl",
"models/props_industrial/bridge.mdl",
"models/props_junk/garbage_takeoutcarton001a.mdl",
"models/props_junk/gascan001a.mdl",
"models/props_junk/glassjug01.mdl",
"models/props_junk/trashdumpster02.mdl",
"models/props_phx/amraam.mdl",
"models/props_phx/ball.mdl",
"models/props_phx/cannonball.mdl",
"models/props_phx/huge/evildisc_corp.mdl",
"models/props_phx/misc/flakshell_big.mdl",
"models/props_phx/misc/potato_launcher_explosive.mdl",
"models/props_phx/mk-82.mdl",
"models/props_phx/oildrum001_explosive.mdl",
"models/props_phx/torpedo.mdl",
"models/props_phx/ww2bomb.mdl",
"models/props_wasteland/cargo_container01.mdl",
"models/props_wasteland/cargo_container01.mdl",
"models/props_wasteland/cargo_container01b.mdl",
"models/props_wasteland/cargo_container01c.mdl",
"models/props_wasteland/depot.mdl",
"models/xqm/coastertrack/special_full_corkscrew_left_4.mdl",
}
-- Add to SQLite database
function FPP.AddDefaultBlockedModels()
MySQLite.begin()
for _, v in pairs(defaultblocked) do
FPP.BlockedModels[v] = true
MySQLite.query("REPLACE INTO FPP_BLOCKEDMODELS1 VALUES(" .. MySQLite.SQLStr(v) .. ");")
end
MySQLite.commit()
end

View File

@@ -0,0 +1,587 @@
FPP = FPP or {}
local plyMeta = FindMetaTable("Player")
local entMeta = FindMetaTable("Entity")
local bit_bor = bit.bor
local bit_band = bit.band
--[[-------------------------------------------------------------------------
Entity data explanation.
Every ent has a field FPPCanTouch. This is a table with one entry per player.
Every bit in FPPCanTouch represents a type.
The first bit says whether the player can physgun the ent
The second bit whether the player can use gravun on the ent
etc.
Then there is the FPPCanTouchWhy var This var follows the same idea as FPPCanTouch
except there are five bits for each type. These 4 bits represent a number that shows the reason
why a player can or cannot touch a prop.
---------------------------------------------------------------------------]]
local touchTypes = {
Physgun = 1,
Gravgun = 2,
Toolgun = 4,
PlayerUse = 8,
EntityDamage = 16
}
local touchTypeNumbers = {
[1] = "Physgun",
[2] = "Gravgun",
[4] = "Toolgun",
[8] = "PlayerUse",
[16] = "EntityDamage"
}
local reasonSize = 4 -- bits
local reasonNumbers = {
["owner"] = 1,
["world"] = 2,
["disconnected"] = 3,
["blocked"] = 4,
["constrained"] = 5,
["buddy"] = 6,
["shared"] = 7,
["player"] = 8,
}
--[[-------------------------------------------------------------------------
Utility functions
---------------------------------------------------------------------------]]
local function getPlySetting(ply, settingName)
return (ply[settingName] or ply:GetInfo(settingName)) == "1"
end
local function getSetting(touchType)
return touchType .. "1", string.upper("FPP_" .. touchType .. "1")
end
local constraints = { -- These little buggers think they're not constraints, but they are
["phys_spring"] = true,
}
local function isConstraint(ent)
return ent:IsConstraint() or constraints[ent:GetClass()] or false
end
--[[-------------------------------------------------------------------------
Touch calculations
---------------------------------------------------------------------------]]
local hardWhiteListed = { -- things that mess up when not allowed
["worldspawn"] = true, -- constraints with the world
["gmod_anchor"] = true, -- used in slider constraints with world
["gmod_wire_hologram"] = true, -- created by Expression2, has no physics, but can be constrained to other entities
["phys_spring"] = true, -- https://developer.valvesoftware.com/wiki/Phys_spring
["env_projectedtexture"] = true, -- https://developer.valvesoftware.com/wiki/Env_projectedtexture
["keyframe_rope"] = true, -- https://developer.valvesoftware.com/wiki/Keyframe_rope
["env_skypaint"] = true, -- https://developer.valvesoftware.com/wiki/Env_skypaint
["env_fog_controller"] = true -- https://developer.valvesoftware.com/wiki/Env_fog_controller
}
local function calculateCanTouchForType(ply, ent, touchType)
if not IsValid(ent) then return false, 0 end
ply.FPP_Privileges = ply.FPP_Privileges or {}
local class = ent:GetClass()
local setting, tablename = getSetting(touchType)
local FPPSettings = FPP.Settings[tablename]
-- hard white list
if hardWhiteListed[class] then return true, reasonNumbers.world end
-- picking up players
if touchType == "Physgun" and ent:IsPlayer() and not getPlySetting(ply, "cl_pickupplayers") then
return false, reasonNumbers.player
end
-- blocked entity
local whitelist = FPPSettings.iswhitelist ~= 0
local isInList = FPP.Blocked[setting][string.lower(class)] or false
local isAdmin = ply.FPP_Privileges.FPP_TouchOtherPlayersProps
local isBlocked = whitelist ~= isInList -- XOR
if isBlocked then
local adminsCanTouchBlocked = FPPSettings.admincanblocked ~= 0
local playersCanBlocked = FPPSettings.canblocked ~= 0
local canPhysgunBlocked = (playersCanBlocked or isAdmin and adminsCanTouchBlocked) and not getPlySetting(ply, "FPP_PrivateSettings_BlockedProps")
if not canPhysgunBlocked then
return canPhysgunBlocked, reasonNumbers.blocked
end
end
-- touch own props
local owner = ent.FPPOwner -- Circumvent CPPI for micro-optimisation
if owner == ply then
return not getPlySetting(ply, "FPP_PrivateSettings_OwnProps"),
reasonNumbers.owner
end
local noTouchOtherPlayerProps = getPlySetting(ply, "FPP_PrivateSettings_OtherPlayerProps")
-- Shared entity
if ent.AllowedPlayers and table.HasValue(ent.AllowedPlayers, ply) then
return not noTouchOtherPlayerProps, reasonNumbers.shared
end
if ent["Share" .. setting] then return not noTouchOtherPlayerProps, reasonNumbers.shared end
if IsValid(owner) then
-- Player is buddies with the owner of the entity
if owner.Buddies and owner.Buddies[ply] and owner.Buddies[ply][touchType] then return not noTouchOtherPlayerProps, reasonNumbers.buddy end
-- Someone else's prop
local adminProps = FPPSettings.adminall ~= 0
return isAdmin and adminProps and not noTouchOtherPlayerProps, reasonNumbers.owner
end
-- World props and disconnected players' props
local adminWorldProps = FPPSettings.adminworldprops ~= 0
local peopleWorldProps = FPPSettings.worldprops ~= 0
local restrictWorld = getPlySetting(ply, "FPP_PrivateSettings_WorldProps")
return not restrictWorld and (peopleWorldProps or (isAdmin and adminWorldProps)),
owner == nil and reasonNumbers.world or reasonNumbers.disconnected
end
local blockedEnts = {
["ai_network"] = true,
["network"] = true, -- alternative name for ai_network
["ambient_generic"] = true,
["beam"] = true,
["bodyque"] = true,
["env_soundscape"] = true,
["env_sprite"] = true,
["env_sun"] = true,
["env_tonemap_controller"] = true,
["func_useableladder"] = true,
["gmod_hands"] = true,
["info_ladder_dismount"] = true,
["info_player_start"] = true,
["info_player_terrorist"] = true,
["light_environment"] = true,
["light_spot"] = true,
["physgun_beam"] = true,
["player_manager"] = true,
["point_spotlight"] = true,
["predicted_viewmodel"] = true,
["scene_manager"] = true,
["shadow_control"] = true,
["soundent"] = true,
["spotlight_end"] = true,
["water_lod_control"] = true,
["gmod_gamerules"] = true,
["bodyqueue"] = true,
["phys_bone_follower"] = true,
}
function FPP.calculateCanTouch(ply, ent)
local canTouch = 0
local reasons = 0
local i = 0
for Bit, touchType in pairs(touchTypeNumbers) do
local canTouchType, why = calculateCanTouchForType(ply, ent, touchType)
if canTouchType then
canTouch = bit.bor(canTouch, Bit)
end
reasons = bit.bor(reasons, bit.lshift(why, i * reasonSize))
i = i + 1
end
ent.FPPCanTouch = ent.FPPCanTouch or {}
ent.FPPCanTouchWhy = ent.FPPCanTouchWhy or {}
local changed = ent.FPPCanTouch[ply] ~= canTouch or ent.FPPCanTouchWhy[ply] ~= reasons or ent.FPPOwnerChanged
ent.FPPCanTouch[ply] = canTouch
ent.FPPCanTouchWhy[ply] = reasons
return changed
end
-- try not to call this with both player.GetAll() and ents.GetAll()
local function recalculateCanTouch(players, entities)
for k, v in pairs(entities) do
if not IsValid(v) then entities[k] = nil continue end
if v:IsEFlagSet(EFL_SERVER_ONLY) then entities[k] = nil continue end
if blockedEnts[v:GetClass()] then entities[k] = nil continue end
if v:IsWeapon() and IsValid(v:GetOwner()) then entities[k] = nil continue end
end
for _, ply in pairs(players) do
if not IsValid(ply) then continue end
-- optimisations
ply.FPPIsAdmin = ply.FPP_Privileges.FPP_TouchOtherPlayersProps
ply.FPP_PrivateSettings_OtherPlayerProps = ply:GetInfo("FPP_PrivateSettings_OtherPlayerProps")
ply.cl_pickupplayers = ply:GetInfo("cl_pickupplayers")
ply.FPP_PrivateSettings_BlockedProps = ply:GetInfo("FPP_PrivateSettings_BlockedProps")
ply.FPP_PrivateSettings_OwnProps = ply:GetInfo("FPP_PrivateSettings_OwnProps")
ply.FPP_PrivateSettings_WorldProps = ply:GetInfo("FPP_PrivateSettings_WorldProps")
local changed = {}
for _, ent in pairs(entities) do
local hasChanged = FPP.calculateCanTouch(ply, ent)
if hasChanged then table.insert(changed, ent) end
end
FPP.plySendTouchData(ply, changed)
-- end optimisations
ply.FPP_PrivateSettings_OtherPlayerProps = nil
ply.cl_pickupplayers = nil
ply.FPP_PrivateSettings_BlockedProps = nil
ply.FPP_PrivateSettings_OwnProps = nil
ply.FPP_PrivateSettings_WorldProps = nil
ply.FPPIsAdmin = nil
end
end
function FPP.recalculateCanTouch(plys, ens)
FPP.calculatePlayerPrivilege("FPP_TouchOtherPlayersProps", function() recalculateCanTouch(plys, ens) end)
end
--[[-------------------------------------------------------------------------
Touch interface
---------------------------------------------------------------------------]]
function FPP.plyCanTouchEnt(ply, ent, touchType)
local entTable = ent:GetTable()
local entCanTouch = entTable.FPPCanTouch
if not entCanTouch then
entCanTouch = {}
entTable.FPPCanTouch = entCanTouch
end
entCanTouch[ply] = entCanTouch[ply] or 0
local allowedPlayers = entTable.AllowedPlayers
if not allowedPlayers then
entTable.AllowedPlayers = {}
end
local canTouch = entCanTouch[ply]
-- if an entity is constrained, return the least of the rights
local entRestrictConstraint = entTable.FPPRestrictConstraint
if entRestrictConstraint and entRestrictConstraint[ply] then
canTouch = bit_band(entRestrictConstraint[ply], entCanTouch[ply])
end
-- return the answer for every touch type if parameter is empty
if not touchType then
return canTouch
end
return bit_bor(canTouch, touchTypes[touchType]) == canTouch
end
local GetTable = entMeta.GetTable
function FPP.entGetOwner(ent)
return GetTable(ent).FPPOwner
end
--[[-------------------------------------------------------------------------
Networking
---------------------------------------------------------------------------]]
util.AddNetworkString("FPP_TouchabilityData")
-- Sends 13 + 8 + 5 + 20 = 46 bits of ownership data per entity
local function netWriteEntData(ply, ent)
-- EntIndex for when it's out of the PVS of the player
net.WriteUInt(ent:EntIndex(), 13)
local owner = ent:CPPIGetOwner()
net.WriteUInt(IsValid(owner) and owner:EntIndex() or 255, 8)
local entTable = ent:GetTable()
net.WriteUInt(entTable.FPPRestrictConstraint and entTable.FPPRestrictConstraint[ply] or entTable.FPPCanTouch[ply], 5) -- touchability information
net.WriteUInt(entTable.FPPConstraintReasons and entTable.FPPConstraintReasons[ply] or entTable.FPPCanTouchWhy[ply], 20) -- reasons
end
-- The net message gets too big with 5826 or more entities. In that case,
-- break up the input into chunks and recurse per chunk.
--
-- Note: There is still a limit on the entity count! If there are too many
-- entities, the client will disconnect with "Disconnect: Client 0
-- overflowed reliable channel..". That limit is above the entity limit of
-- 16384, though, so no further work is done to lift that limit.
local count_limit = 5825
function FPP.plySendTouchData(ply, ents)
local count = #ents
if count == 0 then return end
if count > count_limit then
local accumulator = {}
for i = 1, count do
table.insert(accumulator, ents[i])
if i % count_limit == 0 then
FPP.plySendTouchData(ply, accumulator)
accumulator = {}
end
end
if #accumulator > 0 then
FPP.plySendTouchData(ply, accumulator)
end
return
end
net.Start("FPP_TouchabilityData")
for i = 1, count do
netWriteEntData(ply, ents[i])
net.WriteBit(i == count)
end
net.Send(ply)
end
--[[-------------------------------------------------------------------------
Events that trigger recalculation
---------------------------------------------------------------------------]]
local function handleConstraintCreation(ent)
local ent1, ent2 = ent:GetConstrainedEntities()
ent1, ent2 = ent1 or ent.Ent1, ent2 or ent.Ent2
if not ent1 or not ent2 or not ent1.FPPCanTouch or not ent2.FPPCanTouch then return end
local reason = 0
local i = 0
for Bit, touchType in pairs(touchTypeNumbers) do
reason = bit.bor(reason, bit.lshift(reasonNumbers.constrained, i * reasonSize))
i = i + 1
end
for _, ply in ipairs(player.GetAll()) do
local touch1, touch2 = FPP.plyCanTouchEnt(ply, ent1), FPP.plyCanTouchEnt(ply, ent2)
-- The constrained entities have the same touching rights.
if touch1 == touch2 then continue end
local restrictedAccess = bit.band(touch1, touch2)
local send = {}
for _, e in pairs(constraint.GetAllConstrainedEntities(ent1) or {}) do
if not IsValid(e) then continue end
if FPP.plyCanTouchEnt(ply, e) == restrictedAccess then continue end
e.FPPRestrictConstraint = e.FPPRestrictConstraint or {}
e.FPPConstraintReasons = e.FPPConstraintReasons or {}
e.FPPRestrictConstraint[ply] = restrictedAccess
e.FPPConstraintReasons[ply] = reason
table.insert(send, e)
end
FPP.plySendTouchData(ply, send)
end
end
--[[-------------------------------------------------------------------------
On entity created
---------------------------------------------------------------------------]]
local function onEntitiesCreated(ents)
-- Table from player to list of entities that need to be networked
local sendToPlayers = {}
for _, ent in ipairs(ents) do
if not ent:IsValid() then continue end
if isConstraint(ent) then
handleConstraintCreation(ent)
continue
end
-- Don't send information about server only entities to the clients
if ent:GetSolid() == 0 or ent:IsEFlagSet(EFL_SERVER_ONLY) then
continue
end
if blockedEnts[ent:GetClass()] then continue end
for _, ply in ipairs(player.GetAll()) do
local changed = FPP.calculateCanTouch(ply, ent)
-- Only send data that has been changed
if not changed then continue end
sendToPlayers[ply] = sendToPlayers[ply] or {}
table.insert(sendToPlayers[ply], ent)
end
end
for ply, entsToSend in pairs(sendToPlayers) do
FPP.plySendTouchData(ply, entsToSend)
end
end
-- Make a queue of entities created per frame, so the server will send out a maximum-
-- of one message per player per frame
local entQueue = {}
local timerFunc = function()
onEntitiesCreated(entQueue)
entQueue = {}
timer.Remove("FPP_OnEntityCreatedTimer")
end
hook.Add("OnEntityCreated", "FPP_EntityCreated", function(ent)
table.insert(entQueue, ent)
if timer.Exists("FPP_OnEntityCreatedTimer") then return end
timer.Create("FPP_OnEntityCreatedTimer", 0, 1, timerFunc)
end)
--[[-------------------------------------------------------------------------
On entity removed
---------------------------------------------------------------------------]]
-- Recalculates touchability information for constrained entities
-- Note: Assumes normal touchability information is up to date!
-- Update constraints, O(players * (entities + constraints))
function FPP.RecalculateConstrainedEntities(players, entities)
for i, ent in pairs(entities) do
if not IsValid(ent) then entities[i] = nil continue end
if ent:IsEFlagSet(EFL_SERVER_ONLY) then entities[i] = nil continue end
if blockedEnts[ent:GetClass()] then entities[i] = nil continue end
ent.FPPRestrictConstraint = ent.FPPRestrictConstraint or {}
ent.FPPConstraintReasons = ent.FPPConstraintReasons or {}
end
-- constrained entities form a graph.
-- and graphs are things you can traverse with BFS
for _, ply in pairs(players) do
local discovered = {}
-- BFS vars
local BFSQueue = {}
local black, gray = {}, {} -- black = fully discovered, gray = seen, but discovery from this point is needed
local value -- used as key and value of the BFSQueue
for _, ent in pairs(entities) do
if discovered[ent] then continue end -- We've seen this ent in a graph
ent.FPPCanTouch = ent.FPPCanTouch or {}
ent.FPPCanTouch[ply] = ent.FPPCanTouch[ply] or 0
local left, right = 1, 2
BFSQueue[left] = ent
local FPP_CanTouch = ent.FPPCanTouch[ply] -- Find out the canTouch state
while BFSQueue[left] do
value = BFSQueue[left]
BFSQueue[left] = nil
left = left + 1
for _, constr in pairs(value.Constraints or {}) do
local otherEnt = constr.Ent1 == value and constr.Ent2 or constr.Ent1
if not IsValid(otherEnt) or gray[otherEnt] or black[otherEnt] then continue end
gray[otherEnt] = true
BFSQueue[right] = otherEnt
right = right + 1
end
black[value] = true
discovered[value] = true
-- The entity doesn't necessarily have CanTouch data at this point
value.FPPCanTouch = value.FPPCanTouch or {}
value.FPPCanTouch[ply] = value.FPPCanTouch[ply] or 0
FPP_CanTouch = bit.band(FPP_CanTouch or 0, value.FPPCanTouch[ply])
end
-- now update the ents to the client
local updated = {}
for e in pairs(black) do
if FPP.plyCanTouchEnt(ply, e) ~= FPP_CanTouch then
e.FPPRestrictConstraint = e.FPPRestrictConstraint or {}
e.FPPRestrictConstraint[ply] = e.FPPCanTouch[ply] ~= FPP_CanTouch and FPP_CanTouch or nil
table.insert(updated, e)
end
end
FPP.plySendTouchData(ply, updated)
-- reset BFS information for next BFS round
black = {}
gray = {}
end
end
end
local entMem = {}
local function constraintRemovedTimer(ent1, ent2)
if not IsValid(ent1) and not IsValid(ent2) then return end
entMem = {}
local allConstrainedEnts = {}
table.Add(allConstrainedEnts, constraint.GetAllConstrainedEntities(ent1))
table.Add(allConstrainedEnts, constraint.GetAllConstrainedEntities(ent2))
FPP.RecalculateConstrainedEntities(player.GetAll(), allConstrainedEnts)
end
local function handleConstraintRemoved(ent)
local ent1, ent2 = ent:GetConstrainedEntities()
ent1, ent2 = ent1 or ent.Ent1, ent2 or ent.Ent2
if not IsValid(ent1) or not IsValid(ent2) then return end
-- prevent the function from being called too often when many constraints are removed at once
if entMem[ent1] or entMem[ent2] then return end
entMem[ent1] = true
entMem[ent2] = true
timer.Create("FPP_ConstraintRemovedTimer", 0, 1, function() constraintRemovedTimer(ent1, ent2) end)
end
local function onEntityRemoved(ent)
if isConstraint(ent) then handleConstraintRemoved(ent) end
end
hook.Add("EntityRemoved", "FPP_OnEntityRemoved", onEntityRemoved)
--[[-------------------------------------------------------------------------
Player disconnected
---------------------------------------------------------------------------]]
local function playerDisconnected(ply)
local ownedEnts = {}
for _, ent in ipairs(ents.GetAll()) do
if ent:CPPIGetOwner() == ply then
table.insert(ownedEnts, ent)
end
end
timer.Simple(0, function() FPP.recalculateCanTouch(player.GetAll(), ownedEnts) end)
end
hook.Add("PlayerDisconnected", "FPP_PlayerDisconnected", playerDisconnected)
--[[-------------------------------------------------------------------------
Usergroup changed
---------------------------------------------------------------------------]]
local function userGroupRecalculate(ply)
if not IsValid(ply) or not ply:IsPlayer() then return end
timer.Create("FPP_recalculate_cantouch_" .. ply:UserID(), 0, 1, function()
FPP.recalculateCanTouch({ply}, ents.GetAll())
end)
end
FPP.oldSetUserGroup = FPP.oldSetUserGroup or plyMeta.SetUserGroup
function plyMeta:SetUserGroup(group)
userGroupRecalculate(self)
return FPP.oldSetUserGroup(self, group)
end
FPP.oldSetNWString = FPP.oldSetNWString or entMeta.SetNWString
function entMeta:SetNWString(str, val)
if str ~= "usergroup" then return FPP.oldSetNWString(self, str, val) end
userGroupRecalculate(self)
return FPP.oldSetNWString(self, str, val)
end
FPP.oldSetNetworkedString = FPP.oldSetNetworkedString or entMeta.SetNetworkedString
function entMeta:SetNetworkedString(str, val)
if str ~= "usergroup" then return FPP.oldSetNetworkedString(self, str, val) end
userGroupRecalculate(self)
return FPP.oldSetNetworkedString(self, str, val)
end

View File

@@ -0,0 +1,988 @@
FPP = FPP or {}
util.AddNetworkString("FPP_Groups")
util.AddNetworkString("FPP_GroupMembers")
util.AddNetworkString("FPP_RestrictedToolList")
util.AddNetworkString("FPP_BlockedModels")
FPP.Blocked = FPP.Blocked or {}
FPP.Blocked.Physgun1 = FPP.Blocked.Physgun1 or {}
FPP.Blocked.Spawning1 = FPP.Blocked.Spawning1 or {}
FPP.Blocked.Gravgun1 = FPP.Blocked.Gravgun1 or {}
FPP.Blocked.Toolgun1 = FPP.Blocked.Toolgun1 or {}
FPP.Blocked.PlayerUse1 = FPP.Blocked.PlayerUse1 or {}
FPP.Blocked.EntityDamage1 = FPP.Blocked.EntityDamage1 or {}
FPP.BlockedModels = FPP.BlockedModels or {}
FPP.RestrictedTools = FPP.RestrictedTools or {}
FPP.RestrictedToolsPlayers = FPP.RestrictedToolsPlayers or {}
FPP.Groups = FPP.Groups or {}
FPP.GroupMembers = FPP.GroupMembers or {}
function FPP.Notify(ply, text, bool, total_time)
if ply:EntIndex() == 0 then
ServerLog(text)
return
end
umsg.Start("FPP_Notify", ply)
umsg.String(text)
umsg.Bool(bool)
umsg.Float(total_time or 6)
umsg.End()
ply:PrintMessage(HUD_PRINTCONSOLE, text)
end
function FPP.NotifyAll(text, bool, total_time)
umsg.Start("FPP_Notify")
umsg.String(text)
umsg.Bool(bool)
umsg.Float(total_time or 6)
umsg.End()
for _, ply in ipairs(player.GetAll()) do
ply:PrintMessage(HUD_PRINTCONSOLE, text)
end
end
local function getSettingsChangedEntities(settingsType, setting)
local plys, entities = {}, {}
local blockedString = string.sub(settingsType, 5, 5) .. string.lower(string.sub(settingsType, 6))
blockedString = blockedString == "Playeruse1" and "PlayerUse1" or blockedString -- dirty hack for stupid naming system.
blockedString = blockedString == "Entitydamage1" and "EntityDamage1" or blockedString -- dirty hack for stupid naming system.
if setting == "adminall" then
for _, v in ipairs(ents.GetAll()) do
if not IsValid(v) then continue end
local owner = v:CPPIGetOwner()
if IsValid(owner) then table.insert(entities, v) end
end
for _, v in ipairs(player.GetAll()) do
v.FPP_Privileges = v.FPP_Privileges or {}
if v.FPP_Privileges.FPP_TouchOtherPlayersProps then table.insert(plys, v) end
end
return plys, entities
elseif setting == "worldprops" or setting == "adminworldprops" then
for _, v in ipairs(ents.GetAll()) do
if FPP.Blocked[blockedString][string.lower(v:GetClass())] then continue end
local owner = v:CPPIGetOwner()
if not IsValid(owner) then table.insert(entities, v) end
end
for _, v in ipairs(player.GetAll()) do
v.FPP_Privileges = v.FPP_Privileges or {}
if v.FPP_Privileges.FPP_TouchOtherPlayersProps then table.insert(plys, v) end
end
return setting == "adminworldprops" and plys or player.GetAll(), entities
elseif setting == "canblocked" or setting == "admincanblocked" then
for _, v in ipairs(ents.GetAll()) do
if not FPP.Blocked[blockedString][string.lower(v:GetClass())] then continue end
table.insert(entities, v)
end
for _, v in ipairs(player.GetAll()) do
v.FPP_Privileges = v.FPP_Privileges or {}
if v.FPP_Privileges.FPP_TouchOtherPlayersProps then table.insert(plys, v) end
end
return setting == "admincanblocked" and plys or player.GetAll(), entities
elseif setting == "iswhitelist" then
return player.GetAll(), ents.GetAll()
end
end
util.AddNetworkString("FPP_Settings")
local function SendSettings(ply)
net.Start("FPP_Settings")
FPP.ForAllSettings(function(k, s, v)
net.WriteDouble(v)
end)
net.Send(ply)
end
util.AddNetworkString("FPP_Settings_Update")
local function updateFPPSetting(kind, setting, value)
local skipKind, skipSetting = 0, 0
FPP.Settings[kind][setting] = value
local finalSkipKind
FPP.ForAllSettings(function(k, s)
skipKind = skipKind + 1
if k ~= kind then return true end
finalSkipKind = skipKind - skipSetting
skipSetting = skipSetting + 1
if s ~= setting then return end
return true
end)
net.Start("FPP_Settings_Update")
net.WriteUInt(finalSkipKind, 8)
net.WriteUInt(skipSetting, 8)
net.WriteDouble(value)
net.Broadcast()
end
local function runIfAccess(priv, f)
return function(ply, cmd, args)
CAMI.PlayerHasAccess(ply, priv, function(allowed, _)
if allowed then return f(ply, cmd, args) end
FPP.Notify(ply, string.format("You need the '%s' privilege in order to be able to use this command", priv), false)
end)
end
end
local function FPP_SetSetting(ply, cmd, args)
if not args[1] then FPP.Notify(ply, "FPP_setting: First argument (setting name) not given!", false) return end
if not args[3] then FPP.Notify(ply, "FPP_setting: Third argument (the value of the setting) not given", false) return end
if not FPP.Settings[args[1]] then
FPP.Notify(ply, ("FPP_setting: Setting %s does not exist! Settings that DO exist:"):format(args[1]), false)
for setting, _ in pairs(FPP.Settings) do
FPP.Notify(ply, setting, false)
end
return
end
if not FPP.Settings[args[1]][args[2]] then FPP.Notify(ply, ("FPP_setting: Setting %s.%s does not exist!"):format(args[1], args[2]), false) return end
updateFPPSetting(args[1], args[2], tonumber(args[3]))
MySQLite.queryValue("SELECT var FROM " .. args[1] .. " WHERE var = " .. sql.SQLStr(args[2]) .. ";", function(data)
if not data then
MySQLite.query("INSERT INTO " .. args[1] .. " VALUES(" .. sql.SQLStr(args[2]) .. ", " .. args[3] .. ");")
elseif tonumber(data) ~= args[3] then
MySQLite.query("UPDATE " .. args[1] .. " SET setting = " .. args[3] .. " WHERE var = " .. sql.SQLStr(args[2]) .. ";")
end
FPP.NotifyAll(((ply.Nick and ply:Nick()) or "Console") .. " set " .. string.lower(string.gsub(args[1], "FPP_", "")) .. " " .. args[2] .. " to " .. tostring(args[3]), tobool(tonumber(args[3])))
end)
FPP.calculatePlayerPrivilege("FPP_TouchOtherPlayersProps", function()
local plys, entities = getSettingsChangedEntities(args[1], args[2])
if not plys or not entities or #plys == 0 or #entities == 0 then return end
FPP.recalculateCanTouch(plys, entities)
end)
end
concommand.Add("FPP_setting", runIfAccess("FPP_Settings", FPP_SetSetting))
local function AddBlocked(ply, cmd, args)
if not args[1] then FPP.Notify(ply, "FPP_AddBlocked: Block list not given", false) return end
if not args[2] then FPP.Notify(ply, "FPP_AddBlocked: Entity to block not given", false) return end
if not FPP.Blocked[args[1]] then FPP.Notify(ply, ("FPP_AddBlocked: Block list %s does not exist"):format(args[1]), false) return end
args[2] = string.lower(args[2])
if FPP.Blocked[args[1]][args[2]] then return end
FPP.Blocked[args[1]][args[2]] = true
MySQLite.query(string.format("INSERT INTO FPP_BLOCKED1 (var, setting) VALUES(%s, %s);", sql.SQLStr(args[1]), sql.SQLStr(args[2])))
FPP.NotifyAll(((ply.Nick and ply:Nick()) or "Console") .. " added " .. args[2] .. " to the " .. args[1] .. " black/whitelist", true)
FPP.recalculateCanTouch(player.GetAll(), ents.FindByClass(args[2]))
end
concommand.Add("FPP_AddBlocked", runIfAccess("FPP_Settings", AddBlocked))
-- Models can differ between server and client. Famous example being:
-- models/props_phx/cannonball_solid.mdl <-- serverside
-- models/props_phx/cannonball.mdl <-- clientside
-- Similar problems occur with effects
local function getIntendedBlockedModels(model, ent)
model = string.Replace(string.lower(model), "\\", "/")
if not IsValid(ent) then return {model} end
if ent:GetClass() == "prop_effect" then return {ent.AttachedEntity:GetModel()} end
if model ~= ent:GetModel() then return {model, ent:GetModel()} end
return {model}
end
local function AddBlockedModel(ply, cmd, args)
if not args[1] then FPP.Notify(ply, "FPP_AddBlockedModel: Model not given", false) return end
local models = getIntendedBlockedModels(args[1], tonumber(args[2]) and Entity(args[2]) or nil)
for _, model in pairs(models) do
if FPP.BlockedModels[model] then FPP.Notify(ply, string.format([["%s" is already in the black/whitelist]], model), false) continue end
FPP.BlockedModels[model] = true
MySQLite.query("REPLACE INTO FPP_BLOCKEDMODELS1 VALUES(" .. sql.SQLStr(model) .. ");")
FPP.NotifyAll(((ply.Nick and ply:Nick()) or "Console") .. " added " .. model .. " to the blocked models black/whitelist", true)
end
end
concommand.Add("FPP_AddBlockedModel", runIfAccess("FPP_Settings", AddBlockedModel))
local function RemoveBlocked(ply, cmd, args)
if not args[1] then FPP.Notify(ply, "FPP_RemoveBlocked: Block list not given", false) return end
if not args[2] then FPP.Notify(ply, "FPP_RemoveBlocked: Entity to block not given", false) return end
if not FPP.Blocked[args[1]] then FPP.Notify(ply, ("FPP_RemoveBlocked: Block list %s does not exist"):format(args[1]), false) return end
FPP.Blocked[args[1]][args[2]] = nil
MySQLite.query("DELETE FROM FPP_BLOCKED1 WHERE var = " .. sql.SQLStr(args[1]) .. " AND setting = " .. sql.SQLStr(args[2]) .. ";")
FPP.NotifyAll(((ply.Nick and ply:Nick()) or "Console") .. " removed " .. args[2] .. " from the " .. args[1] .. " black/whitelist", false)
FPP.recalculateCanTouch(player.GetAll(), ents.FindByClass(args[2]))
end
concommand.Add("FPP_RemoveBlocked", runIfAccess("FPP_Settings", RemoveBlocked))
local function RemoveBlockedModel(ply, cmd, args)
if not args[1] then FPP.Notify(ply, "FPP_RemoveBlockedModel: Model not given", false) return end
local models = getIntendedBlockedModels(args[1], tonumber(args[2]) and Entity(args[2]) or nil)
for _, model in pairs(models) do
FPP.BlockedModels[model] = nil
MySQLite.query("DELETE FROM FPP_BLOCKEDMODELS1 WHERE model = " .. sql.SQLStr(model) .. ";")
FPP.NotifyAll(((ply.Nick and ply:Nick()) or "Console") .. " removed " .. model .. " from the blocked models black/whitelist", false)
end
end
concommand.Add("FPP_RemoveBlockedModel", runIfAccess("FPP_Settings", RemoveBlockedModel))
local allowedShares = {
SharePhysgun1 = true,
ShareGravgun1 = true,
SharePlayerUse1 = true,
ShareEntityDamage1 = true,
ShareToolgun1 = true
}
local function ShareProp(ply, cmd, args)
if not args[1] then FPP.Notify(ply, "FPP_ShareProp: Entity not given", false) return end
if not IsValid(Entity(args[1])) then FPP.Notify(ply, "FPP_ShareProp: Entity not valid", false) return end
if not args[2] then FPP.Notify(ply, "FPP_ShareProp: Player to share with not given", false) return end
local ent = Entity(args[1])
if ent:CPPIGetOwner() ~= ply then
FPP.Notify(ply, "You do not have the right to share this entity.", false)
return
end
if not tonumber(args[2]) or not IsValid(Player(tonumber(args[2]))) then -- This is for sharing prop per utility
if not allowedShares[args[2]] then
FPP.Notify(ply, "FPP_ShareProp: Player to share with doesn't exist", false)
return
end
ent[args[2]] = tobool(args[3])
else -- This is for sharing prop per player
local target = Player(tonumber(args[2]))
local toggle = tobool(args[3])
-- Make the table if it isn't there
if not ent.AllowedPlayers and toggle then
ent.AllowedPlayers = {target}
else
if toggle and not table.HasValue(ent.AllowedPlayers, target) then
table.insert(ent.AllowedPlayers, target)
FPP.Notify(target, ply:Nick() .. " shared an entity with you!", true)
elseif not toggle then
for k, v in pairs(ent.AllowedPlayers or {}) do
if v == target then
table.remove(ent.AllowedPlayers, k)
FPP.Notify(target, ply:Nick() .. " unshared an entity with you!", false)
end
end
end
end
end
FPP.recalculateCanTouch(player.GetAll(), {ent})
end
concommand.Add("FPP_ShareProp", ShareProp)
local function RetrieveSettings()
MySQLite.begin()
for k in pairs(FPP.Settings) do
MySQLite.queueQuery("SELECT setting, var FROM " .. k .. ";", function(data)
if not data then return end
for _, value in pairs(data) do
if FPP.Settings[k][value.var] == nil then
-- Likely an old setting that has since been removed from FPP.
-- This setting however still exists in the DB. Time to remove it.
MySQLite.query("DELETE FROM " .. k .. " WHERE var = " .. sql.SQLStr(value.var) .. ";")
continue
end
FPP.Settings[k][value.var] = tonumber(value.setting)
end
end)
end
MySQLite.commit(function()
SendSettings(player.GetAll())
end)
end
local defaultBlocked = {
Physgun1 = {
["func_breakable_surf"] = true,
["func_brush"] = true,
["func_button"] = true,
["func_door"] = true,
["prop_door_rotating"] = true,
["func_door_rotating"] = true
},
Spawning1 = {
["func_breakable_surf"] = true,
["player"] = true,
["func_door"] = true,
["prop_door_rotating"] = true,
["func_door_rotating"] = true,
["ent_explosivegrenade"] = true,
["ent_mad_grenade"] = true,
["ent_flashgrenade"] = true,
["gmod_wire_field_device"] = true
},
Gravgun1 = {["func_breakable_surf"] = true, ["vehicle_"] = true},
Toolgun1 = {
["func_breakable_surf"] = true,
["func_button"] = true,
["player"] = true,
["func_door"] = true,
["prop_door_rotating"] = true,
["func_door_rotating"] = true
},
PlayerUse1 = {},
EntityDamage1 = {}
}
-- Fills the FPP blocked table with default things that are to be blocked
function FPP.FillDefaultBlocked()
local count = 0
MySQLite.begin()
-- All values that are to be inserted
local allValues = {}
for k, v in pairs(defaultBlocked) do
for a in pairs(v) do
FPP.Blocked[k][a] = true
count = count + 1
if not MySQLite.isMySQL() then
MySQLite.query("REPLACE INTO FPP_BLOCKED1 VALUES(" .. count .. ", " .. sql.SQLStr(k) .. ", " .. sql.SQLStr(a) .. ");")
else
table.insert(allValues, string.format("(%i, %s, %s)", count, sql.SQLStr(k), sql.SQLStr(a)))
end
end
end
-- Run it all in a single query if using MySQL.
if MySQLite.isMySQL() then
MySQLite.query(string.format("INSERT IGNORE INTO FPP_BLOCKED1 VALUES %s", table.concat(allValues, ",")))
end
MySQLite.commit()
end
local function RetrieveBlocked()
MySQLite.query("SELECT * FROM FPP_BLOCKED1;", function(data)
if istable(data) then
for _, v in pairs(data) do
if not FPP.Blocked[v.var] then
ErrorNoHalt((v.var or "(nil var)") .. " blocked type does not exist! (Setting: " .. (v.setting or "") .. ")")
continue
end
FPP.Blocked[v.var][string.lower(v.setting)] = true
end
else
-- Give third party addons 5 seconds to add default blocked items
timer.Simple(5, FPP.FillDefaultBlocked)
end
end)
end
--[[-------------------------------------------------------------------------
Default blocked entities
Only save in the database on first start
---------------------------------------------------------------------------]]
function FPP.AddDefaultBlocked(types, classname)
classname = string.lower(classname)
if isstring(types) then
defaultBlocked[types] = defaultBlocked[types] or {}
defaultBlocked[types][classname] = true
return
end
for _, v in pairs(types) do
defaultBlocked[v] = defaultBlocked[v] or {}
defaultBlocked[v][classname] = true
end
end
local function RetrieveBlockedModels()
FPP.BlockedModels = FPP.BlockedModels or {}
-- Sometimes when the database retrieval is corrupt,
-- only parts of the table will be retrieved
-- This is a workaround
if not MySQLite.isMySQL() then
local count = MySQLite.queryValue("SELECT COUNT(*) FROM FPP_BLOCKEDMODELS1;") or 0
if tonumber(count) == 0 then
FPP.AddDefaultBlockedModels() -- Load the default blocked models on first run
end
-- Select with offsets of a thousand.
-- That's about the maximum it can receive properly at once
for i = 0, count, 1000 do
MySQLite.query("SELECT * FROM FPP_BLOCKEDMODELS1 LIMIT 1000 OFFSET " .. i .. ";", function(data)
for _, v in ipairs(data or {}) do
FPP.BlockedModels[v.model] = true
end
end)
end
return
end
-- Retrieve the data normally from MySQL
MySQLite.query("SELECT * FROM FPP_BLOCKEDMODELS1;", function(data)
if not data or #data == 0 then
FPP.AddDefaultBlockedModels() -- Load the default blocked models on first run
end
for _, v in ipairs(data or {}) do
if not v.model then continue end
FPP.BlockedModels[v.model] = true
end
end)
end
local function RetrieveRestrictedTools()
MySQLite.query("SELECT * FROM FPP_TOOLADMINONLY;", function(data)
if istable(data) then
for _, v in ipairs(data) do
FPP.RestrictedTools[v.toolname] = {}
FPP.RestrictedTools[v.toolname]["admin"] = tonumber(v.adminonly)
end
end
end)
MySQLite.query("SELECT * FROM FPP_TOOLRESTRICTPERSON1;", function(perplayerData)
if not istable(perplayerData) then return end
for _, v in ipairs(perplayerData) do
FPP.RestrictedToolsPlayers[v.toolname] = FPP.RestrictedToolsPlayers[v.toolname] or {}
FPP.RestrictedToolsPlayers[v.toolname][v.steamid] = tobool(v.allow)
end
end)
MySQLite.query("SELECT * FROM FPP_TOOLTEAMRESTRICT;", function(data)
if not data then return end
for _, v in ipairs(data) do
FPP.RestrictedTools[v.toolname] = FPP.RestrictedTools[v.toolname] or {}
FPP.RestrictedTools[v.toolname]["team"] = FPP.RestrictedTools[v.toolname]["team"] or {}
table.insert(FPP.RestrictedTools[v.toolname]["team"], tonumber(v.team))
end
end)
end
local function RetrieveGroups()
MySQLite.query("SELECT * FROM FPP_GROUPS3;", function(data)
if not istable(data) then
MySQLite.query("REPLACE INTO FPP_GROUPS3 VALUES('default', 1);")
FPP.Groups["default"] = {}
FPP.Groups["default"].tools = {}
FPP.Groups["default"].allowdefault = true
return
end -- if there are no groups then there isn't much to load
for _, v in ipairs(data) do
FPP.Groups[v.groupname] = {}
FPP.Groups[v.groupname].tools = {}
FPP.Groups[v.groupname].allowdefault = tobool(v.allowdefault)
end
MySQLite.query("SELECT * FROM FPP_GROUPTOOL;", function(grouptooldata)
if not grouptooldata then return end
for _, v in ipairs(grouptooldata) do
FPP.Groups[v.groupname] = FPP.Groups[v.groupname] or {}
FPP.Groups[v.groupname].tools = FPP.Groups[v.groupname].tools or {}
table.insert(FPP.Groups[v.groupname].tools, v.tool)
end
end)
MySQLite.query("SELECT * FROM FPP_GROUPMEMBERS1;", function(members)
if not istable(members) then return end
for _, v in ipairs(members) do
FPP.GroupMembers[v.steamid] = v.groupname
end
end)
end)
end
hook.Add("PlayerInitialSpawn", "FPP_SendSettings", SendSettings)
local function AddGroup(ply, cmd, args)
if not args[1] then FPP.Notify(ply, "Invalid argument(s)", false) return end-- Args: 1 = name, optional: 2 = allowdefault
local name = string.lower(args[1])
local allowdefault = tonumber(args[2]) or 1
if FPP.Groups[name] then
FPP.Notify(ply, "Group already exists", false)
return
end
FPP.Groups[name] = {}
FPP.Groups[name].allowdefault = tobool(allowdefault)
FPP.Groups[name].tools = {}
MySQLite.query("REPLACE INTO FPP_GROUPS3 VALUES(" .. sql.SQLStr(name) .. ", " .. sql.SQLStr(allowdefault) .. ");")
FPP.Notify(ply, "Group added successfully", true)
end
concommand.Add("FPP_AddGroup", runIfAccess("FPP_Settings", AddGroup))
hook.Add("InitPostEntity", "FPP_Load_CAMI", function()
if not CAMI then return end
for groupName, _ in pairs(CAMI.GetUsergroups()) do
if FPP.Groups[groupName] then continue end
FPP.Groups[groupName] = {}
FPP.Groups[groupName].allowdefault = true
FPP.Groups[groupName].tools = {}
end
end)
local function RemoveGroup(ply, cmd, args)
if not args[1] then FPP.Notify(ply, "Invalid argument(s)", false) return end-- Args: 1 = name
local name = string.lower(args[1])
if not FPP.Groups[name] then
FPP.Notify(ply, "Group does not exists", false)
return
end
if name == "default" then
FPP.Notify(ply, "Can not remove default group", false)
return
end
FPP.Groups[name] = nil
MySQLite.query("DELETE FROM FPP_GROUPS3 WHERE groupname = " .. sql.SQLStr(name) .. ";")
MySQLite.query("DELETE FROM FPP_GROUPTOOL WHERE groupname = " .. sql.SQLStr(name) .. ";")
for k, v in pairs(FPP.GroupMembers) do
if v == name then
FPP.GroupMembers[k] = nil -- Set group to standard if group is removed
end
end
FPP.Notify(ply, "Group removed successfully", true)
end
concommand.Add("FPP_RemoveGroup", runIfAccess("FPP_Settings", RemoveGroup))
local function GroupChangeAllowDefault(ply, cmd, args)
if not args[2] then FPP.Notify(ply, "Invalid argument(s)", false) return end-- Args: 1 = groupname, 2 = new value 1/0
local name = string.lower(args[1])
local newval = tonumber(args[2])
if not FPP.Groups[name] then
FPP.Notify(ply, "Group does not exists", false)
return
end
FPP.Groups[name].allowdefault = tobool(newval)
MySQLite.query("REPLACE INTO FPP_GROUPS3 VALUES(" .. sql.SQLStr(name) .. ", " .. sql.SQLStr(newval) .. ");")
FPP.Notify(ply, "Group status changed successfully", true)
end
concommand.Add("FPP_ChangeGroupStatus", runIfAccess("FPP_Settings", GroupChangeAllowDefault))
local function GroupAddTool(ply, cmd, args)
if not args[2] then FPP.Notify(ply, "Invalid argument(s)", false) return end-- Args: 1 = groupname, 2 = tool
local name = args[1]
local tool = string.lower(args[2])
if not FPP.Groups[name] then
FPP.Notify(ply, "Group does not exists", false)
return
end
FPP.Groups[name].tools = FPP.Groups[name].tools or {}
if table.HasValue(FPP.Groups[name].tools, tool) then
FPP.Notify(ply, "Tool is already in group!", false)
return
end
table.insert(FPP.Groups[name].tools, tool)
MySQLite.query("REPLACE INTO FPP_GROUPTOOL VALUES(" .. sql.SQLStr(name) .. ", " .. sql.SQLStr(tool) .. ");")
FPP.Notify(ply, "Tool added successfully", true)
end
concommand.Add("FPP_AddGroupTool", runIfAccess("FPP_Settings", GroupAddTool))
local function GroupRemoveTool(ply, cmd, args)
if not args[2] then FPP.Notify(ply, "Invalid argument(s)", false) return end-- Args: 1 = groupname, 2 = tool
local name = args[1]
local tool = string.lower(args[2])
if not FPP.Groups[name] then
FPP.Notify(ply, "Group does not exists", false)
return
end
if not table.HasValue(FPP.Groups[name].tools, tool) then
FPP.Notify(ply, "Tool does not exist in group!", false)
return
end
for k, v in pairs(FPP.Groups[name].tools) do
if v == tool then
table.remove(FPP.Groups[name].tools, k)
end
end
MySQLite.query("DELETE FROM FPP_GROUPTOOL WHERE groupname = " .. sql.SQLStr(name) .. " AND tool = " .. sql.SQLStr(tool) .. ";")
FPP.Notify(ply, "Tool removed successfully", true)
end
concommand.Add("FPP_RemoveGroupTool", runIfAccess("FPP_Settings", GroupRemoveTool))
-- Args: 1 = player, 2 = group
local function PlayerSetGroup(ply, cmd, args)
if not args[2] then FPP.Notify(ply, "Group not given", false) return end
local name = args[1]
local group = string.lower(args[2])
if IsValid(Player(tonumber(name) or 0)) then name = Player(tonumber(name)):SteamID()
elseif not string.find(name, "STEAM") and name ~= "UNKNOWN" and name ~= "BOT" then FPP.Notify(ply, "Player not found", false) return end
if not FPP.Groups[group] and (not CAMI or not CAMI.GetUsergroup(group)) then
FPP.Notify(ply, "Group does not exists", false)
return
end
if group ~= "default" then
MySQLite.query("REPLACE INTO FPP_GROUPMEMBERS1 VALUES(" .. sql.SQLStr(name) .. ", " .. sql.SQLStr(group) .. ");")
FPP.GroupMembers[name] = group
else
FPP.GroupMembers[name] = nil
MySQLite.query("DELETE FROM FPP_GROUPMEMBERS1 WHERE steamid = " .. sql.SQLStr(name) .. ";")
end
FPP.Notify(ply, "Player group successfully set", true)
end
concommand.Add("FPP_SetPlayerGroup", runIfAccess("FPP_Settings", PlayerSetGroup))
local function SendGroupData(ply, cmd, args)
net.Start("FPP_Groups")
net.WriteTable(FPP.Groups)
net.Send(ply)
end
concommand.Add("FPP_SendGroups", runIfAccess("FPP_Settings", SendGroupData))
local function SendGroupMemberData(ply, cmd, args)
net.Start("FPP_GroupMembers")
net.WriteTable(FPP.GroupMembers)
net.Send(ply)
end
concommand.Add("FPP_SendGroupMembers", runIfAccess("FPP_Settings", SendGroupMemberData))
local function SendBlocked(ply, cmd, args)
if not args[1] or not FPP.Blocked[args[1]] then return end
ply.FPPUmsg1 = ply.FPPUmsg1 or {}
ply.FPPUmsg1[args[1]] = ply.FPPUmsg1[args[1]] or 0
if ply.FPPUmsg1[args[1]] > CurTime() - 5 then return end
ply.FPPUmsg1[args[1]] = CurTime()
for k in pairs(FPP.Blocked[args[1]]) do
umsg.Start("FPP_blockedlist", ply)
umsg.String(args[1])
umsg.String(k)
umsg.End()
end
end
concommand.Add("FPP_sendblocked", SendBlocked)
local function SendBlockedModels(ply, cmd, args)
ply.FPPUmsg2 = ply.FPPUmsg2 or 0
if ply.FPPUmsg2 > CurTime() - 10 then return end
ply.FPPUmsg2 = CurTime()
local models = {}
for k in pairs(FPP.BlockedModels) do table.insert(models, k) end
local data = util.Compress(table.concat(models, "\0"))
if not data then return end
net.Start("FPP_BlockedModels")
net.WriteData(data, #data)
net.Send(ply)
end
concommand.Add("FPP_sendblockedmodels", SendBlockedModels)
local function SendRestrictedTools(ply, cmd, args)
ply.FPPUmsg3 = ply.FPPUmsg3 or 0
if ply.FPPUmsg3 > CurTime() - 5 then return end
ply.FPPUmsg3 = CurTime()
if not args[1] then return end
net.Start("FPP_RestrictedToolList")
net.WriteString(args[1]) -- tool name
net.WriteUInt(FPP.RestrictedTools[args[1]] and FPP.RestrictedTools[args[1]].admin or 0, 2) -- user, admin or superadmin
local teams = FPP.RestrictedTools[args[1]] and FPP.RestrictedTools[args[1]].team or {}
net.WriteUInt(#teams, 10)
for _, t in pairs(teams) do
net.WriteUInt(t, 10)
end
net.Send(ply)
end
concommand.Add("FPP_SendRestrictTool", SendRestrictedTools)
-- Fallback owner, will own entities after disconnect
local function setFallbackOwner(ply, fallback)
ply.FPPFallbackOwner = fallback:SteamID()
end
local function changeFallbackOwner(ply, _, args)
local fallback = tonumber(args[1]) and Player(tonumber(args[1]))
if tonumber(args[1]) == -1 then
ply.FPPFallbackOwner = nil
FPP.Notify(ply, "Fallback owner set", true)
return
end
if not IsValid(fallback) or not fallback:IsPlayer() or fallback == ply then FPP.Notify(ply, "Player invalid", false) return end
setFallbackOwner(ply, fallback)
FPP.Notify(ply, "Fallback owner set", true)
end
concommand.Add("FPP_FallbackOwner", changeFallbackOwner)
--Buddies!
local function changeBuddies(ply, buddy, settings)
if not IsValid(ply) then return end
ply.Buddies = ply.Buddies or {}
ply.Buddies[buddy] = settings
local CPPIBuddies = {}
for k, v in pairs(ply.Buddies) do if table.HasValue(v, true) then table.insert(CPPIBuddies, k) end end
-- Also run at player spawn because clients send their buddies through this command
hook.Run("CPPIFriendsChanged", ply, CPPIBuddies)
-- Update the prop protection
local affectedProps = {}
for _, v in ipairs(ents.GetAll()) do
local owner = v:CPPIGetOwner()
if owner ~= ply then continue end
table.insert(affectedProps, v)
end
FPP.recalculateCanTouch({buddy}, affectedProps)
FPP.RecalculateConstrainedEntities({buddy}, affectedProps)
end
local function SetBuddy(ply, cmd, args)
if not args[6] then FPP.Notify(ply, "FPP_SetBuddy: Not enough arguments given!", false) return end
local buddy = tonumber(args[1]) and Player(tonumber(args[1]))
if not IsValid(buddy) then FPP.Notify(ply, "Player invalid", false) return end
for k, v in pairs(args) do args[k] = tonumber(v) end
local settings = {Physgun = tobool(args[2]), Gravgun = tobool(args[3]), Toolgun = tobool(args[4]), PlayerUse = tobool(args[5]), EntityDamage = tobool(args[6])}
-- Antispam measure
timer.Create("FPP_BuddiesUpdate" .. ply:UserID() .. ", " .. buddy:UserID(), 1, 1, function() changeBuddies(ply, buddy, settings) end)
end
concommand.Add("FPP_SetBuddy", SetBuddy)
local function CleanupDisconnected(ply, cmd, args)
if not args[1] then FPP.Notify(ply, "Invalid argument", false) return end
if args[1] == "disconnected" then
for _, v in ipairs(ents.GetAll()) do
local owner = v:CPPIGetOwner()
if owner and not IsValid(owner) then
v:Remove()
end
end
FPP.NotifyAll(((ply.Nick and ply:Nick()) or "Console") .. " removed all disconnected players' props", true)
return
elseif not tonumber(args[1]) or not IsValid(Player(tonumber(args[1]))) then
FPP.Notify(ply, "Invalid player", false)
return
end
local target_ply = Player(args[1])
for _, v in ipairs(ents.GetAll()) do
local owner = v:CPPIGetOwner()
if owner == target_ply then
if v:IsWeapon() and IsValid(v:GetOwner()) then
continue
end
v:Remove()
end
end
FPP.NotifyAll(((ply.Nick and ply:Nick()) or "Console") .. " removed " .. target_ply:Nick() .. "'s entities", true)
end
concommand.Add("FPP_Cleanup", runIfAccess("FPP_Cleanup", CleanupDisconnected))
local function SetToolRestrict(ply, cmd, args)
if not args[3] then FPP.Notify(ply, "Invalid argument(s)", false) return end--FPP_restricttool <toolname> <type(admin/team)> <toggle(1/0)>
local toolname = args[1]
local RestrictWho = tonumber(args[2]) or args[2]-- "team" or "admin"
local teamtoggle = tonumber(args[4]) --this argument only exists when restricting a tool for a team
FPP.RestrictedTools[toolname] = FPP.RestrictedTools[toolname] or {}
if RestrictWho == "admin" then
FPP.RestrictedTools[toolname].admin = args[3] --weapons.Get("gmod_tool").Tool
--Save to database!
MySQLite.query("REPLACE INTO FPP_TOOLADMINONLY VALUES(" .. sql.SQLStr(toolname) .. ", " .. sql.SQLStr(args[3]) .. ");")
FPP.NotifyAll(((ply.Nick and ply:Nick()) or "Console") .. " changed the admin status of " .. toolname , true)
elseif RestrictWho == "team" then
FPP.RestrictedTools[toolname]["team"] = FPP.RestrictedTools[toolname]["team"] or {}
if teamtoggle == 0 then
for k, v in pairs(FPP.RestrictedTools[toolname]["team"]) do
if v == tonumber(args[3]) then
table.remove(FPP.RestrictedTools[toolname]["team"], k)
break
end
end
elseif not table.HasValue(FPP.RestrictedTools[toolname]["team"], tonumber(args[3])) and teamtoggle == 1 then
table.insert(FPP.RestrictedTools[toolname]["team"], tonumber(args[3]))
end --Remove from the table if it's in there AND it's 0 otherwise do nothing
if tobool(teamtoggle) then -- if the team restrict is enabled
FPP.NotifyAll(((ply.Nick and ply:Nick()) or "Console") .. " restricted " .. toolname .. " to certain teams", true)
MySQLite.query("REPLACE INTO FPP_TOOLTEAMRESTRICT VALUES(" .. sql.SQLStr(toolname) .. ", " .. tonumber(args[3]) .. ");")
else -- otherwise if the restriction for the team is being removed
FPP.NotifyAll(((ply.Nick and ply:Nick()) or "Console") .. " removed teamrestrictions from " .. toolname, true)
MySQLite.query("DELETE FROM FPP_TOOLTEAMRESTRICT WHERE toolname = " .. sql.SQLStr(toolname) .. " AND team = " .. tonumber(args[3]))
end
end
end
concommand.Add("FPP_restricttool", runIfAccess("FPP_Settings", SetToolRestrict))
local function RestrictToolPerson(ply, cmd, args)
if not args[3] then FPP.Notify(ply, "Invalid argument(s)", false) return end--FPP_restricttoolperson <toolname> <userid> <disallow, allow, remove(0,1,2)>
local toolname = args[1]
local target = Player(tonumber(args[2]))
local access = tonumber(args[3])
if not target:IsValid() then FPP.Notify(ply, "Invalid argument(s)", false) return end
if access < 0 or access > 2 then FPP.Notify(ply, "Invalid argument(s)", false) return end
FPP.RestrictedToolsPlayers[toolname] = FPP.RestrictedToolsPlayers[toolname] or {}
-- Disallow, even if other people can use it
if access == 0 or access == 1 then
FPP.RestrictedToolsPlayers[toolname][target:SteamID()] = access == 1
MySQLite.query("REPLACE INTO FPP_TOOLRESTRICTPERSON1 VALUES(" .. sql.SQLStr(toolname) .. ", " .. sql.SQLStr(target:SteamID()) .. ", " .. access .. ");")
elseif access == 2 then
-- reset tool status(make him like everyone else)
FPP.RestrictedToolsPlayers[toolname][target:SteamID()] = nil
MySQLite.query("DELETE FROM FPP_TOOLRESTRICTPERSON1 WHERE toolname = " .. sql.SQLStr(toolname) .. " AND steamid = " .. sql.SQLStr(target:SteamID()) .. ";")
end
FPP.Notify(ply, "Tool restrictions set successfully", true)
end
concommand.Add("FPP_restricttoolplayer", runIfAccess("FPP_Settings", RestrictToolPerson))
local function resetAllSetting(ply)
MySQLite.begin()
MySQLite.queueQuery("DELETE FROM FPP_PHYSGUN1")
MySQLite.queueQuery("DELETE FROM FPP_GRAVGUN1")
MySQLite.queueQuery("DELETE FROM FPP_TOOLGUN1")
MySQLite.queueQuery("DELETE FROM FPP_PLAYERUSE1")
MySQLite.queueQuery("DELETE FROM FPP_ENTITYDAMAGE1")
MySQLite.queueQuery("DELETE FROM FPP_GLOBALSETTINGS1")
MySQLite.queueQuery("DELETE FROM FPP_ANTISPAM1")
MySQLite.queueQuery("DELETE FROM FPP_BLOCKMODELSETTINGS1")
MySQLite.commit(function()
FPP.Settings = nil
FPP.Settings = table.Copy(FPP.InitialSettings)
SendSettings(player.GetAll())
if not IsValid(ply) then return end
FPP.Notify(ply, "Settings successfully reset.", true)
end)
end
concommand.Add("FPP_ResetAllSettings", runIfAccess("FPP_Settings", resetAllSetting))
local function resetBlockedModels(ply)
FPP.BlockedModels = {}
MySQLite.query("DELETE FROM FPP_BLOCKEDMODELS1", FPP.AddDefaultBlockedModels)
FPP.Notify(ply, "Settings successfully reset.", true)
end
concommand.Add("FPP_ResetBlockedModels", runIfAccess("FPP_Settings", resetBlockedModels))
local function refreshPrivatePlayerSettings(ply)
timer.Remove("FPP_RefreshPrivatePlayerSettings" .. ply:EntIndex())
timer.Create("FPP_RefreshPrivatePlayerSettings" .. ply:EntIndex(), 4, 1, function() FPP.recalculateCanTouch({ply}, ents.GetAll()) end)
end
concommand.Add("_FPP_RefreshPrivatePlayerSettings", refreshPrivatePlayerSettings)
--[[-------------------------------------------------------------------------
Load all FPP settings
---------------------------------------------------------------------------]]
function FPP.Init(callback)
MySQLite.begin()
MySQLite.queueQuery("CREATE TABLE IF NOT EXISTS FPP_BLOCKED1(id INTEGER NOT NULL, var VARCHAR(40) NOT NULL, setting VARCHAR(100) NOT NULL, PRIMARY KEY(id));")
MySQLite.queueQuery("CREATE TABLE IF NOT EXISTS FPP_PHYSGUN1(var VARCHAR(40) NOT NULL, setting INTEGER NOT NULL, PRIMARY KEY(var));")
MySQLite.queueQuery("CREATE TABLE IF NOT EXISTS FPP_GRAVGUN1(var VARCHAR(40) NOT NULL, setting INTEGER NOT NULL, PRIMARY KEY(var));")
MySQLite.queueQuery("CREATE TABLE IF NOT EXISTS FPP_TOOLGUN1(var VARCHAR(40) NOT NULL, setting INTEGER NOT NULL, PRIMARY KEY(var));")
MySQLite.queueQuery("CREATE TABLE IF NOT EXISTS FPP_PLAYERUSE1(var VARCHAR(40) NOT NULL, setting INTEGER NOT NULL, PRIMARY KEY(var));")
MySQLite.queueQuery("CREATE TABLE IF NOT EXISTS FPP_ENTITYDAMAGE1(var VARCHAR(40) NOT NULL, setting INTEGER NOT NULL, PRIMARY KEY(var));")
MySQLite.queueQuery("CREATE TABLE IF NOT EXISTS FPP_GLOBALSETTINGS1(var VARCHAR(40) NOT NULL, setting INTEGER NOT NULL, PRIMARY KEY(var));")
MySQLite.queueQuery("CREATE TABLE IF NOT EXISTS FPP_BLOCKMODELSETTINGS1(var VARCHAR(40) NOT NULL, setting INTEGER NOT NULL, PRIMARY KEY(var));")
MySQLite.queueQuery("CREATE TABLE IF NOT EXISTS FPP_ANTISPAM1(var VARCHAR(40) NOT NULL, setting INTEGER NOT NULL, PRIMARY KEY(var));")
MySQLite.queueQuery("CREATE TABLE IF NOT EXISTS FPP_TOOLADMINONLY(toolname VARCHAR(40) NOT NULL, adminonly INTEGER NOT NULL, PRIMARY KEY(toolname));")
MySQLite.queueQuery("CREATE TABLE IF NOT EXISTS FPP_TOOLTEAMRESTRICT(toolname VARCHAR(40) NOT NULL, team INTEGER NOT NULL, PRIMARY KEY(toolname, team));")
MySQLite.queueQuery("CREATE TABLE IF NOT EXISTS FPP_TOOLRESTRICTPERSON1(toolname VARCHAR(40) NOT NULL, steamid VARCHAR(40) NOT NULL, allow INTEGER NOT NULL, PRIMARY KEY(steamid, toolname));")
MySQLite.queueQuery("CREATE TABLE IF NOT EXISTS FPP_GROUPS3(groupname VARCHAR(40) NOT NULL, allowdefault INTEGER NOT NULL, PRIMARY KEY(groupname));")
MySQLite.queueQuery("CREATE TABLE IF NOT EXISTS FPP_GROUPTOOL(groupname VARCHAR(40) NOT NULL, tool VARCHAR(45) NOT NULL, PRIMARY KEY(groupname, tool));")
MySQLite.queueQuery("CREATE TABLE IF NOT EXISTS FPP_GROUPMEMBERS1(steamid VARCHAR(40) NOT NULL, groupname VARCHAR(40) NOT NULL, PRIMARY KEY(steamid));")
MySQLite.queueQuery("CREATE TABLE IF NOT EXISTS FPP_BLOCKEDMODELS1(model VARCHAR(140) NOT NULL PRIMARY KEY);")
if MySQLite.isMySQL() then
MySQLite.queueQuery("ALTER TABLE FPP_BLOCKED1 CHANGE id id INTEGER AUTO_INCREMENT;")
end
MySQLite.commit(function()
RetrieveBlocked()
RetrieveBlockedModels()
RetrieveRestrictedTools()
RetrieveGroups()
RetrieveSettings()
-- Callback when FPP is done creating the tables
if callback then callback() end
end)
end
local assbackup = ASS_RegisterPlugin -- Suddenly after witing this code, ASS spamprotection and propprotection broke. I have no clue why. I guess you should use FPP then
if assbackup then
function ASS_RegisterPlugin(plugin, ...)
if plugin.Name == "Sandbox Spam Protection" or plugin.Name == "Sandbox Prop Protection" then
return
end
return assbackup(plugin, ...)
end
end

View File

@@ -0,0 +1,112 @@
CPPI = CPPI or {}
CPPI.CPPI_DEFER = 102112 --\102\112 = fp
CPPI.CPPI_NOTIMPLEMENTED = 7080 --\70\80 = FP
function CPPI:GetName()
return "Falco's prop protection"
end
function CPPI:GetVersion()
return "universal.1"
end
function CPPI:GetInterfaceVersion()
return 1.3
end
function CPPI:GetNameFromUID(uid)
return CPPI.CPPI_NOTIMPLEMENTED
end
local PLAYER = FindMetaTable("Player")
function PLAYER:CPPIGetFriends()
if not self.Buddies then return CPPI.CPPI_DEFER end
local FriendsTable = {}
for k, v in pairs(self.Buddies) do
if not table.HasValue(v, true) then continue end -- not buddies in anything
table.insert(FriendsTable, k)
end
return FriendsTable
end
local ENTITY = FindMetaTable("Entity")
function ENTITY:CPPIGetOwner()
local Owner = FPP.entGetOwner(self)
if not IsValid(Owner) or not Owner:IsPlayer() then return SERVER and Owner or nil, self.FPPOwnerID end
return Owner, Owner:UniqueID()
end
if SERVER then
function ENTITY:CPPISetOwner(ply)
if ply == self.FPPOwner then return end
assert(ply == nil or IsEntity(ply), "The owner of an entity must be set to either nil, NULL or a valid entity.")
local valid = IsValid(ply) and ply:IsPlayer()
local steamId = valid and ply:SteamID() or nil
local canSetOwner = hook.Run("CPPIAssignOwnership", ply, self, valid and ply:UniqueID() or ply)
if canSetOwner == false then return false end
ply = canSetOwner ~= nil and canSetOwner ~= true and canSetOwner or ply
self.FPPOwner = ply
self.FPPOwnerID = steamId
self.FPPOwnerChanged = true
FPP.recalculateCanTouch(player.GetAll(), {self})
self.FPPOwnerChanged = nil
return true
end
function ENTITY:CPPISetOwnerUID(UID)
local ply = UID and player.GetByUniqueID(tostring(UID)) or nil
if UID and not ply then return false end
return self:CPPISetOwner(ply)
end
function ENTITY:CPPICanTool(ply, tool)
local cantool = FPP.Protect.CanTool(ply, nil, tool, self)
return cantool == nil and true or cantool
end
function ENTITY:CPPICanPhysgun(ply)
if not tobool(FPP.Settings.FPP_PHYSGUN1.toggle) then return true end
return FPP.plyCanTouchEnt(ply, self, "Physgun")
end
function ENTITY:CPPICanPickup(ply)
if not tobool(FPP.Settings.FPP_PHYSGUN1.toggle) then return true end
return FPP.plyCanTouchEnt(ply, self, "Gravgun")
end
function ENTITY:CPPICanPunt(ply)
if not tobool(FPP.Settings.FPP_GRAVGUN1.toggle) then return true end
return FPP.plyCanTouchEnt(ply, self, "Gravgun")
end
function ENTITY:CPPICanUse(ply)
if not tobool(FPP.Settings.FPP_PLAYERUSE1.toggle) then return true end
return FPP.plyCanTouchEnt(ply, self, "PlayerUse")
end
function ENTITY:CPPICanDamage(ply)
if not tobool(FPP.Settings.FPP_ENTITYDAMAGE1.toggle) then return true end
return FPP.plyCanTouchEnt(ply, self, "EntityDamage")
end
function ENTITY:CPPIDrive(ply)
local candrive = FPP.Protect.CanDrive(ply, self)
return candrive == nil and true or candrive
end
function ENTITY:CPPICanProperty(ply, property)
local canproperty = FPP.Protect.CanProperty(ply, property, self)
return canproperty == nil and true or canproperty
end
function ENTITY:CPPICanEditVariable(ply, key, val, editTbl)
return self:CPPICanProperty(ply, "editentity")
end
end

View File

@@ -0,0 +1,136 @@
-- These are the default settings. Don't mind changing these.
FPP = FPP or {}
-- Don't reset the settings when they're already there
if FPP.Settings then
return
end
FPP.Settings = {}
FPP.Settings.FPP_PHYSGUN1 = {
toggle = 1,
adminall = 1,
worldprops = 0,
adminworldprops = 1,
canblocked = 0,
admincanblocked = 0,
shownocross = 1,
checkconstrained = 1,
reloadprotection = 1,
iswhitelist = 0}
FPP.Settings.FPP_GRAVGUN1 = {
toggle = 1,
adminall = 1,
worldprops = 1,
adminworldprops = 1,
canblocked = 0,
admincanblocked = 0,
shownocross = 1,
checkconstrained = 1,
noshooting = 1,
iswhitelist = 0}
FPP.Settings.FPP_TOOLGUN1 = {
toggle = 1,
adminall = 1,
worldprops = 0,
adminworldprops = 0,
canblocked = 0,
admincanblocked = 0,
shownocross = 1,
checkconstrained = 1,
iswhitelist = 0,
duplicatorprotect = 1,
duplicatenoweapons = 1,
spawniswhitelist = 0,
spawnadmincanweapon = 0,
spawnadmincanblocked = 0}
FPP.Settings.FPP_PLAYERUSE1 = {
toggle = 0,
adminall = 1,
worldprops = 1,
adminworldprops = 1,
canblocked = 0,
admincanblocked = 1,
shownocross = 1,
checkconstrained = 0,
iswhitelist = 0}
FPP.Settings.FPP_ENTITYDAMAGE1 = {
toggle = 1,
protectpropdamage = 1,
adminall = 1,
worldprops = 1,
adminworldprops = 1,
canblocked = 0,
admincanblocked = 0,
shownocross = 1,
checkconstrained = 0,
iswhitelist = 0}
FPP.Settings.FPP_GLOBALSETTINGS1 = {
freezedisconnected = 0,
cleanupdisconnected = 1,
cleanupdisconnectedtime = 120,
cleanupadmin = 1,
antie2minge = 1}
FPP.Settings.FPP_ANTISPAM1 = {
toggle = 1,
antispawninprop = 0,
bigpropantispam = 1,
bigpropsize = 5.85,
bigpropwait = 1.5,
smallpropdowngradecount = 3,
smallpropghostlimit = 2,
smallpropdenylimit = 6,
duplicatorlimit = 3
}
FPP.Settings.FPP_BLOCKMODELSETTINGS1 = {
toggle = 1,
propsonly = 0,
iswhitelist = 0
}
FPP.InitialSettings = table.Copy(FPP.Settings)
function FPP.ForAllSettings(fn)
-- Loop in sorted pairs for deterministic order
for kind, sets in SortedPairs(FPP.Settings) do
for setting, val in SortedPairs(sets) do
if fn(kind, setting, val) then break end
end
end
end
--[[-------------------------------------------------------------------------
CAMI
Register the CAMI privilege
---------------------------------------------------------------------------]]
CAMI.RegisterPrivilege{
Name = "FPP_Settings",
MinAccess = "superadmin" -- By default only superadmins can change settings
}
CAMI.RegisterPrivilege{
Name = "FPP_Cleanup",
MinAccess = "admin"
}
CAMI.RegisterPrivilege{
Name = "FPP_TouchOtherPlayersProps",
MinAccess = "admin"
}
function FPP.calculatePlayerPrivilege(priv, callback)
local plys = player.GetAll()
local count = #plys
for _, ply in ipairs(plys) do
local function onRes(b)
count = count - 1
ply.FPP_Privileges = ply.FPPPrivileges or {}
ply.FPP_Privileges[priv] = b
if count == 0 then callback() end
end
CAMI.PlayerHasAccess(ply, priv, onRes)
end
end