Skip to content

Commit e9a70f0

Browse files
committed
Merge branch 'Hackathon' into feature/double-halving
# Please enter a commit message to explain why this merge is necessary, # especially if it merges an updated upstream into a topic branch. # # Lines starting with '#' will be ignored, and an empty message aborts # the commit.
2 parents 68258d2 + 1cd6b1f commit e9a70f0

3 files changed

Lines changed: 115 additions & 22 deletions

File tree

OpenRA.Game/CopilotCommandServer.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,25 @@ private void LogError(string message)
272272
Console.WriteLine($"[{timestamp}] [ERROR] CopilotCommandServer: {message}");
273273
}
274274

275+
Player ResolvePlayer(MCPRequest request)
276+
{
277+
if (string.IsNullOrEmpty(request.PlayerId))
278+
return world.LocalPlayer;
279+
280+
// Try as ClientIndex (integer)
281+
if (int.TryParse(request.PlayerId, out var clientIndex))
282+
{
283+
var byIndex = world.Players.FirstOrDefault(p => p.ClientIndex == clientIndex && !p.NonCombatant);
284+
if (byIndex != null) return byIndex;
285+
}
286+
287+
// Try as InternalName (e.g. "Multi0", "Multi1")
288+
var byName = world.Players.FirstOrDefault(p => p.InternalName == request.PlayerId);
289+
if (byName != null) return byName;
290+
291+
return world.LocalPlayer;
292+
}
293+
275294
async Task HandleClient(Socket clientSocket)
276295
{
277296
using (clientSocket)
@@ -346,6 +365,12 @@ async Task HandleClient(Socket clientSocket)
346365
return;
347366
}
348367

368+
// 解析玩家身份并注入到 Params
369+
var resolvedPlayer = ResolvePlayer(request);
370+
if (request.Params == null)
371+
request.Params = new JObject();
372+
request.Params["__playerId"] = resolvedPlayer.InternalName;
373+
349374
// 处理命令
350375
if (CommandHandlers.TryGetValue(request.Command, out var commandHandler))
351376
{

OpenRA.Game/CopilotModels.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ public class MCPRequest
2121

2222
[JsonProperty("language")]
2323
public string Language { get; set; } = "zh";
24+
25+
[JsonProperty("playerId")]
26+
public string PlayerId { get; set; }
2427
}
2528

2629
public class MCPResponse

OpenRA.Mods.Common/ServerCommands.cs

Lines changed: 87 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,15 @@ public static bool IsFactionOk(Player player, Actor actor, string faction)
3030
return actor.OccupiesSpace != null;
3131
}
3232

