Allow kicking dead players

This commit is contained in:
Gustas
2022-12-06 15:48:04 +02:00
committed by Matthias Mailänder
parent c4bd9fb7aa
commit bf00577d33
7 changed files with 106 additions and 43 deletions

View File

@@ -791,9 +791,12 @@ namespace OpenRA.Server
// Make sure the written file is not valid // Make sure the written file is not valid
// TODO: storing a serverside replay on desync would be extremely useful // TODO: storing a serverside replay on desync would be extremely useful
recorder.Metadata = null; if (recorder != null)
{
recorder.Metadata = null;
recorder.Dispose(); recorder.Dispose();
}
// Stop the recording // Stop the recording
recorder = null; recorder = null;
@@ -856,17 +859,14 @@ namespace OpenRA.Server
void RecordOrder(int frame, byte[] data, int from) void RecordOrder(int frame, byte[] data, int from)
{ {
if (recorder != null) recorder?.ReceiveFrame(from, frame, data);
{
recorder.ReceiveFrame(from, frame, data);
if (data.Length > 0 && data[0] == (byte)OrderType.SyncHash) if (data.Length > 0 && data[0] == (byte)OrderType.SyncHash)
{ {
if (data.Length == Order.SyncHashOrderLength) if (data.Length == Order.SyncHashOrderLength)
HandleSyncOrder(frame, data); HandleSyncOrder(frame, data);
else else
Log.Write("server", $"Dropped sync order with length {data.Length} from client {from}. Expected length {Order.SyncHashOrderLength}."); Log.Write("server", $"Dropped sync order with length {data.Length} from client {from}. Expected length {Order.SyncHashOrderLength}.");
}
} }
} }
@@ -1163,6 +1163,16 @@ namespace OpenRA.Server
return LobbyInfo.ClientWithIndex(conn.PlayerIndex); return LobbyInfo.ClientWithIndex(conn.PlayerIndex);
} }
/// <Remarks> Does not check if client is admin </Remarks>
public bool CanKickClient(Session.Client kickee)
{
if (State != ServerState.GameStarted || kickee.IsObserver)
return true;
var player = worldPlayers.FirstOrDefault(p => p?.ClientIndex == kickee.Index);
return player != null && player.Outcome != WinState.Undefined;
}
public void DropClient(Connection toDrop) public void DropClient(Connection toDrop)
{ {
lock (LobbyInfo) lock (LobbyInfo)
@@ -1323,24 +1333,22 @@ namespace OpenRA.Server
foreach (var cmpi in Map.WorldActorInfo.TraitInfos<ICreatePlayersInfo>()) foreach (var cmpi in Map.WorldActorInfo.TraitInfos<ICreatePlayersInfo>())
cmpi.CreateServerPlayers(Map, LobbyInfo, worldPlayers, playerRandom); cmpi.CreateServerPlayers(Map, LobbyInfo, worldPlayers, playerRandom);
if (recorder != null) gameInfo = new GameInformation
{ {
gameInfo = new GameInformation Mod = Game.ModData.Manifest.Id,
{ Version = Game.ModData.Manifest.Metadata.Version,
Mod = Game.ModData.Manifest.Id, MapUid = Map.Uid,
Version = Game.ModData.Manifest.Metadata.Version, MapTitle = Map.Title,
MapUid = Map.Uid, StartTimeUtc = DateTime.UtcNow,
MapTitle = Map.Title, };
StartTimeUtc = DateTime.UtcNow,
};
// Replay metadata should only include the playable players // Replay metadata should only include the playable players
foreach (var p in worldPlayers) foreach (var p in worldPlayers)
if (p != null) if (p != null)
gameInfo.Players.Add(p); gameInfo.Players.Add(p);
if (recorder != null)
recorder.Metadata = new ReplayMetadata(gameInfo); recorder.Metadata = new ReplayMetadata(gameInfo);
}
SyncLobbyInfo(); SyncLobbyInfo();

View File

@@ -48,6 +48,9 @@ namespace OpenRA.Mods.Common.Server
[TranslationReference] [TranslationReference]
const string KickNone = "notification-kick-none"; const string KickNone = "notification-kick-none";
[TranslationReference]
const string NoKickSelf = "notification-kick-self";
[TranslationReference] [TranslationReference]
const string NoKickGameStarted = "notification-no-kick-game-started"; const string NoKickGameStarted = "notification-no-kick-game-started";
@@ -794,7 +797,13 @@ namespace OpenRA.Mods.Common.Server
} }
var kickClient = server.GetClient(kickConn); var kickClient = server.GetClient(kickConn);
if (server.State == ServerState.GameStarted && !kickClient.IsObserver) if (client == kickClient)
{
server.SendLocalizedMessageTo(conn, NoKickSelf);
return true;
}
if (!server.CanKickClient(kickClient))
{ {
server.SendLocalizedMessageTo(conn, NoKickGameStarted); server.SendLocalizedMessageTo(conn, NoKickGameStarted);
return true; return true;

View File

@@ -114,6 +114,22 @@ namespace OpenRA.Mods.Common.Widgets.Logic
.GroupBy(p => (world.LobbyInfo.ClientWithIndex(p.Player.ClientIndex) ?? new Session.Client()).Team) .GroupBy(p => (world.LobbyInfo.ClientWithIndex(p.Player.ClientIndex) ?? new Session.Client()).Team)
.OrderByDescending(g => g.Sum(gg => gg.PlayerStatistics?.Experience ?? 0)); .OrderByDescending(g => g.Sum(gg => gg.PlayerStatistics?.Experience ?? 0));
void KickAction(Session.Client client)
{
hideMenu(true);
ConfirmationDialogs.ButtonPrompt(modData,
title: KickTitle,
titleArguments: Translation.Arguments("player", client.Name),
text: KickPrompt,
onConfirm: () =>
{
orderManager.IssueOrder(Order.Command($"kick {client.Index} {false}"));
hideMenu(false);
},
onCancel: () => hideMenu(false),
confirmText: KickAccept);
}
foreach (var t in teams) foreach (var t in teams)
{ {
if (teams.Count() > 1) if (teams.Count() > 1)
@@ -165,6 +181,10 @@ namespace OpenRA.Mods.Common.Widgets.Logic
muteCheckbox.IsVisible = () => !pp.IsBot && client.State != Session.ClientState.Disconnected && pp.ClientIndex != orderManager.LocalClient?.Index; muteCheckbox.IsVisible = () => !pp.IsBot && client.State != Session.ClientState.Disconnected && pp.ClientIndex != orderManager.LocalClient?.Index;
muteCheckbox.GetTooltipText = () => muteCheckbox.IsChecked() ? unmuteTooltip : muteTooltip; muteCheckbox.GetTooltipText = () => muteCheckbox.IsChecked() ? unmuteTooltip : muteTooltip;
var kickButton = item.Get<ButtonWidget>("KICK");
kickButton.IsVisible = () => Game.IsHost && client.Index != orderManager.LocalClient?.Index && client.State != Session.ClientState.Disconnected && pp.WinState != WinState.Undefined && !pp.IsBot;
kickButton.OnClick = () => KickAction(client);
playerPanel.AddChild(item); playerPanel.AddChild(item);
} }
} }
@@ -198,21 +218,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var kickButton = item.Get<ButtonWidget>("KICK"); var kickButton = item.Get<ButtonWidget>("KICK");
kickButton.IsVisible = () => Game.IsHost && client.Index != orderManager.LocalClient?.Index && client.State != Session.ClientState.Disconnected; kickButton.IsVisible = () => Game.IsHost && client.Index != orderManager.LocalClient?.Index && client.State != Session.ClientState.Disconnected;
kickButton.OnClick = () => kickButton.OnClick = () => KickAction(client);
{
hideMenu(true);
ConfirmationDialogs.ButtonPrompt(modData,
title: KickTitle,
titleArguments: Translation.Arguments("player", client.Name),
text: KickPrompt,
onConfirm: () =>
{
orderManager.IssueOrder(Order.Command($"kick {client.Index} {false}"));
hideMenu(false);
},
onCancel: () => hideMenu(false),
confirmText: KickAccept);
};
var muteCheckbox = item.Get<CheckboxWidget>("MUTE"); var muteCheckbox = item.Get<CheckboxWidget>("MUTE");
muteCheckbox.IsChecked = () => TextNotificationsManager.MutedPlayers[client.Index]; muteCheckbox.IsChecked = () => TextNotificationsManager.MutedPlayers[client.Index];

