Skip to content

Commit

Permalink
Sanitize guard scan radius of units (FAForever#3891)
Browse files Browse the repository at this point in the history
This was a heavy hitter on performance. The guard scan radius is used for units that are on attack move or patrolling, but the logic was always performed regardless of the unit state.
  • Loading branch information
Garanas authored May 29, 2022
1 parent 3b6a5e0 commit 3c98292
Show file tree
Hide file tree
Showing 540 changed files with 18,610 additions and 111 deletions.
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"nuxt.isNuxtApp": false
}
59 changes: 3 additions & 56 deletions lua/system/Blueprints.lua
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ DiskFindFiles = DiskFindFiles
doscript("/lua/system/blueprints-ai.lua")
doscript("/lua/system/blueprints-lod.lua")
doscript("/lua/system/blueprints-projectiles.lua")
doscript("/lua/system/blueprints-units.lua")

--- Load in the pre game data that is defined in the lobby through the preference file.
local function LoadPreGameData()
Expand Down Expand Up @@ -765,60 +766,6 @@ function PreModBlueprints(all_bps)
}
end

-- # Hotfix for Guard Scan Radius value

-- Mod in AI.GuardScanRadius = Longest weapon range * longest tracking radius
-- Takes ACU/SCU enhancements into account
-- fixes move-attack range issues
-- Most Air units have the GSR defined already, this is just making certain they don't get included
local modGSR = not (bp.AI and bp.AI.GuardScanRadius) and (
(bp.CategoriesHash.MOBILE and (bp.CategoriesHash.LAND or bp.CategoriesHash.NAVAL) and (bp.CategoriesHash.DIRECTFIRE or bp.CategoriesHash.INDIRECTFIRE or bp.CategoriesHash.ANTINAVY or bp.CategoriesHash.ENGINEER)) or
(bp.CategoriesHash.STRUCTURE and (bp.CategoriesHash.DIRECTFIRE or bp.CategoriesHash.INDIRECTFIRE) and (bp.CategoriesHash.DEFENSE or bp.CategoriesHash.ARTILLERY)) or bp.CategoriesHash.DUMMYGSRWEAPON
)

if modGSR then
local br = nil

if bp.CategoriesHash.ENGINEER and not bp.CategoriesHash.SUBCOMMANDER and not bp.CategoriesHash.COMMAND then
br = 26
elseif bp.CategoriesHash.SCOUT then
br = 10
elseif bp.Weapon then
local range = 0
local tracking = 1.05

for i, w in bp.Weapon do
local ignore = w.CountedProjectile or w.RangeCategory == 'UWRC_AntiAir' or w.WeaponCategory == 'Defense'
if not ignore then
if w.MaxRadius then
range = math.max(w.MaxRadius, range)
end
if w.TrackingRadius then
tracking = math.max(w.TrackingRadius, tracking)
end
end
end

for name, array in bp.Enhancements or {} do
for key, value in array do
if key == 'NewMaxRadius' then
range = math.max(value, range)
end
end
end

br = (range * tracking)
end

if br then
if not bp.AI then bp.AI = {} end
bp.AI.GuardScanRadius = br
if not bp.AI.GuardReturnRadius then
bp.AI.GuardReturnRadius = 3
end
end
end

-- # Synchronize hashed categories with actual categories

bp.Categories = table.unhash(bp.CategoriesHash)
Expand Down Expand Up @@ -882,9 +829,9 @@ function PostModBlueprints(all_bps)
CalculateLODs(all_bps)
BlueprintLoaderUpdateProgress()

-- post process projectiles for easier access to information
-- post process units and projectiles for easier access to information and sanitizing some fields
PostProcessProjectiles(all_bps.Projectile)

PostProcessUnits(all_bps.Unit)
end
-----------------------------------------------------------------------------------------------
--- Loads all blueprints with optional parameters
Expand Down
4 changes: 2 additions & 2 deletions lua/system/blueprints-projectiles.lua
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
--- Post-processes the provided projectile blueprint
local function PostProcessProjectile(projectile)

-- create hash tables for quick lookup
-- # create hash tables for quick lookup

