Add vote kick

This commit is contained in:
Gustas
2023-08-13 13:38:12 +03:00
committed by Pavel Penev
parent 686040a316
commit 144e716cdf
10 changed files with 468 additions and 38 deletions

View File

@@ -22,7 +22,7 @@ using S = OpenRA.Server.Server;
namespace OpenRA.Mods.Common.Server
{
public class LobbyCommands : ServerTrait, IInterpretCommand, INotifyServerStart, INotifyServerEmpty, IClientJoined
public class LobbyCommands : ServerTrait, IInterpretCommand, INotifyServerStart, INotifyServerEmpty, IClientJoined, OpenRA.Server.ITick
{
[TranslationReference]
const string CustomRules = "notification-custom-rules";
@@ -55,6 +55,9 @@ namespace OpenRA.Mods.Common.Server
const string NoKickGameStarted = "notification-no-kick-game-started";
[TranslationReference("admin", "player")]
const string AdminKicked = "notification-admin-kicked";
[TranslationReference("player")]
const string Kicked = "notification-kicked";
[TranslationReference("admin", "player")]
@@ -156,6 +159,9 @@ namespace OpenRA.Mods.Common.Server
[TranslationReference]
const string YouWereKicked = "notification-you-were-kicked";
[TranslationReference]
const string VoteKickDisabled = "notification-vote-kick-disabled";
readonly IDictionary<string, Func<S, Connection, Session.Client, string, bool>> commandHandlers = new Dictionary<string, Func<S, Connection, Session.Client, string, bool>>
{
{ "state", State },
@@ -170,6 +176,7 @@ namespace OpenRA.Mods.Common.Server
{ "option", Option },
{ "assignteams", AssignTeams },
{ "kick", Kick },
{ "vote_kick", VoteKick },
{ "make_admin", MakeAdmin },
{ "make_spectator", MakeSpectator },
{ "name", Name },
@@ -207,7 +214,7 @@ namespace OpenRA.Mods.Common.Server
lock (server.LobbyInfo)
{
// Kick command is always valid for the host
if (command.StartsWith("kick ", StringComparison.Ordinal))
if (command.StartsWith("kick ", StringComparison.Ordinal) || command.StartsWith("vote_kick ", StringComparison.Ordinal))
return true;
if (server.State == ServerState.GameStarted)
@@ -805,14 +812,14 @@ namespace OpenRA.Mods.Common.Server
return true;
}
if (!server.CanKickClient(kickClient))
if (server.State == ServerState.GameStarted && !kickClient.IsObserver && !server.HasClientWonOrLost(kickClient))
{
server.SendLocalizedMessageTo(conn, NoKickGameStarted);
return true;
}
Log.Write("server", $"Kicking client {kickClientID}.");
server.SendLocalizedMessage(Kicked, Translation.Arguments("admin", client.Name, "player", kickClient.Name));
server.SendLocalizedMessage(AdminKicked, Translation.Arguments("admin", client.Name, "player", kickClient.Name));
server.SendOrderTo(kickConn, "ServerError", YouWereKicked);
server.DropClient(kickConn);
@@ -830,6 +837,62 @@ namespace OpenRA.Mods.Common.Server
}
}
static bool VoteKick(S server, Connection conn, Session.Client client, string s)
{
lock (server.LobbyInfo)
{
var split = s.Split(' ');
if (split.Length != 2)
{
server.SendLocalizedMessageTo(conn, MalformedCommand, Translation.Arguments("command", "vote_kick"));
return true;
}
if (!server.Settings.EnableVoteKick)
{
server.SendLocalizedMessageTo(conn, VoteKickDisabled);
return true;
}
var kickConn = Exts.TryParseInt32Invariant(split[0], out var kickClientID)
? server.Conns.SingleOrDefault(c => server.GetClient(c)?.Index == kickClientID) : null;
if (kickConn == null)
{
server.SendLocalizedMessageTo(conn, KickNone);
return true;
}
var kickClient = server.GetClient(kickConn);
if (client == kickClient)
{
server.SendLocalizedMessageTo(conn, NoKickSelf);
return true;
}
if (!bool.TryParse(split[1], out var vote))
{
server.SendLocalizedMessageTo(conn, MalformedCommand, Translation.Arguments("command", "vote_kick"));
return true;
}
if (server.VoteKickTracker.VoteKick(conn, client, kickConn, kickClient, kickClientID, vote))
{
Log.Write("server", $"Kicking client {kickClientID}.");
server.SendLocalizedMessage(Kicked, Translation.Arguments("player", kickClient.Name));
server.SendOrderTo(kickConn, "ServerError", YouWereKicked);
server.DropClient(kickConn);
server.SyncLobbyClients();
server.SyncLobbySlots();
}
return true;
}
}
void OpenRA.Server.ITick.Tick(S server) => server.VoteKickTracker.Tick();
static bool MakeAdmin(S server, Connection conn, Session.Client client, string s)
{
lock (server.LobbyInfo)

View File

@@ -50,6 +50,9 @@ namespace OpenRA.Mods.Common.Widgets.Logic
[TranslationReference]
const string Gone = "label-client-state-disconnected";
[TranslationReference]
const string KickTooltip = "button-kick-player";
[TranslationReference("player")]
const string KickTitle = "dialog-kick.title";
@@ -59,6 +62,30 @@ namespace OpenRA.Mods.Common.Widgets.Logic
[TranslationReference]
const string KickAccept = "dialog-kick.confirm";
[TranslationReference]
const string KickVoteTooltip = "button-vote-kick-player";
[TranslationReference("player")]
const string VoteKickTitle = "dialog-vote-kick.title";
[TranslationReference]
const string VoteKickPrompt = "dialog-vote-kick.prompt";
[TranslationReference("bots")]
const string VoteKickPromptBreakBots = "dialog-vote-kick.prompt-break-bots";
[TranslationReference]
const string VoteKickVoteStart = "dialog-vote-kick.vote-start";
[TranslationReference]
const string VoteKickVoteFor = "dialog-vote-kick.vote-for";
[TranslationReference]
const string VoteKickVoteAgainst = "dialog-vote-kick.vote-against";
[TranslationReference]
const string VoteKickVoteCancel = "dialog-vote-kick.vote-cancel";
[ObjectCreator.UseCtor]
public GameInfoStatsLogic(Widget widget, ModData modData, World world, OrderManager orderManager, WorldRenderer worldRenderer, Action<bool> hideMenu)
{
@@ -107,6 +134,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var spectatorTemplate = playerPanel.Get("SPECTATOR_TEMPLATE");
var unmuteTooltip = TranslationProvider.GetString(Unmute);
var muteTooltip = TranslationProvider.GetString(Mute);
var kickTooltip = TranslationProvider.GetString(KickTooltip);
var voteKickTooltip = TranslationProvider.GetString(KickVoteTooltip);
playerPanel.RemoveChildren();
var teams = world.Players.Where(p => !p.NonCombatant && p.Playable)
@@ -116,22 +145,78 @@ namespace OpenRA.Mods.Common.Widgets.Logic
.OrderByDescending(g => g.Sum(gg => gg.PlayerStatistics?.Experience ?? 0))
.ToList();
void KickAction(Session.Client client)
void KickAction(Session.Client client, Func<bool> isVoteKick)
{
hideMenu(true);
ConfirmationDialogs.ButtonPrompt(modData,
title: KickTitle,
titleArguments: Translation.Arguments("player", client.Name),
text: KickPrompt,
onConfirm: () =>
if (isVoteKick())
{
var botsCount = 0;
if (client.IsAdmin)
botsCount = world.Players.Count(p => p.IsBot && p.WinState == WinState.Undefined);
if (UnitOrders.KickVoteTarget == null)
{
orderManager.IssueOrder(Order.Command($"kick {client.Index} {false}"));
hideMenu(false);
},
onCancel: () => hideMenu(false),
confirmText: KickAccept);
ConfirmationDialogs.ButtonPrompt(modData,
title: VoteKickTitle,
titleArguments: Translation.Arguments("player", client.Name),
text: botsCount > 0 ? VoteKickPromptBreakBots : VoteKickPrompt,
textArguments: Translation.Arguments("bots", botsCount),
onConfirm: () =>
{
orderManager.IssueOrder(Order.Command($"vote_kick {client.Index} {true}"));
hideMenu(false);
},
confirmText: VoteKickVoteStart,
onCancel: () => hideMenu(false));
return;
}
ConfirmationDialogs.ButtonPrompt(modData,
title: VoteKickTitle,
titleArguments: Translation.Arguments("player", client.Name),
text: botsCount > 0 ? VoteKickPromptBreakBots : VoteKickPrompt,
textArguments: Translation.Arguments("bots", botsCount),
onConfirm: () =>
{
orderManager.IssueOrder(Order.Command($"vote_kick {client.Index} {true}"));
hideMenu(false);
},
confirmText: VoteKickVoteFor,
onOther: () =>
{
Ui.CloseWindow();
orderManager.IssueOrder(Order.Command($"vote_kick {client.Index} {false}"));
hideMenu(false);
},
otherText: VoteKickVoteAgainst,
onCancel: () => hideMenu(false),
cancelText: VoteKickVoteCancel);
}
else
{
ConfirmationDialogs.ButtonPrompt(modData,
title: KickTitle,
titleArguments: Translation.Arguments("player", client.Name),
text: KickPrompt,
onConfirm: () =>
{
orderManager.IssueOrder(Order.Command($"kick {client.Index} {false}"));
hideMenu(false);
},
confirmText: KickAccept,
onCancel: () => hideMenu(false));
}
}
var localClient = orderManager.LocalClient;
var localPlayer = localClient == null ? null : world.Players.FirstOrDefault(player => player.ClientIndex == localClient.Index);
bool LocalPlayerCanKick() => localClient != null
&& (Game.IsHost || ((!orderManager.LocalClient.IsObserver) && localPlayer.WinState == WinState.Undefined));
bool CanClientBeKicked(Session.Client client, Func<bool> isVoteKick) =>
client.Index != localClient.Index && client.State != Session.ClientState.Disconnected
&& (!client.IsAdmin || orderManager.LobbyInfo.GlobalSettings.Dedicated)
&& (!isVoteKick() || UnitOrders.KickVoteTarget == null || UnitOrders.KickVoteTarget == client.Index);
foreach (var t in teams)
{
if (teams.Count > 1)
@@ -184,8 +269,10 @@ namespace OpenRA.Mods.Common.Widgets.Logic
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);
bool IsVoteKick() => !Game.IsHost || pp.WinState == WinState.Undefined;
kickButton.IsVisible = () => !pp.IsBot && LocalPlayerCanKick() && CanClientBeKicked(client, IsVoteKick);
kickButton.OnClick = () => KickAction(client, IsVoteKick);
kickButton.GetTooltipText = () => IsVoteKick() ? voteKickTooltip : kickTooltip;
playerPanel.AddChild(item);
}
@@ -219,8 +306,10 @@ namespace OpenRA.Mods.Common.Widgets.Logic
};
var kickButton = item.Get<ButtonWidget>("KICK");
kickButton.IsVisible = () => Game.IsHost && client.Index != orderManager.LocalClient?.Index && client.State != Session.ClientState.Disconnected;
kickButton.OnClick = () => KickAction(client);
bool IsVoteKick() => !Game.IsHost;
kickButton.IsVisible = () => LocalPlayerCanKick() && CanClientBeKicked(client, IsVoteKick);
kickButton.OnClick = () => KickAction(client, IsVoteKick);
kickButton.GetTooltipText = () => IsVoteKick() ? voteKickTooltip : kickTooltip;
var muteCheckbox = item.Get<CheckboxWidget>("MUTE");
muteCheckbox.IsChecked = () => TextNotificationsManager.MutedPlayers[client.Index];