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.Net.Sockets;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
|
using Shared;
|
||||||
using Shared.Packet.Packets;
|
using Shared.Packet.Packets;
|
||||||
|
|
||||||
namespace Server;
|
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) {
|
private static bool IsIPv4(string str) {
|
||||||
return IPAddress.TryParse(str, out IPAddress? ip)
|
return IPAddress.TryParse(str, out IPAddress? ip)
|
||||||
|
@ -62,6 +69,10 @@ public static class BanLists {
|
||||||
return Profiles.Contains(id);
|
return Profiles.Contains(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool IsStageBanned(string stage) {
|
||||||
|
return Stages.Contains(stage);
|
||||||
|
}
|
||||||
|
|
||||||
public static bool IsClientBanned(Client user) {
|
public static bool IsClientBanned(Client user) {
|
||||||
return IsProfileBanned(user) || IsIPv4Banned(user);
|
return IsProfileBanned(user) || IsIPv4Banned(user);
|
||||||
}
|
}
|
||||||
|
@ -91,6 +102,10 @@ public static class BanLists {
|
||||||
Profiles.Add(id);
|
Profiles.Add(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void BanStage(string stage) {
|
||||||
|
Stages.Add(stage);
|
||||||
|
}
|
||||||
|
|
||||||
private static void BanClient(Client user) {
|
private static void BanClient(Client user) {
|
||||||
BanProfile(user);
|
BanProfile(user);
|
||||||
BanIPv4(user);
|
BanIPv4(user);
|
||||||
|
@ -121,22 +136,36 @@ public static class BanLists {
|
||||||
Profiles.Remove(id);
|
Profiles.Remove(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void UnbanStage(string stage) {
|
||||||
|
Stages.Remove(stage);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private static void Save() {
|
private static void Save() {
|
||||||
Settings.SaveSettings(true);
|
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;
|
user.Ignored = true;
|
||||||
Task.Run(async () => {
|
Task.Run(async () => {
|
||||||
|
if (delay_ms > 0) {
|
||||||
|
await Task.Delay(delay_ms);
|
||||||
|
}
|
||||||
await user.Send(new ChangeStagePacket {
|
await user.Send(new ChangeStagePacket {
|
||||||
Id = (permanent ? "$agogus/ban4lyfe" : "$among$us/cr4sh%"),
|
Id = (permanent ? "$agogus/ban4lyfe" : "$among$us/cr4sh%"),
|
||||||
Stage = (permanent ? "$ejected" : "$agogusStage"),
|
Stage = (permanent ? "$ejected" : "$agogusStage"),
|
||||||
Scenario = (sbyte) (permanent ? 69 : 21),
|
Scenario = (sbyte) (permanent ? 69 : 21),
|
||||||
SubScenarioType = (byte) (permanent ? 21 : 69),
|
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) {
|
public static string HandleBanCommand(string[] args, MUCH much) {
|
||||||
if (args.Length == 0) {
|
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];
|
string cmd = args[0];
|
||||||
|
@ -157,7 +186,7 @@ public static class BanLists {
|
||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
default:
|
default:
|
||||||
return "Usage: ban {list|enable|disable|player|profile|ip} ...";
|
return "Usage: ban {list|enable|disable|player|profile|ip|stage} ...";
|
||||||
|
|
||||||
case "list":
|
case "list":
|
||||||
if (args.Length != 0) {
|
if (args.Length != 0) {
|
||||||
|
@ -176,6 +205,11 @@ public static class BanLists {
|
||||||
list.Append(string.Join("\n- ", Profiles));
|
list.Append(string.Join("\n- ", Profiles));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Stages.Count > 0) {
|
||||||
|
list.Append("\nBanned stages:\n- ");
|
||||||
|
list.Append(string.Join("\n- ", Stages));
|
||||||
|
}
|
||||||
|
|
||||||
return list.ToString();
|
return list.ToString();
|
||||||
|
|
||||||
case "enable":
|
case "enable":
|
||||||
|
@ -247,13 +281,35 @@ public static class BanLists {
|
||||||
CrashMultiple(args, much);
|
CrashMultiple(args, much);
|
||||||
Save();
|
Save();
|
||||||
return "Banned ip: " + args[0];
|
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) {
|
public static string HandleUnbanCommand(string[] args) {
|
||||||
if (args.Length != 2) {
|
if (args.Length != 2) {
|
||||||
return "Usage: unban {profile|ip} <value>";
|
return "Usage: unban {profile|ip|stage} <value>";
|
||||||
}
|
}
|
||||||
|
|
||||||
string cmd = args[0];
|
string cmd = args[0];
|
||||||
|
@ -261,7 +317,7 @@ public static class BanLists {
|
||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
default:
|
default:
|
||||||
return "Usage: unban {profile|ip} <value>";
|
return "Usage: unban {profile|ip|stage} <value>";
|
||||||
|
|
||||||
case "profile":
|
case "profile":
|
||||||
if (!Guid.TryParse(val, out Guid id)) {
|
if (!Guid.TryParse(val, out Guid id)) {
|
||||||
|
@ -284,6 +340,22 @@ public static class BanLists {
|
||||||
UnbanIPv4(val);
|
UnbanIPv4(val);
|
||||||
Save();
|
Save();
|
||||||
return "Unbanned ip: " + val;
|
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) => {
|
server.PacketHandler = (c, p) => {
|
||||||
switch (p) {
|
switch (p) {
|
||||||
case GamePacket gamePacket: {
|
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.Logger.Info($"Got game packet {gamePacket.Stage}->{gamePacket.ScenarioNum}");
|
||||||
c.Metadata["scenario"] = gamePacket.ScenarioNum;
|
c.Metadata["scenario"] = gamePacket.ScenarioNum;
|
||||||
c.Metadata["2d"] = gamePacket.Is2d;
|
c.Metadata["2d"] = gamePacket.Is2d;
|
||||||
|
|
|
@ -62,6 +62,7 @@ public class Settings {
|
||||||
public bool Enabled { get; set; } = false;
|
public bool Enabled { get; set; } = false;
|
||||||
public ISet<Guid> Players { get; set; } = new SortedSet<Guid>();
|
public ISet<Guid> Players { get; set; } = new SortedSet<Guid>();
|
||||||
public ISet<string> IpAddresses { get; set; } = new SortedSet<string>();
|
public ISet<string> IpAddresses { get; set; } = new SortedSet<string>();
|
||||||
|
public ISet<string> Stages { get; set; } = new SortedSet<string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public class FlipTable {
|
public class FlipTable {
|
||||||
|
|
|
@ -11,7 +11,7 @@ public static class Stages {
|
||||||
return mapName;
|
return mapName;
|
||||||
}
|
}
|
||||||
// exact stage value
|
// exact stage value
|
||||||
if (Stage2Alias.ContainsKey(input)) {
|
if (IsStage(input)) {
|
||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
// force input value with a !
|
// force input value with a !
|
||||||
|
@ -29,6 +29,32 @@ public static class Stages {
|
||||||
return result;
|
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>() {
|
public static readonly Dictionary<string, string> Alias2Stage = new Dictionary<string, string>() {
|
||||||
{ "cap", "CapWorldHomeStage" },
|
{ "cap", "CapWorldHomeStage" },
|
||||||
{ "cascade", "WaterfallWorldHomeStage" },
|
{ "cascade", "WaterfallWorldHomeStage" },
|
||||||
|
|
Loading…
Reference in New Issue