2025-12-19 17:54:50 +01:00
|
|
|
using System.Diagnostics;
|
2026-03-12 22:33:35 +01:00
|
|
|
using GlobalClassLib;
|
2025-12-19 17:54:50 +01:00
|
|
|
using LiteNetLib;
|
|
|
|
|
using LiteNetLib.Utils;
|
|
|
|
|
using PacketLib;
|
|
|
|
|
|
2026-03-28 09:59:31 +01:00
|
|
|
namespace ONDServer.Net;
|
2025-12-19 17:54:50 +01:00
|
|
|
|
2026-03-28 09:59:31 +01:00
|
|
|
public static class Server {
|
2025-12-19 17:54:50 +01:00
|
|
|
public static ServerPlayer P1;
|
|
|
|
|
public static ServerPlayer P2;
|
2026-01-25 11:16:54 +01:00
|
|
|
public static readonly Dictionary<int, ServerPlayer> Players = new();
|
2026-03-28 09:59:31 +01:00
|
|
|
public static ServerPlayer OtherPlayer(ServerPlayer player) => player.State.Pid == P1.State.Pid ? P2 : P1;
|
2026-03-21 21:23:33 +01:00
|
|
|
|
|
|
|
|
public static bool AutoStop{ get; set; } = true;
|
2025-12-19 17:54:50 +01:00
|
|
|
|
|
|
|
|
private static EventBasedNetListener listener;
|
|
|
|
|
private static NetManager server;
|
|
|
|
|
|
2026-03-28 09:59:31 +01:00
|
|
|
private const int TARGET_TICK_RATE = 30;
|
|
|
|
|
private const double TARGET_DELTA_TIME = 1.0 / TARGET_TICK_RATE;
|
|
|
|
|
private const long TARGET_TICK_TIME_MS = 1000 / TARGET_TICK_RATE;
|
2025-12-19 17:54:50 +01:00
|
|
|
|
|
|
|
|
private static NetDataWriter writer;
|
|
|
|
|
private static NetPacketProcessor processor;
|
|
|
|
|
|
|
|
|
|
private static bool isRunning;
|
2026-02-26 16:24:55 +01:00
|
|
|
|
2026-03-28 09:59:31 +01:00
|
|
|
// AI generated
|
2025-12-19 17:54:50 +01:00
|
|
|
public static void Start(int port) {
|
|
|
|
|
writer = new NetDataWriter();
|
|
|
|
|
processor = new NetPacketProcessor();
|
|
|
|
|
|
|
|
|
|
NestedTypeManager.AutoRegister(processor);
|
|
|
|
|
|
|
|
|
|
processor.SubscribeReusable<JoinPacket, NetPeer>(OnJoinReceived);
|
|
|
|
|
processor.SubscribeReusable<PlayerCommandPacket, NetPeer>(OnCommandReceived);
|
|
|
|
|
|
|
|
|
|
listener = new EventBasedNetListener(); // ← Initialize the listener!
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
server = new NetManager(listener){
|
|
|
|
|
AutoRecycle = true
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Console.WriteLine($"Starting server on {port}");
|
|
|
|
|
|
|
|
|
|
listener.ConnectionRequestEvent += request => {
|
|
|
|
|
Console.WriteLine($"Connection Request from {request.RemoteEndPoint}");
|
2026-01-25 11:16:54 +01:00
|
|
|
if (Players.Count >= 2){
|
2025-12-19 17:54:50 +01:00
|
|
|
Console.WriteLine($"{request.RemoteEndPoint} denied, server full");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
request.Accept();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
listener.NetworkReceiveEvent += (peer, reader, channel, method) => {
|
|
|
|
|
OnNetworkReceive(peer, reader, method);
|
|
|
|
|
};
|
|
|
|
|
|
2026-03-28 09:59:31 +01:00
|
|
|
listener.PeerDisconnectedEvent += OnPeerDisconnected;
|
2025-12-19 17:54:50 +01:00
|
|
|
|
|
|
|
|
server.Start(port);
|
2026-02-01 14:53:27 +01:00
|
|
|
|
2025-12-19 17:54:50 +01:00
|
|
|
Run();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static void SendPacket<T>(T packet, NetPeer peer, DeliveryMethod deliveryMethod = DeliveryMethod.ReliableOrdered) where T : class, new() {
|
|
|
|
|
if (peer != null) {
|
|
|
|
|
writer.Reset();
|
|
|
|
|
processor.Write(writer, packet);
|
|
|
|
|
peer.Send(writer, deliveryMethod);
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-01-25 11:16:54 +01:00
|
|
|
public static void SendPacket<T>(T packet, int pid, DeliveryMethod deliveryMethod = DeliveryMethod.ReliableOrdered) where T : class, new() {
|
2026-03-28 09:59:31 +01:00
|
|
|
SendPacket(packet, Players[pid].Peer, deliveryMethod);
|
2025-12-19 17:54:50 +01:00
|
|
|
}
|
|
|
|
|
public static void SendPacketToAll<T>(T packet, DeliveryMethod deliveryMethod = DeliveryMethod.ReliableOrdered) where T : class, new() {
|
2026-01-25 11:16:54 +01:00
|
|
|
foreach (var player in Players.Values){
|
2026-03-28 09:59:31 +01:00
|
|
|
SendPacket(packet, player.Peer, deliveryMethod);
|
2025-12-19 17:54:50 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-25 11:16:54 +01:00
|
|
|
public static void SendUpdate(GameEvent[] gevents, int pid) {
|
2026-03-28 09:59:31 +01:00
|
|
|
SendPacket(new UpdatePlayerPacket{Events = gevents}, pid);
|
2025-12-19 17:54:50 +01:00
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static void SendUpdateToAll(GameEvent[] gevents) {
|
2026-03-28 09:59:31 +01:00
|
|
|
SendPacketToAll(new UpdatePlayerPacket{Events = gevents});
|
2025-12-19 17:54:50 +01:00
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static void OnJoinReceived(JoinPacket packet, NetPeer peer) {
|
2026-03-28 09:59:31 +01:00
|
|
|
Console.WriteLine($"Received join from {packet.Username} (pid: {(uint)peer.Id})");
|
2025-12-19 17:54:50 +01:00
|
|
|
|
2026-01-25 11:16:54 +01:00
|
|
|
ServerPlayer newPlayer = (Players[peer.Id] = new ServerPlayer {
|
2026-03-28 09:59:31 +01:00
|
|
|
Peer = peer,
|
|
|
|
|
State = new PlayerState(peer.Id, GameLogic.START_CAMERA, [false, false, false],
|
|
|
|
|
Power.MAX_POWER_VALUE, false),
|
|
|
|
|
Username = packet.Username
|
2025-12-19 17:54:50 +01:00
|
|
|
});
|
2026-03-08 16:55:49 +01:00
|
|
|
|
|
|
|
|
|
2026-01-25 11:16:54 +01:00
|
|
|
if (Players.Count == 1){
|
2026-03-28 09:59:31 +01:00
|
|
|
newPlayer.State.OfficeTileId = 10;
|
|
|
|
|
newPlayer.State.NeighbouringTiles = [5, 11, 15];
|
|
|
|
|
SendPacket(new JoinAcceptPacket { State = newPlayer.State }, peer);
|
2025-12-19 17:54:50 +01:00
|
|
|
P1 = newPlayer;
|
|
|
|
|
}
|
|
|
|
|
else{
|
2026-03-28 09:59:31 +01:00
|
|
|
newPlayer.State.OfficeTileId = 14;
|
|
|
|
|
newPlayer.State.NeighbouringTiles = [19, 13, 9];
|
2026-03-08 16:55:49 +01:00
|
|
|
|
2026-03-28 09:59:31 +01:00
|
|
|
SendPacket(new JoinAcceptPacket { State = newPlayer.State }, peer);
|
2025-12-19 17:54:50 +01:00
|
|
|
P2 = newPlayer;
|
2026-03-08 16:55:49 +01:00
|
|
|
|
2026-03-28 09:59:31 +01:00
|
|
|
SendPacket(new OpponentInitPacket{State = newPlayer.State, Username = newPlayer.Username}, P1.Peer);
|
|
|
|
|
SendPacket(new OpponentInitPacket{State = P1.State, Username = P1.Username}, P2.Peer);
|
2026-03-08 16:55:49 +01:00
|
|
|
GameLogic.Init();
|
2025-12-19 17:54:50 +01:00
|
|
|
}
|
2026-03-08 16:55:49 +01:00
|
|
|
|
2025-12-19 17:54:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static void OnNetworkReceive(NetPeer peer, NetPacketReader reader, DeliveryMethod deliveryMethod) {
|
|
|
|
|
processor.ReadAllPackets(reader, peer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static void OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo) {
|
2026-03-25 16:37:18 +01:00
|
|
|
if (Players.Count == 2){
|
2026-03-28 09:59:31 +01:00
|
|
|
GameLogic.DeclareWinner(OtherPlayer(Players.Values.First(p => Equals(p.Peer, peer))));
|
2026-03-25 16:37:18 +01:00
|
|
|
}
|
2026-03-21 21:23:33 +01:00
|
|
|
|
2026-03-25 16:37:18 +01:00
|
|
|
Players.Remove(peer.Id);
|
|
|
|
|
if (Players.Count == 0){
|
|
|
|
|
Stop();
|
2025-12-19 17:54:50 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static void OnCommandReceived(PlayerCommandPacket packet, NetPeer peer) {
|
2026-03-28 09:59:31 +01:00
|
|
|
CommandProcessor.Evaluate(packet.Commands, peer.Id);
|
2025-12-19 17:54:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static void Update() {
|
|
|
|
|
server.PollEvents();
|
2026-03-08 16:55:49 +01:00
|
|
|
GameLogic.Update();
|
2025-12-19 17:54:50 +01:00
|
|
|
}
|
|
|
|
|
|
2026-03-28 09:59:31 +01:00
|
|
|
// AI generated
|
2025-12-19 17:54:50 +01:00
|
|
|
public static void Run(CancellationToken cancellationToken = default)
|
|
|
|
|
{
|
|
|
|
|
isRunning = true;
|
|
|
|
|
var stopwatch = Stopwatch.StartNew();
|
|
|
|
|
long previousTicks = 0;
|
|
|
|
|
|
|
|
|
|
while (isRunning && !cancellationToken.IsCancellationRequested)
|
|
|
|
|
{
|
|
|
|
|
long currentTicks = stopwatch.ElapsedMilliseconds;
|
|
|
|
|
long elapsed = currentTicks - previousTicks;
|
|
|
|
|
|
2026-03-28 09:59:31 +01:00
|
|
|
if (elapsed >= TARGET_TICK_TIME_MS)
|
2025-12-19 17:54:50 +01:00
|
|
|
{
|
|
|
|
|
Update();
|
|
|
|
|
|
|
|
|
|
previousTicks = currentTicks;
|
2026-03-28 09:59:31 +01:00
|
|
|
if (elapsed > TARGET_TICK_TIME_MS * 2)
|
2025-12-19 17:54:50 +01:00
|
|
|
{
|
2026-03-28 09:59:31 +01:00
|
|
|
Console.WriteLine($"Warning: Tick took {elapsed}ms (target: {TARGET_TICK_TIME_MS}ms)");
|
2025-12-19 17:54:50 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
|
2026-03-28 09:59:31 +01:00
|
|
|
int sleepTime = (int)(TARGET_TICK_TIME_MS - elapsed - 2);
|
2025-12-19 17:54:50 +01:00
|
|
|
if (sleepTime > 0)
|
|
|
|
|
{
|
|
|
|
|
Thread.Sleep(sleepTime);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-28 09:59:31 +01:00
|
|
|
while (stopwatch.ElapsedMilliseconds - previousTicks < TARGET_TICK_TIME_MS){
|
2025-12-19 17:54:50 +01:00
|
|
|
Thread.SpinWait(1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static void Stop()
|
|
|
|
|
{
|
|
|
|
|
isRunning = false;
|
2026-03-21 21:23:33 +01:00
|
|
|
server.Stop();
|
2025-12-19 17:54:50 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|