From 67cba65800f7b8a9f0a28876cf36ee7c95c1bfe8 Mon Sep 17 00:00:00 2001 From: reaperrr Date: Sun, 18 Nov 2018 14:58:03 +0100 Subject: [PATCH] Fix bot module plumbing Fixes the issues pointed out after the original harvester module was merged. Also merges the update rules as discussed on IRC. --- OpenRA.Game/Traits/TraitsInterfaces.cs | 1 + OpenRA.Mods.Common/AI/BotOrderManager.cs | 54 ------------- OpenRA.Mods.Common/AI/DummyAI.cs | 2 + OpenRA.Mods.Common/AI/HackyAI.cs | 32 ++++++-- OpenRA.Mods.Common/OpenRA.Mods.Common.csproj | 4 +- .../Traits/BotModules/HarvesterBotModule.cs | 13 +--- OpenRA.Mods.Common/TraitsInterfaces.cs | 4 + .../Rules/20180923/AddBotOrderManager.cs | 76 ------------------- ...rBotModule.cs => ExtractHackyAIModules.cs} | 8 +- OpenRA.Mods.Common/UpdateRules/UpdatePath.cs | 3 +- mods/cnc/rules/ai.yaml | 1 - mods/d2k/rules/ai.yaml | 1 - mods/ra/rules/ai.yaml | 1 - mods/ts/rules/ai.yaml | 1 - 14 files changed, 44 insertions(+), 157 deletions(-) delete mode 100644 OpenRA.Mods.Common/AI/BotOrderManager.cs delete mode 100644 OpenRA.Mods.Common/UpdateRules/Rules/20180923/AddBotOrderManager.cs rename OpenRA.Mods.Common/UpdateRules/Rules/20180923/{AddHarvesterBotModule.cs => ExtractHackyAIModules.cs} (93%) diff --git a/OpenRA.Game/Traits/TraitsInterfaces.cs b/OpenRA.Game/Traits/TraitsInterfaces.cs index 5a6d9656ce..d3c0894dee 100644 --- a/OpenRA.Game/Traits/TraitsInterfaces.cs +++ b/OpenRA.Game/Traits/TraitsInterfaces.cs @@ -365,6 +365,7 @@ namespace OpenRA.Traits public interface IBot { void Activate(Player p); + void QueueOrder(Order order); IBotInfo Info { get; } } diff --git a/OpenRA.Mods.Common/AI/BotOrderManager.cs b/OpenRA.Mods.Common/AI/BotOrderManager.cs deleted file mode 100644 index 97987d61ad..0000000000 --- a/OpenRA.Mods.Common/AI/BotOrderManager.cs +++ /dev/null @@ -1,54 +0,0 @@ -#region Copyright & License Information -/* - * Copyright 2007-2018 The OpenRA Developers (see AUTHORS) - * This file is part of OpenRA, which is free software. It is made - * available to you under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. For more - * information, see COPYING. - */ -#endregion - -using System; -using System.Collections.Generic; -using OpenRA.Traits; - -namespace OpenRA.Mods.Common.AI -{ - public sealed class BotOrderManagerInfo : ITraitInfo - { - [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; - - public object Create(ActorInitializer init) { return new BotOrderManager(this); } - } - - public sealed class BotOrderManager : ITick - { - readonly BotOrderManagerInfo info; - readonly Queue orders = new Queue(); - - public BotOrderManager(BotOrderManagerInfo info) - { - this.info = info; - } - - public void QueueOrder(Order order) - { - orders.Enqueue(order); - } - - void IssueOrders(World world) - { - var ordersToIssueThisTick = Math.Min((orders.Count + info.MinOrderQuotientPerTick - 1) / info.MinOrderQuotientPerTick, orders.Count); - for (var i = 0; i < ordersToIssueThisTick; i++) - world.IssueOrder(orders.Dequeue()); - } - - void ITick.Tick(Actor self) - { - // Make sure we tick after all of the bot modules so that we don't introduce an additional tick delay - self.World.AddFrameEndTask(IssueOrders); - } - } -} diff --git a/OpenRA.Mods.Common/AI/DummyAI.cs b/OpenRA.Mods.Common/AI/DummyAI.cs index a49923c663..4ae4329576 100644 --- a/OpenRA.Mods.Common/AI/DummyAI.cs +++ b/OpenRA.Mods.Common/AI/DummyAI.cs @@ -44,6 +44,8 @@ namespace OpenRA.Mods.Common.AI Enabled = true; } + void IBot.QueueOrder(Order order) { } + IBotInfo IBot.Info { get { return info; } } } } diff --git a/OpenRA.Mods.Common/AI/HackyAI.cs b/OpenRA.Mods.Common/AI/HackyAI.cs index 979a922121..a055125c0b 100644 --- a/OpenRA.Mods.Common/AI/HackyAI.cs +++ b/OpenRA.Mods.Common/AI/HackyAI.cs @@ -19,7 +19,7 @@ using OpenRA.Traits; namespace OpenRA.Mods.Common.AI { - public sealed class HackyAIInfo : IBotInfo, ITraitInfo, Requires + public sealed class HackyAIInfo : IBotInfo, ITraitInfo { public class UnitCategories { @@ -71,6 +71,9 @@ namespace OpenRA.Mods.Common.AI [Desc("Minimum delay (in ticks) between creating squads.")] public readonly int MinimumAttackForceDelay = 0; + [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 excess power the AI should try to maintain.")] public readonly int MinimumExcessPower = 0; @@ -258,10 +261,12 @@ namespace OpenRA.Mods.Common.AI public List Squads = new List(); public Player Player { get; private set; } + readonly Queue orders = new Queue(); + readonly Func isEnemyUnit; readonly Predicate unitCannotBeOrdered; - BotOrderManager botOrderManager; + IBotTick[] tickModules; CPos initialBaseCenter; PowerManager playerPower; @@ -317,7 +322,7 @@ namespace OpenRA.Mods.Common.AI IsEnabled = true; playerPower = p.PlayerActor.TraitOrDefault(); playerResource = p.PlayerActor.Trait(); - botOrderManager = p.PlayerActor.Trait(); + tickModules = p.PlayerActor.TraitsImplementing().ToArray(); supportPowerManager = new AISupportPowerManager(this, p); @@ -344,10 +349,15 @@ namespace OpenRA.Mods.Common.AI resourceTypeIndices.Set(tileset.GetTerrainIndex(t.TerrainType), true); } - // DEPRECATED: Bot modules should queue orders directly. + void IBot.QueueOrder(Order order) + { + orders.Enqueue(order); + } + + // DEPRECATED: Modules should use IBot.QueueOrder instead public void QueueOrder(Order order) { - botOrderManager.QueueOrder(order); + orders.Enqueue(order); } ActorInfo ChooseRandomUnitToBuild(ProductionQueue queue) @@ -535,6 +545,18 @@ namespace OpenRA.Mods.Common.AI foreach (var b in builders) b.Tick(); + + // TODO: Add an option to include this in CheckSyncAroundUnsyncedCode. + // Checking sync for this is too expensive to include it by default, + // so it should be implemented as separate sub-option checkbox. + using (new PerfSample("tick_bots")) + foreach (var t in tickModules) + if (t.IsTraitEnabled()) + t.BotTick(this); + + 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 FindClosestEnemy(WPos pos) diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj index d5e1c1b633..ce0c752af5 100644 --- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj +++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj @@ -123,7 +123,6 @@ - @@ -942,8 +941,7 @@ - - + diff --git a/OpenRA.Mods.Common/Traits/BotModules/HarvesterBotModule.cs b/OpenRA.Mods.Common/Traits/BotModules/HarvesterBotModule.cs index 5547ba00ee..a90f7257bf 100644 --- a/OpenRA.Mods.Common/Traits/BotModules/HarvesterBotModule.cs +++ b/OpenRA.Mods.Common/Traits/BotModules/HarvesterBotModule.cs @@ -20,7 +20,7 @@ using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits { [Desc("Put this on the Player actor. Manages bot harvesters to ensure they always continue harvesting as long as there are resources on the map.")] - public class HarvesterBotModuleInfo : ConditionalTraitInfo, Requires + public class HarvesterBotModuleInfo : ConditionalTraitInfo { [Desc("Interval (in ticks) between giving out orders to idle harvesters.")] public readonly int ScanForIdleHarvestersInterval = 20; @@ -31,7 +31,7 @@ namespace OpenRA.Mods.Common.Traits public override object Create(ActorInitializer init) { return new HarvesterBotModule(init.Self, this); } } - public class HarvesterBotModule : ConditionalTrait, ITick + public class HarvesterBotModule : ConditionalTrait, IBotTick { readonly World world; readonly Player player; @@ -40,7 +40,6 @@ namespace OpenRA.Mods.Common.Traits DomainIndex domainIndex; ResourceLayer resLayer; ResourceClaimLayer claimLayer; - BotOrderManager botOrderManager; List harvesters = new List(); int scanForIdleHarvestersTicks; @@ -58,15 +57,11 @@ namespace OpenRA.Mods.Common.Traits domainIndex = world.WorldActor.Trait(); resLayer = world.WorldActor.TraitOrDefault(); claimLayer = world.WorldActor.TraitOrDefault(); - botOrderManager = self.Owner.PlayerActor.Trait(); scanForIdleHarvestersTicks = Info.ScanForIdleHarvestersInterval; } - void ITick.Tick(Actor self) + void IBotTick.BotTick(IBot bot) { - if (IsTraitDisabled) - return; - if (resLayer == null || resLayer.IsResourceLayerEmpty) return; @@ -103,7 +98,7 @@ namespace OpenRA.Mods.Common.Traits // Tell the idle harvester to quit slacking: var newSafeResourcePatch = FindNextResource(harvester, harv); AIUtils.BotDebug("AI: Harvester {0} is idle. Ordering to {1} in search for new resources.".F(harvester, newSafeResourcePatch)); - botOrderManager.QueueOrder(new Order("Harvest", harvester, Target.FromCell(world, newSafeResourcePatch), false)); + bot.QueueOrder(new Order("Harvest", harvester, Target.FromCell(world, newSafeResourcePatch), false)); } } diff --git a/OpenRA.Mods.Common/TraitsInterfaces.cs b/OpenRA.Mods.Common/TraitsInterfaces.cs index c5ee6f92d2..0df53d122f 100644 --- a/OpenRA.Mods.Common/TraitsInterfaces.cs +++ b/OpenRA.Mods.Common/TraitsInterfaces.cs @@ -14,6 +14,7 @@ using System.Drawing; using OpenRA.Activities; using OpenRA.Graphics; using OpenRA.Mods.Common.Activities; +using OpenRA.Mods.Common.AI; using OpenRA.Mods.Common.Graphics; using OpenRA.Primitives; using OpenRA.Traits; @@ -446,4 +447,7 @@ namespace OpenRA.Mods.Common.Traits [RequireExplicitImplementation] public interface IPreventsShroudReset { bool PreventShroudReset(Actor self); } + + [RequireExplicitImplementation] + public interface IBotTick { void BotTick(IBot bot); } } diff --git a/OpenRA.Mods.Common/UpdateRules/Rules/20180923/AddBotOrderManager.cs b/OpenRA.Mods.Common/UpdateRules/Rules/20180923/AddBotOrderManager.cs deleted file mode 100644 index 3d72950d51..0000000000 --- a/OpenRA.Mods.Common/UpdateRules/Rules/20180923/AddBotOrderManager.cs +++ /dev/null @@ -1,76 +0,0 @@ -#region Copyright & License Information -/* - * Copyright 2007-2018 The OpenRA Developers (see AUTHORS) - * This file is part of OpenRA, which is free software. It is made - * available to you under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. For more - * information, see COPYING. - */ -#endregion - -using System.Collections.Generic; -using System.Linq; - -namespace OpenRA.Mods.Common.UpdateRules.Rules -{ - public class AddBotOrderManager : UpdateRule - { - public override string Name { get { return "Split bot order management from HackyAI to BotOrderManager"; } } - public override string Description - { - get - { - return "The MinOrderQuotientPerTick property and all bot order handling have been moved from HackyAI\n" + - "to the new BotOrderManager."; - } - } - - bool showMessage; - bool messageShown; - - public override IEnumerable AfterUpdate(ModData modData) - { - var message = "You may want to manually change MinOrderQuotientPerTick on BotOrderManager,\n" + - "if you were using a custom value on any AI."; - - if (showMessage && !messageShown) - yield return message; - - messageShown = true; - } - - public override IEnumerable UpdateActorNode(ModData modData, MiniYamlNode actorNode) - { - if (actorNode.Key != "Player") - yield break; - - var hackyAIs = actorNode.ChildrenMatching("HackyAI"); - if (!hackyAIs.Any()) - yield break; - - foreach (var hackyAINode in hackyAIs) - { - // We no longer support individual values for each AI, - // and in practice the default of 5 has proven to be a solid middle-ground, - // so just removing any custom value and notifying the modder about it should suffice. - var minQuotient = hackyAINode.LastChildMatching("MinOrderQuotientPerTick"); - if (minQuotient != null) - { - hackyAINode.RemoveNode(minQuotient); - if (!showMessage) - showMessage = true; - } - } - - var botOrderManager = actorNode.LastChildMatching("BotOrderManager"); - if (botOrderManager == null) - { - var addBotOrderManager = new MiniYamlNode("BotOrderManager", ""); - actorNode.AddNode(addBotOrderManager); - } - - yield break; - } - } -} diff --git a/OpenRA.Mods.Common/UpdateRules/Rules/20180923/AddHarvesterBotModule.cs b/OpenRA.Mods.Common/UpdateRules/Rules/20180923/ExtractHackyAIModules.cs similarity index 93% rename from OpenRA.Mods.Common/UpdateRules/Rules/20180923/AddHarvesterBotModule.cs rename to OpenRA.Mods.Common/UpdateRules/Rules/20180923/ExtractHackyAIModules.cs index e993aae6ae..29b13d9a6a 100644 --- a/OpenRA.Mods.Common/UpdateRules/Rules/20180923/AddHarvesterBotModule.cs +++ b/OpenRA.Mods.Common/UpdateRules/Rules/20180923/ExtractHackyAIModules.cs @@ -14,15 +14,15 @@ using System.Linq; namespace OpenRA.Mods.Common.UpdateRules.Rules { - public class AddHarvesterBotModule : UpdateRule + public class ExtractHackyAIModules : UpdateRule { - public override string Name { get { return "Split HackyAI harvester handling to HarvesterBotModule"; } } + public override string Name { get { return "Split HackyAI logic handling to BotModules"; } } public override string Description { get { - return "Some properties and all harvester handling have been moved from HackyAI\n" + - "to the new HarvesterBotModule."; + return "Most properties and logic are being moved from HackyAI\n" + + "to *BotModules."; } } diff --git a/OpenRA.Mods.Common/UpdateRules/UpdatePath.cs b/OpenRA.Mods.Common/UpdateRules/UpdatePath.cs index c480de2659..88bd2b0fb9 100644 --- a/OpenRA.Mods.Common/UpdateRules/UpdatePath.cs +++ b/OpenRA.Mods.Common/UpdateRules/UpdatePath.cs @@ -105,8 +105,7 @@ namespace OpenRA.Mods.Common.UpdateRules new RemovedDemolishLocking(), new RequireProductionType(), new CloakRequiresConditionToPause(), - new AddBotOrderManager(), - new AddHarvesterBotModule(), + new ExtractHackyAIModules(), new RemoveNegativeDamageFullHealthCheck(), new RemoveResourceExplodeModifier(), }) diff --git a/mods/cnc/rules/ai.yaml b/mods/cnc/rules/ai.yaml index 2dd5d9a7d9..92b1e00627 100644 --- a/mods/cnc/rules/ai.yaml +++ b/mods/cnc/rules/ai.yaml @@ -1,5 +1,4 @@ Player: - BotOrderManager: HackyAI@Cabal: Name: Cabal Type: cabal diff --git a/mods/d2k/rules/ai.yaml b/mods/d2k/rules/ai.yaml index cf5dba7a5b..4930529b17 100644 --- a/mods/d2k/rules/ai.yaml +++ b/mods/d2k/rules/ai.yaml @@ -1,5 +1,4 @@ Player: - BotOrderManager: HackyAI@Omnius: Name: Omnius Type: omnius diff --git a/mods/ra/rules/ai.yaml b/mods/ra/rules/ai.yaml index 41e8c495d0..f40f32d5ad 100644 --- a/mods/ra/rules/ai.yaml +++ b/mods/ra/rules/ai.yaml @@ -1,5 +1,4 @@ Player: - BotOrderManager: HackyAI@RushAI: Name: Rush AI Type: rush diff --git a/mods/ts/rules/ai.yaml b/mods/ts/rules/ai.yaml index f8558f5c99..b927ae536f 100644 --- a/mods/ts/rules/ai.yaml +++ b/mods/ts/rules/ai.yaml @@ -1,5 +1,4 @@ Player: - BotOrderManager: HackyAI@TestAI: Name: Test AI Type: test