add commands: `ban stage <stage-name>` and `unban stage <stage-name>`
To kick players from the server when they enter a banned stage. <stage-name> can also be a kingdom alias, which bans/unbans all stages in that kingdom. Because we aren't banning the player, d/c them would be no good, because of the client auto reconnect. Instead send them the crash and ignore all packets by them until they d/c on their own. This is an alternative solution for issue #43.
This commit is contained in:
parent
7082f28e5f
commit
bf0bfae10b
|
@ -2,6 +2,7 @@ using System.Net;
|
|||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
|
||||
using Shared;
|
||||
using Shared.Packet.Packets;
|
||||
|
||||
namespace Server;
|
||||
|
@ -30,6 +31,12 @@ public static class BanLists {
|
|||
}
|
||||
}
|
||||
|
||||
private static ISet<string> Stages {
|
||||
get {
|
||||
return Settings.Instance.BanList.Stages;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static bool IsIPv4(string str) {
|
||||
return IPAddress.TryParse(str, out IPAddress? ip)
|
||||
|
@ -62,6 +69,10 @@ public static class BanLists {
|
|||
return Profiles.Contains(id);
|
||||
}
|
||||
|
||||
public static bool IsStageBanned(string stage) {
|
||||
return Stages.Contains(stage);
|
||||
}
|
||||
|
||||
public static bool IsClientBanned(Client user) {
|
||||
return IsProfileBanned(user) || IsIPv4Banned(user);
|
||||
}
|
||||
|
@ -91,6 +102,10 @@ public static class BanLists {
|
|||
Profiles.Add(id);
|
||||
}
|
||||
|
||||
private static void BanStage(string stage) {
|
||||
Stages.Add(stage);
|
||||
}
|
||||
|
||||
private static void BanClient(Client user) {
|
||||
BanProfile(user);
|
||||
BanIPv4(user);
|
||||
|
@ -121,22 +136,36 @@ public static class BanLists {
|
|||
Profiles.Remove(id);
|
||||
}
|
||||
|
||||
private static void UnbanStage(string stage) {
|
||||
Stages.Remove(stage);
|
||||
}
|
||||
|
||||
|
||||
private static void Save() {
|
||||
Settings.SaveSettings(true);
|
||||
}
|
||||
|
||||
|
||||
public static void Crash(Client user, bool permanent = false) {
|
||||
public static void Crash(
|
||||
Client user,
|
||||
bool permanent = false,
|
||||
bool dispose_user = true,
|
||||
int delay_ms = 0
|
||||
) {
|
||||
user.Ignored = true;
|
||||
Task.Run(async () => {
|
||||
if (delay_ms > 0) {
|
||||
await Task.Delay(delay_ms);
|
||||
}
|
||||
await user.Send(new ChangeStagePacket {
|
||||
Id = (permanent ? "$agogus/ban4lyfe" : "$among$us/cr4sh%"),
|
||||
Stage = (permanent ? "$ejected" : "$agogusStage"),
|
||||
Scenario = (sbyte) (permanent ? 69 : 21),
|
||||
SubScenarioType = (byte) (permanent ? 21 : 69),
|
||||
});
|
||||
user.Dispose();
|
||||
if (dispose_user) {
|
||||
user.Dispose();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -149,7 +178,7 @@ public static class BanLists {
|
|||
|
||||
public static string HandleBanCommand(string[] args, MUCH much) {
|
||||
if (args.Length == 0) {
|
||||
return "Usage: ban {list|enable|disable|player|profile|ip} ...";
|
||||
return "Usage: ban {list|enable|disable|player|profile|ip|stage} ...";
|
||||
}
|
||||
|
||||
string cmd = args[0];
|
||||
|
@ -157,7 +186,7 @@ public static class BanLists {
|
|||
|
||||
switch (cmd) {
|
||||
default:
|
||||
return "Usage: ban {list|enable|disable|player|profile|ip} ...";
|
||||
return "Usage: ban {list|enable|disable|player|profile|ip|stage} ...";
|
||||
|
||||
case "list":
|
||||
if (args.Length != 0) {
|
||||
|
@ -176,6 +205,11 @@ public static class BanLists {
|
|||
list.Append(string.Join("\n- ", Profiles));
|
||||
}
|
||||
|
||||
if (Stages.Count > 0) {
|
||||
list.Append("\nBanned stages:\n- ");
|
||||
list.Append(string.Join("\n- ", Stages));
|
||||
}
|
||||
|
||||
return list.ToString();
|
||||
|
||||
case "enable":
|
||||
|
@ -247,13 +281,35 @@ public static class BanLists {
|
|||
CrashMultiple(args, much);
|
||||
Save();
|
||||
return "Banned ip: " + args[0];
|
||||
|
||||
case "stage":
|
||||
if (args.Length != 1) {
|
||||
return "Usage: ban stage <stage-name>";
|
||||
}
|
||||
string? stage = Shared.Stages.Input2Stage(args[0]);
|
||||
if (stage == null) {
|
||||
return "Invalid stage name!";
|
||||
}
|
||||
if (IsStageBanned(stage)) {
|
||||
return "Stage " + stage + " is already banned.";
|
||||
}
|
||||
var stages = Shared.Stages
|
||||
.StagesByInput(args[0])
|
||||
.Where(s => !IsStageBanned(s))
|
||||
.ToList()
|
||||
;
|
||||
foreach (string s in stages) {
|
||||
BanStage(s);
|
||||
}
|
||||
Save();
|
||||
return "Banned stage: " + string.Join(", ", stages);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static string HandleUnbanCommand(string[] args) {
|
||||
if (args.Length != 2) {
|
||||
return "Usage: unban {profile|ip} <value>";
|
||||
return "Usage: unban {profile|ip|stage} <value>";
|
||||
}
|
||||
|
||||
string cmd = args[0];
|
||||
|
@ -261,7 +317,7 @@ public static class BanLists {
|
|||
|
||||
switch (cmd) {
|
||||
default:
|
||||
return "Usage: unban {profile|ip} <value>";
|
||||
return "Usage: unban {profile|ip|stage} <value>";
|
||||
|
||||
case "profile":
|
||||
if (!Guid.TryParse(val, out Guid id)) {
|
||||
|
@ -284,6 +340,22 @@ public static class BanLists {
|
|||
UnbanIPv4(val);
|
||||
Save();
|
||||
return "Unbanned ip: " + val;
|
||||
|
||||
case "stage":
|
||||
string stage = Shared.Stages.Input2Stage(val) ?? val;
|
||||
if (!IsStageBanned(stage)) {
|
||||
return "Stage " + stage + " is not banned.";
|
||||
}
|
||||
var stages = Shared.Stages
|
||||
.StagesByInput(val)
|
||||
.Where(IsStageBanned)
|
||||
.ToList()
|
||||
;
|
||||
foreach (string s in stages) {
|
||||
UnbanStage(s);
|
||||
}
|
||||
Save();
|
||||
return "Unbanned stage: " + string.Join(", ", stages);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -113,6 +113,11 @@ float MarioSize(bool is2d) => is2d ? 180 : 160;
|
|||
server.PacketHandler = (c, p) => {
|
||||
switch (p) {
|
||||
case GamePacket gamePacket: {
|
||||
if (BanLists.Enabled && BanLists.IsStageBanned(gamePacket.Stage)) {
|
||||
c.Logger.Warn($"Crashing player for entering banned stage {gamePacket.Stage}.");
|
||||
BanLists.Crash(c, false, false, 500);
|
||||
return false;
|
||||
}
|
||||
c.Logger.Info($"Got game packet {gamePacket.Stage}->{gamePacket.ScenarioNum}");
|
||||
c.Metadata["scenario"] = gamePacket.ScenarioNum;
|
||||
c.Metadata["2d"] = gamePacket.Is2d;
|
||||
|
|
|
@ -62,6 +62,7 @@ public class Settings {
|
|||
public bool Enabled { get; set; } = false;
|
||||
public ISet<Guid> Players { get; set; } = new SortedSet<Guid>();
|
||||
public ISet<string> IpAddresses { get; set; } = new SortedSet<string>();
|
||||
public ISet<string> Stages { get; set; } = new SortedSet<string>();
|
||||
}
|
||||
|
||||
public class FlipTable {
|
||||
|
|
|
@ -11,7 +11,7 @@ public static class Stages {
|
|||
return mapName;
|
||||
}
|
||||
// exact stage value
|
||||
if (Stage2Alias.ContainsKey(input)) {
|
||||
if (IsStage(input)) {
|
||||
return input;
|
||||
}
|
||||
// force input value with a !
|
||||
|
@ -29,6 +29,32 @@ public static class Stages {
|
|||
return result;
|
||||
}
|
||||
|
||||
public static bool IsAlias(string input) {
|
||||
return Alias2Stage.ContainsKey(input);
|
||||
}
|
||||
|
||||
public static bool IsStage(string input) {
|
||||
return Stage2Alias.ContainsKey(input);
|
||||
}
|
||||
|
||||
public static IEnumerable<string> StagesByInput(string input) {
|
||||
if (IsAlias(input)) {
|
||||
var stages = Stage2Alias
|
||||
.Where(e => e.Value == input)
|
||||
.Select(e => e.Key)
|
||||
;
|
||||
foreach (string stage in stages) {
|
||||
yield return stage;
|
||||
}
|
||||
}
|
||||
else {
|
||||
string? stage = Input2Stage(input);
|
||||
if (stage != null) {
|
||||
yield return stage;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static readonly Dictionary<string, string> Alias2Stage = new Dictionary<string, string>() {
|
||||
{ "cap", "CapWorldHomeStage" },
|
||||
{ "cascade", "WaterfallWorldHomeStage" },
|
||||
|
|
Loading…
Reference in New Issue