Fix IDE0055
This rule no longer appears to be buggy, so enforce it. Some of the automated fixes are adjusted in order to improve the result. #pragma directives have no option to control indentation, so remove them where possible.
This commit is contained in:
committed by
Pavel Penev
parent
60cbf79c9b
commit
360f24f609
@@ -427,10 +427,7 @@ dotnet_diagnostic.IDE0077.severity = warning
|
|||||||
|
|
||||||
### Formatting Rules (IDE0055)
|
### Formatting Rules (IDE0055)
|
||||||
### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0055
|
### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0055
|
||||||
|
dotnet_diagnostic.IDE0055.severity = warning
|
||||||
# We may eventually wish to enforce this rule, however some existing formatting conflicts with the rule despite being reasonable.
|
|
||||||
# Additionally, the rule is buggy and likes to report spuriously after invoking Format Document in the IDE.
|
|
||||||
dotnet_diagnostic.IDE0055.severity = none
|
|
||||||
|
|
||||||
#dotnet_sort_system_directives_first = true
|
#dotnet_sort_system_directives_first = true
|
||||||
#dotnet_separate_import_directive_groups = false
|
#dotnet_separate_import_directive_groups = false
|
||||||
@@ -448,7 +445,7 @@ dotnet_diagnostic.IDE0055.severity = none
|
|||||||
#csharp_indent_labels = one_less_than_current
|
#csharp_indent_labels = one_less_than_current
|
||||||
#csharp_indent_block_contents = true
|
#csharp_indent_block_contents = true
|
||||||
#csharp_indent_braces = false
|
#csharp_indent_braces = false
|
||||||
#csharp_indent_case_contents_when_block = true
|
csharp_indent_case_contents_when_block = false
|
||||||
|
|
||||||
#csharp_space_after_cast = false
|
#csharp_space_after_cast = false
|
||||||
#csharp_space_after_keywords_in_control_flow_statements = true
|
#csharp_space_after_keywords_in_control_flow_statements = true
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ namespace OpenRA.FileFormats
|
|||||||
var length = IPAddress.NetworkToHostOrder(s.ReadInt32());
|
var length = IPAddress.NetworkToHostOrder(s.ReadInt32());
|
||||||
var type = s.ReadASCII(4);
|
var type = s.ReadASCII(4);
|
||||||
var content = s.ReadBytes(length);
|
var content = s.ReadBytes(length);
|
||||||
/*var crc = */s.ReadInt32();
|
s.ReadInt32(); // crc
|
||||||
|
|
||||||
if (!headerParsed && type != "IHDR")
|
if (!headerParsed && type != "IHDR")
|
||||||
throw new InvalidDataException("Invalid PNG file - header does not appear first.");
|
throw new InvalidDataException("Invalid PNG file - header does not appear first.");
|
||||||
@@ -76,7 +76,7 @@ namespace OpenRA.FileFormats
|
|||||||
Data = new byte[Width * Height * PixelStride];
|
Data = new byte[Width * Height * PixelStride];
|
||||||
|
|
||||||
var compression = ms.ReadUInt8();
|
var compression = ms.ReadUInt8();
|
||||||
/*var filter = */ms.ReadUInt8();
|
ms.ReadUInt8(); // filter
|
||||||
var interlace = ms.ReadUInt8();
|
var interlace = ms.ReadUInt8();
|
||||||
|
|
||||||
if (compression != 0)
|
if (compression != 0)
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ namespace OpenRA.Graphics
|
|||||||
: base("model")
|
: base("model")
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
public override ShaderVertexAttribute[] Attributes { get; } = new[]
|
public override ShaderVertexAttribute[] Attributes { get; } = new[]
|
||||||
{
|
{
|
||||||
new ShaderVertexAttribute("aVertexPosition", ShaderVertexAttributeType.Float, 3, 0),
|
new ShaderVertexAttribute("aVertexPosition", ShaderVertexAttributeType.Float, 3, 0),
|
||||||
new ShaderVertexAttribute("aVertexTexCoord", ShaderVertexAttributeType.Float, 4, 12),
|
new ShaderVertexAttribute("aVertexTexCoord", ShaderVertexAttributeType.Float, 4, 12),
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ namespace OpenRA.Graphics
|
|||||||
|
|
||||||
indexRowStride = 6 * map.MapSize.X;
|
indexRowStride = 6 * map.MapSize.X;
|
||||||
lock (IndexBuffers)
|
lock (IndexBuffers)
|
||||||
{
|
{
|
||||||
indexBufferWrapper = IndexBuffers.GetValue(world, world => new IndexBufferRc(world));
|
indexBufferWrapper = IndexBuffers.GetValue(world, world => new IndexBufferRc(world));
|
||||||
indexBufferWrapper.AddRef();
|
indexBufferWrapper.AddRef();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ namespace OpenRA.Graphics
|
|||||||
: base("combined")
|
: base("combined")
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
public override ShaderVertexAttribute[] Attributes { get; } = new[]
|
public override ShaderVertexAttribute[] Attributes { get; } = new[]
|
||||||
{
|
{
|
||||||
new ShaderVertexAttribute("aVertexPosition", ShaderVertexAttributeType.Float, 3, 0),
|
new ShaderVertexAttribute("aVertexPosition", ShaderVertexAttributeType.Float, 3, 0),
|
||||||
new ShaderVertexAttribute("aVertexTexCoord", ShaderVertexAttributeType.Float, 4, 12),
|
new ShaderVertexAttribute("aVertexTexCoord", ShaderVertexAttributeType.Float, 4, 12),
|
||||||
|
|||||||
@@ -152,8 +152,9 @@ namespace OpenRA
|
|||||||
// If a more specific init is not available, fall back to an unnamed init.
|
// If a more specific init is not available, fall back to an unnamed init.
|
||||||
// If duplicate inits are defined, take the last to match standard yaml override expectations
|
// If duplicate inits are defined, take the last to match standard yaml override expectations
|
||||||
if (info != null && !string.IsNullOrEmpty(info.InstanceName))
|
if (info != null && !string.IsNullOrEmpty(info.InstanceName))
|
||||||
return inits.LastOrDefault(i => i.InstanceName == info.InstanceName) ??
|
return
|
||||||
inits.LastOrDefault(i => string.IsNullOrEmpty(i.InstanceName));
|
inits.LastOrDefault(i => i.InstanceName == info.InstanceName) ??
|
||||||
|
inits.LastOrDefault(i => string.IsNullOrEmpty(i.InstanceName));
|
||||||
|
|
||||||
// Untagged traits will only use untagged inits
|
// Untagged traits will only use untagged inits
|
||||||
return inits.LastOrDefault(i => string.IsNullOrEmpty(i.InstanceName));
|
return inits.LastOrDefault(i => string.IsNullOrEmpty(i.InstanceName));
|
||||||
|
|||||||
@@ -58,158 +58,158 @@ namespace OpenRA.Network
|
|||||||
|
|
||||||
// Client side translated server message
|
// Client side translated server message
|
||||||
case "LocalizedMessage":
|
case "LocalizedMessage":
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(order.TargetString))
|
if (string.IsNullOrEmpty(order.TargetString))
|
||||||
break;
|
|
||||||
|
|
||||||
var yaml = MiniYaml.FromString(order.TargetString);
|
|
||||||
foreach (var node in yaml)
|
|
||||||
{
|
|
||||||
var localizedMessage = new LocalizedMessage(node.Value);
|
|
||||||
if (localizedMessage.Key == Joined)
|
|
||||||
TextNotificationsManager.AddPlayerJoinedLine(localizedMessage.Key, localizedMessage.Arguments);
|
|
||||||
else if (localizedMessage.Key == Left)
|
|
||||||
TextNotificationsManager.AddPlayerLeftLine(localizedMessage.Key, localizedMessage.Arguments);
|
|
||||||
else
|
|
||||||
TextNotificationsManager.AddSystemLine(localizedMessage.Key, localizedMessage.Arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
var yaml = MiniYaml.FromString(order.TargetString);
|
||||||
|
foreach (var node in yaml)
|
||||||
|
{
|
||||||
|
var localizedMessage = new LocalizedMessage(node.Value);
|
||||||
|
if (localizedMessage.Key == Joined)
|
||||||
|
TextNotificationsManager.AddPlayerJoinedLine(localizedMessage.Key, localizedMessage.Arguments);
|
||||||
|
else if (localizedMessage.Key == Left)
|
||||||
|
TextNotificationsManager.AddPlayerLeftLine(localizedMessage.Key, localizedMessage.Arguments);
|
||||||
|
else
|
||||||
|
TextNotificationsManager.AddSystemLine(localizedMessage.Key, localizedMessage.Arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case "DisableChatEntry":
|
case "DisableChatEntry":
|
||||||
{
|
{
|
||||||
if (OrderNotFromServerOrWorldIsReplay(clientId, world))
|
if (OrderNotFromServerOrWorldIsReplay(clientId, world))
|
||||||
break;
|
|
||||||
|
|
||||||
// Server may send MaxValue to indicate that it is disabled until further notice
|
|
||||||
if (order.ExtraData == uint.MaxValue)
|
|
||||||
TextNotificationsManager.ChatDisabledUntil = uint.MaxValue;
|
|
||||||
else
|
|
||||||
TextNotificationsManager.ChatDisabledUntil = Game.RunTime + order.ExtraData;
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
// Server may send MaxValue to indicate that it is disabled until further notice
|
||||||
|
if (order.ExtraData == uint.MaxValue)
|
||||||
|
TextNotificationsManager.ChatDisabledUntil = uint.MaxValue;
|
||||||
|
else
|
||||||
|
TextNotificationsManager.ChatDisabledUntil = Game.RunTime + order.ExtraData;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case "StartKickVote":
|
case "StartKickVote":
|
||||||
{
|
{
|
||||||
if (OrderNotFromServerOrWorldIsReplay(clientId, world))
|
if (OrderNotFromServerOrWorldIsReplay(clientId, world))
|
||||||
break;
|
|
||||||
|
|
||||||
KickVoteTarget = (int)order.ExtraData;
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
KickVoteTarget = (int)order.ExtraData;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case "EndKickVote":
|
case "EndKickVote":
|
||||||
{
|
{
|
||||||
if (OrderNotFromServerOrWorldIsReplay(clientId, world))
|
if (OrderNotFromServerOrWorldIsReplay(clientId, world))
|
||||||
break;
|
|
||||||
|
|
||||||
if (KickVoteTarget == (int)order.ExtraData)
|
|
||||||
KickVoteTarget = null;
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
if (KickVoteTarget == (int)order.ExtraData)
|
||||||
|
KickVoteTarget = null;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case "Chat":
|
case "Chat":
|
||||||
|
{
|
||||||
|
var client = orderManager.LobbyInfo.ClientWithIndex(clientId);
|
||||||
|
if (client == null)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Cut chat messages to the hard limit to avoid exploits
|
||||||
|
var message = order.TargetString;
|
||||||
|
if (message.Length > ChatMessageMaxLength)
|
||||||
|
message = order.TargetString[..ChatMessageMaxLength];
|
||||||
|
|
||||||
|
// ExtraData 0 means this is a normal chat order, everything else is team chat
|
||||||
|
if (order.ExtraData == 0)
|
||||||
{
|
{
|
||||||
var client = orderManager.LobbyInfo.ClientWithIndex(clientId);
|
var p = world?.FindPlayerByClient(client);
|
||||||
if (client == null)
|
var suffix = (p != null && p.WinState == WinState.Lost) ? " (Dead)" : "";
|
||||||
break;
|
suffix = client.IsObserver ? " (Spectator)" : suffix;
|
||||||
|
|
||||||
// Cut chat messages to the hard limit to avoid exploits
|
if (orderManager.LocalClient != null && client != orderManager.LocalClient && client.Team > 0 && client.Team == orderManager.LocalClient.Team)
|
||||||
var message = order.TargetString;
|
suffix += " (Ally)";
|
||||||
if (message.Length > ChatMessageMaxLength)
|
|
||||||
message = order.TargetString[..ChatMessageMaxLength];
|
|
||||||
|
|
||||||
// ExtraData 0 means this is a normal chat order, everything else is team chat
|
TextNotificationsManager.AddChatLine(clientId, client.Name + suffix, message, client.Color);
|
||||||
if (order.ExtraData == 0)
|
break;
|
||||||
{
|
}
|
||||||
var p = world?.FindPlayerByClient(client);
|
|
||||||
var suffix = (p != null && p.WinState == WinState.Lost) ? " (Dead)" : "";
|
|
||||||
suffix = client.IsObserver ? " (Spectator)" : suffix;
|
|
||||||
|
|
||||||
if (orderManager.LocalClient != null && client != orderManager.LocalClient && client.Team > 0 && client.Team == orderManager.LocalClient.Team)
|
// We are still in the lobby
|
||||||
suffix += " (Ally)";
|
if (world == null)
|
||||||
|
{
|
||||||
TextNotificationsManager.AddChatLine(clientId, client.Name + suffix, message, client.Color);
|
var prefix = order.ExtraData == uint.MaxValue ? "[Spectators] " : "[Team] ";
|
||||||
break;
|
if (orderManager.LocalClient != null && client.Team == orderManager.LocalClient.Team)
|
||||||
}
|
TextNotificationsManager.AddChatLine(clientId, prefix + client.Name, message, client.Color);
|
||||||
|
|
||||||
// We are still in the lobby
|
|
||||||
if (world == null)
|
|
||||||
{
|
|
||||||
var prefix = order.ExtraData == uint.MaxValue ? "[Spectators] " : "[Team] ";
|
|
||||||
if (orderManager.LocalClient != null && client.Team == orderManager.LocalClient.Team)
|
|
||||||
TextNotificationsManager.AddChatLine(clientId, prefix + client.Name, message, client.Color);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
var player = world.FindPlayerByClient(client);
|
|
||||||
var localClientIsObserver = world.IsReplay || (orderManager.LocalClient != null && orderManager.LocalClient.IsObserver)
|
|
||||||
|| (world.LocalPlayer != null && world.LocalPlayer.WinState != WinState.Undefined);
|
|
||||||
|
|
||||||
// ExtraData gives us the team number, uint.MaxValue means Spectators
|
|
||||||
if (order.ExtraData == uint.MaxValue && localClientIsObserver)
|
|
||||||
{
|
|
||||||
// Validate before adding the line
|
|
||||||
if (client.IsObserver || (player != null && player.WinState != WinState.Undefined))
|
|
||||||
TextNotificationsManager.AddChatLine(clientId, "[Spectators] " + client.Name, message, client.Color);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
var valid = client.Team == order.ExtraData && player != null && player.WinState == WinState.Undefined;
|
|
||||||
var isSameTeam = orderManager.LocalClient != null && order.ExtraData == orderManager.LocalClient.Team
|
|
||||||
&& world.LocalPlayer != null && world.LocalPlayer.WinState == WinState.Undefined;
|
|
||||||
|
|
||||||
if (valid && (isSameTeam || world.IsReplay))
|
|
||||||
TextNotificationsManager.AddChatLine(clientId, "[Team" + (world.IsReplay ? " " + order.ExtraData : "") + "] " + client.Name, message, client.Color);
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var player = world.FindPlayerByClient(client);
|
||||||
|
var localClientIsObserver = world.IsReplay || (orderManager.LocalClient != null && orderManager.LocalClient.IsObserver)
|
||||||
|
|| (world.LocalPlayer != null && world.LocalPlayer.WinState != WinState.Undefined);
|
||||||
|
|
||||||
|
// ExtraData gives us the team number, uint.MaxValue means Spectators
|
||||||
|
if (order.ExtraData == uint.MaxValue && localClientIsObserver)
|
||||||
|
{
|
||||||
|
// Validate before adding the line
|
||||||
|
if (client.IsObserver || (player != null && player.WinState != WinState.Undefined))
|
||||||
|
TextNotificationsManager.AddChatLine(clientId, "[Spectators] " + client.Name, message, client.Color);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
var valid = client.Team == order.ExtraData && player != null && player.WinState == WinState.Undefined;
|
||||||
|
var isSameTeam = orderManager.LocalClient != null && order.ExtraData == orderManager.LocalClient.Team
|
||||||
|
&& world.LocalPlayer != null && world.LocalPlayer.WinState == WinState.Undefined;
|
||||||
|
|
||||||
|
if (valid && (isSameTeam || world.IsReplay))
|
||||||
|
TextNotificationsManager.AddChatLine(clientId, "[Team" + (world.IsReplay ? " " + order.ExtraData : "") + "] " + client.Name, message, client.Color);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case "StartGame":
|
case "StartGame":
|
||||||
|
{
|
||||||
|
if (Game.ModData.MapCache[orderManager.LobbyInfo.GlobalSettings.Map].Status != MapStatus.Available)
|
||||||
{
|
{
|
||||||
if (Game.ModData.MapCache[orderManager.LobbyInfo.GlobalSettings.Map].Status != MapStatus.Available)
|
Game.Disconnect();
|
||||||
{
|
Game.LoadShellMap();
|
||||||
Game.Disconnect();
|
|
||||||
Game.LoadShellMap();
|
|
||||||
|
|
||||||
// TODO: After adding a startup error dialog, notify the replay load failure.
|
// TODO: After adding a startup error dialog, notify the replay load failure.
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(order.TargetString))
|
|
||||||
{
|
|
||||||
var data = MiniYaml.FromString(order.TargetString);
|
|
||||||
var saveLastOrdersFrame = data.FirstOrDefault(n => n.Key == "SaveLastOrdersFrame");
|
|
||||||
if (saveLastOrdersFrame != null)
|
|
||||||
orderManager.GameSaveLastFrame =
|
|
||||||
FieldLoader.GetValue<int>("saveLastOrdersFrame", saveLastOrdersFrame.Value.Value);
|
|
||||||
|
|
||||||
var saveSyncFrame = data.FirstOrDefault(n => n.Key == "SaveSyncFrame");
|
|
||||||
if (saveSyncFrame != null)
|
|
||||||
orderManager.GameSaveLastSyncFrame =
|
|
||||||
FieldLoader.GetValue<int>("SaveSyncFrame", saveSyncFrame.Value.Value);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
TextNotificationsManager.AddSystemLine(GameStarted);
|
|
||||||
|
|
||||||
Game.StartGame(orderManager.LobbyInfo.GlobalSettings.Map, WorldType.Regular);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(order.TargetString))
|
||||||
|
{
|
||||||
|
var data = MiniYaml.FromString(order.TargetString);
|
||||||
|
var saveLastOrdersFrame = data.FirstOrDefault(n => n.Key == "SaveLastOrdersFrame");
|
||||||
|
if (saveLastOrdersFrame != null)
|
||||||
|
orderManager.GameSaveLastFrame =
|
||||||
|
FieldLoader.GetValue<int>("saveLastOrdersFrame", saveLastOrdersFrame.Value.Value);
|
||||||
|
|
||||||
|
var saveSyncFrame = data.FirstOrDefault(n => n.Key == "SaveSyncFrame");
|
||||||
|
if (saveSyncFrame != null)
|
||||||
|
orderManager.GameSaveLastSyncFrame =
|
||||||
|
FieldLoader.GetValue<int>("SaveSyncFrame", saveSyncFrame.Value.Value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
TextNotificationsManager.AddSystemLine(GameStarted);
|
||||||
|
|
||||||
|
Game.StartGame(orderManager.LobbyInfo.GlobalSettings.Map, WorldType.Regular);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case "SaveTraitData":
|
case "SaveTraitData":
|
||||||
{
|
{
|
||||||
var data = MiniYaml.FromString(order.TargetString)[0];
|
var data = MiniYaml.FromString(order.TargetString)[0];
|
||||||
var traitIndex = Exts.ParseInt32Invariant(data.Key);
|
var traitIndex = Exts.ParseInt32Invariant(data.Key);
|
||||||
|
|
||||||
world?.AddGameSaveTraitData(traitIndex, data.Value);
|
world?.AddGameSaveTraitData(traitIndex, data.Value);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case "GameSaved":
|
case "GameSaved":
|
||||||
if (!orderManager.World.IsReplay)
|
if (!orderManager.World.IsReplay)
|
||||||
@@ -220,188 +220,188 @@ namespace OpenRA.Network
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case "PauseGame":
|
case "PauseGame":
|
||||||
|
{
|
||||||
|
var client = orderManager.LobbyInfo.ClientWithIndex(clientId);
|
||||||
|
if (client != null)
|
||||||
{
|
{
|
||||||
var client = orderManager.LobbyInfo.ClientWithIndex(clientId);
|
var pause = order.TargetString == "Pause";
|
||||||
if (client != null)
|
|
||||||
{
|
|
||||||
var pause = order.TargetString == "Pause";
|
|
||||||
|
|
||||||
// Prevent injected unpause orders from restarting a finished game
|
// Prevent injected unpause orders from restarting a finished game
|
||||||
if (orderManager.World.IsGameOver && !pause)
|
if (orderManager.World.IsGameOver && !pause)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (orderManager.World.Paused != pause && world != null && world.LobbyInfo.NonBotClients.Count() > 1)
|
if (orderManager.World.Paused != pause && world != null && world.LobbyInfo.NonBotClients.Count() > 1)
|
||||||
TextNotificationsManager.AddSystemLine(pause ? GamePaused : GameUnpaused, Translation.Arguments("player", client.Name));
|
TextNotificationsManager.AddSystemLine(pause ? GamePaused : GameUnpaused, Translation.Arguments("player", client.Name));
|
||||||
|
|
||||||
orderManager.World.Paused = pause;
|
orderManager.World.Paused = pause;
|
||||||
orderManager.World.PredictedPaused = pause;
|
orderManager.World.PredictedPaused = pause;
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case "HandshakeRequest":
|
case "HandshakeRequest":
|
||||||
|
{
|
||||||
|
// Switch to the server's mod if we need and are able to
|
||||||
|
var mod = Game.ModData.Manifest;
|
||||||
|
var request = HandshakeRequest.Deserialize(order.TargetString);
|
||||||
|
|
||||||
|
var externalKey = ExternalMod.MakeKey(request.Mod, request.Version);
|
||||||
|
if ((request.Mod != mod.Id || request.Version != mod.Metadata.Version) &&
|
||||||
|
Game.ExternalMods.TryGetValue(externalKey, out var external))
|
||||||
{
|
{
|
||||||
// Switch to the server's mod if we need and are able to
|
// The ConnectionFailedLogic will prompt the user to switch mods
|
||||||
var mod = Game.ModData.Manifest;
|
CurrentServerSettings.ServerExternalMod = external;
|
||||||
var request = HandshakeRequest.Deserialize(order.TargetString);
|
orderManager.Connection.Dispose();
|
||||||
|
|
||||||
var externalKey = ExternalMod.MakeKey(request.Mod, request.Version);
|
|
||||||
if ((request.Mod != mod.Id || request.Version != mod.Metadata.Version) &&
|
|
||||||
Game.ExternalMods.TryGetValue(externalKey, out var external))
|
|
||||||
{
|
|
||||||
// The ConnectionFailedLogic will prompt the user to switch mods
|
|
||||||
CurrentServerSettings.ServerExternalMod = external;
|
|
||||||
orderManager.Connection.Dispose();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
Game.Settings.Player.Name = Settings.SanitizedPlayerName(Game.Settings.Player.Name);
|
|
||||||
Game.Settings.Save();
|
|
||||||
|
|
||||||
// Otherwise send the handshake with our current settings and let the server reject us
|
|
||||||
var info = new Session.Client()
|
|
||||||
{
|
|
||||||
Name = Game.Settings.Player.Name,
|
|
||||||
PreferredColor = Game.Settings.Player.Color,
|
|
||||||
Color = Game.Settings.Player.Color,
|
|
||||||
Faction = "Random",
|
|
||||||
SpawnPoint = 0,
|
|
||||||
Team = 0,
|
|
||||||
State = Session.ClientState.Invalid
|
|
||||||
};
|
|
||||||
|
|
||||||
var localProfile = Game.LocalPlayerProfile;
|
|
||||||
var response = new HandshakeResponse()
|
|
||||||
{
|
|
||||||
Client = info,
|
|
||||||
Mod = mod.Id,
|
|
||||||
Version = mod.Metadata.Version,
|
|
||||||
Password = CurrentServerSettings.Password,
|
|
||||||
Fingerprint = localProfile.Fingerprint,
|
|
||||||
OrdersProtocol = ProtocolVersion.Orders
|
|
||||||
};
|
|
||||||
|
|
||||||
if (request.AuthToken != null && response.Fingerprint != null)
|
|
||||||
response.AuthSignature = localProfile.Sign(request.AuthToken);
|
|
||||||
|
|
||||||
orderManager.IssueOrder(new Order("HandshakeResponse", null, false)
|
|
||||||
{
|
|
||||||
Type = OrderType.Handshake,
|
|
||||||
IsImmediate = true,
|
|
||||||
TargetString = response.Serialize()
|
|
||||||
});
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Game.Settings.Player.Name = Settings.SanitizedPlayerName(Game.Settings.Player.Name);
|
||||||
|
Game.Settings.Save();
|
||||||
|
|
||||||
|
// Otherwise send the handshake with our current settings and let the server reject us
|
||||||
|
var info = new Session.Client()
|
||||||
|
{
|
||||||
|
Name = Game.Settings.Player.Name,
|
||||||
|
PreferredColor = Game.Settings.Player.Color,
|
||||||
|
Color = Game.Settings.Player.Color,
|
||||||
|
Faction = "Random",
|
||||||
|
SpawnPoint = 0,
|
||||||
|
Team = 0,
|
||||||
|
State = Session.ClientState.Invalid
|
||||||
|
};
|
||||||
|
|
||||||
|
var localProfile = Game.LocalPlayerProfile;
|
||||||
|
var response = new HandshakeResponse()
|
||||||
|
{
|
||||||
|
Client = info,
|
||||||
|
Mod = mod.Id,
|
||||||
|
Version = mod.Metadata.Version,
|
||||||
|
Password = CurrentServerSettings.Password,
|
||||||
|
Fingerprint = localProfile.Fingerprint,
|
||||||
|
OrdersProtocol = ProtocolVersion.Orders
|
||||||
|
};
|
||||||
|
|
||||||
|
if (request.AuthToken != null && response.Fingerprint != null)
|
||||||
|
response.AuthSignature = localProfile.Sign(request.AuthToken);
|
||||||
|
|
||||||
|
orderManager.IssueOrder(new Order("HandshakeResponse", null, false)
|
||||||
|
{
|
||||||
|
Type = OrderType.Handshake,
|
||||||
|
IsImmediate = true,
|
||||||
|
TargetString = response.Serialize()
|
||||||
|
});
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case "ServerError":
|
case "ServerError":
|
||||||
{
|
{
|
||||||
orderManager.ServerError = order.TargetString;
|
orderManager.ServerError = order.TargetString;
|
||||||
orderManager.AuthenticationFailed = false;
|
orderManager.AuthenticationFailed = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case "AuthenticationError":
|
case "AuthenticationError":
|
||||||
{
|
{
|
||||||
// The ConnectionFailedLogic will prompt the user for the password
|
// The ConnectionFailedLogic will prompt the user for the password
|
||||||
orderManager.ServerError = order.TargetString;
|
orderManager.ServerError = order.TargetString;
|
||||||
orderManager.AuthenticationFailed = true;
|
orderManager.AuthenticationFailed = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case "SyncInfo":
|
case "SyncInfo":
|
||||||
{
|
{
|
||||||
orderManager.LobbyInfo = Session.Deserialize(order.TargetString);
|
orderManager.LobbyInfo = Session.Deserialize(order.TargetString);
|
||||||
Game.SyncLobbyInfo();
|
Game.SyncLobbyInfo();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case "SyncLobbyClients":
|
case "SyncLobbyClients":
|
||||||
|
{
|
||||||
|
var clients = new List<Session.Client>();
|
||||||
|
var nodes = MiniYaml.FromString(order.TargetString);
|
||||||
|
foreach (var node in nodes)
|
||||||
{
|
{
|
||||||
var clients = new List<Session.Client>();
|
var strings = node.Key.Split('@');
|
||||||
var nodes = MiniYaml.FromString(order.TargetString);
|
if (strings[0] == "Client")
|
||||||
foreach (var node in nodes)
|
clients.Add(Session.Client.Deserialize(node.Value));
|
||||||
{
|
|
||||||
var strings = node.Key.Split('@');
|
|
||||||
if (strings[0] == "Client")
|
|
||||||
clients.Add(Session.Client.Deserialize(node.Value));
|
|
||||||
}
|
|
||||||
|
|
||||||
orderManager.LobbyInfo.Clients = clients;
|
|
||||||
Game.SyncLobbyInfo();
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
orderManager.LobbyInfo.Clients = clients;
|
||||||
|
Game.SyncLobbyInfo();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case "SyncLobbySlots":
|
case "SyncLobbySlots":
|
||||||
|
{
|
||||||
|
var slots = new Dictionary<string, Session.Slot>();
|
||||||
|
var nodes = MiniYaml.FromString(order.TargetString);
|
||||||
|
foreach (var node in nodes)
|
||||||
{
|
{
|
||||||
var slots = new Dictionary<string, Session.Slot>();
|
var strings = node.Key.Split('@');
|
||||||
var nodes = MiniYaml.FromString(order.TargetString);
|
if (strings[0] == "Slot")
|
||||||
foreach (var node in nodes)
|
|
||||||
{
|
{
|
||||||
var strings = node.Key.Split('@');
|
var slot = Session.Slot.Deserialize(node.Value);
|
||||||
if (strings[0] == "Slot")
|
slots.Add(slot.PlayerReference, slot);
|
||||||
{
|
|
||||||
var slot = Session.Slot.Deserialize(node.Value);
|
|
||||||
slots.Add(slot.PlayerReference, slot);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
orderManager.LobbyInfo.Slots = slots;
|
|
||||||
Game.SyncLobbyInfo();
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
orderManager.LobbyInfo.Slots = slots;
|
||||||
|
Game.SyncLobbyInfo();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case "SyncLobbyGlobalSettings":
|
case "SyncLobbyGlobalSettings":
|
||||||
|
{
|
||||||
|
var nodes = MiniYaml.FromString(order.TargetString);
|
||||||
|
foreach (var node in nodes)
|
||||||
{
|
{
|
||||||
var nodes = MiniYaml.FromString(order.TargetString);
|
var strings = node.Key.Split('@');
|
||||||
foreach (var node in nodes)
|
if (strings[0] == "GlobalSettings")
|
||||||
{
|
orderManager.LobbyInfo.GlobalSettings = Session.Global.Deserialize(node.Value);
|
||||||
var strings = node.Key.Split('@');
|
|
||||||
if (strings[0] == "GlobalSettings")
|
|
||||||
orderManager.LobbyInfo.GlobalSettings = Session.Global.Deserialize(node.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
Game.SyncLobbyInfo();
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Game.SyncLobbyInfo();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case "SyncConnectionQuality":
|
case "SyncConnectionQuality":
|
||||||
|
{
|
||||||
|
var nodes = MiniYaml.FromString(order.TargetString);
|
||||||
|
foreach (var node in nodes)
|
||||||
{
|
{
|
||||||
var nodes = MiniYaml.FromString(order.TargetString);
|
var strings = node.Key.Split('@');
|
||||||
foreach (var node in nodes)
|
if (strings[0] == "ConnectionQuality")
|
||||||
{
|
{
|
||||||
var strings = node.Key.Split('@');
|
var client = orderManager.LobbyInfo.Clients.FirstOrDefault(c => c.Index == Exts.ParseInt32Invariant(strings[1]));
|
||||||
if (strings[0] == "ConnectionQuality")
|
if (client != null)
|
||||||
{
|
client.ConnectionQuality = FieldLoader.GetValue<Session.ConnectionQuality>("ConnectionQuality", node.Value.Value);
|
||||||
var client = orderManager.LobbyInfo.Clients.FirstOrDefault(c => c.Index == Exts.ParseInt32Invariant(strings[1]));
|
|
||||||
if (client != null)
|
|
||||||
client.ConnectionQuality = FieldLoader.GetValue<Session.ConnectionQuality>("ConnectionQuality", node.Value.Value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case "SyncMapPool":
|
case "SyncMapPool":
|
||||||
{
|
{
|
||||||
orderManager.ServerMapPool = FieldLoader.GetValue<HashSet<string>>("SyncMapPool", order.TargetString);
|
orderManager.ServerMapPool = FieldLoader.GetValue<HashSet<string>>("SyncMapPool", order.TargetString);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
if (world == null)
|
if (world == null)
|
||||||
break;
|
|
||||||
|
|
||||||
if (order.GroupedActors == null)
|
|
||||||
ResolveOrder(order, world, orderManager, clientId);
|
|
||||||
else
|
|
||||||
foreach (var subject in order.GroupedActors)
|
|
||||||
ResolveOrder(Order.FromGroupedOrder(order, subject), world, orderManager, clientId);
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
if (order.GroupedActors == null)
|
||||||
|
ResolveOrder(order, world, orderManager, clientId);
|
||||||
|
else
|
||||||
|
foreach (var subject in order.GroupedActors)
|
||||||
|
ResolveOrder(Order.FromGroupedOrder(order, subject), world, orderManager, clientId);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -274,7 +274,7 @@ namespace OpenRA
|
|||||||
throw new DirectoryNotFoundException(path);
|
throw new DirectoryNotFoundException(path);
|
||||||
|
|
||||||
if (!path.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal) &&
|
if (!path.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal) &&
|
||||||
!path.EndsWith(Path.AltDirectorySeparatorChar.ToString(), StringComparison.Ordinal))
|
!path.EndsWith(Path.AltDirectorySeparatorChar.ToString(), StringComparison.Ordinal))
|
||||||
path += Path.DirectorySeparatorChar;
|
path += Path.DirectorySeparatorChar;
|
||||||
|
|
||||||
engineDirAccessed = true;
|
engineDirAccessed = true;
|
||||||
|
|||||||
@@ -169,12 +169,12 @@ namespace OpenRA.Primitives
|
|||||||
|
|
||||||
byte alpha = 255;
|
byte alpha = 255;
|
||||||
if (!byte.TryParse(value.AsSpan(0, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var red)
|
if (!byte.TryParse(value.AsSpan(0, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var red)
|
||||||
|| !byte.TryParse(value.AsSpan(2, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var green)
|
|| !byte.TryParse(value.AsSpan(2, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var green)
|
||||||
|| !byte.TryParse(value.AsSpan(4, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var blue))
|
|| !byte.TryParse(value.AsSpan(4, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var blue))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (value.Length == 8
|
if (value.Length == 8
|
||||||
&& !byte.TryParse(value.AsSpan(6, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out alpha))
|
&& !byte.TryParse(value.AsSpan(6, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out alpha))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
color = FromArgb(alpha, red, green, blue);
|
color = FromArgb(alpha, red, green, blue);
|
||||||
|
|||||||
@@ -280,7 +280,8 @@ namespace OpenRA.Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}) { Name = $"Connection listener ({listener.LocalEndpoint})", IsBackground = true }.Start();
|
})
|
||||||
|
{ Name = $"Connection listener ({listener.LocalEndpoint})", IsBackground = true }.Start();
|
||||||
}
|
}
|
||||||
catch (SocketException ex)
|
catch (SocketException ex)
|
||||||
{
|
{
|
||||||
@@ -990,70 +991,42 @@ namespace OpenRA.Server
|
|||||||
switch (o.OrderString)
|
switch (o.OrderString)
|
||||||
{
|
{
|
||||||
case "Command":
|
case "Command":
|
||||||
|
{
|
||||||
|
var handledBy = serverTraits.WithInterface<IInterpretCommand>()
|
||||||
|
.FirstOrDefault(t => t.InterpretCommand(this, conn, GetClient(conn), o.TargetString));
|
||||||
|
|
||||||
|
if (handledBy == null)
|
||||||
{
|
{
|
||||||
var handledBy = serverTraits.WithInterface<IInterpretCommand>()
|
Log.Write("server", $"Unknown server command: {o.TargetString}");
|
||||||
.FirstOrDefault(t => t.InterpretCommand(this, conn, GetClient(conn), o.TargetString));
|
SendLocalizedMessageTo(conn, UnknownServerCommand, Translation.Arguments("command", o.TargetString));
|
||||||
|
|
||||||
if (handledBy == null)
|
|
||||||
{
|
|
||||||
Log.Write("server", $"Unknown server command: {o.TargetString}");
|
|
||||||
SendLocalizedMessageTo(conn, UnknownServerCommand, Translation.Arguments("command", o.TargetString));
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case "Chat":
|
case "Chat":
|
||||||
{
|
{
|
||||||
if (Type == ServerType.Local || !playerMessageTracker.IsPlayerAtFloodLimit(conn))
|
if (Type == ServerType.Local || !playerMessageTracker.IsPlayerAtFloodLimit(conn))
|
||||||
DispatchOrdersToClients(conn, 0, o.Serialize());
|
DispatchOrdersToClients(conn, 0, o.Serialize());
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case "GameSaveTraitData":
|
case "GameSaveTraitData":
|
||||||
|
{
|
||||||
|
if (GameSave != null)
|
||||||
{
|
{
|
||||||
if (GameSave != null)
|
var data = MiniYaml.FromString(o.TargetString)[0];
|
||||||
{
|
GameSave.AddTraitData(OpenRA.Exts.ParseInt32Invariant(data.Key), data.Value);
|
||||||
var data = MiniYaml.FromString(o.TargetString)[0];
|
|
||||||
GameSave.AddTraitData(OpenRA.Exts.ParseInt32Invariant(data.Key), data.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case "CreateGameSave":
|
case "CreateGameSave":
|
||||||
|
{
|
||||||
|
if (GameSave != null)
|
||||||
{
|
{
|
||||||
if (GameSave != null)
|
|
||||||
{
|
|
||||||
// Sanitize potentially malicious input
|
|
||||||
var filename = o.TargetString;
|
|
||||||
var invalidIndex = -1;
|
|
||||||
var invalidChars = Path.GetInvalidFileNameChars();
|
|
||||||
while ((invalidIndex = filename.IndexOfAny(invalidChars)) != -1)
|
|
||||||
filename = filename.Remove(invalidIndex, 1);
|
|
||||||
|
|
||||||
var baseSavePath = Path.Combine(
|
|
||||||
Platform.SupportDir,
|
|
||||||
"Saves",
|
|
||||||
ModData.Manifest.Id,
|
|
||||||
ModData.Manifest.Metadata.Version);
|
|
||||||
|
|
||||||
if (!Directory.Exists(baseSavePath))
|
|
||||||
Directory.CreateDirectory(baseSavePath);
|
|
||||||
|
|
||||||
GameSave.Save(Path.Combine(baseSavePath, filename));
|
|
||||||
DispatchServerOrdersToClients(Order.FromTargetString("GameSaved", filename, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case "LoadGameSave":
|
|
||||||
{
|
|
||||||
if (Type == ServerType.Dedicated || State >= ServerState.GameStarted)
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Sanitize potentially malicious input
|
// Sanitize potentially malicious input
|
||||||
var filename = o.TargetString;
|
var filename = o.TargetString;
|
||||||
var invalidIndex = -1;
|
var invalidIndex = -1;
|
||||||
@@ -1061,62 +1034,90 @@ namespace OpenRA.Server
|
|||||||
while ((invalidIndex = filename.IndexOfAny(invalidChars)) != -1)
|
while ((invalidIndex = filename.IndexOfAny(invalidChars)) != -1)
|
||||||
filename = filename.Remove(invalidIndex, 1);
|
filename = filename.Remove(invalidIndex, 1);
|
||||||
|
|
||||||
var savePath = Path.Combine(
|
var baseSavePath = Path.Combine(
|
||||||
Platform.SupportDir,
|
Platform.SupportDir,
|
||||||
"Saves",
|
"Saves",
|
||||||
ModData.Manifest.Id,
|
ModData.Manifest.Id,
|
||||||
ModData.Manifest.Metadata.Version,
|
ModData.Manifest.Metadata.Version);
|
||||||
filename);
|
|
||||||
|
|
||||||
GameSave = new GameSave(savePath);
|
if (!Directory.Exists(baseSavePath))
|
||||||
LobbyInfo.GlobalSettings = GameSave.GlobalSettings;
|
Directory.CreateDirectory(baseSavePath);
|
||||||
LobbyInfo.Slots = GameSave.Slots;
|
|
||||||
|
|
||||||
// Reassign clients to slots
|
GameSave.Save(Path.Combine(baseSavePath, filename));
|
||||||
// - Bot ordering is preserved
|
DispatchServerOrdersToClients(Order.FromTargetString("GameSaved", filename, true));
|
||||||
// - Humans are assigned on a first-come-first-serve basis
|
|
||||||
// - Leftover humans become spectators
|
|
||||||
|
|
||||||
// Start by removing all bots and assigning all players as spectators
|
|
||||||
foreach (var c in LobbyInfo.Clients)
|
|
||||||
{
|
|
||||||
if (c.Bot != null)
|
|
||||||
LobbyInfo.Clients.Remove(c);
|
|
||||||
else
|
|
||||||
c.Slot = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rebuild/remap the saved client state
|
|
||||||
// TODO: Multiplayer saves should leave all humans as spectators so they can manually pick slots
|
|
||||||
var adminClientIndex = LobbyInfo.Clients.First(c => c.IsAdmin).Index;
|
|
||||||
foreach (var kv in GameSave.SlotClients)
|
|
||||||
{
|
|
||||||
if (kv.Value.Bot != null)
|
|
||||||
{
|
|
||||||
var bot = new Session.Client()
|
|
||||||
{
|
|
||||||
Index = ChooseFreePlayerIndex(),
|
|
||||||
State = Session.ClientState.NotReady,
|
|
||||||
BotControllerClientIndex = adminClientIndex
|
|
||||||
};
|
|
||||||
|
|
||||||
kv.Value.ApplyTo(bot);
|
|
||||||
LobbyInfo.Clients.Add(bot);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// This will throw if the server doesn't have enough human clients to fill all player slots
|
|
||||||
// See TODO above - this isn't a problem in practice because MP saves won't use this
|
|
||||||
var client = LobbyInfo.Clients.First(c => c.Slot == null);
|
|
||||||
kv.Value.ApplyTo(client);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SyncLobbyInfo();
|
|
||||||
SyncLobbyClients();
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "LoadGameSave":
|
||||||
|
{
|
||||||
|
if (Type == ServerType.Dedicated || State >= ServerState.GameStarted)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Sanitize potentially malicious input
|
||||||
|
var filename = o.TargetString;
|
||||||
|
var invalidIndex = -1;
|
||||||
|
var invalidChars = Path.GetInvalidFileNameChars();
|
||||||
|
while ((invalidIndex = filename.IndexOfAny(invalidChars)) != -1)
|
||||||
|
filename = filename.Remove(invalidIndex, 1);
|
||||||
|
|
||||||
|
var savePath = Path.Combine(
|
||||||
|
Platform.SupportDir,
|
||||||
|
"Saves",
|
||||||
|
ModData.Manifest.Id,
|
||||||
|
ModData.Manifest.Metadata.Version,
|
||||||
|
filename);
|
||||||
|
|
||||||
|
GameSave = new GameSave(savePath);
|
||||||
|
LobbyInfo.GlobalSettings = GameSave.GlobalSettings;
|
||||||
|
LobbyInfo.Slots = GameSave.Slots;
|
||||||
|
|
||||||
|
// Reassign clients to slots
|
||||||
|
// - Bot ordering is preserved
|
||||||
|
// - Humans are assigned on a first-come-first-serve basis
|
||||||
|
// - Leftover humans become spectators
|
||||||
|
|
||||||
|
// Start by removing all bots and assigning all players as spectators
|
||||||
|
foreach (var c in LobbyInfo.Clients)
|
||||||
|
{
|
||||||
|
if (c.Bot != null)
|
||||||
|
LobbyInfo.Clients.Remove(c);
|
||||||
|
else
|
||||||
|
c.Slot = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rebuild/remap the saved client state
|
||||||
|
// TODO: Multiplayer saves should leave all humans as spectators so they can manually pick slots
|
||||||
|
var adminClientIndex = LobbyInfo.Clients.First(c => c.IsAdmin).Index;
|
||||||
|
foreach (var kv in GameSave.SlotClients)
|
||||||
|
{
|
||||||
|
if (kv.Value.Bot != null)
|
||||||
|
{
|
||||||
|
var bot = new Session.Client()
|
||||||
|
{
|
||||||
|
Index = ChooseFreePlayerIndex(),
|
||||||
|
State = Session.ClientState.NotReady,
|
||||||
|
BotControllerClientIndex = adminClientIndex
|
||||||
|
};
|
||||||
|
|
||||||
|
kv.Value.ApplyTo(bot);
|
||||||
|
LobbyInfo.Clients.Add(bot);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// This will throw if the server doesn't have enough human clients to fill all player slots
|
||||||
|
// See TODO above - this isn't a problem in practice because MP saves won't use this
|
||||||
|
var client = LobbyInfo.Clients.First(c => c.Slot == null);
|
||||||
|
kv.Value.ApplyTo(client);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SyncLobbyInfo();
|
||||||
|
SyncLobbyClients();
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1504,9 +1505,9 @@ namespace OpenRA.Server
|
|||||||
readonly int[] pingHistory;
|
readonly int[] pingHistory;
|
||||||
|
|
||||||
// TODO: future net code changes
|
// TODO: future net code changes
|
||||||
#pragma warning disable IDE0052
|
#pragma warning disable IDE0052
|
||||||
readonly byte queueLength;
|
readonly byte queueLength;
|
||||||
#pragma warning restore IDE0052
|
#pragma warning restore IDE0052
|
||||||
|
|
||||||
public ConnectionPingEvent(Connection connection, int[] pingHistory, byte queueLength)
|
public ConnectionPingEvent(Connection connection, int[] pingHistory, byte queueLength)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -157,28 +157,26 @@ namespace OpenRA
|
|||||||
var lsq = x * x + y * y + z * z + w * w;
|
var lsq = x * x + y * y + z * z + w * w;
|
||||||
|
|
||||||
// Quaternion components use 10 bits, so there's no risk of overflow
|
// Quaternion components use 10 bits, so there's no risk of overflow
|
||||||
#pragma warning disable SA1115 // Allow blank lines to visually separate matrix rows
|
|
||||||
mtx = new Int32Matrix4x4(
|
mtx = new Int32Matrix4x4(
|
||||||
lsq - 2 * (y * y + z * z),
|
lsq - 2 * (y * y + z * z),
|
||||||
2 * (x * y + z * w),
|
2 * (x * y + z * w),
|
||||||
2 * (x * z - y * w),
|
2 * (x * z - y * w),
|
||||||
0,
|
0,
|
||||||
|
/* row */
|
||||||
2 * (x * y - z * w),
|
2 * (x * y - z * w),
|
||||||
lsq - 2 * (x * x + z * z),
|
lsq - 2 * (x * x + z * z),
|
||||||
2 * (y * z + x * w),
|
2 * (y * z + x * w),
|
||||||
0,
|
0,
|
||||||
|
/* row */
|
||||||
2 * (x * z + y * w),
|
2 * (x * z + y * w),
|
||||||
2 * (y * z - x * w),
|
2 * (y * z - x * w),
|
||||||
lsq - 2 * (x * x + y * y),
|
lsq - 2 * (x * x + y * y),
|
||||||
0,
|
0,
|
||||||
|
/* row */
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
lsq);
|
lsq);
|
||||||
#pragma warning restore SA1115
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Int32Matrix4x4 AsMatrix()
|
public Int32Matrix4x4 AsMatrix()
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ namespace OpenRA.Mods.Cnc.Activities
|
|||||||
// The target may become hidden between the initial order request and the first tick (e.g. if queued)
|
// The target may become hidden between the initial order request and the first tick (e.g. if queued)
|
||||||
// Moving to any position (even if quite stale) is still better than immediately giving up
|
// Moving to any position (even if quite stale) is still better than immediately giving up
|
||||||
if ((target.Type == TargetType.Actor && target.Actor.CanBeViewedByPlayer(self.Owner))
|
if ((target.Type == TargetType.Actor && target.Actor.CanBeViewedByPlayer(self.Owner))
|
||||||
|| target.Type == TargetType.FrozenActor || target.Type == TargetType.Terrain)
|
|| target.Type == TargetType.FrozenActor || target.Type == TargetType.Terrain)
|
||||||
{
|
{
|
||||||
lastVisibleTarget = Target.FromPos(target.CenterPosition);
|
lastVisibleTarget = Target.FromPos(target.CenterPosition);
|
||||||
lastVisibleMinRange = attack.GetMinimumRangeVersusTarget(target);
|
lastVisibleMinRange = attack.GetMinimumRangeVersusTarget(target);
|
||||||
|
|||||||
@@ -171,53 +171,53 @@ namespace OpenRA.Mods.Cnc.AudioLoaders
|
|||||||
{
|
{
|
||||||
// Sound data
|
// Sound data
|
||||||
case 1:
|
case 1:
|
||||||
|
{
|
||||||
|
if (block.Length < 2)
|
||||||
|
throw new InvalidDataException("Invalid sound data block length in voc file");
|
||||||
|
var freqDiv = stream.ReadUInt8();
|
||||||
|
block.SampleBlock.Rate = GetSampleRateFromVocRate(freqDiv);
|
||||||
|
var codec = stream.ReadUInt8();
|
||||||
|
if (codec != 0)
|
||||||
|
throw new InvalidDataException("Unhandled codec used in voc file");
|
||||||
|
skip = block.Length - 2;
|
||||||
|
block.SampleBlock.Samples = skip;
|
||||||
|
block.SampleBlock.Offset = stream.Position;
|
||||||
|
|
||||||
|
// See if last block contained additional information
|
||||||
|
if (blockList.Count > 0)
|
||||||
{
|
{
|
||||||
if (block.Length < 2)
|
var b = blockList.Last();
|
||||||
throw new InvalidDataException("Invalid sound data block length in voc file");
|
if (b.Code == 8)
|
||||||
var freqDiv = stream.ReadUInt8();
|
|
||||||
block.SampleBlock.Rate = GetSampleRateFromVocRate(freqDiv);
|
|
||||||
var codec = stream.ReadUInt8();
|
|
||||||
if (codec != 0)
|
|
||||||
throw new InvalidDataException("Unhandled codec used in voc file");
|
|
||||||
skip = block.Length - 2;
|
|
||||||
block.SampleBlock.Samples = skip;
|
|
||||||
block.SampleBlock.Offset = stream.Position;
|
|
||||||
|
|
||||||
// See if last block contained additional information
|
|
||||||
if (blockList.Count > 0)
|
|
||||||
{
|
{
|
||||||
var b = blockList.Last();
|
block.SampleBlock.Rate = b.SampleBlock.Rate;
|
||||||
if (b.Code == 8)
|
blockList.Remove(b);
|
||||||
{
|
|
||||||
block.SampleBlock.Rate = b.SampleBlock.Rate;
|
|
||||||
blockList.Remove(b);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sampleRate = Math.Max(sampleRate, block.SampleBlock.Rate);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sampleRate = Math.Max(sampleRate, block.SampleBlock.Rate);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// Silence
|
// Silence
|
||||||
case 3:
|
case 3:
|
||||||
{
|
{
|
||||||
if (block.Length != 3)
|
if (block.Length != 3)
|
||||||
throw new InvalidDataException("Invalid silence block length in voc file");
|
throw new InvalidDataException("Invalid silence block length in voc file");
|
||||||
block.SampleBlock.Offset = 0;
|
block.SampleBlock.Offset = 0;
|
||||||
block.SampleBlock.Samples = stream.ReadUInt16() + 1;
|
block.SampleBlock.Samples = stream.ReadUInt16() + 1;
|
||||||
var freqDiv = stream.ReadUInt8();
|
var freqDiv = stream.ReadUInt8();
|
||||||
block.SampleBlock.Rate = GetSampleRateFromVocRate(freqDiv);
|
block.SampleBlock.Rate = GetSampleRateFromVocRate(freqDiv);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Repeat start
|
// Repeat start
|
||||||
case 6:
|
case 6:
|
||||||
{
|
{
|
||||||
if (block.Length != 2)
|
if (block.Length != 2)
|
||||||
throw new InvalidDataException("Invalid repeat start block length in voc file");
|
throw new InvalidDataException("Invalid repeat start block length in voc file");
|
||||||
block.LoopBlock.Count = stream.ReadUInt16() + 1;
|
block.LoopBlock.Count = stream.ReadUInt16() + 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Repeat end
|
// Repeat end
|
||||||
case 7:
|
case 7:
|
||||||
@@ -225,23 +225,23 @@ namespace OpenRA.Mods.Cnc.AudioLoaders
|
|||||||
|
|
||||||
// Extra info
|
// Extra info
|
||||||
case 8:
|
case 8:
|
||||||
{
|
{
|
||||||
if (block.Length != 4)
|
if (block.Length != 4)
|
||||||
throw new InvalidDataException("Invalid info block length in voc file");
|
throw new InvalidDataException("Invalid info block length in voc file");
|
||||||
int freqDiv = stream.ReadUInt16();
|
int freqDiv = stream.ReadUInt16();
|
||||||
if (freqDiv == 65536)
|
if (freqDiv == 65536)
|
||||||
throw new InvalidDataException("Invalid frequency divisor 65536 in voc file");
|
throw new InvalidDataException("Invalid frequency divisor 65536 in voc file");
|
||||||
var codec = stream.ReadUInt8();
|
var codec = stream.ReadUInt8();
|
||||||
if (codec != 0)
|
if (codec != 0)
|
||||||
throw new InvalidDataException("Unhandled codec used in voc file");
|
throw new InvalidDataException("Unhandled codec used in voc file");
|
||||||
var channels = stream.ReadUInt8() + 1;
|
var channels = stream.ReadUInt8() + 1;
|
||||||
if (channels != 1)
|
if (channels != 1)
|
||||||
throw new InvalidDataException("Unhandled number of channels in voc file");
|
throw new InvalidDataException("Unhandled number of channels in voc file");
|
||||||
block.SampleBlock.Offset = 0;
|
block.SampleBlock.Offset = 0;
|
||||||
block.SampleBlock.Samples = 0;
|
block.SampleBlock.Samples = 0;
|
||||||
block.SampleBlock.Rate = (int)(256000000L / (65536L - freqDiv));
|
block.SampleBlock.Rate = (int)(256000000L / (65536L - freqDiv));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sound data (New format)
|
// Sound data (New format)
|
||||||
case 9:
|
case 9:
|
||||||
|
|||||||
@@ -251,9 +251,7 @@ namespace OpenRA.Mods.Cnc.FileFormats
|
|||||||
*op++ = *ip++;
|
*op++ = *ip++;
|
||||||
if (t > 2)
|
if (t > 2)
|
||||||
{
|
{
|
||||||
#pragma warning disable IDE0047
|
*op++ = *ip++;
|
||||||
(*op++) = *ip++;
|
|
||||||
#pragma warning restore IDE0047
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -68,13 +68,13 @@ namespace OpenRA.Mods.Cnc.FileFormats
|
|||||||
// Decode FORM chunk
|
// Decode FORM chunk
|
||||||
if (stream.ReadASCII(4) != "FORM")
|
if (stream.ReadASCII(4) != "FORM")
|
||||||
throw new InvalidDataException("Invalid vqa (invalid FORM section)");
|
throw new InvalidDataException("Invalid vqa (invalid FORM section)");
|
||||||
/*var length = */stream.ReadUInt32();
|
stream.ReadUInt32(); // length
|
||||||
|
|
||||||
if (stream.ReadASCII(8) != "WVQAVQHD")
|
if (stream.ReadASCII(8) != "WVQAVQHD")
|
||||||
throw new InvalidDataException("Invalid vqa (not WVQAVQHD)");
|
throw new InvalidDataException("Invalid vqa (not WVQAVQHD)");
|
||||||
/*var length2 = */stream.ReadUInt32();
|
stream.ReadUInt32(); // length2
|
||||||
|
|
||||||
/*var version = */stream.ReadUInt16();
|
stream.ReadUInt16(); // version
|
||||||
videoFlags = stream.ReadUInt16();
|
videoFlags = stream.ReadUInt16();
|
||||||
FrameCount = stream.ReadUInt16();
|
FrameCount = stream.ReadUInt16();
|
||||||
Width = stream.ReadUInt16();
|
Width = stream.ReadUInt16();
|
||||||
@@ -87,20 +87,20 @@ namespace OpenRA.Mods.Cnc.FileFormats
|
|||||||
blocks = new int2(Width / blockWidth, Height / blockHeight);
|
blocks = new int2(Width / blockWidth, Height / blockHeight);
|
||||||
|
|
||||||
numColors = stream.ReadUInt16();
|
numColors = stream.ReadUInt16();
|
||||||
/*var maxBlocks = */stream.ReadUInt16();
|
stream.ReadUInt16(); // maxBlocks
|
||||||
/*var unknown1 = */stream.ReadUInt16();
|
stream.ReadUInt16(); // unknown1
|
||||||
/*var unknown2 = */stream.ReadUInt32();
|
stream.ReadUInt32(); // unknown2
|
||||||
|
|
||||||
// Audio
|
// Audio
|
||||||
SampleRate = stream.ReadUInt16();
|
SampleRate = stream.ReadUInt16();
|
||||||
AudioChannels = stream.ReadUInt8();
|
AudioChannels = stream.ReadUInt8();
|
||||||
SampleBits = stream.ReadUInt8();
|
SampleBits = stream.ReadUInt8();
|
||||||
|
|
||||||
/*var unknown3 =*/stream.ReadUInt32();
|
stream.ReadUInt32(); // unknown3
|
||||||
/*var unknown4 =*/stream.ReadUInt16();
|
stream.ReadUInt16(); // unknown4
|
||||||
/*maxCbfzSize =*/stream.ReadUInt32(); // Unreliable
|
stream.ReadUInt32(); // maxCbfzSize, unreliable
|
||||||
|
|
||||||
/*var unknown5 =*/stream.ReadUInt32();
|
stream.ReadUInt32(); // unknown5
|
||||||
|
|
||||||
if (IsHqVqa)
|
if (IsHqVqa)
|
||||||
{
|
{
|
||||||
@@ -131,8 +131,8 @@ namespace OpenRA.Mods.Cnc.FileFormats
|
|||||||
throw new NotSupportedException($"Vqa uses unknown Subtype: {type}");
|
throw new NotSupportedException($"Vqa uses unknown Subtype: {type}");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*var length = */stream.ReadUInt16();
|
stream.ReadUInt16(); // length
|
||||||
/*var unknown4 = */stream.ReadUInt16();
|
stream.ReadUInt16(); // unknown4
|
||||||
|
|
||||||
// Frame offsets
|
// Frame offsets
|
||||||
offsets = new uint[FrameCount];
|
offsets = new uint[FrameCount];
|
||||||
|
|||||||
@@ -45,13 +45,13 @@ namespace OpenRA.Mods.Cnc.FileFormats
|
|||||||
|
|
||||||
FrameCount = stream.ReadUInt16();
|
FrameCount = stream.ReadUInt16();
|
||||||
|
|
||||||
/*var x = */stream.ReadUInt16();
|
stream.ReadUInt16(); // x
|
||||||
/*var y = */stream.ReadUInt16();
|
stream.ReadUInt16(); // y
|
||||||
|
|
||||||
Width = stream.ReadUInt16();
|
Width = stream.ReadUInt16();
|
||||||
Height = stream.ReadUInt16();
|
Height = stream.ReadUInt16();
|
||||||
|
|
||||||
/*var delta = */stream.ReadUInt16(); /* + 37*/
|
stream.ReadUInt16(); // delta (+37)
|
||||||
var flags = stream.ReadUInt16();
|
var flags = stream.ReadUInt16();
|
||||||
|
|
||||||
frameOffsets = new uint[FrameCount + 2];
|
frameOffsets = new uint[FrameCount + 2];
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ namespace OpenRA.Mods.Cnc.FileSystem
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
/* var signature = */ s.ReadASCII(4);
|
s.ReadASCII(4); // signature
|
||||||
|
|
||||||
// Total archive size.
|
// Total archive size.
|
||||||
s.ReadUInt32();
|
s.ReadUInt32();
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ namespace OpenRA.Mods.Cnc.FileSystem
|
|||||||
{
|
{
|
||||||
s.Seek(offset, SeekOrigin.Begin);
|
s.Seek(offset, SeekOrigin.Begin);
|
||||||
var numFiles = s.ReadUInt16();
|
var numFiles = s.ReadUInt16();
|
||||||
/*uint dataSize = */s.ReadUInt32();
|
s.ReadUInt32(); // dataSize
|
||||||
|
|
||||||
var items = new List<PackageEntry>();
|
var items = new List<PackageEntry>();
|
||||||
for (var i = 0; i < numFiles; i++)
|
for (var i = 0; i < numFiles; i++)
|
||||||
|
|||||||
@@ -69,38 +69,38 @@ namespace OpenRA.Mods.Cnc.FileSystem
|
|||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case PackageHashType.Classic:
|
case PackageHashType.Classic:
|
||||||
{
|
{
|
||||||
for (var p = 0; p < padding; p++)
|
for (var p = 0; p < padding; p++)
|
||||||
upperPaddedName[paddedLength - 1 - p] = '\0';
|
upperPaddedName[paddedLength - 1 - p] = '\0';
|
||||||
|
|
||||||
var asciiBytes = paddedLength < 64 ? stackalloc byte[paddedLength] : new byte[paddedLength];
|
var asciiBytes = paddedLength < 64 ? stackalloc byte[paddedLength] : new byte[paddedLength];
|
||||||
Encoding.ASCII.GetBytes(upperPaddedName, asciiBytes);
|
Encoding.ASCII.GetBytes(upperPaddedName, asciiBytes);
|
||||||
|
|
||||||
var data = MemoryMarshal.Cast<byte, uint>(asciiBytes);
|
var data = MemoryMarshal.Cast<byte, uint>(asciiBytes);
|
||||||
var result = 0u;
|
var result = 0u;
|
||||||
foreach (var next in data)
|
foreach (var next in data)
|
||||||
result = ((result << 1) | (result >> 31)) + next;
|
result = ((result << 1) | (result >> 31)) + next;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
case PackageHashType.CRC32:
|
case PackageHashType.CRC32:
|
||||||
|
{
|
||||||
|
var length = name.Length;
|
||||||
|
var lengthRoundedDownToFour = length / 4 * 4;
|
||||||
|
if (length != lengthRoundedDownToFour)
|
||||||
{
|
{
|
||||||
var length = name.Length;
|
upperPaddedName[length] = (char)(length - lengthRoundedDownToFour);
|
||||||
var lengthRoundedDownToFour = length / 4 * 4;
|
for (var p = 1; p < padding; p++)
|
||||||
if (length != lengthRoundedDownToFour)
|
upperPaddedName[length + p] = upperPaddedName[lengthRoundedDownToFour];
|
||||||
{
|
|
||||||
upperPaddedName[length] = (char)(length - lengthRoundedDownToFour);
|
|
||||||
for (var p = 1; p < padding; p++)
|
|
||||||
upperPaddedName[length + p] = upperPaddedName[lengthRoundedDownToFour];
|
|
||||||
}
|
|
||||||
|
|
||||||
var asciiBytes = paddedLength < 64 ? stackalloc byte[paddedLength] : new byte[paddedLength];
|
|
||||||
Encoding.ASCII.GetBytes(upperPaddedName, asciiBytes);
|
|
||||||
|
|
||||||
return CRC32.Calculate(asciiBytes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var asciiBytes = paddedLength < 64 ? stackalloc byte[paddedLength] : new byte[paddedLength];
|
||||||
|
Encoding.ASCII.GetBytes(upperPaddedName, asciiBytes);
|
||||||
|
|
||||||
|
return CRC32.Calculate(asciiBytes);
|
||||||
|
}
|
||||||
|
|
||||||
default: throw new NotImplementedException($"Unknown hash type `{type}`");
|
default: throw new NotImplementedException($"Unknown hash type `{type}`");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ namespace OpenRA.Mods.Cnc.Graphics
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Desc("A sprite sequence that can have tileset-specific variants and has the oddities " +
|
[Desc("A sprite sequence that can have tileset-specific variants and has the oddities " +
|
||||||
"that come with first-generation Westwood titles.")]
|
"that come with first-generation Westwood titles.")]
|
||||||
public class ClassicTilesetSpecificSpriteSequence : ClassicSpriteSequence
|
public class ClassicTilesetSpecificSpriteSequence : ClassicSpriteSequence
|
||||||
{
|
{
|
||||||
[Desc("Dictionary of <tileset name>: filename to override the Filename key.")]
|
[Desc("Dictionary of <tileset name>: filename to override the Filename key.")]
|
||||||
|
|||||||
@@ -37,7 +37,8 @@ namespace OpenRA.Mods.Cnc.Graphics
|
|||||||
: this(renderer, models, pos, zOffset, camera, scale,
|
: this(renderer, models, pos, zOffset, camera, scale,
|
||||||
lightSource, lightAmbientColor, lightDiffuseColor,
|
lightSource, lightAmbientColor, lightDiffuseColor,
|
||||||
color, normals, shadow, 1f,
|
color, normals, shadow, 1f,
|
||||||
float3.Ones, TintModifiers.None) { }
|
float3.Ones, TintModifiers.None)
|
||||||
|
{ }
|
||||||
|
|
||||||
public ModelRenderable(
|
public ModelRenderable(
|
||||||
ModelRenderer renderer, IEnumerable<ModelAnimation> models, WPos pos, int zOffset, in WRot camera, float scale,
|
ModelRenderer renderer, IEnumerable<ModelAnimation> models, WPos pos, int zOffset, in WRot camera, float scale,
|
||||||
|
|||||||
@@ -172,26 +172,26 @@ namespace OpenRA.Mods.Cnc.SpriteLoaders
|
|||||||
{
|
{
|
||||||
case Format.XORPrev:
|
case Format.XORPrev:
|
||||||
case Format.XORLCW:
|
case Format.XORLCW:
|
||||||
|
{
|
||||||
|
if (h.RefImage.Data == null)
|
||||||
{
|
{
|
||||||
if (h.RefImage.Data == null)
|
++recurseDepth;
|
||||||
{
|
Decompress(h.RefImage);
|
||||||
++recurseDepth;
|
--recurseDepth;
|
||||||
Decompress(h.RefImage);
|
|
||||||
--recurseDepth;
|
|
||||||
}
|
|
||||||
|
|
||||||
h.Data = CopyImageData(h.RefImage.Data);
|
|
||||||
XORDeltaCompression.DecodeInto(shpBytes, h.Data, (int)(h.FileOffset - shpBytesFileOffset));
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h.Data = CopyImageData(h.RefImage.Data);
|
||||||
|
XORDeltaCompression.DecodeInto(shpBytes, h.Data, (int)(h.FileOffset - shpBytesFileOffset));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case Format.LCW:
|
case Format.LCW:
|
||||||
{
|
{
|
||||||
var imageBytes = new byte[Size.Width * Size.Height];
|
var imageBytes = new byte[Size.Width * Size.Height];
|
||||||
LCWCompression.DecodeInto(shpBytes, imageBytes, (int)(h.FileOffset - shpBytesFileOffset));
|
LCWCompression.DecodeInto(shpBytes, imageBytes, (int)(h.FileOffset - shpBytesFileOffset));
|
||||||
h.Data = imageBytes;
|
h.Data = imageBytes;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new InvalidDataException();
|
throw new InvalidDataException();
|
||||||
|
|||||||
@@ -196,18 +196,24 @@ namespace OpenRA.Mods.Cnc.Traits
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Activity MoveTo(CPos cell, int nearEnough = 0, Actor ignoreActor = null,
|
public Activity MoveTo(CPos cell, int nearEnough = 0, Actor ignoreActor = null,
|
||||||
bool evaluateNearestMovableCell = false, Color? targetLineColor = null) { return null; }
|
bool evaluateNearestMovableCell = false, Color? targetLineColor = null)
|
||||||
|
{ return null; }
|
||||||
public Activity MoveWithinRange(in Target target, WDist range,
|
public Activity MoveWithinRange(in Target target, WDist range,
|
||||||
WPos? initialTargetPosition = null, Color? targetLineColor = null) { return null; }
|
WPos? initialTargetPosition = null, Color? targetLineColor = null)
|
||||||
|
{ return null; }
|
||||||
public Activity MoveWithinRange(in Target target, WDist minRange, WDist maxRange,
|
public Activity MoveWithinRange(in Target target, WDist minRange, WDist maxRange,
|
||||||
WPos? initialTargetPosition = null, Color? targetLineColor = null) { return null; }
|
WPos? initialTargetPosition = null, Color? targetLineColor = null)
|
||||||
|
{ return null; }
|
||||||
public Activity MoveFollow(Actor self, in Target target, WDist minRange, WDist maxRange,
|
public Activity MoveFollow(Actor self, in Target target, WDist minRange, WDist maxRange,
|
||||||
WPos? initialTargetPosition = null, Color? targetLineColor = null) { return null; }
|
WPos? initialTargetPosition = null, Color? targetLineColor = null)
|
||||||
|
{ return null; }
|
||||||
public Activity ReturnToCell(Actor self) { return null; }
|
public Activity ReturnToCell(Actor self) { return null; }
|
||||||
public Activity MoveToTarget(Actor self, in Target target,
|
public Activity MoveToTarget(Actor self, in Target target,
|
||||||
WPos? initialTargetPosition = null, Color? targetLineColor = null) { return null; }
|
WPos? initialTargetPosition = null, Color? targetLineColor = null)
|
||||||
|
{ return null; }
|
||||||
public Activity MoveOntoTarget(Actor self, in Target target, in WVec offset,
|
public Activity MoveOntoTarget(Actor self, in Target target, in WVec offset,
|
||||||
WAngle? facing, Color? targetLineColor = null) { return null; }
|
WAngle? facing, Color? targetLineColor = null)
|
||||||
|
{ return null; }
|
||||||
public Activity MoveIntoTarget(Actor self, in Target target) { return null; }
|
public Activity MoveIntoTarget(Actor self, in Target target) { return null; }
|
||||||
public Activity LocalMove(Actor self, WPos fromPos, WPos toPos) { return null; }
|
public Activity LocalMove(Actor self, WPos fromPos, WPos toPos) { return null; }
|
||||||
|
|
||||||
|
|||||||
@@ -194,7 +194,7 @@ namespace OpenRA.Mods.Cnc.Traits
|
|||||||
var correctionTransform = Util.MatrixMultiply(translateMtx, FlipMtx);
|
var correctionTransform = Util.MatrixMultiply(translateMtx, FlipMtx);
|
||||||
var shadowCorrectionTransform = Util.MatrixMultiply(shadowTranslateMtx, ShadowScaleFlipMtx);
|
var shadowCorrectionTransform = Util.MatrixMultiply(shadowTranslateMtx, ShadowScaleFlipMtx);
|
||||||
|
|
||||||
doRender.Add((sprite.Sheet, () =>
|
void RenderFunc()
|
||||||
{
|
{
|
||||||
foreach (var m in models)
|
foreach (var m in models)
|
||||||
{
|
{
|
||||||
@@ -235,7 +235,9 @@ namespace OpenRA.Mods.Cnc.Traits
|
|||||||
ShadowAmbient, ShadowDiffuse, shadowPalette.TextureIndex, normals.TextureIndex);
|
ShadowAmbient, ShadowDiffuse, shadowPalette.TextureIndex, normals.TextureIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}));
|
}
|
||||||
|
|
||||||
|
doRender.Add((sprite.Sheet, RenderFunc));
|
||||||
|
|
||||||
var screenLightVector = Util.MatrixVectorMultiply(invShadowTransform, ZVector);
|
var screenLightVector = Util.MatrixVectorMultiply(invShadowTransform, ZVector);
|
||||||
screenLightVector = Util.MatrixVectorMultiply(cameraTransform, screenLightVector);
|
screenLightVector = Util.MatrixVectorMultiply(cameraTransform, screenLightVector);
|
||||||
|
|||||||
@@ -119,10 +119,10 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
|
|||||||
var rx = mf.ReadUInt16();
|
var rx = mf.ReadUInt16();
|
||||||
var ry = mf.ReadUInt16();
|
var ry = mf.ReadUInt16();
|
||||||
var tilenum = mf.ReadUInt16();
|
var tilenum = mf.ReadUInt16();
|
||||||
/*var zero1 = */mf.ReadInt16();
|
mf.ReadInt16(); // zero1
|
||||||
var subtile = mf.ReadUInt8();
|
var subtile = mf.ReadUInt8();
|
||||||
var z = mf.ReadUInt8();
|
var z = mf.ReadUInt8();
|
||||||
/*var zero2 = */mf.ReadUInt8();
|
mf.ReadUInt8(); // zero2
|
||||||
|
|
||||||
var uv = ToMPos(rx, ry, fullSize.X);
|
var uv = ToMPos(rx, ry, fullSize.X);
|
||||||
|
|
||||||
|
|||||||
@@ -115,8 +115,8 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
|
|||||||
|
|
||||||
var templateWidth = s.ReadUInt32();
|
var templateWidth = s.ReadUInt32();
|
||||||
var templateHeight = s.ReadUInt32();
|
var templateHeight = s.ReadUInt32();
|
||||||
/* var tileWidth = */s.ReadInt32();
|
s.ReadInt32(); // tileWidth
|
||||||
/* var tileHeight = */s.ReadInt32();
|
s.ReadInt32(); // tileHeight
|
||||||
var offsets = new uint[templateWidth * templateHeight];
|
var offsets = new uint[templateWidth * templateHeight];
|
||||||
for (var j = 0; j < offsets.Length; j++)
|
for (var j = 0; j < offsets.Length; j++)
|
||||||
offsets[j] = s.ReadUInt32();
|
offsets[j] = s.ReadUInt32();
|
||||||
|
|||||||
@@ -40,15 +40,15 @@ namespace OpenRA.Mods.Cnc.VideoLoaders
|
|||||||
if (frames <= 1) // TODO: find a better way to differentiate .shp icons
|
if (frames <= 1) // TODO: find a better way to differentiate .shp icons
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
/* var x = */ s.ReadUInt16();
|
s.ReadUInt16(); // x
|
||||||
/* var y = */ s.ReadUInt16();
|
s.ReadUInt16(); // y
|
||||||
var width = s.ReadUInt16();
|
var width = s.ReadUInt16();
|
||||||
var height = s.ReadUInt16();
|
var height = s.ReadUInt16();
|
||||||
|
|
||||||
if (width <= 0 || height <= 0)
|
if (width <= 0 || height <= 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
/*var delta = */ s.ReadUInt16(); /* + 37;*/
|
s.ReadUInt16(); // delta (+37)
|
||||||
|
|
||||||
var flags = s.ReadUInt16();
|
var flags = s.ReadUInt16();
|
||||||
|
|
||||||
@@ -58,7 +58,7 @@ namespace OpenRA.Mods.Cnc.VideoLoaders
|
|||||||
|
|
||||||
if (flags == 1)
|
if (flags == 1)
|
||||||
{
|
{
|
||||||
/* var palette = */ s.ReadBytes(768);
|
s.ReadBytes(768); // palette
|
||||||
for (var i = 0; i < offsets.Length; i++)
|
for (var i = 0; i < offsets.Length; i++)
|
||||||
offsets[i] += 768;
|
offsets[i] += 768;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ namespace OpenRA.Mods.Common.Activities
|
|||||||
// The target may become hidden between the initial order request and the first tick (e.g. if queued)
|
// The target may become hidden between the initial order request and the first tick (e.g. if queued)
|
||||||
// Moving to any position (even if quite stale) is still better than immediately giving up
|
// Moving to any position (even if quite stale) is still better than immediately giving up
|
||||||
if ((target.Type == TargetType.Actor && target.Actor.CanBeViewedByPlayer(self.Owner))
|
if ((target.Type == TargetType.Actor && target.Actor.CanBeViewedByPlayer(self.Owner))
|
||||||
|| target.Type == TargetType.FrozenActor || target.Type == TargetType.Terrain)
|
|| target.Type == TargetType.FrozenActor || target.Type == TargetType.Terrain)
|
||||||
lastVisibleTarget = Target.FromPos(target.CenterPosition);
|
lastVisibleTarget = Target.FromPos(target.CenterPosition);
|
||||||
else if (initialTargetPosition.HasValue)
|
else if (initialTargetPosition.HasValue)
|
||||||
lastVisibleTarget = Target.FromPos(initialTargetPosition.Value);
|
lastVisibleTarget = Target.FromPos(initialTargetPosition.Value);
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ namespace OpenRA.Mods.Common.Activities
|
|||||||
// The target may become hidden between the initial order request and the first tick (e.g. if queued)
|
// The target may become hidden between the initial order request and the first tick (e.g. if queued)
|
||||||
// Moving to any position (even if quite stale) is still better than immediately giving up
|
// Moving to any position (even if quite stale) is still better than immediately giving up
|
||||||
if ((target.Type == TargetType.Actor && target.Actor.CanBeViewedByPlayer(self.Owner))
|
if ((target.Type == TargetType.Actor && target.Actor.CanBeViewedByPlayer(self.Owner))
|
||||||
|| target.Type == TargetType.FrozenActor || target.Type == TargetType.Terrain)
|
|| target.Type == TargetType.FrozenActor || target.Type == TargetType.Terrain)
|
||||||
lastVisibleTarget = Target.FromPos(target.CenterPosition);
|
lastVisibleTarget = Target.FromPos(target.CenterPosition);
|
||||||
else if (initialTargetPosition.HasValue)
|
else if (initialTargetPosition.HasValue)
|
||||||
lastVisibleTarget = Target.FromPos(initialTargetPosition.Value);
|
lastVisibleTarget = Target.FromPos(initialTargetPosition.Value);
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ namespace OpenRA.Mods.Common.Activities
|
|||||||
// The target may become hidden between the initial order request and the first tick (e.g. if queued)
|
// The target may become hidden between the initial order request and the first tick (e.g. if queued)
|
||||||
// Moving to any position (even if quite stale) is still better than immediately giving up
|
// Moving to any position (even if quite stale) is still better than immediately giving up
|
||||||
if ((target.Type == TargetType.Actor && target.Actor.CanBeViewedByPlayer(self.Owner))
|
if ((target.Type == TargetType.Actor && target.Actor.CanBeViewedByPlayer(self.Owner))
|
||||||
|| target.Type == TargetType.FrozenActor || target.Type == TargetType.Terrain)
|
|| target.Type == TargetType.FrozenActor || target.Type == TargetType.Terrain)
|
||||||
{
|
{
|
||||||
lastVisibleTarget = Target.FromPos(target.CenterPosition);
|
lastVisibleTarget = Target.FromPos(target.CenterPosition);
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ namespace OpenRA.Mods.Common.Activities
|
|||||||
// The target may become hidden between the initial order request and the first tick (e.g. if queued)
|
// The target may become hidden between the initial order request and the first tick (e.g. if queued)
|
||||||
// Moving to any position (even if quite stale) is still better than immediately giving up
|
// Moving to any position (even if quite stale) is still better than immediately giving up
|
||||||
if ((target.Type == TargetType.Actor && target.Actor.CanBeViewedByPlayer(self.Owner))
|
if ((target.Type == TargetType.Actor && target.Actor.CanBeViewedByPlayer(self.Owner))
|
||||||
|| target.Type == TargetType.FrozenActor || target.Type == TargetType.Terrain)
|
|| target.Type == TargetType.FrozenActor || target.Type == TargetType.Terrain)
|
||||||
lastVisibleTarget = Target.FromPos(target.CenterPosition);
|
lastVisibleTarget = Target.FromPos(target.CenterPosition);
|
||||||
else if (initialTargetPosition.HasValue)
|
else if (initialTargetPosition.HasValue)
|
||||||
lastVisibleTarget = Target.FromPos(initialTargetPosition.Value);
|
lastVisibleTarget = Target.FromPos(initialTargetPosition.Value);
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ namespace OpenRA.Mods.Common.Activities
|
|||||||
// The target may become hidden between the initial order request and the first tick (e.g. if queued)
|
// The target may become hidden between the initial order request and the first tick (e.g. if queued)
|
||||||
// Moving to any position (even if quite stale) is still better than immediately giving up
|
// Moving to any position (even if quite stale) is still better than immediately giving up
|
||||||
if ((target.Type == TargetType.Actor && target.Actor.CanBeViewedByPlayer(self.Owner))
|
if ((target.Type == TargetType.Actor && target.Actor.CanBeViewedByPlayer(self.Owner))
|
||||||
|| target.Type == TargetType.FrozenActor || target.Type == TargetType.Terrain)
|
|| target.Type == TargetType.FrozenActor || target.Type == TargetType.Terrain)
|
||||||
{
|
{
|
||||||
lastVisibleTarget = Target.FromPos(target.CenterPosition);
|
lastVisibleTarget = Target.FromPos(target.CenterPosition);
|
||||||
SetVisibleTargetLocation(self, target);
|
SetVisibleTargetLocation(self, target);
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ namespace OpenRA.Mods.Common.Widgets
|
|||||||
{
|
{
|
||||||
editorActorPreview = editorLayer.Add(actor);
|
editorActorPreview = editorLayer.Add(actor);
|
||||||
Text = TranslationProvider.GetString(AddedActor,
|
Text = TranslationProvider.GetString(AddedActor,
|
||||||
Translation.Arguments("name", editorActorPreview.Info.Name, "id", editorActorPreview.ID));
|
Translation.Arguments("name", editorActorPreview.Info.Name, "id", editorActorPreview.ID));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Undo()
|
public void Undo()
|
||||||
|
|||||||
@@ -49,11 +49,11 @@ namespace OpenRA.Mods.Common.FileSystem
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Parse package header
|
// Parse package header
|
||||||
/*var signature = */s.ReadUInt32();
|
s.ReadUInt32(); // signature
|
||||||
s.Position += 8;
|
s.Position += 8;
|
||||||
/*var FileCount = */s.ReadUInt16();
|
s.ReadUInt16(); // FileCount
|
||||||
s.Position += 4;
|
s.Position += 4;
|
||||||
/*var ArchiveSize = */s.ReadUInt32();
|
s.ReadUInt32(); // ArchiveSize
|
||||||
s.Position += 19;
|
s.Position += 19;
|
||||||
var tocAddress = s.ReadInt32();
|
var tocAddress = s.ReadInt32();
|
||||||
s.Position += 4;
|
s.Position += 4;
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ namespace OpenRA.Mods.Common.Graphics
|
|||||||
protected static readonly SpriteSequenceField<int> Tick = new(nameof(Tick), 40);
|
protected static readonly SpriteSequenceField<int> Tick = new(nameof(Tick), 40);
|
||||||
|
|
||||||
[Desc("Value controlling the Z-order. A higher values means rendering on top of other sprites at the same position. " +
|
[Desc("Value controlling the Z-order. A higher values means rendering on top of other sprites at the same position. " +
|
||||||
"Use power of 2 values to avoid glitches.")]
|
"Use power of 2 values to avoid glitches.")]
|
||||||
protected static readonly SpriteSequenceField<WDist> ZOffset = new(nameof(ZOffset), WDist.Zero);
|
protected static readonly SpriteSequenceField<WDist> ZOffset = new(nameof(ZOffset), WDist.Zero);
|
||||||
|
|
||||||
[Desc("Additional sprite depth Z offset to apply as a function of sprite Y (0: vertical, 1: flat on terrain)")]
|
[Desc("Additional sprite depth Z offset to apply as a function of sprite Y (0: vertical, 1: flat on terrain)")]
|
||||||
|
|||||||
@@ -38,7 +38,8 @@ namespace OpenRA.Mods.Common.Graphics
|
|||||||
: this(font, pos, zOffset, color,
|
: this(font, pos, zOffset, color,
|
||||||
ChromeMetrics.Get<Color>("TextContrastColorDark"),
|
ChromeMetrics.Get<Color>("TextContrastColorDark"),
|
||||||
ChromeMetrics.Get<Color>("TextContrastColorLight"),
|
ChromeMetrics.Get<Color>("TextContrastColorLight"),
|
||||||
text) { }
|
text)
|
||||||
|
{ }
|
||||||
|
|
||||||
public WPos Pos { get; }
|
public WPos Pos { get; }
|
||||||
public int ZOffset { get; }
|
public int ZOffset { get; }
|
||||||
|
|||||||
@@ -40,7 +40,8 @@ namespace OpenRA.Mods.Common.Graphics
|
|||||||
: this(font, effectiveWorldPos, screenPos, zOffset, color,
|
: this(font, effectiveWorldPos, screenPos, zOffset, color,
|
||||||
ChromeMetrics.Get<Color>("TextContrastColorDark"),
|
ChromeMetrics.Get<Color>("TextContrastColorDark"),
|
||||||
ChromeMetrics.Get<Color>("TextContrastColorLight"),
|
ChromeMetrics.Get<Color>("TextContrastColorLight"),
|
||||||
text) { }
|
text)
|
||||||
|
{ }
|
||||||
|
|
||||||
public WPos Pos { get; }
|
public WPos Pos { get; }
|
||||||
public int ZOffset { get; }
|
public int ZOffset { get; }
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ namespace OpenRA.Mods.Common.Scripting
|
|||||||
public Actor[] NamedActors => sma.Actors.Values.ToArray();
|
public Actor[] NamedActors => sma.Actors.Values.ToArray();
|
||||||
|
|
||||||
[Desc("Returns the actor that was specified with a given name in " +
|
[Desc("Returns the actor that was specified with a given name in " +
|
||||||
"the map file (or nil, if the actor is dead or not found).")]
|
"the map file (or nil, if the actor is dead or not found).")]
|
||||||
public Actor NamedActor(string actorName)
|
public Actor NamedActor(string actorName)
|
||||||
{
|
{
|
||||||
if (!sma.Actors.TryGetValue(actorName, out var ret))
|
if (!sma.Actors.TryGetValue(actorName, out var ret))
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ namespace OpenRA.Mods.Common.Scripting
|
|||||||
public int PowerDrained => pm.PowerDrained;
|
public int PowerDrained => pm.PowerDrained;
|
||||||
|
|
||||||
[Desc("Returns the player's power state " +
|
[Desc("Returns the player's power state " +
|
||||||
"(\"Normal\", \"Low\" or \"Critical\").")]
|
"(\"Normal\", \"Low\" or \"Critical\").")]
|
||||||
public string PowerState => pm.PowerState.ToString();
|
public string PowerState => pm.PowerState.ToString();
|
||||||
|
|
||||||
[Desc("Triggers low power for the chosen amount of ticks.")]
|
[Desc("Triggers low power for the chosen amount of ticks.")]
|
||||||
|
|||||||
@@ -1192,7 +1192,7 @@ namespace OpenRA.Mods.Common.Server
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (!Exts.TryParseInt32Invariant(parts[1], out var spawnPoint)
|
if (!Exts.TryParseInt32Invariant(parts[1], out var spawnPoint)
|
||||||
|| spawnPoint < 0 || spawnPoint > server.Map.SpawnPoints.Length)
|
|| spawnPoint < 0 || spawnPoint > server.Map.SpawnPoints.Length)
|
||||||
{
|
{
|
||||||
Log.Write("server", $"Invalid spawn point: {parts[1]}");
|
Log.Write("server", $"Invalid spawn point: {parts[1]}");
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -1262,9 +1262,8 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
foreach (var cell in rallyPoint)
|
foreach (var cell in rallyPoint)
|
||||||
self.QueueActivity(new AttackMoveActivity(self, () => aircraft.MoveTo(cell, 1, evaluateNearestMovableCell: true, targetLineColor: Color.OrangeRed)));
|
self.QueueActivity(new AttackMoveActivity(self, () => aircraft.MoveTo(cell, 1, evaluateNearestMovableCell: true, targetLineColor: Color.OrangeRed)));
|
||||||
}
|
}
|
||||||
else
|
else if (self.World.Map.DistanceAboveTerrain(aircraft.CenterPosition).Length <= aircraft.LandAltitude.Length)
|
||||||
if (self.World.Map.DistanceAboveTerrain(aircraft.CenterPosition).Length <= aircraft.LandAltitude.Length)
|
QueueChild(new TakeOff(self));
|
||||||
QueueChild(new TakeOff(self));
|
|
||||||
|
|
||||||
aircraft.UnReserve();
|
aircraft.UnReserve();
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
IsAiming = CanAimAtTarget(self, OpportunityTarget, opportunityForceAttack);
|
IsAiming = CanAimAtTarget(self, OpportunityTarget, opportunityForceAttack);
|
||||||
|
|
||||||
if (!IsAiming && Info.OpportunityFire && autoTarget != null &&
|
if (!IsAiming && Info.OpportunityFire && autoTarget != null &&
|
||||||
!autoTarget.IsTraitDisabled && autoTarget.Stance >= UnitStance.Defend)
|
!autoTarget.IsTraitDisabled && autoTarget.Stance >= UnitStance.Defend)
|
||||||
{
|
{
|
||||||
OpportunityTarget = autoTarget.ScanForTarget(self, false, false);
|
OpportunityTarget = autoTarget.ScanForTarget(self, false, false);
|
||||||
opportunityForceAttack = false;
|
opportunityForceAttack = false;
|
||||||
@@ -265,7 +265,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
// The target may become hidden between the initial order request and the first tick (e.g. if queued)
|
// The target may become hidden between the initial order request and the first tick (e.g. if queued)
|
||||||
// Moving to any position (even if quite stale) is still better than immediately giving up
|
// Moving to any position (even if quite stale) is still better than immediately giving up
|
||||||
if ((target.Type == TargetType.Actor && target.Actor.CanBeViewedByPlayer(self.Owner))
|
if ((target.Type == TargetType.Actor && target.Actor.CanBeViewedByPlayer(self.Owner))
|
||||||
|| target.Type == TargetType.FrozenActor || target.Type == TargetType.Terrain)
|
|| target.Type == TargetType.FrozenActor || target.Type == TargetType.Terrain)
|
||||||
{
|
{
|
||||||
lastVisibleTarget = Target.FromPos(target.CenterPosition);
|
lastVisibleTarget = Target.FromPos(target.CenterPosition);
|
||||||
lastVisibleMaximumRange = attack.GetMaximumRangeVersusTarget(target);
|
lastVisibleMaximumRange = attack.GetMaximumRangeVersusTarget(target);
|
||||||
|
|||||||
@@ -204,9 +204,9 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
|
|
||||||
var explored = self.Owner.Shroud.IsExplored(location);
|
var explored = self.Owner.Shroud.IsExplored(location);
|
||||||
if (!self.World.Map.Contains(location) ||
|
if (!self.World.Map.Contains(location) ||
|
||||||
!(self.CurrentActivity is Transform || mobile.transforms.Any(t => !t.IsTraitDisabled && !t.IsTraitPaused))
|
!(self.CurrentActivity is Transform || mobile.transforms.Any(t => !t.IsTraitDisabled && !t.IsTraitPaused))
|
||||||
|| (!explored && !mobile.locomotor.Info.MoveIntoShroud)
|
|| (!explored && !mobile.locomotor.Info.MoveIntoShroud)
|
||||||
|| (explored && !CanEnterCell(self, location)))
|
|| (explored && !CanEnterCell(self, location)))
|
||||||
cursor = mobile.Info.BlockedCursor;
|
cursor = mobile.Info.BlockedCursor;
|
||||||
else if (!explored || !mobile.Info.TerrainCursors.TryGetValue(self.World.Map.GetTerrainInfo(location).Type, out cursor))
|
else if (!explored || !mobile.Info.TerrainCursors.TryGetValue(self.World.Map.GetTerrainInfo(location).Type, out cursor))
|
||||||
cursor = mobile.Info.Cursor;
|
cursor = mobile.Info.Cursor;
|
||||||
|
|||||||
@@ -409,7 +409,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
}
|
}
|
||||||
|
|
||||||
void INotifyKilled.Killed(Actor self, AttackInfo e)
|
void INotifyKilled.Killed(Actor self, AttackInfo e)
|
||||||
{
|
{
|
||||||
// IsAtGroundLevel contains Map.Contains(self.Location) check.
|
// IsAtGroundLevel contains Map.Contains(self.Location) check.
|
||||||
if (Info.EjectOnDeath && self.IsAtGroundLevel() && (!checkTerrainType || Info.UnloadTerrainTypes.Contains(self.World.Map.GetTerrainInfo(self.Location).Type)))
|
if (Info.EjectOnDeath && self.IsAtGroundLevel() && (!checkTerrainType || Info.UnloadTerrainTypes.Contains(self.World.Map.GetTerrainInfo(self.Location).Type)))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -217,8 +217,10 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
chargedToken = self.RevokeCondition(chargedToken);
|
chargedToken = self.RevokeCondition(chargedToken);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
if (chargedToken == Actor.InvalidConditionToken)
|
if (chargedToken == Actor.InvalidConditionToken)
|
||||||
chargedToken = self.GrantCondition(Info.ChargedCondition);
|
chargedToken = self.GrantCondition(Info.ChargedCondition);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
// TODO: This won't make sense for MP saves
|
// TODO: This won't make sense for MP saves
|
||||||
var localPlayer = worldRenderer.World.LocalPlayer;
|
var localPlayer = worldRenderer.World.LocalPlayer;
|
||||||
if ((localPlayer != null && localPlayer.PlayerActor != self) ||
|
if ((localPlayer != null && localPlayer.PlayerActor != self) ||
|
||||||
(localPlayer == null && self.Owner != self.World.Players.FirstOrDefault(p => p.IsBot)))
|
(localPlayer == null && self.Owner != self.World.Players.FirstOrDefault(p => p.IsBot)))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var nodes = new List<MiniYamlNode>()
|
var nodes = new List<MiniYamlNode>()
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
|
|||||||
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
|
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
|
||||||
{
|
{
|
||||||
if (actorNode.ChildrenMatching("Selection").Any(x => !x.IsRemoval())
|
if (actorNode.ChildrenMatching("Selection").Any(x => !x.IsRemoval())
|
||||||
&& !actorNode.ChildrenMatching("ControlGroups").Any())
|
&& !actorNode.ChildrenMatching("ControlGroups").Any())
|
||||||
actorNode.AddNode(new MiniYamlNodeBuilder("ControlGroups", ""));
|
actorNode.AddNode(new MiniYamlNodeBuilder("ControlGroups", ""));
|
||||||
|
|
||||||
yield break;
|
yield break;
|
||||||
|
|||||||
@@ -27,10 +27,11 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
|
|||||||
public override IEnumerable<string> AfterUpdate(ModData modData)
|
public override IEnumerable<string> AfterUpdate(ModData modData)
|
||||||
{
|
{
|
||||||
if (locations.Count > 0)
|
if (locations.Count > 0)
|
||||||
yield return "The *Palette fields have been removed from the *PlaceBuildingPreview traits.\n" +
|
yield return
|
||||||
"You may wish to inspect the following definitions and define new Alpha or\n" +
|
"The *Palette fields have been removed from the *PlaceBuildingPreview traits.\n" +
|
||||||
"LineBuildSegmentAlpha properties as appropriate to recreate transparency effects:\n" +
|
"You may wish to inspect the following definitions and define new Alpha or\n" +
|
||||||
UpdateUtils.FormatMessageList(locations);
|
"LineBuildSegmentAlpha properties as appropriate to recreate transparency effects:\n" +
|
||||||
|
UpdateUtils.FormatMessageList(locations);
|
||||||
|
|
||||||
locations.Clear();
|
locations.Clear();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,8 +24,9 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
|
|||||||
public override IEnumerable<string> AfterUpdate(ModData modData)
|
public override IEnumerable<string> AfterUpdate(ModData modData)
|
||||||
{
|
{
|
||||||
if (locations.Count > 0)
|
if (locations.Count > 0)
|
||||||
yield return "You must define new Color fields on the following traits:\n" +
|
yield return
|
||||||
UpdateUtils.FormatMessageList(locations);
|
"You must define new Color fields on the following traits:\n" +
|
||||||
|
UpdateUtils.FormatMessageList(locations);
|
||||||
|
|
||||||
locations.Clear();
|
locations.Clear();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,9 +56,10 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
|
|||||||
|
|
||||||
if (requiredMetadata.Count != 0)
|
if (requiredMetadata.Count != 0)
|
||||||
{
|
{
|
||||||
yield return $"The ExplicitSequenceFilenames rule requires {requiredMetadata.JoinWith(", ")}\n" +
|
yield return
|
||||||
"to be defined under the SpriteSequenceFormat definition in mod.yaml.\n" +
|
$"The ExplicitSequenceFilenames rule requires {requiredMetadata.JoinWith(", ")}\n" +
|
||||||
"Add these definitions back and run the update rule again.";
|
"to be defined under the SpriteSequenceFormat definition in mod.yaml.\n" +
|
||||||
|
"Add these definitions back and run the update rule again.";
|
||||||
disabled = true;
|
disabled = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -457,8 +458,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
|
|||||||
|
|
||||||
var overrideFilename = filename;
|
var overrideFilename = filename;
|
||||||
if (useTilesetCode)
|
if (useTilesetCode)
|
||||||
overrideFilename = filename[..1] + tilesetCodes[sequenceTileset] +
|
overrideFilename = filename[..1] + tilesetCodes[sequenceTileset] + filename[2..];
|
||||||
filename[2..];
|
|
||||||
|
|
||||||
if (addExtension)
|
if (addExtension)
|
||||||
overrideFilename += useTilesetExtension ? tilesetExtensions[sequenceTileset] : defaultSpriteExtension;
|
overrideFilename += useTilesetExtension ? tilesetExtensions[sequenceTileset] : defaultSpriteExtension;
|
||||||
|
|||||||
@@ -26,9 +26,10 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
|
|||||||
public override IEnumerable<string> AfterUpdate(ModData modData)
|
public override IEnumerable<string> AfterUpdate(ModData modData)
|
||||||
{
|
{
|
||||||
if (locations.Count > 0)
|
if (locations.Count > 0)
|
||||||
yield return "The PaletteFromEmbeddedSpritePalette trait no longer references a sequence.\n" +
|
yield return
|
||||||
"You must manually define Filename (and Frame if needed) on the following actors:\n" +
|
"The PaletteFromEmbeddedSpritePalette trait no longer references a sequence.\n" +
|
||||||
UpdateUtils.FormatMessageList(locations);
|
"You must manually define Filename (and Frame if needed) on the following actors:\n" +
|
||||||
|
UpdateUtils.FormatMessageList(locations);
|
||||||
|
|
||||||
locations.Clear();
|
locations.Clear();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,9 +79,7 @@ namespace OpenRA.Mods.Common.UtilityCommands
|
|||||||
}
|
}
|
||||||
|
|
||||||
var png = new Png(pngData, SpriteFrameType.Indexed8, frameSize.Width, frameSize.Height, palColors);
|
var png = new Png(pngData, SpriteFrameType.Indexed8, frameSize.Width, frameSize.Height, palColors);
|
||||||
#pragma warning disable SA1003
|
|
||||||
png.Save($"{prefix}-{count++:D4}.png");
|
png.Save($"{prefix}-{count++:D4}.png");
|
||||||
#pragma warning restore SA1003
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine("Saved {0}-[0..{1}].png", prefix, count - 1);
|
Console.WriteLine("Saved {0}-[0..{1}].png", prefix, count - 1);
|
||||||
|
|||||||
@@ -73,9 +73,7 @@ namespace OpenRA.Mods.Common.Widgets
|
|||||||
{
|
{
|
||||||
for (var s = 0; s < 256; s++)
|
for (var s = 0; s < 256; s++)
|
||||||
{
|
{
|
||||||
#pragma warning disable IDE0047
|
*(c + s * 256 + v) = Color.FromAhsv(newHue.Value, 1 - s / 255f, v / 255f).ToArgb();
|
||||||
(*(c + s * 256 + v)) = Color.FromAhsv(newHue.Value, 1 - s / 255f, v / 255f).ToArgb();
|
|
||||||
#pragma warning restore IDE0047
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,9 +39,7 @@ namespace OpenRA.Mods.Common.Widgets
|
|||||||
var c = (int*)cc;
|
var c = (int*)cc;
|
||||||
for (var h = 0; h < 256; h++)
|
for (var h = 0; h < 256; h++)
|
||||||
{
|
{
|
||||||
#pragma warning disable IDE0047
|
*(c + 0 * 256 + h) = Color.FromAhsv(h / 255f, 1, 1).ToArgb();
|
||||||
(*(c + 0 * 256 + h)) = Color.FromAhsv(h / 255f, 1, 1).ToArgb();
|
|
||||||
#pragma warning restore IDE0047
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,24 +65,24 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
|||||||
labelText = viewport.ResourceTooltip;
|
labelText = viewport.ResourceTooltip;
|
||||||
break;
|
break;
|
||||||
case WorldTooltipType.Actor:
|
case WorldTooltipType.Actor:
|
||||||
{
|
{
|
||||||
o = viewport.ActorTooltip.Owner;
|
o = viewport.ActorTooltip.Owner;
|
||||||
showOwner = o != null && !o.NonCombatant && viewport.ActorTooltip.TooltipInfo.IsOwnerRowVisible;
|
showOwner = o != null && !o.NonCombatant && viewport.ActorTooltip.TooltipInfo.IsOwnerRowVisible;
|
||||||
|
|
||||||
var stance = o == null || world.RenderPlayer == null ? PlayerRelationship.None : o.RelationshipWith(world.RenderPlayer);
|
var stance = o == null || world.RenderPlayer == null ? PlayerRelationship.None : o.RelationshipWith(world.RenderPlayer);
|
||||||
labelText = viewport.ActorTooltip.TooltipInfo.TooltipForPlayerStance(stance);
|
labelText = viewport.ActorTooltip.TooltipInfo.TooltipForPlayerStance(stance);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case WorldTooltipType.FrozenActor:
|
case WorldTooltipType.FrozenActor:
|
||||||
{
|
{
|
||||||
o = viewport.FrozenActorTooltip.TooltipOwner;
|
o = viewport.FrozenActorTooltip.TooltipOwner;
|
||||||
showOwner = o != null && !o.NonCombatant && viewport.FrozenActorTooltip.TooltipInfo.IsOwnerRowVisible;
|
showOwner = o != null && !o.NonCombatant && viewport.FrozenActorTooltip.TooltipInfo.IsOwnerRowVisible;
|
||||||
|
|
||||||
var stance = o == null || world.RenderPlayer == null ? PlayerRelationship.None : o.RelationshipWith(world.RenderPlayer);
|
var stance = o == null || world.RenderPlayer == null ? PlayerRelationship.None : o.RelationshipWith(world.RenderPlayer);
|
||||||
labelText = viewport.FrozenActorTooltip.TooltipInfo.TooltipForPlayerStance(stance);
|
labelText = viewport.FrozenActorTooltip.TooltipInfo.TooltipForPlayerStance(stance);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (viewport.ActorTooltipExtra != null)
|
if (viewport.ActorTooltipExtra != null)
|
||||||
|
|||||||
@@ -749,8 +749,9 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
|||||||
var players = item.GetOrNull<LabelWithTooltipWidget>("PLAYERS");
|
var players = item.GetOrNull<LabelWithTooltipWidget>("PLAYERS");
|
||||||
if (players != null)
|
if (players != null)
|
||||||
{
|
{
|
||||||
var label = $"{game.Players + game.Bots} / {game.MaxPlayers + game.Bots}"
|
var label =
|
||||||
+ (game.Spectators > 0 ? $" + {game.Spectators}" : "");
|
$"{game.Players + game.Bots} / {game.MaxPlayers + game.Bots}"
|
||||||
|
+ (game.Spectators > 0 ? $" + {game.Spectators}" : "");
|
||||||
|
|
||||||
var color = canJoin ? players.TextColor : incompatibleGameColor;
|
var color = canJoin ? players.TextColor : incompatibleGameColor;
|
||||||
players.GetText = () => label;
|
players.GetText = () => label;
|
||||||
|
|||||||
@@ -79,147 +79,87 @@ namespace OpenRA.Platforms.Default
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case SDL.SDL_EventType.SDL_WINDOWEVENT:
|
case SDL.SDL_EventType.SDL_WINDOWEVENT:
|
||||||
|
{
|
||||||
|
switch (e.window.windowEvent)
|
||||||
{
|
{
|
||||||
switch (e.window.windowEvent)
|
case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_FOCUS_LOST:
|
||||||
{
|
device.HasInputFocus = false;
|
||||||
case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_FOCUS_LOST:
|
break;
|
||||||
device.HasInputFocus = false;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_FOCUS_GAINED:
|
case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_FOCUS_GAINED:
|
||||||
device.HasInputFocus = true;
|
device.HasInputFocus = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Triggered when moving between displays with different DPI settings
|
// Triggered when moving between displays with different DPI settings
|
||||||
case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_SIZE_CHANGED:
|
case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_SIZE_CHANGED:
|
||||||
device.WindowSizeChanged();
|
device.WindowSizeChanged();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_HIDDEN:
|
case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_HIDDEN:
|
||||||
case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_MINIMIZED:
|
case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_MINIMIZED:
|
||||||
device.IsSuspended = true;
|
device.IsSuspended = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_EXPOSED:
|
case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_EXPOSED:
|
||||||
case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_SHOWN:
|
case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_SHOWN:
|
||||||
case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_MAXIMIZED:
|
case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_MAXIMIZED:
|
||||||
case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_RESTORED:
|
case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_RESTORED:
|
||||||
device.IsSuspended = false;
|
device.IsSuspended = false;
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case SDL.SDL_EventType.SDL_MOUSEBUTTONDOWN:
|
case SDL.SDL_EventType.SDL_MOUSEBUTTONDOWN:
|
||||||
case SDL.SDL_EventType.SDL_MOUSEBUTTONUP:
|
case SDL.SDL_EventType.SDL_MOUSEBUTTONUP:
|
||||||
|
{
|
||||||
|
// Mouse 1, Mouse 2 and Mouse 3 are handled as mouse inputs
|
||||||
|
// Mouse 4 and Mouse 5 are treated as (pseudo) keyboard inputs
|
||||||
|
if (e.button.button == SDL.SDL_BUTTON_LEFT ||
|
||||||
|
e.button.button == SDL.SDL_BUTTON_MIDDLE ||
|
||||||
|
e.button.button == SDL.SDL_BUTTON_RIGHT)
|
||||||
{
|
{
|
||||||
// Mouse 1, Mouse 2 and Mouse 3 are handled as mouse inputs
|
if (pendingMotion != null)
|
||||||
// Mouse 4 and Mouse 5 are treated as (pseudo) keyboard inputs
|
|
||||||
if (e.button.button == SDL.SDL_BUTTON_LEFT ||
|
|
||||||
e.button.button == SDL.SDL_BUTTON_MIDDLE ||
|
|
||||||
e.button.button == SDL.SDL_BUTTON_RIGHT)
|
|
||||||
{
|
{
|
||||||
if (pendingMotion != null)
|
inputHandler.OnMouseInput(pendingMotion.Value);
|
||||||
{
|
pendingMotion = null;
|
||||||
inputHandler.OnMouseInput(pendingMotion.Value);
|
|
||||||
pendingMotion = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var button = MakeButton(e.button.button);
|
|
||||||
|
|
||||||
if (e.type == SDL.SDL_EventType.SDL_MOUSEBUTTONDOWN)
|
|
||||||
lastButtonBits |= button;
|
|
||||||
else
|
|
||||||
lastButtonBits &= ~button;
|
|
||||||
|
|
||||||
var input = lockedMousePosition ?? new int2(e.button.x, e.button.y);
|
|
||||||
var pos = EventPosition(device, input.X, input.Y);
|
|
||||||
|
|
||||||
if (e.type == SDL.SDL_EventType.SDL_MOUSEBUTTONDOWN)
|
|
||||||
inputHandler.OnMouseInput(new MouseInput(
|
|
||||||
MouseInputEvent.Down, button, pos, int2.Zero, mods,
|
|
||||||
MultiTapDetection.DetectFromMouse(e.button.button, pos)));
|
|
||||||
else
|
|
||||||
inputHandler.OnMouseInput(new MouseInput(
|
|
||||||
MouseInputEvent.Up, button, pos, int2.Zero, mods,
|
|
||||||
MultiTapDetection.InfoFromMouse(e.button.button)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e.button.button == SDL.SDL_BUTTON_X1 ||
|
var button = MakeButton(e.button.button);
|
||||||
e.button.button == SDL.SDL_BUTTON_X2)
|
|
||||||
{
|
|
||||||
Keycode keyCode;
|
|
||||||
|
|
||||||
if (e.button.button == SDL.SDL_BUTTON_X1)
|
if (e.type == SDL.SDL_EventType.SDL_MOUSEBUTTONDOWN)
|
||||||
keyCode = Keycode.MOUSE4;
|
lastButtonBits |= button;
|
||||||
else
|
else
|
||||||
keyCode = Keycode.MOUSE5;
|
lastButtonBits &= ~button;
|
||||||
|
|
||||||
var type = e.type == SDL.SDL_EventType.SDL_MOUSEBUTTONDOWN ?
|
var input = lockedMousePosition ?? new int2(e.button.x, e.button.y);
|
||||||
KeyInputEvent.Down : KeyInputEvent.Up;
|
|
||||||
|
|
||||||
var tapCount = e.type == SDL.SDL_EventType.SDL_MOUSEBUTTONDOWN ?
|
|
||||||
MultiTapDetection.DetectFromKeyboard(keyCode, mods) :
|
|
||||||
MultiTapDetection.InfoFromKeyboard(keyCode, mods);
|
|
||||||
|
|
||||||
var keyEvent = new KeyInput
|
|
||||||
{
|
|
||||||
Event = type,
|
|
||||||
Key = keyCode,
|
|
||||||
Modifiers = mods,
|
|
||||||
UnicodeChar = '?',
|
|
||||||
MultiTapCount = tapCount,
|
|
||||||
IsRepeat = e.key.repeat != 0
|
|
||||||
};
|
|
||||||
inputHandler.OnKeyInput(keyEvent);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case SDL.SDL_EventType.SDL_MOUSEMOTION:
|
|
||||||
{
|
|
||||||
var mousePos = new int2(e.motion.x, e.motion.y);
|
|
||||||
var input = lockedMousePosition ?? mousePos;
|
|
||||||
var pos = EventPosition(device, input.X, input.Y);
|
var pos = EventPosition(device, input.X, input.Y);
|
||||||
|
|
||||||
var delta = lockedMousePosition == null
|
if (e.type == SDL.SDL_EventType.SDL_MOUSEBUTTONDOWN)
|
||||||
? EventPosition(device, e.motion.xrel, e.motion.yrel)
|
inputHandler.OnMouseInput(new MouseInput(
|
||||||
: mousePos - lockedMousePosition.Value;
|
MouseInputEvent.Down, button, pos, int2.Zero, mods,
|
||||||
|
MultiTapDetection.DetectFromMouse(e.button.button, pos)));
|
||||||
pendingMotion = new MouseInput(
|
else
|
||||||
MouseInputEvent.Move, lastButtonBits, pos, delta, mods, 0);
|
inputHandler.OnMouseInput(new MouseInput(
|
||||||
|
MouseInputEvent.Up, button, pos, int2.Zero, mods,
|
||||||
break;
|
MultiTapDetection.InfoFromMouse(e.button.button)));
|
||||||
}
|
}
|
||||||
|
|
||||||
case SDL.SDL_EventType.SDL_MOUSEWHEEL:
|
if (e.button.button == SDL.SDL_BUTTON_X1 ||
|
||||||
|
e.button.button == SDL.SDL_BUTTON_X2)
|
||||||
{
|
{
|
||||||
SDL.SDL_GetMouseState(out var x, out var y);
|
Keycode keyCode;
|
||||||
|
|
||||||
var pos = EventPosition(device, x, y);
|
if (e.button.button == SDL.SDL_BUTTON_X1)
|
||||||
inputHandler.OnMouseInput(new MouseInput(MouseInputEvent.Scroll, MouseButton.None, pos, new int2(0, e.wheel.y), mods, 0));
|
keyCode = Keycode.MOUSE4;
|
||||||
|
else
|
||||||
|
keyCode = Keycode.MOUSE5;
|
||||||
|
|
||||||
break;
|
var type = e.type == SDL.SDL_EventType.SDL_MOUSEBUTTONDOWN ?
|
||||||
}
|
|
||||||
|
|
||||||
case SDL.SDL_EventType.SDL_TEXTINPUT:
|
|
||||||
{
|
|
||||||
var rawBytes = new byte[SDL.SDL_TEXTINPUTEVENT_TEXT_SIZE];
|
|
||||||
unsafe { Marshal.Copy((IntPtr)e.text.text, rawBytes, 0, SDL.SDL_TEXTINPUTEVENT_TEXT_SIZE); }
|
|
||||||
inputHandler.OnTextInput(Encoding.UTF8.GetString(rawBytes, 0, Array.IndexOf(rawBytes, (byte)0)));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case SDL.SDL_EventType.SDL_KEYDOWN:
|
|
||||||
case SDL.SDL_EventType.SDL_KEYUP:
|
|
||||||
{
|
|
||||||
var keyCode = (Keycode)e.key.keysym.sym;
|
|
||||||
var type = e.type == SDL.SDL_EventType.SDL_KEYDOWN ?
|
|
||||||
KeyInputEvent.Down : KeyInputEvent.Up;
|
KeyInputEvent.Down : KeyInputEvent.Up;
|
||||||
|
|
||||||
var tapCount = e.type == SDL.SDL_EventType.SDL_KEYDOWN ?
|
var tapCount = e.type == SDL.SDL_EventType.SDL_MOUSEBUTTONDOWN ?
|
||||||
MultiTapDetection.DetectFromKeyboard(keyCode, mods) :
|
MultiTapDetection.DetectFromKeyboard(keyCode, mods) :
|
||||||
MultiTapDetection.InfoFromKeyboard(keyCode, mods);
|
MultiTapDetection.InfoFromKeyboard(keyCode, mods);
|
||||||
|
|
||||||
@@ -228,20 +168,80 @@ namespace OpenRA.Platforms.Default
|
|||||||
Event = type,
|
Event = type,
|
||||||
Key = keyCode,
|
Key = keyCode,
|
||||||
Modifiers = mods,
|
Modifiers = mods,
|
||||||
UnicodeChar = (char)e.key.keysym.sym,
|
UnicodeChar = '?',
|
||||||
MultiTapCount = tapCount,
|
MultiTapCount = tapCount,
|
||||||
IsRepeat = e.key.repeat != 0
|
IsRepeat = e.key.repeat != 0
|
||||||
};
|
};
|
||||||
|
inputHandler.OnKeyInput(keyEvent);
|
||||||
// Special case workaround for windows users
|
|
||||||
if (e.key.keysym.sym == SDL.SDL_Keycode.SDLK_F4 && mods.HasModifier(Modifiers.Alt) &&
|
|
||||||
Platform.CurrentPlatform == PlatformType.Windows)
|
|
||||||
Game.Exit();
|
|
||||||
else
|
|
||||||
inputHandler.OnKeyInput(keyEvent);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SDL.SDL_EventType.SDL_MOUSEMOTION:
|
||||||
|
{
|
||||||
|
var mousePos = new int2(e.motion.x, e.motion.y);
|
||||||
|
var input = lockedMousePosition ?? mousePos;
|
||||||
|
var pos = EventPosition(device, input.X, input.Y);
|
||||||
|
|
||||||
|
var delta = lockedMousePosition == null
|
||||||
|
? EventPosition(device, e.motion.xrel, e.motion.yrel)
|
||||||
|
: mousePos - lockedMousePosition.Value;
|
||||||
|
|
||||||
|
pendingMotion = new MouseInput(
|
||||||
|
MouseInputEvent.Move, lastButtonBits, pos, delta, mods, 0);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SDL.SDL_EventType.SDL_MOUSEWHEEL:
|
||||||
|
{
|
||||||
|
SDL.SDL_GetMouseState(out var x, out var y);
|
||||||
|
|
||||||
|
var pos = EventPosition(device, x, y);
|
||||||
|
inputHandler.OnMouseInput(new MouseInput(MouseInputEvent.Scroll, MouseButton.None, pos, new int2(0, e.wheel.y), mods, 0));
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SDL.SDL_EventType.SDL_TEXTINPUT:
|
||||||
|
{
|
||||||
|
var rawBytes = new byte[SDL.SDL_TEXTINPUTEVENT_TEXT_SIZE];
|
||||||
|
unsafe { Marshal.Copy((IntPtr)e.text.text, rawBytes, 0, SDL.SDL_TEXTINPUTEVENT_TEXT_SIZE); }
|
||||||
|
inputHandler.OnTextInput(Encoding.UTF8.GetString(rawBytes, 0, Array.IndexOf(rawBytes, (byte)0)));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SDL.SDL_EventType.SDL_KEYDOWN:
|
||||||
|
case SDL.SDL_EventType.SDL_KEYUP:
|
||||||
|
{
|
||||||
|
var keyCode = (Keycode)e.key.keysym.sym;
|
||||||
|
var type = e.type == SDL.SDL_EventType.SDL_KEYDOWN ?
|
||||||
|
KeyInputEvent.Down : KeyInputEvent.Up;
|
||||||
|
|
||||||
|
var tapCount = e.type == SDL.SDL_EventType.SDL_KEYDOWN ?
|
||||||
|
MultiTapDetection.DetectFromKeyboard(keyCode, mods) :
|
||||||
|
MultiTapDetection.InfoFromKeyboard(keyCode, mods);
|
||||||
|
|
||||||
|
var keyEvent = new KeyInput
|
||||||
|
{
|
||||||
|
Event = type,
|
||||||
|
Key = keyCode,
|
||||||
|
Modifiers = mods,
|
||||||
|
UnicodeChar = (char)e.key.keysym.sym,
|
||||||
|
MultiTapCount = tapCount,
|
||||||
|
IsRepeat = e.key.repeat != 0
|
||||||
|
};
|
||||||
|
|
||||||
|
// Special case workaround for windows users
|
||||||
|
if (e.key.keysym.sym == SDL.SDL_Keycode.SDLK_F4 && mods.HasModifier(Modifiers.Alt) &&
|
||||||
|
Platform.CurrentPlatform == PlatformType.Windows)
|
||||||
|
Game.Exit();
|
||||||
|
else
|
||||||
|
inputHandler.OnKeyInput(keyEvent);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ namespace OpenRA.Platforms.Default
|
|||||||
getCreateVertexBuffer =
|
getCreateVertexBuffer =
|
||||||
tuple =>
|
tuple =>
|
||||||
{
|
{
|
||||||
(object t, var type) = ((int, Type))tuple;
|
(object t, var type) = ((int, Type))tuple;
|
||||||
var vertexBuffer = context.GetType().GetMethod(nameof(CreateVertexBuffer)).MakeGenericMethod(type).Invoke(context, new[] { t });
|
var vertexBuffer = context.GetType().GetMethod(nameof(CreateVertexBuffer)).MakeGenericMethod(type).Invoke(context, new[] { t });
|
||||||
return typeof(ThreadedVertexBuffer<>).MakeGenericType(type).GetConstructors()[0].Invoke(new[] { this, vertexBuffer });
|
return typeof(ThreadedVertexBuffer<>).MakeGenericType(type).GetConstructors()[0].Invoke(new[] { this, vertexBuffer });
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user