View File

@@ -133,6 +133,19 @@ Container@SKIRMISH_STATS:
Checkmark: mute Checkmark: mute
Background: checkbox-toggle Background: checkbox-toggle
TooltipContainer: TOOLTIP_CONTAINER TooltipContainer: TOOLTIP_CONTAINER
Button@KICK:
X: 485
Width: 25
Height: 25
Background: checkbox-toggle
TooltipContainer: TOOLTIP_CONTAINER
TooltipText: Kick this player
Children:
Image:
ImageCollection: lobby-bits
ImageName: kick
X: 7
Y: 7
Container@SPECTATOR_TEMPLATE: Container@SPECTATOR_TEMPLATE:
Width: PARENT_RIGHT - 27 Width: PARENT_RIGHT - 27
Height: 25 Height: 25

View File

@@ -130,6 +130,19 @@ Container@SKIRMISH_STATS:
Checkmark: mute Checkmark: mute
Background: checkbox-toggle Background: checkbox-toggle
TooltipContainer: TOOLTIP_CONTAINER TooltipContainer: TOOLTIP_CONTAINER
Button@KICK:
X: 485
Width: 25
Height: 25
Background: checkbox-toggle
TooltipContainer: TOOLTIP_CONTAINER
TooltipText: Kick this player
Children:
Image:
ImageCollection: lobby-bits
ImageName: kick
X: 7
Y: 7
Container@SPECTATOR_TEMPLATE: Container@SPECTATOR_TEMPLATE:
Width: PARENT_RIGHT - 26 Width: PARENT_RIGHT - 26
Height: 25 Height: 25

