Merge pull request #8770 from RoosterDragon/ai-order-throttle

Throttle AI order speed to prevent lag spikes
This commit is contained in:
Matthias Mailänder
2015-07-27 07:37:01 +02:00
6 changed files with 53 additions and 30 deletions

View File

@@ -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,

View File

@@ -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 });
}
}

View File

@@ -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);

View File

@@ -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))
{

View File

@@ -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 });
}
}

View File

@@ -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)