Throttle the issuing of orders by the AI.
To prevent the AI generating lag spikes by issuing too many orders in a single tick, it is now limited in how many orders can be issued per tick. Extra orders are queued. This allows the performance hit of issuing multiple orders to be spread out over several ticks.
This commit is contained in:
@@ -70,7 +70,7 @@ namespace OpenRA.Mods.Common.AI
|
||||
return false;
|
||||
|
||||
HackyAI.BotDebug("AI: {0} is starting production of {1}".F(player, item.Name));
|
||||
world.IssueOrder(Order.StartProduction(queue.Actor, item.Name, 1));
|
||||
ai.QueueOrder(Order.StartProduction(queue.Actor, item.Name, 1));
|
||||
}
|
||||
else if (currentBuilding.Done)
|
||||
{
|
||||
@@ -87,11 +87,11 @@ namespace OpenRA.Mods.Common.AI
|
||||
if (location == null)
|
||||
{
|
||||
HackyAI.BotDebug("AI: {0} has nowhere to place {1}".F(player, currentBuilding.Item));
|
||||
world.IssueOrder(Order.CancelProduction(queue.Actor, currentBuilding.Item, 1));
|
||||
ai.QueueOrder(Order.CancelProduction(queue.Actor, currentBuilding.Item, 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
world.IssueOrder(new Order("PlaceBuilding", player.PlayerActor, false)
|
||||
ai.QueueOrder(new Order("PlaceBuilding", player.PlayerActor, false)
|
||||
{
|
||||
TargetLocation = location.Value,
|
||||
TargetString = currentBuilding.Item,
|
||||
|
||||
@@ -58,6 +58,9 @@ namespace OpenRA.Mods.Common.AI
|
||||
[Desc("How long to wait (in ticks) between structure production checks ticks when actively building things.")]
|
||||
public readonly int StructureProductionActiveDelay = 10;
|
||||
|
||||
[Desc("Minimum portion of pending orders to issue each tick (e.g. 5 issues at least 1/5th of all pending orders). Excess orders remain queued for subsequent ticks.")]
|
||||
public readonly int MinOrderQuotientPerTick = 5;
|
||||
|
||||
[Desc("Minimum range at which to build defensive structures near a combat hotspot.")]
|
||||
public readonly int MinimumDefenseRadius = 5;
|
||||
|
||||
@@ -189,7 +192,7 @@ namespace OpenRA.Mods.Common.AI
|
||||
// Units that the ai already knows about. Any unit not on this list needs to be given a role.
|
||||
List<Actor> activeUnits = new List<Actor>();
|
||||
|
||||
public const int FeedbackTime = 30; // ticks; = a bit over 1s. must be >= netlag.
|
||||
public const int FeedbackTime = 30; // ticks; = a bit over 1s. must be >= netlag.
|
||||
|
||||
public readonly World World;
|
||||
public Map Map { get { return World.Map; } }
|
||||
@@ -200,6 +203,8 @@ namespace OpenRA.Mods.Common.AI
|
||||
int attackForceTicks;
|
||||
int minAttackForceDelayTicks;
|
||||
|
||||
readonly Queue<Order> orders = new Queue<Order>();
|
||||
|
||||
public HackyAI(HackyAIInfo info, ActorInitializer init)
|
||||
{
|
||||
Info = info;
|
||||
@@ -245,6 +250,11 @@ namespace OpenRA.Mods.Common.AI
|
||||
resourceTypeIndices.Set(World.TileSet.GetTerrainIndex(t.TerrainType), true);
|
||||
}
|
||||
|
||||
public void QueueOrder(Order order)
|
||||
{
|
||||
orders.Enqueue(order);
|
||||
}
|
||||
|
||||
ActorInfo ChooseRandomUnitToBuild(ProductionQueue queue)
|
||||
{
|
||||
var buildableThings = queue.BuildableItems();
|
||||
@@ -449,6 +459,10 @@ namespace OpenRA.Mods.Common.AI
|
||||
|
||||
foreach (var b in builders)
|
||||
b.Tick();
|
||||
|
||||
var ordersToIssueThisTick = Math.Min((orders.Count + Info.MinOrderQuotientPerTick - 1) / Info.MinOrderQuotientPerTick, orders.Count);
|
||||
for (var i = 0; i < ordersToIssueThisTick; i++)
|
||||
World.IssueOrder(orders.Dequeue());
|
||||
}
|
||||
|
||||
internal Actor ChooseEnemyTarget()
|
||||
@@ -592,7 +606,7 @@ namespace OpenRA.Mods.Common.AI
|
||||
continue;
|
||||
|
||||
// Tell the idle harvester to quit slacking:
|
||||
World.IssueOrder(new Order("Harvest", a, false));
|
||||
QueueOrder(new Order("Harvest", a, false));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -606,7 +620,7 @@ namespace OpenRA.Mods.Common.AI
|
||||
foreach (var a in newUnits)
|
||||
{
|
||||
if (a.HasTrait<Harvester>())
|
||||
World.IssueOrder(new Order("Harvest", a, false));
|
||||
QueueOrder(new Order("Harvest", a, false));
|
||||
else
|
||||
unitsHangingAroundTheBase.Add(a);
|
||||
|
||||
@@ -702,7 +716,7 @@ namespace OpenRA.Mods.Common.AI
|
||||
!IsRallyPointValid(rp.Trait.Location, rp.Actor.Info.Traits.GetOrDefault<BuildingInfo>())).ToArray();
|
||||
|
||||
foreach (var a in buildings)
|
||||
World.IssueOrder(new Order("SetRallyPoint", a.Actor, false) { TargetLocation = ChooseRallyLocationNear(a.Actor), SuppressVisualFeedback = true });
|
||||
QueueOrder(new Order("SetRallyPoint", a.Actor, false) { TargetLocation = ChooseRallyLocationNear(a.Actor), SuppressVisualFeedback = true });
|
||||
}
|
||||
|
||||
// Won't work for shipyards...
|
||||
@@ -734,7 +748,7 @@ namespace OpenRA.Mods.Common.AI
|
||||
// Don't transform the mcv if it is a fact
|
||||
// HACK: This needs to query against MCVs directly
|
||||
if (mcv.HasTrait<Mobile>())
|
||||
World.IssueOrder(new Order("DeployTransform", mcv, false));
|
||||
QueueOrder(new Order("DeployTransform", mcv, false));
|
||||
}
|
||||
else
|
||||
BotDebug("AI: Can't find BaseBuildUnit.");
|
||||
@@ -759,8 +773,8 @@ namespace OpenRA.Mods.Common.AI
|
||||
if (desiredLocation == null)
|
||||
continue;
|
||||
|
||||
World.IssueOrder(new Order("Move", mcv, true) { TargetLocation = desiredLocation.Value });
|
||||
World.IssueOrder(new Order("DeployTransform", mcv, true));
|
||||
QueueOrder(new Order("Move", mcv, true) { TargetLocation = desiredLocation.Value });
|
||||
QueueOrder(new Order("DeployTransform", mcv, true));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -814,7 +828,7 @@ namespace OpenRA.Mods.Common.AI
|
||||
// Valid target found, delay by a few ticks to avoid rescanning before power fires via order
|
||||
BotDebug("AI: {2} found new target location {0} for support power {1}.", attackLocation, sp.Info.OrderName, Player.PlayerName);
|
||||
waitingPowers[sp] += 10;
|
||||
World.IssueOrder(new Order(sp.Info.OrderName, supportPowerMngr.Self, false) { TargetLocation = attackLocation.Value, SuppressVisualFeedback = true });
|
||||
QueueOrder(new Order(sp.Info.OrderName, supportPowerMngr.Self, false) { TargetLocation = attackLocation.Value, SuppressVisualFeedback = true });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -923,7 +937,7 @@ namespace OpenRA.Mods.Common.AI
|
||||
ChooseUnitToBuild(queue);
|
||||
|
||||
if (unit != null && Info.UnitsToBuild.Any(u => u.Key == unit.Name))
|
||||
World.IssueOrder(Order.StartProduction(queue.Actor, unit.Name, 1));
|
||||
QueueOrder(Order.StartProduction(queue.Actor, unit.Name, 1));
|
||||
}
|
||||
|
||||
void BuildUnit(string category, string name)
|
||||
@@ -933,7 +947,7 @@ namespace OpenRA.Mods.Common.AI
|
||||
return;
|
||||
|
||||
if (Map.Rules.Actors[name] != null)
|
||||
World.IssueOrder(Order.StartProduction(queue.Actor, name, 1));
|
||||
QueueOrder(Order.StartProduction(queue.Actor, name, 1));
|
||||
}
|
||||
|
||||
public void Damaged(Actor self, AttackInfo e)
|
||||
@@ -952,7 +966,7 @@ namespace OpenRA.Mods.Common.AI
|
||||
{
|
||||
BotDebug("Bot noticed damage {0} {1}->{2}, repairing.",
|
||||
self, e.PreviousDamageState, e.DamageState);
|
||||
World.IssueOrder(new Order("RepairBuilding", self.Owner.PlayerActor, false) { TargetActor = self });
|
||||
QueueOrder(new Order("RepairBuilding", self.Owner.PlayerActor, false) { TargetActor = self });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -215,7 +215,7 @@ namespace OpenRA.Mods.Common.AI
|
||||
{
|
||||
if (IsRearm(a))
|
||||
continue;
|
||||
owner.World.IssueOrder(new Order("ReturnToBase", a, false));
|
||||
owner.Bot.QueueOrder(new Order("ReturnToBase", a, false));
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -224,7 +224,7 @@ namespace OpenRA.Mods.Common.AI
|
||||
}
|
||||
|
||||
if (owner.TargetActor.HasTrait<ITargetable>() && CanAttackTarget(a, owner.TargetActor))
|
||||
owner.World.IssueOrder(new Order("Attack", a, false) { TargetActor = owner.TargetActor });
|
||||
owner.Bot.QueueOrder(new Order("Attack", a, false) { TargetActor = owner.TargetActor });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,11 +247,11 @@ namespace OpenRA.Mods.Common.AI
|
||||
if (IsRearm(a))
|
||||
continue;
|
||||
|
||||
owner.World.IssueOrder(new Order("ReturnToBase", a, false));
|
||||
owner.Bot.QueueOrder(new Order("ReturnToBase", a, false));
|
||||
continue;
|
||||
}
|
||||
|
||||
owner.World.IssueOrder(new Order("Move", a, false) { TargetLocation = RandomBuildingLocation(owner) });
|
||||
owner.Bot.QueueOrder(new Order("Move", a, false) { TargetLocation = RandomBuildingLocation(owner) });
|
||||
}
|
||||
|
||||
owner.FuzzyStateMachine.ChangeState(owner, new AirIdleState(), true);
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace OpenRA.Mods.Common.AI
|
||||
if (owner.AttackOrFleeFuzzy.CanAttack(owner.Units, enemyUnits))
|
||||
{
|
||||
foreach (var u in owner.Units)
|
||||
owner.World.IssueOrder(new Order("AttackMove", u, false) { TargetLocation = owner.TargetActor.Location });
|
||||
owner.Bot.QueueOrder(new Order("AttackMove", u, false) { TargetLocation = owner.TargetActor.Location });
|
||||
|
||||
// We have gathered sufficient units. Attack the nearest enemy unit.
|
||||
owner.FuzzyStateMachine.ChangeState(owner, new GroundUnitsAttackMoveState(), true);
|
||||
@@ -87,9 +87,9 @@ namespace OpenRA.Mods.Common.AI
|
||||
.Where(a => a.Owner == owner.Units.FirstOrDefault().Owner && owner.Units.Contains(a)).ToHashSet();
|
||||
if (ownUnits.Count < owner.Units.Count)
|
||||
{
|
||||
owner.World.IssueOrder(new Order("Stop", leader, false));
|
||||
owner.Bot.QueueOrder(new Order("Stop", leader, false));
|
||||
foreach (var unit in owner.Units.Where(a => !ownUnits.Contains(a)))
|
||||
owner.World.IssueOrder(new Order("AttackMove", unit, false) { TargetLocation = leader.Location });
|
||||
owner.Bot.QueueOrder(new Order("AttackMove", unit, false) { TargetLocation = leader.Location });
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -105,7 +105,7 @@ namespace OpenRA.Mods.Common.AI
|
||||
}
|
||||
else
|
||||
foreach (var a in owner.Units)
|
||||
owner.World.IssueOrder(new Order("AttackMove", a, false) { TargetLocation = owner.TargetActor.Location });
|
||||
owner.Bot.QueueOrder(new Order("AttackMove", a, false) { TargetLocation = owner.TargetActor.Location });
|
||||
}
|
||||
|
||||
if (ShouldFlee(owner))
|
||||
@@ -141,7 +141,7 @@ namespace OpenRA.Mods.Common.AI
|
||||
|
||||
foreach (var a in owner.Units)
|
||||
if (!BusyAttack(a))
|
||||
owner.World.IssueOrder(new Order("Attack", a, false) { TargetActor = owner.Bot.FindClosestEnemy(a.CenterPosition) });
|
||||
owner.Bot.QueueOrder(new Order("Attack", a, false) { TargetActor = owner.Bot.FindClosestEnemy(a.CenterPosition) });
|
||||
|
||||
if (ShouldFlee(owner))
|
||||
{
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace OpenRA.Mods.Common.AI
|
||||
else
|
||||
{
|
||||
foreach (var a in owner.Units)
|
||||
owner.World.IssueOrder(new Order("AttackMove", a, false) { TargetLocation = owner.TargetActor.Location });
|
||||
owner.Bot.QueueOrder(new Order("AttackMove", a, false) { TargetLocation = owner.TargetActor.Location });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace OpenRA.Mods.Common.AI
|
||||
{
|
||||
var loc = RandomBuildingLocation(squad);
|
||||
foreach (var a in squad.Units)
|
||||
squad.World.IssueOrder(new Order("Move", a, false) { TargetLocation = loc });
|
||||
squad.Bot.QueueOrder(new Order("Move", a, false) { TargetLocation = loc });
|
||||
}
|
||||
|
||||
protected static CPos RandomBuildingLocation(Squad squad)
|
||||
|
||||
Reference in New Issue
Block a user