mirror of
https://github.com/Sanae6/SmoOnlineServer.git
synced 2024-11-23 19:55:18 +00:00
Add tag commands and improve command handling
This commit is contained in:
parent
3974b7022e
commit
ad0a40878d
5 changed files with 119 additions and 12 deletions
|
@ -1,7 +1,7 @@
|
|||
namespace Server;
|
||||
|
||||
public static class CommandHandler {
|
||||
public delegate string Handler(string[] args);
|
||||
public delegate Response Handler(string[] args);
|
||||
|
||||
public static Dictionary<string, Handler> Handlers = new Dictionary<string, Handler>();
|
||||
|
||||
|
@ -13,7 +13,7 @@ public static class CommandHandler {
|
|||
Handlers[name] = handler;
|
||||
}
|
||||
|
||||
public static string GetResult(string input) {
|
||||
public static Response GetResult(string input) {
|
||||
try {
|
||||
string[] args = input.Split(' ');
|
||||
if (args.Length == 0) return "No command entered, see help command for valid commands";
|
||||
|
@ -24,4 +24,16 @@ public static class CommandHandler {
|
|||
return $"An error occured while trying to process your command: {e}";
|
||||
}
|
||||
}
|
||||
|
||||
public class Response {
|
||||
public string[] ReturnStrings = null!;
|
||||
private Response(){}
|
||||
|
||||
public static implicit operator Response(string value) => new Response {
|
||||
ReturnStrings = value.Split('\n')
|
||||
};
|
||||
public static implicit operator Response(string[] values) => new Response {
|
||||
ReturnStrings = values
|
||||
};
|
||||
}
|
||||
}
|
|
@ -34,7 +34,8 @@ async Task ClientSyncShineBag(Client client) {
|
|||
ShineId = shine
|
||||
});
|
||||
}
|
||||
} catch {
|
||||
}
|
||||
catch {
|
||||
// errors that can happen when sending will crash the server :)
|
||||
}
|
||||
}
|
||||
|
@ -42,7 +43,8 @@ async Task ClientSyncShineBag(Client client) {
|
|||
async void SyncShineBag() {
|
||||
try {
|
||||
await Parallel.ForEachAsync(server.Clients.ToArray(), async (client, _) => await ClientSyncShineBag(client));
|
||||
} catch {
|
||||
}
|
||||
catch {
|
||||
// errors that can happen shines change will crash the server :)
|
||||
}
|
||||
}
|
||||
|
@ -59,9 +61,17 @@ float MarioSize(bool is2d) {
|
|||
|
||||
server.PacketHandler = (c, p) => {
|
||||
{
|
||||
if (p is GamePacket gamePacket) {
|
||||
switch (p) {
|
||||
case GamePacket gamePacket: {
|
||||
c.Metadata["scenario"] = gamePacket.ScenarioNum;
|
||||
c.Metadata["2d"] = gamePacket.Is2d;
|
||||
break;
|
||||
}
|
||||
case TagPacket tagPacket: {
|
||||
if ((tagPacket.UpdateType & TagPacket.TagUpdate.State) != 0) c.Metadata["seeking"] = tagPacket.IsIt;
|
||||
if ((tagPacket.UpdateType & TagPacket.TagUpdate.Time) != 0) c.Metadata["time"] = new Time(tagPacket.Minutes, tagPacket.Seconds, DateTime.Now);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
switch (p) {
|
||||
|
@ -124,10 +134,76 @@ CommandHandler.RegisterCommand("scenario", args => {
|
|||
}
|
||||
});
|
||||
|
||||
CommandHandler.RegisterCommand("list", _ => $"List: {string.Join(", ", server.Clients.Select(x => $"{x.Name} ({x.Id})"))}");
|
||||
CommandHandler.RegisterCommand("tag", args => {
|
||||
const string optionUsage = "Valid options:\n\ttime <user/*> <minutes[0-65535]> <seconds[0-59]>\n\tseeking <user/*> <true/false>\n\tstart <time> <seekers>";
|
||||
if (args.Length < 3)
|
||||
return optionUsage;
|
||||
switch (args[0]) {
|
||||
case "time" when args.Length == 3: {
|
||||
if (args[1] != "*" && server.Clients.All(x => x.Name != args[1])) return $"Cannot find user {args[1]}";
|
||||
Client? client = server.Clients.FirstOrDefault(x => x.Name == args[1]);
|
||||
if (!ushort.TryParse(args[2], out ushort minutes)) return $"Invalid time for minutes {args[2]} (range: 0-65535)";
|
||||
if (!byte.TryParse(args[3], out byte seconds) || seconds >= 60) return $"Invalid time for seconds {args[3]} (range: 0-59)";
|
||||
TagPacket tagPacket = new TagPacket {
|
||||
UpdateType = TagPacket.TagUpdate.Time,
|
||||
Minutes = minutes,
|
||||
Seconds = seconds
|
||||
};
|
||||
if (args[1] == "*")
|
||||
server.Broadcast(tagPacket);
|
||||
else
|
||||
client?.Send(tagPacket);
|
||||
return $"Set time for {(args[1] == "*" ? "everyone" : args[1])} to {minutes}:{seconds}";
|
||||
}
|
||||
case "seeking" when args.Length == 3: {
|
||||
if (args[1] != "*" && server.Clients.All(x => x.Name != args[1])) return $"Cannot find user {args[1]}";
|
||||
Client? client = server.Clients.FirstOrDefault(x => x.Name == args[1]);
|
||||
if (!bool.TryParse(args[2], out bool seeking)) return $"Usage: tag seeking {args[1]} <true/false>";
|
||||
TagPacket tagPacket = new TagPacket {
|
||||
UpdateType = TagPacket.TagUpdate.State,
|
||||
IsIt = seeking
|
||||
};
|
||||
if (args[1] == "*")
|
||||
server.Broadcast(tagPacket);
|
||||
else
|
||||
client?.Send(tagPacket);
|
||||
return $"Set {(args[1] == "*" ? "everyone" : args[1])} to {(seeking ? "seeker" : "hider")}";
|
||||
}
|
||||
case "start" when args.Length > 2: {
|
||||
if (!byte.TryParse(args[1], out byte time)) return $"Invalid countdown seconds {args[1]} (range: 0-255)";
|
||||
string[] seekerNames = args[2..];
|
||||
Client[] seekers = server.Clients.Where(c => seekerNames.Contains(c.Name)).ToArray();
|
||||
if (seekers.Length != seekerNames.Length)
|
||||
return $"Couldn't find seeker{(seekerNames.Length > 1 ? "s" : "")}: {string.Join(", ", seekerNames.Where(name => server.Clients.All(c => c.Name != name)))}";
|
||||
Task.Run(async () => {
|
||||
int realTime = 1000 * time;
|
||||
await Task.Delay(realTime);
|
||||
await Task.WhenAll(
|
||||
Parallel.ForEachAsync(seekers, async (seeker, _) =>
|
||||
await seeker.Send(new TagPacket {
|
||||
UpdateType = TagPacket.TagUpdate.State,
|
||||
IsIt = true
|
||||
})),
|
||||
Parallel.ForEachAsync(server.Clients.Except(seekers), async (hider, _) =>
|
||||
await hider.Send(new TagPacket {
|
||||
UpdateType = TagPacket.TagUpdate.State,
|
||||
IsIt = true
|
||||
})
|
||||
)
|
||||
);
|
||||
consoleLogger.Info($"Started game with seekers {string.Join(", ", seekerNames)}");
|
||||
});
|
||||
return $"Starting game in {time} seconds with seekers {string.Join(", ", seekerNames)}";
|
||||
}
|
||||
default:
|
||||
return optionUsage;
|
||||
}
|
||||
});
|
||||
|
||||
CommandHandler.RegisterCommand("list", _ => $"List: {string.Join("\n\t", server.Clients.Select(x => x.Name))}");
|
||||
|
||||
CommandHandler.RegisterCommand("flip", args => {
|
||||
const string optionUsage = "Valid options: list, add <user id>, remove <user id>, set <true/false>, pov <both/self/others>";
|
||||
const string optionUsage = "Valid options: \n\tlist\n\tadd <user id>\n\tremove <user id>\n\tset <true/false>\n\tpov <both/self/others>";
|
||||
if (args.Length < 1)
|
||||
return optionUsage;
|
||||
switch (args[0]) {
|
||||
|
@ -221,7 +297,11 @@ Task.Run(() => {
|
|||
consoleLogger.Info("Run help command for valid commands.");
|
||||
while (true) {
|
||||
string? text = Console.ReadLine();
|
||||
if (text != null) consoleLogger.Info(CommandHandler.GetResult(text));
|
||||
if (text != null) {
|
||||
foreach (string returnString in CommandHandler.GetResult(text).ReturnStrings) {
|
||||
consoleLogger.Info(returnString);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -86,6 +86,20 @@ public class Server {
|
|||
await Broadcast(memory, sender);
|
||||
}
|
||||
|
||||
public Task Broadcast<T>(T packet) where T : struct, IPacket {
|
||||
return Task.WhenAll(Clients.Where(c => c.Connected).Select(async client => {
|
||||
IMemoryOwner<byte> memory = MemoryPool<byte>.Shared.RentZero(Constants.HeaderSize + packet.Size);
|
||||
PacketHeader header = new PacketHeader {
|
||||
Id = client.Id,
|
||||
Type = Constants.PacketMap[typeof(T)].Type,
|
||||
PacketSize = packet.Size
|
||||
};
|
||||
FillPacket(header, packet, memory.Memory);
|
||||
await client.Send(memory.Memory, client);
|
||||
memory.Dispose();
|
||||
}));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Takes ownership of data and disposes once done.
|
||||
/// </summary>
|
||||
|
|
1
Server/Time.cs
Normal file
1
Server/Time.cs
Normal file
|
@ -0,0 +1 @@
|
|||
public record Time(ushort Minutes, byte Seconds, DateTime When) {}
|
Loading…
Reference in a new issue