projectile.CategoriesCount = 0
projectile.CategoriesHash = { }
Expand All @@ -32,7 +32,7 @@ local function PostProcessProjectile(projectile)
end
end

-- create hash tables for quick lookup
-- # create hash tables for quick lookup

projectile.DoNotCollideListCount = 0
projectile.DoNotCollideListHash = { }
Expand Down
137 changes: 137 additions & 0 deletions lua/system/blueprints-units.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@

-- Post process a unit
local function PostProcessUnit(unit)

-- # create hash tables for quick lookup

unit.CategoriesCount = 0
unit.CategoriesHash = { }
if unit.Categories then
unit.CategoriesCount = table.getn(unit.Categories)
for k, category in unit.Categories do
unit.CategoriesHash[category] = true
end
end

-- # create hash tables for quick lookup

unit.DoNotCollideListCount = 0
unit.DoNotCollideListHash = { }
if unit.DoNotCollideList then
unit.DoNotCollideListCount = table.getn(unit.DoNotCollideList)
for k, category in unit.DoNotCollideList do
unit.DoNotCollideListHash[category] = true
end
end

-- # sanitize guard scan radius

-- The guard scan radius is used when:
-- - A unit attack moves, it determines how far the unit remains from its target
-- - A unit patrols, it determines when the unit decides to engage

-- All of the decisions below are made based on when a unit attack moves, as that is
-- the default meta to use in competitive play. This is by all means not perfect,
-- but it is the best we can do when we need to consider the performance of it all

local isEngineer = unit.CategoriesHash['ENGINEER']
local isStructure = unit.CategoriesHash['STRUCTURE']
local isDummy = unit.CategoriesHash['DUMMYUNIT']
local isLand = unit.CategoriesHash['LAND']

local isTech1 = unit.CategoriesHash['TECH1']
local isTech2 = unit.CategoriesHash['TECH2']
local isTech3 = unit.CategoriesHash['TECH3']
local isExperimental = unit.CategoriesHash['EXPERIMENTAL']

-- do not touch guard scan radius values of engineer-like units, as it is the reason we have
-- the factory-reclaim-bug that we're keen in keeping that at this point
if not isEngineer then

-- guarantee that the table exists
unit.AI = unit.AI or { }

-- if it is set then we use that - allows balance team to make adjustments as they see fit
if not unit.AI.GuardScanRadius then

-- structures don't need this value set
if isStructure or isDummy then
unit.AI.GuardScanRadius = 0

-- engineers need their factory reclaim bug
elseif isEngineer then
unit.AI.GuardScanRadius = 26 -- allows for factory reclaim bug

-- mobile units do need this value set
else
-- check if we have a primary weapon that is actually a weapon
local primaryWeapon = unit.Weapon[1]
if primaryWeapon and not
(
primaryWeapon.DummyWeapon or
primaryWeapon.WeaponCategory == 'Death' or
primaryWeapon.Label == 'DeathImpact' or
primaryWeapon.DisplayName == 'Air Crash'
)
then

local isAntiAir = primaryWeapon.RangeCategory == 'UWRC_AntiAir'
local maxRadius = primaryWeapon.MaxRadius or 0

-- land to air units shouldn't get triggered too fast
if isLand and isAntiAir then
unit.AI.GuardScanRadius = 0.80 * maxRadius

-- all other units will have the default value of 10% on top of their maximum attack radius
else
unit.AI.GuardScanRadius = 1.10 * maxRadius
end

-- units with no weaponry don't need this value set
else
unit.AI.GuardScanRadius = 0
end


-- cap it, some units have extreme values based on their attack radius
if isTech1 and unit.AI.GuardScanRadius > 40 then
unit.AI.GuardScanRadius = 40
elseif isTech2 and unit.AI.GuardScanRadius > 80 then
unit.AI.GuardScanRadius = 80
elseif isTech3 and unit.AI.GuardScanRadius > 120 then
unit.AI.GuardScanRadius = 120
elseif isExperimental and unit.AI.GuardScanRadius > 160 then
unit.AI.GuardScanRadius = 160
end

-- sanitize it
unit.AI.GuardScanRadius = math.floor(unit.AI.GuardScanRadius)