View File

@@ -64,8 +64,9 @@ notification-invalid-configuration-command = Invalid configuration command.
notification-admin-option = Only the host can set that option. notification-admin-option = Only the host can set that option.
notification-error-number-teams = Number of teams could not be parsed: { $raw } notification-error-number-teams = Number of teams could not be parsed: { $raw }
notification-admin-kick = Only the host can kick players. notification-admin-kick = Only the host can kick players.
notification-kick-self = The host is not allowed to kick themselves.
notification-kick-none = No-one in that slot. notification-kick-none = No-one in that slot.
notification-no-kick-game-started = Only spectators can be kicked after the game has started. notification-no-kick-game-started = Only spectators and defeated players can be kicked after the game has started.
notification-admin-clear-spawn = Only admins can clear spawn points. notification-admin-clear-spawn = Only admins can clear spawn points.
notification-spawn-occupied = You cannot occupy the same spawn point as another player. notification-spawn-occupied = You cannot occupy the same spawn point as another player.
notification-spawn-locked = The spawn point is locked to another player slot. notification-spawn-locked = The spawn point is locked to another player slot.

View File

@@ -132,6 +132,19 @@ Container@SKIRMISH_STATS:
Checkmark: mute Checkmark: mute
Background: checkbox-toggle Background: checkbox-toggle
TooltipContainer: TOOLTIP_CONTAINER TooltipContainer: TOOLTIP_CONTAINER
Button@KICK:
X: 485
Width: 25
Height: 25
Background: checkbox-toggle
TooltipContainer: TOOLTIP_CONTAINER
TooltipText: Kick this player
Children:
Image:
ImageCollection: lobby-bits
ImageName: kick
X: 7
Y: 7
Container@SPECTATOR_TEMPLATE: Container@SPECTATOR_TEMPLATE:
Width: PARENT_RIGHT - 27 Width: PARENT_RIGHT - 27
Height: 25 Height: 25