Initial commit
This commit is contained in:
93
gamemodes/darkrp/gamemode/modules/fpp/pp/client/buddies.lua
Normal file
93
gamemodes/darkrp/gamemode/modules/fpp/pp/client/buddies.lua
Normal 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)
|
||||
221
gamemodes/darkrp/gamemode/modules/fpp/pp/client/hud.lua
Normal file
221
gamemodes/darkrp/gamemode/modules/fpp/pp/client/hud.lua
Normal 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)
|
||||
1268
gamemodes/darkrp/gamemode/modules/fpp/pp/client/menu.lua
Normal file
1268
gamemodes/darkrp/gamemode/modules/fpp/pp/client/menu.lua
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
219
gamemodes/darkrp/gamemode/modules/fpp/pp/server/antispam.lua
Normal file
219
gamemodes/darkrp/gamemode/modules/fpp/pp/server/antispam.lua
Normal 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)
|
||||
721
gamemodes/darkrp/gamemode/modules/fpp/pp/server/core.lua
Normal file
721
gamemodes/darkrp/gamemode/modules/fpp/pp/server/core.lua
Normal 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
|
||||
@@ -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
|
||||
587
gamemodes/darkrp/gamemode/modules/fpp/pp/server/ownability.lua
Normal file
587
gamemodes/darkrp/gamemode/modules/fpp/pp/server/ownability.lua
Normal 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
|
||||
988
gamemodes/darkrp/gamemode/modules/fpp/pp/server/settings.lua
Normal file
988
gamemodes/darkrp/gamemode/modules/fpp/pp/server/settings.lua
Normal 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
|
||||
112
gamemodes/darkrp/gamemode/modules/fpp/pp/sh_cppi.lua
Normal file
112
gamemodes/darkrp/gamemode/modules/fpp/pp/sh_cppi.lua
Normal 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
|
||||
136
gamemodes/darkrp/gamemode/modules/fpp/pp/sh_settings.lua
Normal file
136
gamemodes/darkrp/gamemode/modules/fpp/pp/sh_settings.lua
Normal 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
|
||||
Reference in New Issue
Block a user