Implement console commands and settings (flip list broken)

This commit is contained in:
Sanae 2022-02-17 19:56:12 -06:00
parent 78af568603
commit 821301e756
8 changed files with 220 additions and 68 deletions

26
Server/CommandHandler.cs Normal file
View File

@ -0,0 +1,26 @@
namespace Server;
public static class CommandHandler {
public delegate string Handler(string[] args);
public static Dictionary<string, Handler> Handlers = new Dictionary<string, Handler>();
static CommandHandler() {
RegisterCommand("help", _ => $"Valid commands: {string.Join(", ", Handlers.Keys)}");
}
public static void RegisterCommand(string name, Handler handler) {
Handlers[name] = handler;
}
public static string GetResult(string input) {
try {
string[] args = input.Split(' ');
if (args.Length == 0) return "No command entered, see help command for valid commands";
string commandName = args[0];
return Handlers.TryGetValue(commandName, out Handler? handler) ? handler(args[1..]) : $"Invalid command {args[0]}, see help command for valid commands";
} catch (Exception e) {
return $"An error occured while trying to process your command: {e}";
}
}
}

7
Server/FlipOptions.cs Normal file
View File

@ -0,0 +1,7 @@
namespace Server;
public enum FlipOptions {
Both,
Self,
Others
}

View File