end
end
end
end

--- Post-processes all units
function PostProcessUnits(units)
for k, unit in units do

-- local oldGuardScanRadius = unit.AI.GuardScanRadius
-- local oldGuardScanRadiusWasSet = true
-- if not oldGuardScanRadius then
-- oldGuardScanRadiusWasSet = false
-- local primaryWeapon = unit.Weapon[1]
-- if primaryWeapon then
-- local maxRadius = primaryWeapon.MaxRadius or 0
-- local trackingRadius = primaryWeapon.TrackingRadius or 1.0
-- oldGuardScanRadius = trackingRadius * maxRadius
-- else
-- oldGuardScanRadius = 25 -- default value
-- end
-- end

PostProcessUnit(unit)

-- LOG("Processing: " .. unit.BlueprintId .. " - GuardScanRadius: " .. tostring(oldGuardScanRadius) .. " -> " .. tostring(unit.AI.GuardScanRadius) .. " (" .. tostring(unit.General.UnitName) .. ")")
end
end
31 changes: 31 additions & 0 deletions units/DAA0206/DAA0206_script.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#****************************************************************************
#**
#** File : /cdimage/units/DAA0206/DAA0206_script.lua
#** Author(s): Dru Staltman, Eric Williamson, Gordon Duclos, Greg Kohne
#**
#** Summary : Aeon Guided Missile Script
#**
#** Copyright © 2007 Gas Powered Games, Inc. All rights reserved.
#****************************************************************************
local AAirUnit = import('/lua/aeonunits.lua').AAirUnit
local DefaultProjectileWeapon = import('/lua/sim/defaultweapons.lua').DefaultProjectileWeapon
local EffectTemplate = import('/lua/EffectTemplates.lua')
local EffectUtils = import('/lua/effectutilities.lua')

DAA0206 = Class(AAirUnit) {
Weapons = {
Suicide = Class(DefaultProjectileWeapon) {}
},

OnRunOutOfFuel = function(self)
self:Kill()
end,

ProjectileFired = function(self)
self:GetWeapon(1).IdleState.Main = function(self) end
self:PlayUnitSound('Killed')
self:PlayUnitSound('Destroyed')
self:Destroy()
end,
}
TypeClass = DAA0206
1 change: 0 additions & 1 deletion units/DEA0202/DEA0202_unit.bp
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
UnitBlueprint {
AI = {
GuardReturnRadius = 100,
GuardScanRadius = 80,
},
Air = {
AutoLandTime = 1,
Expand Down
1 change: 0 additions & 1 deletion units/DRA0202/DRA0202_unit.bp
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
UnitBlueprint {
AI = {
GuardReturnRadius = 100,
GuardScanRadius = 80,
},
Air = {
AutoLandTime = 1,
Expand Down
15 changes: 15 additions & 0 deletions units/DRL0204/DRL0204_script.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#****************************************************************************
#**
#** File : /cdimage/units/DRL0204/DRL0204_script.lua
#** Author(s): Dru Staltman, Eric Williamson, Gordon Duclos
#**
#** Summary : Cybran Rocket Bot Script
#**
#** Copyright © 2007 Gas Powered Games, Inc. All rights reserved.
#****************************************************************************
DRL0204 = Class(import('/lua/cybranunits.lua').CWalkingLandUnit) {
Weapons = {
RocketBackpack = Class(import('/lua/cybranweapons.lua').CDFRocketIridiumWeapon02) {},
},
}
TypeClass = DRL0204
15 changes: 15 additions & 0 deletions units/OPC1001/OPC1001_Script.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#****************************************************************************
#**
#** File : /cdimage/units/OPC1001/OPC1001_script.lua
#** Author(s): Greg R.
#**
#** Summary : Symbiont Building for OpC1
#**
#** Copyright © 2006 Gas Powered Games, Inc. All rights reserved.
#****************************************************************************
local Unit = import('/lua/sim/Unit.lua').Unit

OPC1001 = Class(Unit) {
}

TypeClass = OPC1001
Loading

0 comments on commit 3c98292

Please sign in to comment.