33+
public static Player ResolvePlayer(JObject json, World world)
34+
{
35+
var playerId = json?.TryGetFieldValue("__playerId")?.ToString();
36+
if (string.IsNullOrEmpty(playerId))
37+
return world.LocalPlayer;
38+
var player = world.Players.FirstOrDefault(p => p.InternalName == playerId);
39+
return player ?? world.LocalPlayer;
40+
}
41+
3342
public static List<Actor> GetTargets(JToken targets, World world, Player player)
3443
{
3544
var result = new List<Actor>();
@@ -178,7 +187,7 @@ public static CPos GetLocation(JToken location)
178187

179188
public static List<Actor> GetTargetsFromJson(JObject json, World world, bool bAllowEmpty = false)
180189
{
181-
var player = world.LocalPlayer;
190+
var player = ResolvePlayer(json, world);
182191
var targets = json.TryGetFieldValue("targets");
183192
if (targets == null)
184193
{
@@ -246,7 +255,7 @@ public static List<Actor> GetTargetsFromJson(JObject json, World world, bool bAl
246255

247256
public static string SelectUnitCommand(JObject json, World world)
248257
{
249-
var player = world.LocalPlayer;
258+
var player = ResolvePlayer(json, world);
250259
var isCombine = json.TryGetFieldValue("isCombine")?.ToObject<int>();
251260
var actors = GetTargetsFromJson(json, world);
252261
var newSelection = SelectionUtils.SelectActorsByOwnerAndSelectionClass(actors, new List<Player> { player }, null).ToList();
@@ -256,7 +265,7 @@ public static string SelectUnitCommand(JObject json, World world)
256265

257266
public static string FormGroupCommand(JObject json, World world)
258267
{
259-
var player = world.LocalPlayer;
268+
var player = ResolvePlayer(json, world);
260269
var groupId = json.TryGetFieldValue("groupId")?.ToObject<int>();
261270
if (groupId == null)
262271
{
@@ -273,7 +282,7 @@ public static string FormGroupCommand(JObject json, World world)
273282

274283
public static string MoveActorCommand(JObject json, World world)
275284
{
276-
var player = world.LocalPlayer;
285+
var player = ResolvePlayer(json, world);
277286
var actors = GetTargetsFromJson(json, world);
278287
CPos? location;
279288
location = null;
@@ -383,7 +392,7 @@ public static string MoveActorInPath(IEnumerable<Actor> actors, List<JToken> pat
383392
public static JObject StartProductionCommand(JObject json, World world)
384393
{
385394
var orders = json.TryGetFieldValue("units")?.ToObject<List<JToken>>();
386-
var player = world.LocalPlayer;
395+
var player = ResolvePlayer(json, world);
387396
var ret_str = "";
388397
var waitId = -1;
389398
var autoPlace = json.TryGetFieldValue("autoPlaceBuilding")?.ToObject<bool>() ?? false;
@@ -468,7 +477,8 @@ public static string CameraMoveCommand(JObject json, World world)
468477
var direction = json.TryGetFieldValue("direction")?.ToObject<string>();
469478
var distance = json.TryGetFieldValue("distance")?.ToObject<int>();
470479
var locationToken = json.TryGetFieldValue("location");
471-
var location = GetTargetLocation(locationToken, world, world.LocalPlayer);
480+
var player = ResolvePlayer(json, world);
481+
var location = GetTargetLocation(locationToken, world, player);
472482
if ((direction == null || distance == null) && location == null)
473483
{
474484
return "No direction Or No Distance Or No Location !!!!!!";
@@ -521,7 +531,7 @@ public static string CameraMoveCommand(JObject json, World world)
521531

522532
public static string AttackCommand(JObject json, World world)
523533
{
524-
var player = world.LocalPlayer;
534+
var player = ResolvePlayer(json, world);
525535
var attacker = GetTargets(json["attackers"], world, player);
526536
var target = GetTargetsFromJson(json, world);
527537

@@ -626,7 +636,7 @@ public static string ViewCommand(JObject json, World world)
626636

627637
public static string OccupyCommand(JObject json, World world)
628638
{
629-
var player = world.LocalPlayer;
639+
var player = ResolvePlayer(json, world);
630640
var actors = GetTargets(json["occupiers"], world, player);
631641
var targets = GetTargets(json["targets"], world, player);
632642

@@ -674,7 +684,7 @@ public static string OccupyCommand(JObject json, World world)
674684
public static string RepairCommand(JObject json, World world)
675685
{
676686
var actors = GetTargetsFromJson(json, world);
677-
var player = world.LocalPlayer;
687+
var player = ResolvePlayer(json, world);
678688
foreach (var a in actors)
679689
{
680690
if (a.Info.HasTraitInfo<RepairableBuildingInfo>())
@@ -742,7 +752,7 @@ public static string StopCommand(JObject json, World world)
742752

743753
public static string SetRallyPointCommand(JObject json, World world)
744754
{
745-
var player = world.LocalPlayer;
755+
var player = ResolvePlayer(json, world);
746756
var actors = GetTargetsFromJson(json, world);
747757
var retstr = "";
748758
var locationToken = json.TryGetFieldValue("location");
@@ -786,7 +796,7 @@ public static string SetRallyPointCommand(JObject json, World world)
786796

787797
public static string ManageProductionCommand(JObject json, World world)
788798
{
789-
var player = world.LocalPlayer;
799+
var player = ResolvePlayer(json, world);
790800

791801
// 获取队列类型
792802
var queueType = json.TryGetFieldValue("queueType")?.ToString();
@@ -875,7 +885,7 @@ public static string ManageProductionCommand(JObject json, World world)
875885

876886
public static string PlaceBuildingCommand(JObject json, World world)
877887
{
878-
var player = world.LocalPlayer;
888+
var player = ResolvePlayer(json, world);
879889

880890
// 获取队列类型
881891
var queueType = json.TryGetFieldValue("queueType")?.ToString();
@@ -1052,7 +1062,7 @@ public static IEnumerable<JObject> QueryFrozenActors(JToken targets, World world
10521062

10531063
public static JObject ActorQueryCommand(JObject json, World world)
10541064
{
1055-
var player = world.LocalPlayer;
1065+
var player = ResolvePlayer(json, world);
10561066
var targets = json.TryGetFieldValue("targets");
10571067
List<Actor> targetActors;
10581068
if (targets == null)
@@ -1138,7 +1148,7 @@ public static JObject WaitQueryCommand(JObject json, World world)
11381148
public static JObject QueryCanProduceCommand(JObject json, World world)
11391149
{
11401150
var orders = json.TryGetFieldValue("units")?.ToObject<List<JToken>>();
1141-
var player = world.LocalPlayer;
1151+
var player = ResolvePlayer(json, world);
11421152
var ret_str = "";
11431153
var canProduce = false;
11441154

@@ -1202,6 +1212,7 @@ public static JObject QueryCanProduceCommand(JObject json, World world)
12021212

12031213
public static JObject FogQueryCommand(JObject json, World world)
12041214
{
1215+
var player = ResolvePlayer(json, world);
12051216
var jpos = json.TryGetFieldValue("pos");
12061217
if (jpos == null)
12071218
{
@@ -1212,14 +1223,15 @@ public static JObject FogQueryCommand(JObject json, World world)
12121223

12131224
var result = new JObject
12141225
{
1215-
["IsVisible"] = world.FogObscures(pos),
1216-
["IsExplored"] = world.ShroudObscures(pos)
1226+
["IsVisible"] = player.Shroud.IsVisible(pos),
1227+
["IsExplored"] = player.Shroud.IsExplored(pos)
12171228
};
12181229
return result;
12191230
}
12201231

12211232
public static JObject MapQueryCommand(JObject json, World world)
12221233
{
1234+
var player = ResolvePlayer(json, world);
12231235
var map = world.Map;
12241236
var width = map.MapSize.X - 2;
12251237
var height = map.MapSize.Y - 2;
@@ -1245,8 +1257,8 @@ public static JObject MapQueryCommand(JObject json, World world)
12451257
{
12461258
var pos = new CPos(x, y);
12471259
heightRow.Add(map.Height[pos]);
1248-
isVisibleRow.Add(!world.FogObscures(pos));
1249-
isExploredRow.Add(!world.ShroudObscures(pos));
1260+
isVisibleRow.Add(player.Shroud.IsVisible(pos));
1261+
isExploredRow.Add(player.Shroud.IsExplored(pos));
12501262
terrainRow.Add(map.Tiles[pos].Type);
12511263
resourcesTypeRow.Add(map.Resources[pos].Type);
12521264
resourcesRow.Add(map.Resources[pos].Index);
@@ -1260,6 +1272,34 @@ public static JObject MapQueryCommand(JObject json, World world)
12601272
resourcesArray.Add(resourcesRow);
12611273
}
12621274

1275+
// Resource spawner actors (MINE/GMINE)
1276+
var resourceActors = new JArray(
1277+
world.ActorsHavingTrait<SeedsResource>()
1278+
.Where(a => a.IsInWorld && !a.IsDead)
1279+
.Select(a => new JObject
1280+
{
1281+
["type"] = a.Info.Name,
1282+
["displayName"] = CopilotsConfig.GetChineseByConfigName(a.Info.Name),
1283+
["resourceType"] = a.Info.TraitInfo<SeedsResourceInfo>().ResourceType,
1284+
["x"] = a.Location.X,
1285+
["y"] = a.Location.Y
1286+
}).ToArray()
1287+
);
1288+
1289+
// Oil wells / cash trickler buildings
1290+
var oilWells = new JArray(
1291+
world.ActorsHavingTrait<CashTrickler>()
1292+
.Where(a => a.IsInWorld && !a.IsDead)
1293+
.Select(a => new JObject
1294+
{
1295+
["type"] = a.Info.Name,
1296+
["displayName"] = CopilotsConfig.GetChineseByConfigName(a.Info.Name),
1297+
["owner"] = a.Owner?.InternalName ?? "Neutral",
1298+
["x"] = a.Location.X,
1299+
["y"] = a.Location.Y
1300+
}).ToArray()
1301+
);
1302+
12631303
var result = new JObject
12641304
{
12651305
["MapWidth"] = width,
@@ -1269,7 +1309,9 @@ public static JObject MapQueryCommand(JObject json, World world)
12691309
["IsExplored"] = isExploredArray,
12701310
["Terrain"] = terrainArray,
12711311
["ResourcesType"] = resourcesTypeArray,
1272-
["Resources"] = resourcesArray
1312+
["Resources"] = resourcesArray,
1313+
["resourceActors"] = resourceActors,
1314+
["oilWells"] = oilWells
12731315
};
12741316

12751317
return result;
@@ -1322,7 +1364,7 @@ public static JObject PingCommand(JObject json, World world)
13221364

13231365
public static JObject PlayerBaseInfoQueryCommand(JObject json, World world)
13241366
{
1325-
var player = world.LocalPlayer;
1367+
var player = ResolvePlayer(json, world);
13261368
var playerRes = player.PlayerActor.Trait<PlayerResources>();
13271369
var powerManager = player.PlayerActor.Trait<PowerManager>();
13281370
if (playerRes == null || powerManager == null)
@@ -1378,7 +1420,7 @@ public static JObject ScreenInfoQueryCommand(JObject json, World world)
13781420

13791421
public static JObject QueryProductionQueueCommand(JObject json, World world)
13801422
{
1381-
var player = world.LocalPlayer;
1423+
var player = ResolvePlayer(json, world);
13821424

13831425
// 获取队列类型
13841426
var queueType = json.TryGetFieldValue("queueType")?.ToString();
@@ -1465,7 +1507,7 @@ public static JObject QueryMatchInfoCommand(JObject json, World world)
14651507
var scoreService = world.WorldActor.TraitOrDefault<CopilotScoreService>();
14661508
if (scoreService == null)
14671509
throw new ArgumentException("ScoreService or ControlPoint manager not found");
1468-
var player = world.LocalPlayer;
1510+
var player = ResolvePlayer(json, world);
14691511
var enemyPlayer = world.Players.FirstOrDefault(p => p != player && !p.NonCombatant);
14701512
var remainingTime = scoreService.RemainingTime;
14711513
if (remainingTime < 0) remainingTime = 0;
@@ -1481,6 +1523,28 @@ public static JObject QueryMatchInfoCommand(JObject json, World world)
14811523
}
14821524

14831525

1526+
public static JObject QueryPlayersCommand(JObject json, World world)
1527+
{
1528+
var players = world.Players
1529+
.Where(p => !p.NonCombatant)
1530+
.Select(p => new JObject
1531+
{
1532+
["internalName"] = p.InternalName,
1533+
["clientIndex"] = p.ClientIndex,
1534+
["faction"] = p.Faction.InternalName,
1535+
["isBot"] = p.IsBot,
1536+
["team"] = p.PlayerReference.Team,
1537+
["color"] = p.Color.ToString(),
1538+
["isLocalPlayer"] = p == world.LocalPlayer
1539+
}).ToArray();
1540+
1541+
var result = new JObject
1542+
{
1543+
["players"] = new JArray(players)
1544+
};
1545+
return result;
1546+
}
1547+
14841548
public void WorldLoaded(World w, WorldRenderer wr)
14851549
{
14861550
if (w.Type == WorldType.Regular && w.CopilotServer != null)
@@ -1514,6 +1578,7 @@ public void WorldLoaded(World w, WorldRenderer wr)
15141578
w.CopilotServer.QueryHandlers["player_baseinfo_query"] = PlayerBaseInfoQueryCommand;
15151579
w.CopilotServer.QueryHandlers["screen_info_query"] = ScreenInfoQueryCommand;
15161580
w.CopilotServer.QueryHandlers["ping"] = PingCommand;
1581+
w.CopilotServer.QueryHandlers["query_players"] = QueryPlayersCommand;
15171582

15181583
CopilotsConfig.LoadConfig();
15191584
CopilotsUtils.WaitInit();

0 commit comments

Comments
 (0)