From b076becd6fa43791ade3803812ea8681a58834ca Mon Sep 17 00:00:00 2001 From: "@permadeath.com" Date: Mon, 17 Aug 2026 10:46:29 -0400 Subject: [PATCH] feat(match): fly the behaviour the scenario asked for A V2 scenario can give each faction its own `bot:` block, and MatchHost was throwing it away: every Princess was constructed on the default profile whatever the file said. lance.blue's Bug Company asks all three pirate lances for Striker - aggression 9, self-preservation 2, herd 7 - and the card on the front page says so, so the match was advertising behaviour it did not run. The lookup has one trap in it. Bot behaviour is keyed by the scenario's own slot name, and the rename above replaces that with the display name the manifest wants shown - so asking for the behaviour of "@lance.blue" finds nothing. The slot each player came from is now remembered as it is renamed, and the lookup goes through that. A V1 scenario carries no such block and keeps the default, which is what it did before. --- src/arena/MatchHost.java | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/src/arena/MatchHost.java b/src/arena/MatchHost.java index 0b0b490..2a543b2 100644 --- a/src/arena/MatchHost.java +++ b/src/arena/MatchHost.java @@ -8,6 +8,7 @@ import java.nio.file.Path; import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; @@ -15,6 +16,7 @@ import java.util.Map; import java.util.Set; import java.util.stream.Collectors; +import megamek.client.bot.princess.BehaviorSettings; import megamek.client.bot.princess.Princess; import megamek.common.Player; import megamek.common.compute.Compute; @@ -25,6 +27,7 @@ import megamek.common.loaders.MekSummaryCache; import megamek.common.net.connections.AbstractConnection; import megamek.common.net.enums.PacketCommand; import megamek.common.net.packets.Packet; +import megamek.common.jacksonAdapters.BotParser; import megamek.common.scenario.Scenario; import megamek.common.scenario.ScenarioLoader; import megamek.server.Server; @@ -100,12 +103,20 @@ public final class MatchHost { // (identity.json, the camo index, --humans), translated through this // one map. Map names = readNamesIndex(a.namesIndex); + // Which scenario slot each player came from, kept because the rename + // below throws it away and the scenario's bot behaviour is keyed by it. + // Looked up under the display name after the rename, so a bot called + // "@lance.blue" can still be matched to the "OPFOR" block that + // configured it. + Map slotOf = new HashMap<>(); for (Player p : game.getPlayersList()) { - String display = names.get(p.getName()); - if (display != null && !display.equals(p.getName())) { - System.out.println("[host] slot " + p.getName() + " plays as " + display); + String slot = p.getName(); + String display = names.get(slot); + if (display != null && !display.equals(slot)) { + System.out.println("[host] slot " + slot + " plays as " + display); p.setName(display); } + slotOf.put(p.getName(), slot); } // Before the Server exists, because constructing one binds the port @@ -169,6 +180,26 @@ public final class MatchHost { List bots = new ArrayList<>(); for (Player p : botSlots) { Princess bot = new Princess(p.getName(), LOCALHOST, a.port); + // The behaviour the scenario asked for, if it asked for one. A V2 + // scenario can give each faction its own `bot:` block - a light + // swarm wants Striker, a gun line wants Sniper - and without this + // every one of them fought on Princess's default profile whatever + // the file said. Only V2 can express it; a V1 scenario has no such + // block and keeps the default. + String slot = slotOf.getOrDefault(p.getName(), p.getName()); + if (scenario.getBotInfo(slot) + instanceof BotParser.PrincessRecord(BehaviorSettings behaviour)) { + bot.setBehaviorSettings(behaviour); + // The knobs, not getDescription(): a behaviour built from a + // scenario's YAML has no description at all, so that logged + // "(No description)" and said nothing about what changed. + System.out.printf( + "[host] %s flies the scenario's behaviour: aggression %d," + + " self-preservation %d, herding %d, bravery %d%n", + p.getName(), behaviour.getHyperAggressionIndex(), + behaviour.getSelfPreservationIndex(), + behaviour.getHerdMentalityIndex(), behaviour.getBraveryIndex()); + } bot.startPrecognition(); bot.connect(); bots.add(bot); -- 2.51.2