mirror of
https://github.com/Sanae6/SmoOnlineServer.git
synced 2024-11-21 18:55:17 +00:00
Implement console commands and settings (flip list broken)
This commit is contained in:
parent
78af568603
commit
821301e756
8 changed files with 220 additions and 68 deletions
26
Server/CommandHandler.cs
Normal file
26
Server/CommandHandler.cs
Normal 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
7
Server/FlipOptions.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace Server;
|
||||
|
||||
public enum FlipOptions {
|
||||
Both,
|
||||
Self,
|
||||
Others
|
||||
}
|
|
@ -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();
|
|
@ -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
65
Server/Settings.cs
Normal 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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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');
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue