Improve performance of AIUtils.FindQueues
The AI would often invoke this method inside of loops, searching for a different category of queue each time. This would result in multiple searches against the trait dictionary to locate matching queues. Now we alter the method to create a lookup of all the queues keyed by category. This allows a single trait search to be performed. UnitBuilderBotModule and BaseBuilderBotModule are updated to fetch this lookup once when required, and pass the results along to avoid calling the method more times than necessary. This improves their performance.
This commit is contained in:
@@ -34,11 +34,12 @@ namespace OpenRA.Mods.Common
|
|||||||
.Any(availableCells => availableCells > 0);
|
.Any(availableCells => availableCells > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IEnumerable<ProductionQueue> FindQueues(Player player, string category)
|
public static ILookup<string, ProductionQueue> FindQueuesByCategory(Player player)
|
||||||
{
|
{
|
||||||
return player.World.ActorsWithTrait<ProductionQueue>()
|
return player.World.ActorsWithTrait<ProductionQueue>()
|
||||||
.Where(a => a.Actor.Owner == player && a.Trait.Info.Type == category && a.Trait.Enabled)
|
.Where(a => a.Actor.Owner == player && a.Trait.Enabled)
|
||||||
.Select(a => a.Trait);
|
.Select(a => a.Trait)
|
||||||
|
.ToLookup(pq => pq.Info.Type);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int CountActorsWithNameAndTrait<T>(string actorName, Player owner)
|
public static int CountActorsWithNameAndTrait<T>(string actorName, Player owner)
|
||||||
|
|||||||
@@ -220,6 +220,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
// PERF: We tick only one type of valid queue at a time
|
// PERF: We tick only one type of valid queue at a time
|
||||||
// if AI gets enough cash, it can fill all of its queues with enough ticks
|
// if AI gets enough cash, it can fill all of its queues with enough ticks
|
||||||
var findQueue = false;
|
var findQueue = false;
|
||||||
|
var queuesByCategory = AIUtils.FindQueuesByCategory(player);
|
||||||
for (int i = 0, builderIndex = currentBuilderIndex; i < builders.Length; i++)
|
for (int i = 0, builderIndex = currentBuilderIndex; i < builders.Length; i++)
|
||||||
{
|
{
|
||||||
if (++builderIndex >= builders.Length)
|
if (++builderIndex >= builders.Length)
|
||||||
@@ -227,7 +228,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
|
|
||||||
--builders[builderIndex].WaitTicks;
|
--builders[builderIndex].WaitTicks;
|
||||||
|
|
||||||
var queues = AIUtils.FindQueues(player, builders[builderIndex].Category).ToArray();
|
var queues = queuesByCategory[builders[builderIndex].Category].ToArray();
|
||||||
if (queues.Length != 0)
|
if (queues.Length != 0)
|
||||||
{
|
{
|
||||||
if (!findQueue)
|
if (!findQueue)
|
||||||
@@ -254,7 +255,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
builders[currentBuilderIndex].Tick(bot);
|
builders[currentBuilderIndex].Tick(bot, queuesByCategory);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IBotRespondToAttack.RespondToAttack(IBot bot, Actor self, AttackInfo e)
|
void IBotRespondToAttack.RespondToAttack(IBot bot, Actor self, AttackInfo e)
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
waterState = WaterCheck.DontCheck;
|
waterState = WaterCheck.DontCheck;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Tick(IBot bot)
|
public void Tick(IBot bot, ILookup<string, ProductionQueue> queuesByCategory)
|
||||||
{
|
{
|
||||||
// If failed to place something N consecutive times, wait M ticks until resuming building production
|
// If failed to place something N consecutive times, wait M ticks until resuming building production
|
||||||
if (failCount >= baseBuilder.Info.MaximumFailedPlacementAttempts && --failRetryTicks <= 0)
|
if (failCount >= baseBuilder.Info.MaximumFailedPlacementAttempts && --failRetryTicks <= 0)
|
||||||
@@ -106,7 +106,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
// PERF: Queue only one actor at a time per category
|
// PERF: Queue only one actor at a time per category
|
||||||
itemQueuedThisTick = false;
|
itemQueuedThisTick = false;
|
||||||
var active = false;
|
var active = false;
|
||||||
foreach (var queue in AIUtils.FindQueues(player, Category))
|
foreach (var queue in queuesByCategory[Category])
|
||||||
{
|
{
|
||||||
if (TickQueue(bot, queue))
|
if (TickQueue(bot, queue))
|
||||||
active = true;
|
active = true;
|
||||||
|
|||||||
@@ -91,25 +91,31 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
|
|
||||||
if (ticks % FeedbackTime == 0)
|
if (ticks % FeedbackTime == 0)
|
||||||
{
|
{
|
||||||
|
ILookup<string, ProductionQueue> queuesByCategory = null;
|
||||||
|
|
||||||
var buildRequest = queuedBuildRequests.FirstOrDefault();
|
var buildRequest = queuedBuildRequests.FirstOrDefault();
|
||||||
if (buildRequest != null)
|
if (buildRequest != null)
|
||||||
{
|
{
|
||||||
BuildUnit(bot, buildRequest);
|
queuesByCategory ??= AIUtils.FindQueuesByCategory(player);
|
||||||
|
BuildUnit(bot, buildRequest, queuesByCategory);
|
||||||
queuedBuildRequests.Remove(buildRequest);
|
queuedBuildRequests.Remove(buildRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Info.IdleBaseUnitsMaximum <= 0 || Info.IdleBaseUnitsMaximum > idleUnitCount)
|
if (Info.IdleBaseUnitsMaximum <= 0 || Info.IdleBaseUnitsMaximum > idleUnitCount)
|
||||||
{
|
{
|
||||||
|
queuesByCategory ??= AIUtils.FindQueuesByCategory(player);
|
||||||
for (var i = 0; i < Info.UnitQueues.Length; i++)
|
for (var i = 0; i < Info.UnitQueues.Length; i++)
|
||||||
{
|
{
|
||||||
if (++currentQueueIndex >= Info.UnitQueues.Length)
|
if (++currentQueueIndex >= Info.UnitQueues.Length)
|
||||||
currentQueueIndex = 0;
|
currentQueueIndex = 0;
|
||||||
|
|
||||||
if (AIUtils.FindQueues(player, Info.UnitQueues[currentQueueIndex]).Any())
|
var category = Info.UnitQueues[currentQueueIndex];
|
||||||
|
var queues = queuesByCategory[category].ToArray();
|
||||||
|
if (queues.Length != 0)
|
||||||
{
|
{
|
||||||
// PERF: We tick only one type of valid queue at a time
|
// PERF: We tick only one type of valid queue at a time
|
||||||
// if AI gets enough cash, it can fill all of its queues with enough ticks
|
// if AI gets enough cash, it can fill all of its queues with enough ticks
|
||||||
BuildRandomUnit(bot, Info.UnitQueues[currentQueueIndex]);
|
BuildRandomUnit(bot, queues);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -127,13 +133,13 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
return queuedBuildRequests.Count(r => r == requestedActor);
|
return queuedBuildRequests.Count(r => r == requestedActor);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BuildRandomUnit(IBot bot, string category)
|
void BuildRandomUnit(IBot bot, ProductionQueue[] queues)
|
||||||
{
|
{
|
||||||
if (Info.UnitsToBuild.Count == 0)
|
if (Info.UnitsToBuild.Count == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Pick a free queue
|
// Pick a free queue
|
||||||
var queue = AIUtils.FindQueues(player, category).FirstOrDefault(q => !q.AllQueued().Any());
|
var queue = queues.FirstOrDefault(q => !q.AllQueued().Any());
|
||||||
if (queue == null)
|
if (queue == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -146,7 +152,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
}
|
}
|
||||||
|
|
||||||
// In cases where we want to build a specific unit but don't know the queue name (because there's more than one possibility)
|
// In cases where we want to build a specific unit but don't know the queue name (because there's more than one possibility)
|
||||||
void BuildUnit(IBot bot, string name)
|
void BuildUnit(IBot bot, string name, ILookup<string, ProductionQueue> queuesByCategory)
|
||||||
{
|
{
|
||||||
var actorInfo = world.Map.Rules.Actors[name];
|
var actorInfo = world.Map.Rules.Actors[name];
|
||||||
if (actorInfo == null)
|
if (actorInfo == null)
|
||||||
@@ -159,7 +165,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
ProductionQueue queue = null;
|
ProductionQueue queue = null;
|
||||||
foreach (var pq in buildableInfo.Queue)
|
foreach (var pq in buildableInfo.Queue)
|
||||||
{
|
{
|
||||||
queue = AIUtils.FindQueues(player, pq).FirstOrDefault(q => !q.AllQueued().Any());
|
queue = queuesByCategory[pq].FirstOrDefault(q => !q.AllQueued().Any());
|
||||||
if (queue != null)
|
if (queue != null)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user