220 lines
8.4 KiB
Lua
220 lines
8.4 KiB
Lua
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)
|