Merge pull request #8770 from RoosterDragon/ai-order-throttle
Throttle AI order speed to prevent lag spikes
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,17 +192,33 @@ 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; } }
|
||||
IBotInfo IBot.Info { get { return this.Info; } }
|
||||
|
||||
int rushTicks;
|
||||
int assignRolesTicks;
|
||||
int attackForceTicks;
|
||||
int minAttackForceDelayTicks;
|
||||
|
||||
readonly Queue<Order> orders = new Queue<Order>();
|
||||
|
||||
public HackyAI(HackyAIInfo info, ActorInitializer init)
|
||||
{
|
||||
Info = info;
|
||||
World = init.World;
|
||||
|
||||
// Avoid all AIs trying to rush in the same tick, randomize their initial rush a little.
|
||||
var smallFractionOfRushInterval = Info.RushInterval / 20;
|
||||
rushTicks = World.SharedRandom.Next(Info.RushInterval - smallFractionOfRushInterval, Info.RushInterval + smallFractionOfRushInterval);
|
||||
|
||||
// Avoid all AIs reevaluating assignments on the same tick, randomize their initial evaluation delay.
|
||||
assignRolesTicks = World.SharedRandom.Next(0, Info.AssignRolesInterval);
|
||||
attackForceTicks = World.SharedRandom.Next(0, Info.AttackForceInterval);
|
||||
minAttackForceDelayTicks = World.SharedRandom.Next(0, Info.MinimumAttackForceDelay);
|
||||
|
||||
foreach (var decision in info.PowerDecisions)
|
||||
powerDecisions.Add(decision.OrderName, decision);
|
||||
}
|
||||
@@ -231,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();
|
||||
@@ -435,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()
|
||||
@@ -520,11 +548,6 @@ namespace OpenRA.Mods.Common.AI
|
||||
return ret;
|
||||
}
|
||||
|
||||
int assignRolesTicks = 0;
|
||||
int rushTicks = 0;
|
||||
int attackForceTicks = 0;
|
||||
int minAttackForceDelayTicks = 0;
|
||||
|
||||
void AssignRolesToIdleUnits(Actor self)
|
||||
{
|
||||
CleanSquads();
|
||||
@@ -583,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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -597,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);
|
||||
|
||||
@@ -693,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...
|
||||
@@ -725,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.");
|
||||
@@ -750,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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -805,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 });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -914,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)
|
||||
@@ -924,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)
|
||||
@@ -943,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