Skip to content

Commit

Permalink
Added translation support for server orders.
Browse files Browse the repository at this point in the history
  • Loading branch information
Mailaender committed Dec 20, 2021
1 parent e321201 commit 137a0d8
Show file tree
Hide file tree
Showing 19 changed files with 676 additions and 153 deletions.
132 changes: 132 additions & 0 deletions OpenRA.Game/Network/LocalizedMessage.cs
@@ -0,0 +1,132 @@
#region Copyright & License Information
/*
* Copyright 2007-2021 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 System.Linq;
using System.Text.RegularExpressions;
using OpenRA.Widgets;

namespace OpenRA.Network
{
[Flags]
public enum SupportedTypes
{
String = 0,
Int = 1,
Long = 2,
}

public class Argument
{
public readonly string Key;
public readonly string Value;
public readonly SupportedTypes Type;

public Argument() { }

public Argument(string key, object value)
{
Key = key;

if (value is string)
{
Type = SupportedTypes.String;
Value = (string)value;
}
else if (value is int)
{
Type = SupportedTypes.Int;
Value = ((int)value).ToString();
}
else if (value is long)
{
Type = SupportedTypes.Long;
Value = ((long)value).ToString();
}
}
}

public class LocalizedMessage
{
public const int ProtocolVersion = 1;

public readonly string Key;

[FieldLoader.LoadUsing(nameof(LoadArguments))]
public readonly Argument[] Arguments;

static object LoadArguments(MiniYaml yaml)
{
var arguments = new List<Argument>();
var argumentsNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Arguments");
if (argumentsNode != null)
{
var regex = new Regex(@"Argument@\d+");
foreach (var argument in argumentsNode.Value.Nodes)
if (regex.IsMatch(argument.Key))
arguments.Add(FieldLoader.Load<Argument>(argument.Value));
}

return arguments.ToArray();
}

static readonly string[] SerializeFields = { "Key" };

public LocalizedMessage(MiniYaml yaml)
{
FieldLoader.Load(this, yaml);
}

public LocalizedMessage(string key, Dictionary<string, object> arguments = null)
{
Key = key;
Arguments = arguments?.Select(a => new Argument(a.Key, a.Value)).ToArray();
}

public string Serialize()
{
var root = new List<MiniYamlNode>() { new MiniYamlNode("Protocol", ProtocolVersion.ToString()) };
foreach (var field in SerializeFields)
root.Add(FieldSaver.SaveField(this, field));

if (Arguments != null)
{
var argumentsNode = new MiniYaml("");
var i = 0;
foreach (var argument in Arguments)
argumentsNode.Nodes.Add(new MiniYamlNode("Argument@" + i++, FieldSaver.Save(argument)));

root.Add(new MiniYamlNode("Arguments", argumentsNode));
}

return new MiniYaml("", root)
.ToLines("LocalizedMessage")
.JoinWith("\n");
}

public string Translate()
{
var argumentDictionary = new Dictionary<string, object>();
foreach (var argument in Arguments)
{
if (argument.Type == SupportedTypes.Int && int.TryParse(argument.Value, out var integerValue))
argumentDictionary.Add(argument.Key, integerValue);
else if (argument.Type == SupportedTypes.Long && long.TryParse(argument.Value, out var longValue))
argumentDictionary.Add(argument.Key, longValue);
else
argumentDictionary.Add(argument.Key, argument.Value);
}

return Ui.Translate(Key, argumentDictionary);
}
}
}
35 changes: 25 additions & 10 deletions OpenRA.Game/Network/Order.cs
Expand Up @@ -38,7 +38,8 @@ enum OrderFields : short
ExtraData = 0x20,
TargetIsCell = 0x40,
Subject = 0x80,
Grouped = 0x100
Grouped = 0x100,
LocalizedString = 0x200,
}

static class OrderFieldsExts
Expand All @@ -63,6 +64,7 @@ public sealed class Order
public ref readonly Target Target => ref target;
public readonly Actor[] GroupedActors;

public string LocalizedString;
public string TargetString;
public CPos ExtraLocation;
public Actor[] ExtraActors;
Expand All @@ -78,12 +80,13 @@ public sealed class Order
readonly Target target;
readonly Target visualFeedbackTarget;

Order(string orderString, Actor subject, in Target target, string targetString, bool queued, Actor[] extraActors, CPos extraLocation, uint extraData, Actor[] groupedActors = null)
Order(string orderString, Actor subject, in Target target, string targetString, string localizedString, bool queued, Actor[] extraActors, CPos extraLocation, uint extraData, Actor[] groupedActors = null)
{
OrderString = orderString ?? "";
Subject = subject;
this.target = target;
TargetString = targetString;
LocalizedString = localizedString;
Queued = queued;
ExtraActors = extraActors;
ExtraLocation = extraLocation;
Expand Down Expand Up @@ -162,6 +165,7 @@ public static Order Deserialize(World world, BinaryReader r)
}

var targetString = flags.HasField(OrderFields.TargetString) ? r.ReadString() : null;
var localizedString = flags.HasField(OrderFields.LocalizedString) ? r.ReadString() : null;
var queued = flags.HasField(OrderFields.Queued);

Actor[] extraActors = null;
Expand All @@ -188,12 +192,12 @@ public static Order Deserialize(World world, BinaryReader r)
}

if (world == null)
return new Order(order, null, target, targetString, queued, extraActors, extraLocation, extraData, groupedActors);
return new Order(order, null, target, targetString, localizedString, queued, extraActors, extraLocation, extraData, groupedActors);

if (subject == null && flags.HasField(OrderFields.Subject))
return null;

return new Order(order, subject, target, targetString, queued, extraActors, extraLocation, extraData, groupedActors);
return new Order(order, subject, target, targetString, localizedString, queued, extraActors, extraLocation, extraData, groupedActors);
}

case OrderType.Handshake:
Expand Down Expand Up @@ -254,9 +258,14 @@ public static Order FromTargetString(string order, string targetString, bool isI
return new Order(order, null, false) { IsImmediate = isImmediate, TargetString = targetString };
}

public static Order FromLocalizedString(string localizedString, bool isImmediate)
{
return new Order("LocalizedMessage", null, false) { IsImmediate = isImmediate, LocalizedString = localizedString };
}

public static Order FromGroupedOrder(Order grouped, Actor subject)
{
return new Order(grouped.OrderString, subject, grouped.Target, grouped.TargetString, grouped.Queued, grouped.ExtraActors, grouped.ExtraLocation, grouped.ExtraData);
return new Order(grouped.OrderString, subject, grouped.Target, grouped.TargetString, null, grouped.Queued, grouped.ExtraActors, grouped.ExtraLocation, grouped.ExtraData);
}

public static Order Command(string text)
Expand All @@ -281,16 +290,16 @@ public static Order CancelProduction(Actor subject, string item, int count)

// For scripting special powers
public Order()
: this(null, null, Target.Invalid, null, false, null, CPos.Zero, 0) { }
: this(null, null, Target.Invalid, null, null, false, null, CPos.Zero, 0) { }

public Order(string orderString, Actor subject, bool queued, Actor[] extraActors = null, Actor[] groupedActors = null)
: this(orderString, subject, Target.Invalid, null, queued, extraActors, CPos.Zero, 0, groupedActors) { }
: this(orderString, subject, Target.Invalid, null, null, queued, extraActors, CPos.Zero, 0, groupedActors) { }

public Order(string orderString, Actor subject, in Target target, bool queued, Actor[] extraActors = null, Actor[] groupedActors = null)
: this(orderString, subject, target, null, queued, extraActors, CPos.Zero, 0, groupedActors) { }
: this(orderString, subject, target, null, null, queued, extraActors, CPos.Zero, 0, groupedActors) { }

public Order(string orderString, Actor subject, Target target, Target visualFeedbackTarget, bool queued)
: this(orderString, subject, target, null, queued, null, CPos.Zero, 0, null)
: this(orderString, subject, target, null, null, queued, null, CPos.Zero, 0, null)
{
this.visualFeedbackTarget = visualFeedbackTarget;
}
Expand All @@ -301,7 +310,7 @@ public byte[] Serialize()
if (Type == OrderType.Handshake)
minLength += TargetString.Length + 1;
else if (Type == OrderType.Fields)
minLength += 4 + 2 + 13 + (TargetString != null ? TargetString.Length + 1 : 0) + 4 + 4 + 4;
minLength += 4 + 2 + 13 + (TargetString != null ? TargetString.Length + 1 : 0) + (LocalizedString != null ? LocalizedString.Length + 1 : 0) + 4 + 4 + 4;

if (ExtraActors != null)
minLength += ExtraActors.Length * 4;
Expand Down Expand Up @@ -354,6 +363,9 @@ public byte[] Serialize()
if (Target.SerializableCell != null)
fields |= OrderFields.TargetIsCell;

if (LocalizedString != null)
fields |= OrderFields.LocalizedString;

w.Write((short)fields);

if (fields.HasField(OrderFields.Subject))
Expand Down Expand Up @@ -391,6 +403,9 @@ public byte[] Serialize()
if (fields.HasField(OrderFields.TargetString))
w.Write(TargetString);

if (fields.HasField(OrderFields.LocalizedString))
w.Write(LocalizedString);

if (fields.HasField(OrderFields.ExtraActors))
{
w.Write(ExtraActors.Length);
Expand Down
17 changes: 17 additions & 0 deletions OpenRA.Game/Network/UnitOrders.cs
Expand Up @@ -13,6 +13,7 @@
using System.Linq;
using OpenRA.Server;
using OpenRA.Traits;
using OpenRA.Widgets;

namespace OpenRA.Network
{
Expand All @@ -34,6 +35,22 @@ internal static void ProcessOrder(OrderManager orderManager, World world, int cl
TextNotificationsManager.AddSystemLine(order.TargetString);
break;

// Localized server message
case "LocalizedMessage":
{
if (string.IsNullOrEmpty(order.LocalizedString))
break;

var yaml = MiniYaml.FromString(order.LocalizedString);
foreach (var node in yaml)
{
var localizedMessage = new LocalizedMessage(node.Value);
TextNotificationsManager.AddSystemLine(localizedMessage.Translate());
}

break;
}

case "DisableChatEntry":
{
// Order must originate from the server
Expand Down
2 changes: 1 addition & 1 deletion OpenRA.Game/Server/Connection.cs
Expand Up @@ -171,7 +171,7 @@ void SendReceiveLoop(object s)
var sent = socket.Send(data, start, length - start, SocketFlags.None, out var error);
if (error == SocketError.WouldBlock)
{
Log.Write("server", "Non-blocking send of {0} bytes failed. Falling back to blocking send.", length - start);
Log.Write("server", $"Non-blocking send of {length - start} bytes failed. Falling back to blocking send.");
socket.Blocking = true;
sent = socket.Send(data, start, length - start, SocketFlags.None);
socket.Blocking = false;
Expand Down
2 changes: 1 addition & 1 deletion OpenRA.Game/Server/ProtocolVersion.cs
Expand Up @@ -77,6 +77,6 @@ public static class ProtocolVersion
// The protocol for server and world orders
// This applies after the handshake has completed, and is provided to support
// alternative server implementations that wish to support multiple versions in parallel
public const int Orders = 18;
public const int Orders = 19;
}
}

0 comments on commit 137a0d8

Please sign in to comment.