mirror of
https://github.com/Sanae6/SmoOnlineServer.git
synced 2024-11-21 18:55:17 +00:00
Add scenario merging and run reformatting on solution
This commit is contained in:
parent
3d4356ccfc
commit
00626ab12f
9 changed files with 86 additions and 36 deletions
|
@ -25,15 +25,19 @@ public class Client : IDisposable {
|
||||||
Socket = socket;
|
Socket = socket;
|
||||||
Logger = new Logger(socket.RemoteEndPoint?.ToString() ?? "Unknown User???");
|
Logger = new Logger(socket.RemoteEndPoint?.ToString() ?? "Unknown User???");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose() {
|
public void Dispose() {
|
||||||
if (Socket?.Connected is true)
|
if (Socket?.Connected is true)
|
||||||
Socket.Disconnect(false);
|
Socket.Disconnect(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public delegate IPacket PacketTransformerDel(Client? sender, IPacket packet);
|
||||||
|
|
||||||
|
public event PacketTransformerDel? PacketTransformer;
|
||||||
|
|
||||||
public async Task Send<T>(T packet, Client? sender = null) where T : struct, IPacket {
|
public async Task Send<T>(T packet, Client? sender = null) where T : struct, IPacket {
|
||||||
IMemoryOwner<byte> memory = MemoryPool<byte>.Shared.RentZero(Constants.MaxPacketSize);
|
IMemoryOwner<byte> memory = MemoryPool<byte>.Shared.RentZero(Constants.MaxPacketSize);
|
||||||
|
packet = (T) (PacketTransformer?.Invoke(sender, packet) ?? packet);
|
||||||
PacketHeader header = new PacketHeader {
|
PacketHeader header = new PacketHeader {
|
||||||
Id = sender?.Id ?? Guid.Empty,
|
Id = sender?.Id ?? Guid.Empty,
|
||||||
Type = Constants.PacketMap[typeof(T)].Type
|
Type = Constants.PacketMap[typeof(T)].Type
|
||||||
|
|
|
@ -19,7 +19,8 @@ public static class CommandHandler {
|
||||||
if (args.Length == 0) return "No command entered, see help command for valid commands";
|
if (args.Length == 0) return "No command entered, see help command for valid commands";
|
||||||
string commandName = args[0];
|
string commandName = args[0];
|
||||||
return Handlers.TryGetValue(commandName, out Handler? handler) ? handler(args[1..]) : $"Invalid command {args[0]}, see help command for valid commands";
|
return Handlers.TryGetValue(commandName, out Handler? handler) ? handler(args[1..]) : $"Invalid command {args[0]}, see help command for valid commands";
|
||||||
} catch (Exception e) {
|
}
|
||||||
|
catch (Exception e) {
|
||||||
return $"An error occured while trying to process your command: {e}";
|
return $"An error occured while trying to process your command: {e}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Server;
|
namespace Server;
|
||||||
|
|
||||||
public enum FlipOptions {
|
public enum FlipOptions {
|
||||||
Both,
|
Both,
|
||||||
|
|
|
@ -15,6 +15,15 @@ Logger consoleLogger = new Logger("Console");
|
||||||
server.ClientJoined += async (c, type) => {
|
server.ClientJoined += async (c, type) => {
|
||||||
c.Metadata["shineSync"] = new ConcurrentBag<int>();
|
c.Metadata["shineSync"] = new ConcurrentBag<int>();
|
||||||
c.Metadata["loadedSave"] = false;
|
c.Metadata["loadedSave"] = false;
|
||||||
|
c.Metadata["scenario"] = 0;
|
||||||
|
c.PacketTransformer += (sender, packet) => {
|
||||||
|
if (Settings.Instance.Scenario.MergeEnabled && packet is PlayerPacket playerPacket) {
|
||||||
|
playerPacket.ScenarioNum = (int) c.Metadata["scenario"];
|
||||||
|
return playerPacket;
|
||||||
|
}
|
||||||
|
|
||||||
|
return packet;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
async Task ClientSyncShineBag(Client client) {
|
async Task ClientSyncShineBag(Client client) {
|
||||||
|
@ -23,7 +32,8 @@ async Task ClientSyncShineBag(Client client) {
|
||||||
await client.Send(new ShinePacket {
|
await client.Send(new ShinePacket {
|
||||||
ShineId = shine
|
ShineId = shine
|
||||||
});
|
});
|
||||||
} catch {
|
}
|
||||||
|
catch {
|
||||||
// errors that can happen when sending will crash the server :)
|
// errors that can happen when sending will crash the server :)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,8 +49,14 @@ timer.Elapsed += (_, _) => { SyncShineBag(); };
|
||||||
timer.Start();
|
timer.Start();
|
||||||
bool flipEnabled = Settings.Instance.Flip.EnabledOnStart;
|
bool flipEnabled = Settings.Instance.Flip.EnabledOnStart;
|
||||||
|
|
||||||
float MarioSize(bool is2d) => is2d ? 180 : 160;
|
float MarioSize(bool is2d) {
|
||||||
|
return is2d ? 180 : 160;
|
||||||
|
}
|
||||||
|
|
||||||
server.PacketHandler = (c, p) => {
|
server.PacketHandler = (c, p) => {
|
||||||
|
{
|
||||||
|
if (p is PlayerPacket playerPacket) c.Metadata["scenario"] = playerPacket.ScenarioNum;
|
||||||
|
}
|
||||||
switch (p) {
|
switch (p) {
|
||||||
case CostumePacket:
|
case CostumePacket:
|
||||||
ClientSyncShineBag(c);
|
ClientSyncShineBag(c);
|
||||||
|
@ -77,12 +93,25 @@ server.PacketHandler = (c, p) => {
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
//
|
|
||||||
// CommandHandler.RegisterCommand("scenario", args => {
|
CommandHandler.RegisterCommand("scenario", args => {
|
||||||
// const string optionUsage = "Valid options: split <"
|
const string optionUsage = "Valid options: merge <true/false>";
|
||||||
// if (args.Length < 1)
|
if (args.Length < 1)
|
||||||
// return
|
return optionUsage;
|
||||||
// });
|
switch (args[0]) {
|
||||||
|
case "merge" when args.Length == 2: {
|
||||||
|
if (bool.TryParse(args[1], out bool result)) {
|
||||||
|
Settings.Instance.Scenario.MergeEnabled = result;
|
||||||
|
Settings.SaveSettings();
|
||||||
|
return result ? "Enabled scenario merge" : "Disabled scenario merge";
|
||||||
|
}
|
||||||
|
|
||||||
|
return optionUsage;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return optionUsage;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
CommandHandler.RegisterCommand("list", _ => $"List: {string.Join(", ", server.Clients.Select(x => $"{x.Name} ({x.Id})"))}");
|
CommandHandler.RegisterCommand("list", _ => $"List: {string.Join(", ", server.Clients.Select(x => $"{x.Name} ({x.Id})"))}");
|
||||||
|
|
||||||
|
@ -142,9 +171,8 @@ CommandHandler.RegisterCommand("shine", args => {
|
||||||
return $"Shines: {string.Join(", ", shineBag)}";
|
return $"Shines: {string.Join(", ", shineBag)}";
|
||||||
case "clear" when args.Length == 1:
|
case "clear" when args.Length == 1:
|
||||||
shineBag.Clear();
|
shineBag.Clear();
|
||||||
foreach (ConcurrentBag<int> playerBag in server.Clients.Select(serverClient => (ConcurrentBag<int>) serverClient.Metadata["shineSync"])) {
|
foreach (ConcurrentBag<int> playerBag in server.Clients.Select(serverClient => (ConcurrentBag<int>) serverClient.Metadata["shineSync"])) playerBag.Clear();
|
||||||
playerBag.Clear();
|
|
||||||
}
|
|
||||||
return "Cleared shine bags";
|
return "Cleared shine bags";
|
||||||
default:
|
default:
|
||||||
return optionUsage;
|
return optionUsage;
|
||||||
|
|
|
@ -38,22 +38,28 @@ public class Server {
|
||||||
}
|
}
|
||||||
|
|
||||||
Task.Run(() => HandleSocket(socket));
|
Task.Run(() => HandleSocket(socket));
|
||||||
} catch {
|
}
|
||||||
|
catch {
|
||||||
// super ignore this
|
// super ignore this
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (OperationCanceledException) {
|
}
|
||||||
|
catch (OperationCanceledException) {
|
||||||
// ignore the exception, it's just for closing the server
|
// ignore the exception, it's just for closing the server
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.Info("Server closing");
|
Logger.Info("Server closing");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
serverSocket.Shutdown(SocketShutdown.Both);
|
serverSocket.Shutdown(SocketShutdown.Both);
|
||||||
} catch (Exception) {
|
}
|
||||||
|
catch (Exception) {
|
||||||
// ignore
|
// ignore
|
||||||
} finally {
|
}
|
||||||
|
finally {
|
||||||
serverSocket.Close();
|
serverSocket.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.Info("Server closed");
|
Logger.Info("Server closed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,11 +72,11 @@ public class Server {
|
||||||
|
|
||||||
// broadcast packets to all clients
|
// broadcast packets to all clients
|
||||||
public delegate void PacketReplacer<in T>(Client from, Client to, T value); // replacer must send
|
public delegate void PacketReplacer<in T>(Client from, Client to, T value); // replacer must send
|
||||||
|
|
||||||
public async Task BroadcastReplace<T>(T packet, Client sender, PacketReplacer<T> packetReplacer) where T : struct, IPacket {
|
public async Task BroadcastReplace<T>(T packet, Client sender, PacketReplacer<T> packetReplacer) where T : struct, IPacket {
|
||||||
foreach (Client client in Clients.Where(client => sender.Id != client.Id)) {
|
foreach (Client client in Clients.Where(client => sender.Id != client.Id)) packetReplacer(sender, client, packet);
|
||||||
packetReplacer(sender, client, packet);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Broadcast<T>(T packet, Client sender) where T : struct, IPacket {
|
public async Task Broadcast<T>(T packet, Client sender) where T : struct, IPacket {
|
||||||
IMemoryOwner<byte> memory = memoryPool.RentZero(Constants.MaxPacketSize);
|
IMemoryOwner<byte> memory = memoryPool.RentZero(Constants.MaxPacketSize);
|
||||||
|
|
||||||
|
@ -107,7 +113,7 @@ public class Server {
|
||||||
|
|
||||||
|
|
||||||
private async void HandleSocket(Socket socket) {
|
private async void HandleSocket(Socket socket) {
|
||||||
Client client = new Client(socket) { Server = this };
|
Client client = new Client(socket) {Server = this};
|
||||||
IMemoryOwner<byte> memory = null!;
|
IMemoryOwner<byte> memory = null!;
|
||||||
bool first = true;
|
bool first = true;
|
||||||
try {
|
try {
|
||||||
|
@ -205,7 +211,9 @@ public class Server {
|
||||||
});
|
});
|
||||||
|
|
||||||
Logger.Info($"Client {client.Name} ({client.Id}/{socket.RemoteEndPoint}) connected.");
|
Logger.Info($"Client {client.Name} ({client.Id}/{socket.RemoteEndPoint}) connected.");
|
||||||
} else if (header.Id != client.Id && client.Id != Guid.Empty) throw new Exception($"Client {client.Name} sent packet with invalid client id {header.Id} instead of {client.Id}");
|
} else if (header.Id != client.Id && client.Id != Guid.Empty) {
|
||||||
|
throw new Exception($"Client {client.Name} sent packet with invalid client id {header.Id} instead of {client.Id}");
|
||||||
|
}
|
||||||
|
|
||||||
if (header.Type == PacketType.Costume) {
|
if (header.Type == PacketType.Costume) {
|
||||||
CostumePacket costumePacket = new CostumePacket {
|
CostumePacket costumePacket = new CostumePacket {
|
||||||
|
@ -223,14 +231,16 @@ public class Server {
|
||||||
memory.Dispose();
|
memory.Dispose();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} catch (Exception e){
|
}
|
||||||
|
catch (Exception e) {
|
||||||
client.Logger.Error($"Packet handler warning: {e}");
|
client.Logger.Error($"Packet handler warning: {e}");
|
||||||
}
|
}
|
||||||
|
|
||||||
Broadcast(memory, client);
|
Broadcast(memory, client);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
}
|
||||||
if (e is SocketException { SocketErrorCode: SocketError.ConnectionReset }) {
|
catch (Exception e) {
|
||||||
|
if (e is SocketException {SocketErrorCode: SocketError.ConnectionReset}) {
|
||||||
client.Logger.Info($"Client {socket.RemoteEndPoint} ({client.Id}) disconnected from the server");
|
client.Logger.Info($"Client {socket.RemoteEndPoint} ({client.Id}) disconnected from the server");
|
||||||
} else {
|
} else {
|
||||||
client.Logger.Error($"Exception on socket {socket.RemoteEndPoint} ({client.Id}) and disconnecting for: {e}");
|
client.Logger.Error($"Exception on socket {socket.RemoteEndPoint} ({client.Id}) and disconnecting for: {e}");
|
||||||
|
|
|
@ -8,11 +8,11 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Shared\Shared.csproj" />
|
<ProjectReference Include="..\Shared\Shared.csproj"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.1"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -20,7 +20,8 @@ public class Settings {
|
||||||
try {
|
try {
|
||||||
Instance = JsonConvert.DeserializeObject<Settings>(text, new StringEnumConverter(new CamelCaseNamingStrategy())) ?? Instance;
|
Instance = JsonConvert.DeserializeObject<Settings>(text, new StringEnumConverter(new CamelCaseNamingStrategy())) ?? Instance;
|
||||||
Logger.Info("Loaded settings from settings.json");
|
Logger.Info("Loaded settings from settings.json");
|
||||||
} catch (Exception e) {
|
}
|
||||||
|
catch (Exception e) {
|
||||||
Logger.Warn($"Failed to load settings.json: {e}");
|
Logger.Warn($"Failed to load settings.json: {e}");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -32,19 +33,25 @@ public class Settings {
|
||||||
try {
|
try {
|
||||||
File.WriteAllText("settings.json", JsonConvert.SerializeObject(Instance, Formatting.Indented, new StringEnumConverter(new CamelCaseNamingStrategy())));
|
File.WriteAllText("settings.json", JsonConvert.SerializeObject(Instance, Formatting.Indented, new StringEnumConverter(new CamelCaseNamingStrategy())));
|
||||||
Logger.Info("Saved settings to settings.json");
|
Logger.Info("Saved settings to settings.json");
|
||||||
} catch (Exception e) {
|
}
|
||||||
|
catch (Exception e) {
|
||||||
Logger.Error($"Failed to save settings.json {e}");
|
Logger.Error($"Failed to save settings.json {e}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ServerTable Server { get; set; } = new ServerTable();
|
public ServerTable Server { get; set; } = new ServerTable();
|
||||||
public FlipTable Flip { get; set; } = new FlipTable();
|
public FlipTable Flip { get; set; } = new FlipTable();
|
||||||
|
public ScenarioTable Scenario { get; set; } = new ScenarioTable();
|
||||||
|
|
||||||
public class ServerTable {
|
public class ServerTable {
|
||||||
public string Address { get; set; } = IPAddress.Any.ToString();
|
public string Address { get; set; } = IPAddress.Any.ToString();
|
||||||
public ushort Port { get; set; } = 1027;
|
public ushort Port { get; set; } = 1027;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class ScenarioTable {
|
||||||
|
public bool MergeEnabled { get; set; } = false;
|
||||||
|
}
|
||||||
|
|
||||||
public class FlipTable {
|
public class FlipTable {
|
||||||
public List<Guid> Players { get; set; } = new List<Guid>();
|
public List<Guid> Players { get; set; } = new List<Guid>();
|
||||||
public bool EnabledOnStart { get; set; } = true;
|
public bool EnabledOnStart { get; set; } = true;
|
||||||
|
|
|
@ -8,8 +8,8 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ObjectDumper.NET" Version="3.3.13" />
|
<PackageReference Include="ObjectDumper.NET" Version="3.3.13"/>
|
||||||
<PackageReference Include="System.Numerics.Vectors" Version="4.5.0" />
|
<PackageReference Include="System.Numerics.Vectors" Version="4.5.0"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -25,6 +25,7 @@ PacketType[] reboundPackets = {
|
||||||
};
|
};
|
||||||
|
|
||||||
string lastCapture = "";
|
string lastCapture = "";
|
||||||
|
|
||||||
async Task S() {
|
async Task S() {
|
||||||
IMemoryOwner<byte> owner = MemoryPool<byte>.Shared.Rent(Constants.MaxPacketSize);
|
IMemoryOwner<byte> owner = MemoryPool<byte>.Shared.Rent(Constants.MaxPacketSize);
|
||||||
while (true) {
|
while (true) {
|
||||||
|
@ -37,9 +38,7 @@ async Task S() {
|
||||||
PlayerPacket playerPacket = new PlayerPacket();
|
PlayerPacket playerPacket = new PlayerPacket();
|
||||||
playerPacket.Deserialize(owner.Memory.Span[Constants.HeaderSize..]);
|
playerPacket.Deserialize(owner.Memory.Span[Constants.HeaderSize..]);
|
||||||
logger.Info(playerPacket.Hack);
|
logger.Info(playerPacket.Hack);
|
||||||
if (playerPacket.Hack != lastCapture) {
|
if (playerPacket.Hack != lastCapture) logger.Info($"Changed to hack: {lastCapture = playerPacket.Hack}");
|
||||||
logger.Info($"Changed to hack: {lastCapture = playerPacket.Hack}");
|
|
||||||
}
|
|
||||||
// cap.Position = playerPacket.Position + Vector3.UnitY * 500f;
|
// cap.Position = playerPacket.Position + Vector3.UnitY * 500f;
|
||||||
// cap.Rotation = Quaternion.CreateFromYawPitchRoll(0,0,0);
|
// cap.Rotation = Quaternion.CreateFromYawPitchRoll(0,0,0);
|
||||||
// cap.CapAnim = "StayR";
|
// cap.CapAnim = "StayR";
|
||||||
|
@ -55,6 +54,7 @@ async Task S() {
|
||||||
// await stream.WriteAsync(owner.Memory[..Constants.MaxPacketSize]);
|
// await stream.WriteAsync(owner.Memory[..Constants.MaxPacketSize]);
|
||||||
// continue;
|
// continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reboundPackets.All(x => x != type)) continue;
|
if (reboundPackets.All(x => x != type)) continue;
|
||||||
header.Id = ownId;
|
header.Id = ownId;
|
||||||
MemoryMarshal.Write(owner.Memory.Span, ref header);
|
MemoryMarshal.Write(owner.Memory.Span, ref header);
|
||||||
|
|
Loading…
Reference in a new issue