From e1e3971c0e55151cfe21e3ecf0624591becd6655 Mon Sep 17 00:00:00 2001 From: march <106459595+marchc1@users.noreply.github.com> Date: Sun, 1 Sep 2024 19:04:09 -0700 Subject: [PATCH 1/6] Optimize debris networking Reduce it to 58 bits per debris. Some info gets removed like color and material, but I don't think that matters too much? That's 7.25 bytes per debris prop. If 500 props exploded, that would only be 3.6kb of data. --- .../core/networking/networking_datatypes.lua | 72 +++++++++++++++++++ lua/acf/damage/debris_cl.lua | 68 ++++++++++++++++++ lua/acf/damage/debris_sv.lua | 12 +++- 3 files changed, 149 insertions(+), 3 deletions(-) create mode 100644 lua/acf/core/networking/networking_datatypes.lua create mode 100644 lua/acf/damage/debris_cl.lua diff --git a/lua/acf/core/networking/networking_datatypes.lua b/lua/acf/core/networking/networking_datatypes.lua new file mode 100644 index 000000000..84c257cbe --- /dev/null +++ b/lua/acf/core/networking/networking_datatypes.lua @@ -0,0 +1,72 @@ +local Network = ACF.Networking + +-- "Grainy" datatypes +-- These are basically less-accurate vectors and angles for non-critical things like debris positions. + +local round = math.Round +local pow = math.pow +local maxSUEdge = pow(2, 14) + +local function UnitsToGrain(Value, Base, Grain) + Value = ((Value / Base) + 1) / 2 + Value = Value * (pow(2, Grain) - 1) + return round(Value) +end + +local function GrainToUnits(Value, Base, Grain) + Value = Value / (pow(2, Grain) - 1) + Value = (Value - 0.5) * Base * 2 + return round(Value) +end + +function Network.ReadGrainyVector(Grain, Base) + assert(isnumber(Grain), "expected vector in argument #1") + + local X = GrainToUnits(net.ReadUInt(Grain), Base or maxSUEdge, Grain) + local Y = GrainToUnits(net.ReadUInt(Grain), Base or maxSUEdge, Grain) + local Z = GrainToUnits(net.ReadUInt(Grain), Base or maxSUEdge, Grain) + + return Vector(X, Y, Z) +end + +function Network.ReadGrainyAngle(Grain, Base) + assert(isnumber(Grain), "expected number in argument #1") + + local X = GrainToUnits(net.ReadUInt(Grain), Base or maxSUEdge, Grain) + local Y = GrainToUnits(net.ReadUInt(Grain), Base or maxSUEdge, Grain) + local Z = GrainToUnits(net.ReadUInt(Grain), Base or maxSUEdge, Grain) + + return Angle(X, Y, Z) +end + +function Network.WriteGrainyVector(Vector, Grain, Base) + assert(isvector(Vector), "expected vector in argument #1") + assert(isnumber(Grain), "expected number in argument #2") + + local X = UnitsToGrain(Vector[1], Base or maxSUEdge, Grain) + local Y = UnitsToGrain(Vector[2], Base or maxSUEdge, Grain) + local Z = UnitsToGrain(Vector[3], Base or maxSUEdge, Grain) + + net.WriteUInt(X, Grain) + net.WriteUInt(Y, Grain) + net.WriteUInt(Z, Grain) +end + +function Network.WriteGrainyAngle(Angle, Grain, Base) + assert(isangle(Angle), "expected angle in argument #1") + assert(isnumber(Grain), "expected number in argument #2") + + local X = UnitsToGrain(Angle[1], Base or maxSUEdge, Grain) + local Y = UnitsToGrain(Angle[2], Base or maxSUEdge, Grain) + local Z = UnitsToGrain(Angle[3], Base or maxSUEdge, Grain) + + net.WriteUInt(X, Grain) + net.WriteUInt(Y, Grain) + net.WriteUInt(Z, Grain) +end + +local stage1 = 10000 +local stage2 = SourceUnitsToGrain(stage1, maxSUEdge, 12) +local stage3 = GrainToSourceUnits(stage2, maxSUEdge, 12) + +print(stage1, stage2, stage3) \ No newline at end of file diff --git a/lua/acf/damage/debris_cl.lua b/lua/acf/damage/debris_cl.lua new file mode 100644 index 000000000..8b88e75d1 --- /dev/null +++ b/lua/acf/damage/debris_cl.lua @@ -0,0 +1,68 @@ +local ACF = ACF +local Network = ACF.Networking + +local acf_debris = GetConVar("acf_debris") + +function ACF.CreateDebris(Model, Position, Angles, Velocity) + if not acf_debris:GetBool() then return end + if not Model then return end + + local Debris = ents.CreateClientProp(Model) + Debris:SetPos(Position) + Debris:SetAngles(Angles) + Debris:SetMaterial("models/props_pipes/GutterMetal01a") + Debris:Spawn() + Debris:SetVelocity(Velocity) + + local ID = "ACF_DebrisWatcher" .. tostring(SysTime()) + timer.Simple(3, function() + local now = CurTime() + hook.Add("Think", ID, function() + local TimeToLive = CurTime() - now + if TimeToLive >= 1 then + Debris:Remove() + hook.Remove("Think", ID) + return + end + + -- This is set up this way primarily because of this code, but it won't work for me, so + -- if someone can figure out why please fix it :) + -- The color is correct, I checked printing it, it just doesnt like having a color + + --local c = Debris:GetColor() + --c.a = (1 - TimeToLive) * 255 + --Debris:SetColor(c) + end) + end) +end + +local EntData = {} + +-- Models MIGHT change, but aren't likely to, especially in combat, so rather than running some intensive model checker on entities +-- it would be better to just store it on creation. It's worth a model or two being wrong every now and then for reducing 40+ bytes +-- into just two bytes when writing the model part of debris +hook.Add("OnEntityCreated", "ACF_Debris_TrackEnts", function(ent) + timer.Simple(0.001, function() + if IsValid(ent) then + local id = ent:EntIndex() + if id ~= -1 then + EntData[ent:EntIndex()] = ent:GetModel() + end + end + end) +end) + +net.Receive("ACF_Debris", function() + local EntID = net.ReadUInt(14) + local Position = Network.ReadGrainyVector(12) + local Angles = Network.ReadGrainyAngle(8) + local Normal = Network.ReadGrainyVector(8, 1) + local Power = net.ReadUInt(16) + + ACF.CreateDebris( + EntData[EntID], + Position, + Angles, + Normal * Power + ) +end) \ No newline at end of file diff --git a/lua/acf/damage/debris_sv.lua b/lua/acf/damage/debris_sv.lua index 513054763..55ba99a10 100644 --- a/lua/acf/damage/debris_sv.lua +++ b/lua/acf/damage/debris_sv.lua @@ -1,16 +1,21 @@ util.AddNetworkString("ACF_Debris") local Contraption = ACF.Contraption +local Network = ACF.Networking local ValidDebris = ACF.ValidDebris local ChildDebris = ACF.ChildDebris local Queue = {} local function SendQueue() for Entity, Data in pairs(Queue) do - local JSON = util.TableToJSON(Data) - net.Start("ACF_Debris") - net.WriteString(JSON) + + net.WriteUInt(Data.ID, 14) + Network.WriteGrainyVector(Data.Position, 12) + Network.WriteGrainyAngle(Data.Angles, 8) + Network.WriteGrainyVector(Data.Normal, 8, 1) + net.WriteUInt(Data.Power, 16) + net.SendPVS(Data.Position) Queue[Entity] = nil @@ -29,6 +34,7 @@ local function DebrisNetter(Entity, Normal, Power, CanGib, Ignite) end Queue[Entity] = { + ID = Entity:EntIndex(), Position = Entity:GetPos(), Angles = Entity:GetAngles(), Material = Entity:GetMaterial(), From f7ad54c91288bb623970daff324ae469502338e4 Mon Sep 17 00:00:00 2001 From: march <106459595+marchc1@users.noreply.github.com> Date: Sun, 1 Sep 2024 19:17:59 -0700 Subject: [PATCH 2/6] Remove debug code --- lua/acf/core/networking/networking_datatypes.lua | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/lua/acf/core/networking/networking_datatypes.lua b/lua/acf/core/networking/networking_datatypes.lua index 84c257cbe..0825748d4 100644 --- a/lua/acf/core/networking/networking_datatypes.lua +++ b/lua/acf/core/networking/networking_datatypes.lua @@ -63,10 +63,4 @@ function Network.WriteGrainyAngle(Angle, Grain, Base) net.WriteUInt(X, Grain) net.WriteUInt(Y, Grain) net.WriteUInt(Z, Grain) -end - -local stage1 = 10000 -local stage2 = SourceUnitsToGrain(stage1, maxSUEdge, 12) -local stage3 = GrainToSourceUnits(stage2, maxSUEdge, 12) - -print(stage1, stage2, stage3) \ No newline at end of file +end \ No newline at end of file From aa73ea858868d9c6efc9bcffe8077821c9cb3cbe Mon Sep 17 00:00:00 2001 From: march <106459595+marchc1@users.noreply.github.com> Date: Sun, 1 Sep 2024 19:18:12 -0700 Subject: [PATCH 3/6] Move debris functions --- lua/acf/damage/damage_cl.lua | 176 +------------------------------- lua/acf/damage/debris_cl.lua | 189 ++++++++++++++++++++++++++++++----- 2 files changed, 165 insertions(+), 200 deletions(-) diff --git a/lua/acf/damage/damage_cl.lua b/lua/acf/damage/damage_cl.lua index 4f2222712..b935b8922 100644 --- a/lua/acf/damage/damage_cl.lua +++ b/lua/acf/damage/damage_cl.lua @@ -95,178 +95,4 @@ do end end end) -end - -do -- Debris Effects ------------------------ - local Effects = ACF.Utilities.Effects - local AllowDebris = GetConVar("acf_debris") - local CollideAll = GetConVar("acf_debris_collision") - local DebrisLife = GetConVar("acf_debris_lifetime") - local GibMult = GetConVar("acf_debris_gibmultiplier") - local GibLife = GetConVar("acf_debris_giblifetime") - local GibModel = "models/gibs/metal_gib%s.mdl" - - local function Particle(Entity, Effect) - return CreateParticleSystem(Entity, Effect, PATTACH_ABSORIGIN_FOLLOW) - end - - local function FadeAway(Entity) - if not IsValid(Entity) then return end - - local Smoke = Entity.SmokeParticle - local Ember = Entity.EmberParticle - - Entity:SetRenderMode(RENDERMODE_TRANSCOLOR) - Entity:SetRenderFX(kRenderFxFadeSlow) -- NOTE: Not synced to CurTime() - Entity:CallOnRemove("ACF_Debris_Fade", function() - Entity:StopAndDestroyParticles() - end) - - if IsValid(Smoke) then Smoke:StopEmission() end - if IsValid(Ember) then Ember:StopEmission() end - - timer.Simple(5, function() - if not IsValid(Entity) then return end - - Entity:Remove() - end) - end - - local function Ignite(Entity, Lifetime, IsGib) - if IsGib then - Particle(Entity, "burning_gib_01") - - timer.Simple(Lifetime * 0.2, function() - if not IsValid(Entity) then return end - - Entity:StopParticlesNamed("burning_gib_01") - end) - else - Entity.SmokeParticle = Particle(Entity, "smoke_small_01b") - - Particle(Entity, "env_fire_small_smoke") - - timer.Simple(Lifetime * 0.4, function() - if not IsValid(Entity) then return end - - Entity:StopParticlesNamed("env_fire_small_smoke") - end) - end - end - - local function CreateDebris(Data) - local Debris = ents.CreateClientProp(Data.Model) - - if not IsValid(Debris) then return end - - local Lifetime = DebrisLife:GetFloat() * math.Rand(0.5, 1) - - Debris:SetPos(Data.Position) - Debris:SetAngles(Data.Angles) - Debris:SetColor(Data.Color) - Debris:SetMaterial(Data.Material) - - if not CollideAll:GetBool() then - Debris:SetCollisionGroup(COLLISION_GROUP_WORLD) - end - - Debris:Spawn() - - Debris.EmberParticle = Particle(Debris, "embers_medium_01") - - if Data.Ignite and math.Rand(0, 0.5) < ACF.DebrisIgniteChance then - Ignite(Debris, Lifetime) - else - Debris.SmokeParticle = Particle(Debris, "smoke_exhaust_01a") - end - - local PhysObj = Debris:GetPhysicsObject() - - if IsValid(PhysObj) then - PhysObj:ApplyForceOffset(Data.Normal * Data.Power, Data.Position + VectorRand() * 20) - end - - timer.Simple(Lifetime, function() - FadeAway(Debris) - end) - - return Debris - end - - local function CreateGib(Data, Min, Max) - local Gib = ents.CreateClientProp(GibModel:format(math.random(1, 5))) - - if not IsValid(Gib) then return end - - local Lifetime = GibLife:GetFloat() * math.Rand(0.5, 1) - local Offset = ACF.RandomVector(Min, Max) - - Offset:Rotate(Data.Angles) - - Gib:SetPos(Data.Position + Offset) - Gib:SetAngles(AngleRand(-180, 180)) - Gib:SetModelScale(math.Rand(0.5, 2)) - Gib:SetMaterial(Data.Material) - Gib:SetColor(Data.Color) - Gib:Spawn() - - Gib.SmokeParticle = Particle(Gib, "smoke_gib_01") - - if math.random() < ACF.DebrisIgniteChance then - Ignite(Gib, Lifetime, true) - end - - local PhysObj = Gib:GetPhysicsObject() - - if IsValid(PhysObj) then - PhysObj:ApplyForceOffset(Data.Normal * Data.Power, Gib:GetPos() + VectorRand() * 20) - end - - timer.Simple(Lifetime, function() - FadeAway(Gib) - end) - - return true - end - - net.Receive("ACF_Debris", function() - local Data = util.JSONToTable(net.ReadString()) - - if not AllowDebris:GetBool() then return end - - local Debris = CreateDebris(Data) - - if IsValid(Debris) then - local Multiplier = GibMult:GetFloat() - local Radius = Debris:BoundingRadius() - local Min = Debris:OBBMins() - local Max = Debris:OBBMaxs() - - if Data.CanGib and Multiplier > 0 then - local GibCount = math.Clamp(Radius * 0.1, 1, math.max(10 * Multiplier, 1)) - - for _ = 1, GibCount do - if not CreateGib(Data, Min, Max) then - break - end - end - end - end - - local EffectTable = { - Origin = Data.Position, -- TODO: Change this to the hit vector, but we need to redefine HitVec as HitNorm - Scale = 20, - } - - Effects.CreateEffect("cball_explode", EffectTable) - end) - - game.AddParticles("particles/fire_01.pcf") - - PrecacheParticleSystem("burning_gib_01") - PrecacheParticleSystem("env_fire_small_smoke") - PrecacheParticleSystem("smoke_gib_01") - PrecacheParticleSystem("smoke_exhaust_01a") - PrecacheParticleSystem("smoke_small_01b") - PrecacheParticleSystem("embers_medium_01") -end ----------------------------------------- +end \ No newline at end of file diff --git a/lua/acf/damage/debris_cl.lua b/lua/acf/damage/debris_cl.lua index 8b88e75d1..fdb592577 100644 --- a/lua/acf/damage/debris_cl.lua +++ b/lua/acf/damage/debris_cl.lua @@ -1,39 +1,164 @@ local ACF = ACF local Network = ACF.Networking -local acf_debris = GetConVar("acf_debris") +local Effects = ACF.Utilities.Effects +local AllowDebris = GetConVar("acf_debris") +local CollideAll = GetConVar("acf_debris_collision") +local DebrisLife = GetConVar("acf_debris_lifetime") +local GibMult = GetConVar("acf_debris_gibmultiplier") +local GibLife = GetConVar("acf_debris_giblifetime") +local GibModel = "models/gibs/metal_gib%s.mdl" -function ACF.CreateDebris(Model, Position, Angles, Velocity) - if not acf_debris:GetBool() then return end - if not Model then return end +local function Particle(Entity, Effect) + return CreateParticleSystem(Entity, Effect, PATTACH_ABSORIGIN_FOLLOW) +end + +local function FadeAway(Entity) + if not IsValid(Entity) then return end + + local Smoke = Entity.SmokeParticle + local Ember = Entity.EmberParticle + + Entity:SetRenderMode(RENDERMODE_TRANSCOLOR) + Entity:SetRenderFX(kRenderFxFadeSlow) -- NOTE: Not synced to CurTime() + Entity:CallOnRemove("ACF_Debris_Fade", function() + Entity:StopAndDestroyParticles() + end) + + if IsValid(Smoke) then Smoke:StopEmission() end + if IsValid(Ember) then Ember:StopEmission() end + + timer.Simple(5, function() + if not IsValid(Entity) then return end + + Entity:Remove() + end) +end + +local function Ignite(Entity, Lifetime, IsGib) + if IsGib then + Particle(Entity, "burning_gib_01") + + timer.Simple(Lifetime * 0.2, function() + if not IsValid(Entity) then return end + + Entity:StopParticlesNamed("burning_gib_01") + end) + else + Entity.SmokeParticle = Particle(Entity, "smoke_small_01b") + Particle(Entity, "env_fire_small_smoke") + + timer.Simple(Lifetime * 0.4, function() + if not IsValid(Entity) then return end + + Entity:StopParticlesNamed("env_fire_small_smoke") + end) + end +end + +local function CreateDebris(Model, Position, Angles, Normal, Power, ShouldIgnite) local Debris = ents.CreateClientProp(Model) + + if not IsValid(Debris) then return end + + local Lifetime = DebrisLife:GetFloat() * math.Rand(0.5, 1) + Debris:SetPos(Position) Debris:SetAngles(Angles) Debris:SetMaterial("models/props_pipes/GutterMetal01a") + + if not CollideAll:GetBool() then + Debris:SetCollisionGroup(COLLISION_GROUP_WORLD) + end + Debris:Spawn() - Debris:SetVelocity(Velocity) - - local ID = "ACF_DebrisWatcher" .. tostring(SysTime()) - timer.Simple(3, function() - local now = CurTime() - hook.Add("Think", ID, function() - local TimeToLive = CurTime() - now - if TimeToLive >= 1 then - Debris:Remove() - hook.Remove("Think", ID) - return - end - -- This is set up this way primarily because of this code, but it won't work for me, so - -- if someone can figure out why please fix it :) - -- The color is correct, I checked printing it, it just doesnt like having a color + Debris.EmberParticle = Particle(Debris, "embers_medium_01") - --local c = Debris:GetColor() - --c.a = (1 - TimeToLive) * 255 - --Debris:SetColor(c) - end) + if ShouldIgnite and math.Rand(0, 0.5) < ACF.DebrisIgniteChance then + Ignite(Debris, Lifetime) + else + Debris.SmokeParticle = Particle(Debris, "smoke_exhaust_01a") + end + + local PhysObj = Debris:GetPhysicsObject() + + if IsValid(PhysObj) then + PhysObj:ApplyForceOffset(Normal * Power, Position + VectorRand() * 20) + end + + timer.Simple(Lifetime, function() + FadeAway(Debris) end) + + return Debris +end + +local function CreateGib(Position, Angles, Normal, Power, Min, Max) + local Gib = ents.CreateClientProp(GibModel:format(math.random(1, 5))) + + if not IsValid(Gib) then return end + + local Lifetime = GibLife:GetFloat() * math.Rand(0.5, 1) + local Offset = ACF.RandomVector(Min, Max) + + Offset:Rotate(Angles) + + Gib:SetPos(Position + Offset) + Gib:SetAngles(AngleRand(-180, 180)) + Gib:SetModelScale(math.Rand(0.5, 2)) + Gib:SetMaterial("models/props_pipes/GutterMetal01a") + Gib:Spawn() + + Gib.SmokeParticle = Particle(Gib, "smoke_gib_01") + + if math.random() < ACF.DebrisIgniteChance then + Ignite(Gib, Lifetime, true) + end + + local PhysObj = Gib:GetPhysicsObject() + + if IsValid(PhysObj) then + PhysObj:ApplyForceOffset(Normal * Power, Gib:GetPos() + VectorRand() * 20) + end + + timer.Simple(Lifetime, function() + FadeAway(Gib) + end) + + return true +end + +function ACF.CreateDebris(Model, Position, Angles, Normal, Power, CanGib, Ignite) + if not AllowDebris:GetBool() then return end + if not Model then return end + + local Debris = CreateDebris(Model, Position, Angles, Normal, Power, CanGib, Ignite) + + if IsValid(Debris) then + local Multiplier = GibMult:GetFloat() + local Radius = Debris:BoundingRadius() + local Min = Debris:OBBMins() + local Max = Debris:OBBMaxs() + + if CanGib and Multiplier > 0 then + local GibCount = math.Clamp(Radius * 0.1, 1, math.max(10 * Multiplier, 1)) + + for _ = 1, GibCount do + if not CreateGib(Position, Angles, Normal, Power, Min, Max) then + break + end + end + end + end + + local EffectTable = { + Origin = Position, -- TODO: Change this to the hit vector, but we need to redefine HitVec as HitNorm + Scale = 20, + } + + Effects.CreateEffect("cball_explode", EffectTable) end local EntData = {} @@ -58,11 +183,25 @@ net.Receive("ACF_Debris", function() local Angles = Network.ReadGrainyAngle(8) local Normal = Network.ReadGrainyVector(8, 1) local Power = net.ReadUInt(16) + local CanGib = net.ReadBool() + local Ignite = net.ReadBool() ACF.CreateDebris( EntData[EntID], Position, Angles, - Normal * Power + Normal, + Power, + CanGib, + Ignite ) -end) \ No newline at end of file +end) + +game.AddParticles("particles/fire_01.pcf") + +PrecacheParticleSystem("burning_gib_01") +PrecacheParticleSystem("env_fire_small_smoke") +PrecacheParticleSystem("smoke_gib_01") +PrecacheParticleSystem("smoke_exhaust_01a") +PrecacheParticleSystem("smoke_small_01b") +PrecacheParticleSystem("embers_medium_01") \ No newline at end of file From 8db686023f5ea924eecd44056870720451477b61 Mon Sep 17 00:00:00 2001 From: march <106459595+marchc1@users.noreply.github.com> Date: Sun, 1 Sep 2024 19:18:22 -0700 Subject: [PATCH 4/6] Write CanGib, Ignite, remove now unused data --- lua/acf/damage/debris_sv.lua | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lua/acf/damage/debris_sv.lua b/lua/acf/damage/debris_sv.lua index 55ba99a10..3c9e9a7a7 100644 --- a/lua/acf/damage/debris_sv.lua +++ b/lua/acf/damage/debris_sv.lua @@ -15,6 +15,8 @@ local function SendQueue() Network.WriteGrainyAngle(Data.Angles, 8) Network.WriteGrainyVector(Data.Normal, 8, 1) net.WriteUInt(Data.Power, 16) + net.WriteBool(Data.CanGib) + net.WriteBool(Data.Ignite) net.SendPVS(Data.Position) @@ -37,9 +39,6 @@ local function DebrisNetter(Entity, Normal, Power, CanGib, Ignite) ID = Entity:EntIndex(), Position = Entity:GetPos(), Angles = Entity:GetAngles(), - Material = Entity:GetMaterial(), - Model = Entity:GetModel(), - Color = Color(New.x, New.y, New.z, Current.a), Normal = Normal, Power = Power, CanGib = CanGib or nil, From 0fb55174f791c1f4240fce2b7225d0c7ca72eed8 Mon Sep 17 00:00:00 2001 From: march <106459595+marchc1@users.noreply.github.com> Date: Sun, 1 Sep 2024 19:19:43 -0700 Subject: [PATCH 5/6] Make the linter happy with me --- lua/acf/damage/debris_sv.lua | 3 --- 1 file changed, 3 deletions(-) diff --git a/lua/acf/damage/debris_sv.lua b/lua/acf/damage/debris_sv.lua index 3c9e9a7a7..440863ab2 100644 --- a/lua/acf/damage/debris_sv.lua +++ b/lua/acf/damage/debris_sv.lua @@ -28,9 +28,6 @@ local function DebrisNetter(Entity, Normal, Power, CanGib, Ignite) if not ACF.GetServerBool("CreateDebris") then return end if Queue[Entity] then return end - local Current = Entity:GetColor() - local New = Vector(Current.r, Current.g, Current.b) * math.Rand(0.3, 0.6) - if not next(Queue) then timer.Create("ACF_DebrisQueue", 0, 1, SendQueue) end From 39a4b9d2be9b2dc177a5d1a0db89d31f035b016a Mon Sep 17 00:00:00 2001 From: thecraftianman <64441307+thecraftianman@users.noreply.github.com> Date: Tue, 3 Sep 2024 23:41:34 -0400 Subject: [PATCH 6/6] Reduce message size further to 55 --- .../core/networking/networking_datatypes.lua | 66 ---------------- lua/acf/damage/debris_cl.lua | 77 +++++++++++-------- lua/acf/damage/debris_sv.lua | 10 +-- 3 files changed, 49 insertions(+), 104 deletions(-) delete mode 100644 lua/acf/core/networking/networking_datatypes.lua diff --git a/lua/acf/core/networking/networking_datatypes.lua b/lua/acf/core/networking/networking_datatypes.lua deleted file mode 100644 index 0825748d4..000000000 --- a/lua/acf/core/networking/networking_datatypes.lua +++ /dev/null @@ -1,66 +0,0 @@ -local Network = ACF.Networking - --- "Grainy" datatypes --- These are basically less-accurate vectors and angles for non-critical things like debris positions. - -local round = math.Round -local pow = math.pow -local maxSUEdge = pow(2, 14) - -local function UnitsToGrain(Value, Base, Grain) - Value = ((Value / Base) + 1) / 2 - Value = Value * (pow(2, Grain) - 1) - return round(Value) -end - -local function GrainToUnits(Value, Base, Grain) - Value = Value / (pow(2, Grain) - 1) - Value = (Value - 0.5) * Base * 2 - return round(Value) -end - -function Network.ReadGrainyVector(Grain, Base) - assert(isnumber(Grain), "expected vector in argument #1") - - local X = GrainToUnits(net.ReadUInt(Grain), Base or maxSUEdge, Grain) - local Y = GrainToUnits(net.ReadUInt(Grain), Base or maxSUEdge, Grain) - local Z = GrainToUnits(net.ReadUInt(Grain), Base or maxSUEdge, Grain) - - return Vector(X, Y, Z) -end - -function Network.ReadGrainyAngle(Grain, Base) - assert(isnumber(Grain), "expected number in argument #1") - - local X = GrainToUnits(net.ReadUInt(Grain), Base or maxSUEdge, Grain) - local Y = GrainToUnits(net.ReadUInt(Grain), Base or maxSUEdge, Grain) - local Z = GrainToUnits(net.ReadUInt(Grain), Base or maxSUEdge, Grain) - - return Angle(X, Y, Z) -end - -function Network.WriteGrainyVector(Vector, Grain, Base) - assert(isvector(Vector), "expected vector in argument #1") - assert(isnumber(Grain), "expected number in argument #2") - - local X = UnitsToGrain(Vector[1], Base or maxSUEdge, Grain) - local Y = UnitsToGrain(Vector[2], Base or maxSUEdge, Grain) - local Z = UnitsToGrain(Vector[3], Base or maxSUEdge, Grain) - - net.WriteUInt(X, Grain) - net.WriteUInt(Y, Grain) - net.WriteUInt(Z, Grain) -end - -function Network.WriteGrainyAngle(Angle, Grain, Base) - assert(isangle(Angle), "expected angle in argument #1") - assert(isnumber(Grain), "expected number in argument #2") - - local X = UnitsToGrain(Angle[1], Base or maxSUEdge, Grain) - local Y = UnitsToGrain(Angle[2], Base or maxSUEdge, Grain) - local Z = UnitsToGrain(Angle[3], Base or maxSUEdge, Grain) - - net.WriteUInt(X, Grain) - net.WriteUInt(Y, Grain) - net.WriteUInt(Z, Grain) -end \ No newline at end of file diff --git a/lua/acf/damage/debris_cl.lua b/lua/acf/damage/debris_cl.lua index fdb592577..62a2fb444 100644 --- a/lua/acf/damage/debris_cl.lua +++ b/lua/acf/damage/debris_cl.lua @@ -1,5 +1,4 @@ local ACF = ACF -local Network = ACF.Networking local Effects = ACF.Utilities.Effects local AllowDebris = GetConVar("acf_debris") @@ -57,7 +56,7 @@ local function Ignite(Entity, Lifetime, IsGib) end end -local function CreateDebris(Model, Position, Angles, Normal, Power, ShouldIgnite) +local function CreateDebris(Model, Position, Angles, Material, Color, Normal, Power, ShouldIgnite) local Debris = ents.CreateClientProp(Model) if not IsValid(Debris) then return end @@ -66,7 +65,8 @@ local function CreateDebris(Model, Position, Angles, Normal, Power, ShouldIgnite Debris:SetPos(Position) Debris:SetAngles(Angles) - Debris:SetMaterial("models/props_pipes/GutterMetal01a") + Debris:SetMaterial(Material) + Debris:SetColor(Color) if not CollideAll:GetBool() then Debris:SetCollisionGroup(COLLISION_GROUP_WORLD) @@ -95,7 +95,7 @@ local function CreateDebris(Model, Position, Angles, Normal, Power, ShouldIgnite return Debris end -local function CreateGib(Position, Angles, Normal, Power, Min, Max) +local function CreateGib(Position, Angles, Material, Color, Normal, Power, Min, Max) local Gib = ents.CreateClientProp(GibModel:format(math.random(1, 5))) if not IsValid(Gib) then return end @@ -108,7 +108,8 @@ local function CreateGib(Position, Angles, Normal, Power, Min, Max) Gib:SetPos(Position + Offset) Gib:SetAngles(AngleRand(-180, 180)) Gib:SetModelScale(math.Rand(0.5, 2)) - Gib:SetMaterial("models/props_pipes/GutterMetal01a") + Gib:SetMaterial(Material) + Gib:SetColor(Color) Gib:Spawn() Gib.SmokeParticle = Particle(Gib, "smoke_gib_01") @@ -130,11 +131,11 @@ local function CreateGib(Position, Angles, Normal, Power, Min, Max) return true end -function ACF.CreateDebris(Model, Position, Angles, Normal, Power, CanGib, Ignite) +function ACF.CreateDebris(Model, Position, Angles, Material, Color, Normal, Power, CanGib, Ignite) if not AllowDebris:GetBool() then return end if not Model then return end - local Debris = CreateDebris(Model, Position, Angles, Normal, Power, CanGib, Ignite) + local Debris = CreateDebris(Model, Position, Angles, Material, Color, Normal, Power, CanGib, Ignite) if IsValid(Debris) then local Multiplier = GibMult:GetFloat() @@ -146,7 +147,7 @@ function ACF.CreateDebris(Model, Position, Angles, Normal, Power, CanGib, Ignite local GibCount = math.Clamp(Radius * 0.1, 1, math.max(10 * Multiplier, 1)) for _ = 1, GibCount do - if not CreateGib(Position, Angles, Normal, Power, Min, Max) then + if not CreateGib(Position, Angles, Material, Color, Normal, Power, Min, Max) then break end end @@ -163,38 +164,50 @@ end local EntData = {} --- Models MIGHT change, but aren't likely to, especially in combat, so rather than running some intensive model checker on entities --- it would be better to just store it on creation. It's worth a model or two being wrong every now and then for reducing 40+ bytes --- into just two bytes when writing the model part of debris -hook.Add("OnEntityCreated", "ACF_Debris_TrackEnts", function(ent) - timer.Simple(0.001, function() - if IsValid(ent) then - local id = ent:EntIndex() - if id ~= -1 then - EntData[ent:EntIndex()] = ent:GetModel() - end - end +-- Store data of potentially ACF-killed entities for debris use, then remove from cache soon after +hook.Add("EntityRemoved", "ACF_Debris_TrackEnts", function(Ent) + local EntID = Ent:EntIndex() + if EntID == -1 then return end + + EntData[EntID] = { + Model = Ent:GetModel(), + Material = Ent:GetMaterial(), + Color = Ent:GetColor(), + Position = Ent:GetPos(), + Angles = Ent:GetAngles(), + } + + timer.Simple(2, function() + if not EntData[EntID] then return end + EntData[EntID] = nil end) end) net.Receive("ACF_Debris", function() - local EntID = net.ReadUInt(14) - local Position = Network.ReadGrainyVector(12) - local Angles = Network.ReadGrainyAngle(8) - local Normal = Network.ReadGrainyVector(8, 1) + local EntID = net.ReadUInt(13) + local Normal = Vector(net.ReadInt(8) / 100, net.ReadInt(8) / 100, net.ReadInt(8) / 100) local Power = net.ReadUInt(16) local CanGib = net.ReadBool() local Ignite = net.ReadBool() - ACF.CreateDebris( - EntData[EntID], - Position, - Angles, - Normal, - Power, - CanGib, - Ignite - ) + timer.Simple(0, function() + local EntInfo = EntData[EntID] + local NewColor = EntInfo.Color:ToVector() * math.Rand(0.3, 0.6) + + ACF.CreateDebris( + EntInfo.Model, + EntInfo.Position, + EntInfo.Angles, + EntInfo.Material, + NewColor:ToColor(), + Normal, + Power, + CanGib, + Ignite + ) + + EntData[EntID] = nil + end) end) game.AddParticles("particles/fire_01.pcf") diff --git a/lua/acf/damage/debris_sv.lua b/lua/acf/damage/debris_sv.lua index 440863ab2..a56c0394c 100644 --- a/lua/acf/damage/debris_sv.lua +++ b/lua/acf/damage/debris_sv.lua @@ -1,7 +1,6 @@ util.AddNetworkString("ACF_Debris") local Contraption = ACF.Contraption -local Network = ACF.Networking local ValidDebris = ACF.ValidDebris local ChildDebris = ACF.ChildDebris local Queue = {} @@ -10,10 +9,10 @@ local function SendQueue() for Entity, Data in pairs(Queue) do net.Start("ACF_Debris") - net.WriteUInt(Data.ID, 14) - Network.WriteGrainyVector(Data.Position, 12) - Network.WriteGrainyAngle(Data.Angles, 8) - Network.WriteGrainyVector(Data.Normal, 8, 1) + net.WriteUInt(Data.ID, 13) + net.WriteInt(Data.Normal.x * 100, 8) + net.WriteInt(Data.Normal.y * 100, 8) + net.WriteInt(Data.Normal.z * 100, 8) net.WriteUInt(Data.Power, 16) net.WriteBool(Data.CanGib) net.WriteBool(Data.Ignite) @@ -35,7 +34,6 @@ local function DebrisNetter(Entity, Normal, Power, CanGib, Ignite) Queue[Entity] = { ID = Entity:EntIndex(), Position = Entity:GetPos(), - Angles = Entity:GetAngles(), Normal = Normal, Power = Power, CanGib = CanGib or nil,