@ -1,7 +1,9 @@
using System.Collections.Concurrent;
using System.Numerics;
using Server;
using Shared;
using Shared.Packet.Packets;
using Tomlyn;
using Timer = System.Timers.Timer;
Server.Server server = new Server.Server();
@ -21,23 +23,17 @@ async Task ClientSyncShineBag(Client client) {
}
async void SyncShineBag() {
await Parallel.ForEachAsync(server.Clients, async (client, _) => {
await ClientSyncShineBag(client);
});
await Parallel.ForEachAsync(server.Clients, async (client, _) => { await ClientSyncShineBag(client); });
}
Timer timer = new Timer(120000);
timer.AutoReset = true;
timer.Enabled = true;
timer.Elapsed += (_, _) => {
SyncShineBag();
};
timer.Elapsed += (_, _) => { SyncShineBag(); };
timer.Start();
bool piss = false;
bool flipEnabled = Settings.Instance.Flip.EnabledOnStart;
Guid lycel = Guid.Parse("d5feae62-2e71-1000-88fd-597ea147ae88");
// Guid lycel = Guid.Parse("5e1f9db4-1c27-1000-a421-4701972e443e");
Guid test = Guid.Parse("00000001-0000-0000-0000-000000000000");
float MarioSize(bool is2d) => is2d ? 180 : 160;
server.PacketHandler = (c, p) => {
switch (p) {
@ -55,18 +51,19 @@ server.PacketHandler = (c, p) => {
SyncShineBag();
break;
}
case PlayerPacket playerPacket when c.Id == lycel && c.Id != test && piss: {
playerPacket.Position += Vector3.UnitY * 160;
case PlayerPacket playerPacket when flipEnabled && Settings.Instance.Flip.Pov is FlipOptions.Both or FlipOptions.Others && Settings.Instance.Flip.Players.Contains(c.Id): {
playerPacket.Position += Vector3.UnitY * MarioSize(playerPacket.Is2d);
playerPacket.Rotation *= Quaternion.CreateFromRotationMatrix(Matrix4x4.CreateRotationX(MathF.PI)) * Quaternion.CreateFromRotationMatrix(Matrix4x4.CreateRotationY(MathF.PI));
server.Broadcast(playerPacket, c);
return false;
}
case PlayerPacket playerPacket when c.Id != lycel && piss: {
case PlayerPacket playerPacket when flipEnabled && Settings.Instance.Flip.Pov is FlipOptions.Both or FlipOptions.Self && !Settings.Instance.Flip.Players.Contains(c.Id): {
server.BroadcastReplace(playerPacket, c, (from, to, sp) => {
if (to.Id == lycel) {
sp.Position += Vector3.UnitY * 160;
if (Settings.Instance.Flip.Players.Contains(to.Id)) {
sp.Position += Vector3.UnitY * MarioSize(playerPacket.Is2d);
sp.Rotation *= Quaternion.CreateFromRotationMatrix(Matrix4x4.CreateRotationX(MathF.PI)) * Quaternion.CreateFromRotationMatrix(Matrix4x4.CreateRotationY(MathF.PI));
}
to.Send(sp, from);
});
return false;
@ -76,12 +73,71 @@ server.PacketHandler = (c, p) => {
return true;
};
Task.Run(() => {
while (true) {
Console.ReadLine();
piss = !piss;
server.Logger.Warn($"Lycel flipped to {piss}");
CommandHandler.RegisterCommand("flip", args => {
const string optionUsage = "Valid options: list, add <user id>, remove <user id>, set <true/false>, pov <both/self/others>";
if (args.Length < 1)
return optionUsage;
switch (args[0]) {
case "list" when args.Length == 1:
return "User ids: " + string.Join(", ", Settings.Instance.Flip.Players.ToList());
case "add" when args.Length == 2: {
if (Guid.TryParse(args[1], out Guid result)) {
Settings.Instance.Flip.Players.Add(result);
Settings.SaveSettings();
return $"Added {result} to flipped players";
} else
return $"Invalid user id {args[1]}";
}
case "remove" when args.Length == 2: {
if (Guid.TryParse(args[1], out Guid result)) {
string output = Settings.Instance.Flip.Players.Remove(result) ? $"Removed {result} to flipped players" : $"User {result} wasn't in the flipped players list";
Settings.SaveSettings();
return output;
}
return $"Invalid user id {args[1]}";
}
case "set" when args.Length == 2: {
if (bool.TryParse(args[1], out bool result)) {
flipEnabled = result;
return result ? "Enabled player flipping for session" : "Disabled player flipping for session";
}
return optionUsage;
}
case "pov" when args.Length == 2: {
if (Enum.TryParse(args[1], true, out FlipOptions result)) {
Settings.Instance.Flip.Pov = result;
Settings.SaveSettings();
return $"Point of view set to {result}";
}
return optionUsage;
}
default:
return optionUsage;
}
});
await server.Listen(1027);
CommandHandler.RegisterCommand("shine", (args) => {
const string optionUsage = "Valid options: list";
if (args.Length < 1)
return optionUsage;
switch (args[0]) {
case "list" when args.Length == 1:
return $"Shines: {string.Join(", ", shineBag)}";
default:
return optionUsage;
}
});
Task.Run(() => {
Logger logger = new Logger("Console");
logger.Info("Run help command for valid commands.");
while (true) {
string? text = Console.ReadLine();
if (text != null) logger.Info(CommandHandler.GetResult(text));
}
});
await server.Listen();

View File

@ -15,13 +15,13 @@ public class Server {
public Func<Client, IPacket, bool>? PacketHandler = null!;
public event Action<Client, ConnectPacket> ClientJoined = null!;
public async Task Listen(ushort port) {
public async Task Listen() {
Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
serverSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
serverSocket.Bind(new IPEndPoint(IPAddress.Any, port));
serverSocket.Bind(new IPEndPoint(IPAddress.Parse(Settings.Instance.Server.Address), Settings.Instance.Server.Port));
serverSocket.Listen();
Logger.Info($"Listening on port {port}");
Logger.Info($"Listening on {serverSocket.LocalEndPoint}");
while (true) {
Socket socket = await serverSocket.AcceptAsync();

65
Server/Settings.cs Normal file
View File

@ -0,0 +1,65 @@
using System.Net;
using Shared;
using Tomlyn;
using Tomlyn.Model;
using Tomlyn.Syntax;
namespace Server;
public class Settings {
public static Settings Instance = new Settings();
private static readonly Logger Logger = new Logger("Settings");
static Settings() {
LoadSettings();
}
public static void LoadSettings() {
if (File.Exists("settings.toml")) {
string text = File.ReadAllText("settings.toml");
if (Toml.TryToModel(text, out Settings? settings, out DiagnosticsBag? bag, options: new TomlModelOptions() {
ConvertTo = (value, _) => {
if (value is string str && Guid.TryParse(str, out Guid result))
return result;
return null;
}
}))
Logger.Info("Loaded settings from settings.toml");
else
Logger.Warn($"Failed to load settings.toml: {bag}");
if (settings != null) Instance = settings;
} else {
SaveSettings();
}
}
public static void SaveSettings(Settings? settings = null) {
try {
File.WriteAllText("settings.toml", Toml.FromModel(settings ?? Instance!, new TomlModelOptions {
ConvertTo = (x, _) => {
if (x is Guid guid)
return guid.ToString();
return null!;
}
}));
Logger.Info("Saved settings to settings.toml");
} catch (Exception e) {
Logger.Error($"Failed to save settings.toml {e}");
}
}
public ServerTable Server { get; set; } = new ServerTable();
public FlipTable Flip { get; set; } = new FlipTable();
public class ServerTable {
public string Address { get; set; } = IPAddress.Any.ToString();
public ushort Port { get; set; } = 1027;
}
public class FlipTable {
public List<Guid> Players { get; set; } = new List<Guid>();
public bool EnabledOnStart { get; set; } = true;
public FlipOptions Pov { get; set; } = FlipOptions.Both;
}
}

View File

@ -1,14 +0,0 @@
using System.Text;
namespace Shared.Packet.Packets;
public struct LogPacket : IPacket {
public string Text;
public void Serialize(Span<byte> data) {
}
public void Deserialize(Span<byte> data) {
Text = Encoding.UTF8.GetString(data[..]).TrimNullTerm();
}
}

View File

@ -6,7 +6,7 @@ namespace Shared.Packet.Packets;
[Packet(PacketType.Player)]
public struct PlayerPacket : IPacket {
public const int NameSize = 0x30;
public const int NameSize = 0x20;
public Vector3 Position;
public Quaternion Rotation;
@ -20,14 +20,17 @@ public struct PlayerPacket : IPacket {
public bool IsIt;
public int ScenarioNum;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = NameSize)]
public string Stage;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x40)]
public string Stage = "";
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = NameSize)]
public string Act;
public string Act = "";
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = NameSize)]
public string SubAct;
public string SubAct = "";
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = NameSize)]
public string Hack = "";
public void Serialize(Span<byte> data) {
int offset = 0;
@ -43,11 +46,13 @@ public struct PlayerPacket : IPacket {
MemoryMarshal.Write(data[offset++..], ref IsIt);
MemoryMarshal.Write(data[offset..], ref ScenarioNum);
offset += 5;
Encoding.UTF8.GetBytes(Stage).CopyTo(data[offset..(offset + NameSize)]);
offset += NameSize;
Encoding.UTF8.GetBytes(Stage).CopyTo(data[offset..(offset + 0x40)]);
offset += 0x40;
Encoding.UTF8.GetBytes(Act).CopyTo(data[offset..(offset + NameSize)]);
offset += NameSize;
Encoding.UTF8.GetBytes(SubAct).CopyTo(data[offset..]);
Encoding.UTF8.GetBytes(SubAct).CopyTo(data[offset..(offset + NameSize)]);
offset += NameSize;
Encoding.UTF8.GetBytes(Hack).CopyTo(data[offset..]);
}
public void Deserialize(Span<byte> data) {
@ -64,10 +69,12 @@ public struct PlayerPacket : IPacket {
// offset++; // padding
ScenarioNum = MemoryMarshal.Read<int>(data[offset..]);
offset += 5;
Stage = Encoding.UTF8.GetString(data[offset..(offset + NameSize)]).TrimEnd('\0');
offset += NameSize;
Stage = Encoding.UTF8.GetString(data[offset..(offset + 0x40)]).TrimEnd('\0');
offset += 0x40;
Act = Encoding.UTF8.GetString(data[offset..(offset + NameSize)]).TrimEnd('\0');
offset += NameSize;
SubAct = Encoding.UTF8.GetString(data[offset..(offset + NameSize)]).TrimEnd('\0');
offset += NameSize;
SubAct = Encoding.UTF8.GetString(data[offset..(offset + NameSize)]).TrimEnd('\0');
}
}

View File

@ -17,13 +17,14 @@ Vector3 basePoint = Vector3.Zero;
PacketType[] reboundPackets = {
PacketType.Player,
// PacketType.Cap,
PacketType.Cap,
PacketType.Capture,
PacketType.Costume,
PacketType.Tag,
PacketType.Shine
};
string lastCapture = "";
async Task S() {
IMemoryOwner<byte> owner = MemoryPool<byte>.Shared.Rent(Constants.MaxPacketSize);
while (true) {
@ -31,25 +32,29 @@ async Task S() {
PacketHeader header = MemoryMarshal.Read<PacketHeader>(owner.Memory.Span);
PacketType type = header.Type;
if (header.Id != otherId) continue;
// if (type is PacketType.Player) {
// CapPacket cap = new CapPacket();
// PlayerPacket playerPacket = new PlayerPacket();
// playerPacket.Deserialize(owner.Memory.Span[Constants.HeaderSize..]);
// cap.Position = playerPacket.Position + Vector3.UnitY * 500f;
// // cap.Rotation = Quaternion.CreateFromYawPitchRoll(0,0,0);
// cap.CapAnim = "StayR";
// playerPacket.Position = new Vector3(1000000f);
// playerPacket.ThrowingCap = true;
// header.Id = ownId;
// MemoryMarshal.Write(owner.Memory.Span, ref header);
// playerPacket.Serialize(owner.Memory.Span[Constants.HeaderSize..]);
// await stream.WriteAsync(owner.Memory[..Constants.MaxPacketSize]);
// header.Type = PacketType.Cap;
// MemoryMarshal.Write(owner.Memory.Span, ref header);
// cap.Serialize(owner.Memory.Span[Constants.HeaderSize..]);
// await stream.WriteAsync(owner.Memory[..Constants.MaxPacketSize]);
// continue;
// }
if (type is PacketType.Player) {
// CapPacket cap = new CapPacket();
PlayerPacket playerPacket = new PlayerPacket();
playerPacket.Deserialize(owner.Memory.Span[Constants.HeaderSize..]);
logger.Info(playerPacket.Hack);
if (playerPacket.Hack != lastCapture) {
logger.Info($"Changed to hack: {lastCapture = playerPacket.Hack}");
}
// cap.Position = playerPacket.Position + Vector3.UnitY * 500f;
// cap.Rotation = Quaternion.CreateFromYawPitchRoll(0,0,0);
// cap.CapAnim = "StayR";
// playerPacket.Position = new Vector3(1000000f);
// playerPacket.ThrowingCap = true;
// header.Id = ownId;
// MemoryMarshal.Write(owner.Memory.Span, ref header);
// playerPacket.Serialize(owner.Memory.Span[Constants.HeaderSize..]);
// await stream.WriteAsync(owner.Memory[..Constants.MaxPacketSize]);
// header.Type = PacketType.Cap;
// MemoryMarshal.Write(owner.Memory.Span, ref header);
// cap.Serialize(owner.Memory.Span[Constants.HeaderSize..]);
// await stream.WriteAsync(owner.Memory[..Constants.MaxPacketSize]);
// continue;
}
if (reboundPackets.All(x => x != type)) continue;
header.Id = ownId;
MemoryMarshal.Write(owner.Memory.Span, ref header);