Skip to content

Commit

Permalink
Persist skirmish settings between sessions.
Browse files Browse the repository at this point in the history
  • Loading branch information
pchote committed Nov 12, 2023
1 parent d8ba261 commit 55bb644
Show file tree
Hide file tree
Showing 2 changed files with 143 additions and 3 deletions.
2 changes: 1 addition & 1 deletion OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1406,7 +1406,7 @@ public static void LoadMapSettings(S server, Session.Global gs, MapPreview map)
}
}

static Color SanitizePlayerColor(S server, Color askedColor, int playerIndex, Connection connectionToEcho = null)
public static Color SanitizePlayerColor(S server, Color askedColor, int playerIndex, Connection connectionToEcho = null)
{
lock (server.LobbyInfo)
{
Expand Down
144 changes: 142 additions & 2 deletions OpenRA.Mods.Common/ServerTraits/SkirmishLogic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,160 @@
*/
#endregion

using System;

Check failure on line 12 in OpenRA.Mods.Common/ServerTraits/SkirmishLogic.cs

View workflow job for this annotation

GitHub Actions / Windows (.NET 6.0)

Using directive is unnecessary.

Check failure on line 12 in OpenRA.Mods.Common/ServerTraits/SkirmishLogic.cs

View workflow job for this annotation

GitHub Actions / Windows (.NET 6.0)

Using directive is unnecessary.

Check failure on line 12 in OpenRA.Mods.Common/ServerTraits/SkirmishLogic.cs

View workflow job for this annotation

GitHub Actions / Linux (.NET 6.0)

Using directive is unnecessary. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005)

Check failure on line 12 in OpenRA.Mods.Common/ServerTraits/SkirmishLogic.cs

View workflow job for this annotation

GitHub Actions / Linux (.NET 6.0)

Using directive is unnecessary. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005)
using System.Collections.Generic;
using System.IO;
using System.Linq;
using OpenRA.Network;
using OpenRA.Primitives;
using OpenRA.Server;
using OpenRA.Traits;
using S = OpenRA.Server.Server;

namespace OpenRA.Mods.Common.Server
{
public class SkirmishLogic : ServerTrait, IClientJoined
public class SkirmishLogic : ServerTrait, IClientJoined, INotifySyncLobbyInfo
{
public void ClientJoined(S server, Connection conn)
class SkirmishSlot
{
[FieldLoader.Serialize(FromYamlKey = true)]
public readonly string Slot;
public readonly Color Color;
public readonly string Faction;
public readonly int SpawnPoint;
public readonly int Team;
public readonly int Handicap;

public SkirmishSlot() { }

public SkirmishSlot(Session.Client c)
{
Slot = c.Slot;
Color = c.Color;
Faction = c.Faction;
SpawnPoint = c.SpawnPoint;
Team = c.Team;
Handicap = c.Handicap;
}

public static void DeserializeToClient(MiniYaml yaml, Session.Client c)
{
var s = FieldLoader.Load<SkirmishSlot>(yaml);
c.Slot = s.Slot;
c.Color = c.PreferredColor = s.Color;
c.Faction = s.Faction;
c.SpawnPoint = s.SpawnPoint;
c.Team = s.Team;
c.Handicap = s.Handicap;
}
}

static bool TryInitializeFromFile(S server, string path, Connection conn)
{
if (!File.Exists(path))
return false;

var nodes = new MiniYaml("", MiniYaml.FromFile(path));
var mapNode = nodes.NodeWithKeyOrDefault("Map");
if (mapNode == null)
return false;

// Only set players and options if the map is available
if (server.LobbyInfo.GlobalSettings.Map != mapNode.Value.Value)
{
var map = server.ModData.MapCache[mapNode.Value.Value];
if (map.Status != MapStatus.Available || !server.InterpretCommand($"map {map.Uid}", conn))
return false;
}

var optionsNode = nodes.NodeWithKeyOrDefault("Options");
if (optionsNode != null)
{
var options = server.Map.PlayerActorInfo.TraitInfos<ILobbyOptions>()
.Concat(server.Map.WorldActorInfo.TraitInfos<ILobbyOptions>())
.SelectMany(t => t.LobbyOptions(server.Map))
.ToDictionary(o => o.Id, o => o);

foreach (var optionNode in optionsNode.Value.Nodes)
{
if (options.TryGetValue(optionNode.Key, out var option) && !option.IsLocked)
{
var oo = server.LobbyInfo.GlobalSettings.LobbyOptions[option.Id];
oo.Value = oo.PreferredValue = optionNode.Value.Value;
}
}
}

var playerNode = nodes.NodeWithKeyOrDefault("Player");
if (playerNode != null)
{
var client = server.GetClient(conn);
SkirmishSlot.DeserializeToClient(playerNode.Value, client);
client.Color = LobbyCommands.SanitizePlayerColor(server, client.Color, client.Index);
}

var botsNode = nodes.NodeWithKeyOrDefault("Bots");
if (botsNode != null)
{
foreach (var botNode in botsNode.Value.Nodes)
{
var botInfo = server.Map.PlayerActorInfo.TraitInfos<IBotInfo>()
.FirstOrDefault(b => b.Type == botNode.Key);

if (botInfo == null)
continue;

var client = new Session.Client
{
Index = server.ChooseFreePlayerIndex(),
Name = botInfo.Name,
Bot = botInfo.Type,
Slot = botNode.Value.Value,
State = Session.ClientState.NotReady,
BotControllerClientIndex = 0
};

SkirmishSlot.DeserializeToClient(botNode.Value, client);

// Validate whether color is allowed and get an alternative if it isn't
if (client.Slot != null && !server.LobbyInfo.Slots[client.Slot].LockColor)
client.Color = LobbyCommands.SanitizePlayerColor(server, client.Color, client.Index);

server.LobbyInfo.Clients.Add(client);
S.SyncClientToPlayerReference(client, server.Map.Players.Players[client.Slot]);
}
}

return true;
}

void INotifySyncLobbyInfo.LobbyInfoSynced(S server)
{
if (server.Type != ServerType.Skirmish)
return;

var path = Path.Combine(Platform.SupportDir, $"skirmish.{server.ModData.Manifest.Id}.yaml");
var playerClient = server.LobbyInfo.NonBotClients.First();
new List<MiniYamlNode>
{
new("Map", server.LobbyInfo.GlobalSettings.Map),
new("Options", new MiniYaml("", server.LobbyInfo.GlobalSettings.LobbyOptions
.Select(kv => new MiniYamlNode(kv.Key, kv.Value.Value)))),
new("Player", FieldSaver.Save(new SkirmishSlot(playerClient))),
new("Bots", new MiniYaml("", server.LobbyInfo.Clients.Where(c => c.IsBot)
.Select(b => new MiniYamlNode(b.Bot, FieldSaver.Save(new SkirmishSlot(b))))))
}.WriteToFile(path);
}

void IClientJoined.ClientJoined(S server, Connection conn)
{
if (server.Type != ServerType.Skirmish)
return;

var skirmishFile = Path.Combine(Platform.SupportDir, $"skirmish.{server.ModData.Manifest.Id}.yaml");
if (TryInitializeFromFile(server, skirmishFile, conn))
return;

var slot = server.LobbyInfo.FirstEmptyBotSlot();
var bot = server.Map.PlayerActorInfo.TraitInfos<IBotInfo>().Select(t => t.Type).FirstOrDefault();
var botController = server.LobbyInfo.Clients.FirstOrDefault(c => c.IsAdmin);
Expand Down

0 comments on commit 55bb644

Please sign in to comment.