using LiteNetLib; using LiteNetLib.Utils; namespace OnekoOnline.Net; class Server { public static bool ServerRunning = false; readonly Dictionary users = []; readonly EventBasedNetListener Listener; readonly NetManager NetServer; public Server(int Port, int MaxConnections, string ServerPassword = "") { Listener = new(); NetServer = new(Listener); ServerRunning = NetServer.Start(Port); if (!ServerRunning) return; Listener.ConnectionRequestEvent += request => { if (NetServer.ConnectedPeersCount < MaxConnections) request.AcceptIfKey(ServerPassword); else request.Reject(); }; Listener.PeerConnectedEvent += peer => { Console.WriteLine($"New connection from {peer.EndPoint}!"); users.Add(peer.Id, new ServerUser(peer.Id, peer)); }; Listener.PeerDisconnectedEvent += (peer, info) => { Console.WriteLine($"{peer.EndPoint} Disconnected! Reason: {info.Reason}"); users.Remove(peer.Id); NetDataWriter writer = new(); //Send all users a disconnect message foreach (ServerUser user in users.Values) { user.Peer.Send(new PacketInfo(PacketType.Disconnect, peer.Id).Serialize(), DeliveryMethod.ReliableUnordered); } }; Listener.NetworkReceiveEvent += (fromPeer, dataReader, channel, DeliveryMethod) => { PacketInfo info = dataReader.GetPacketInfo(); ServerUser user = users[fromPeer.Id]; //Size limits for packet types if (info.Type == PacketType.UserInfo && dataReader.AvailableBytes > 40000) return; else if (info.Type != PacketType.UserInfo && dataReader.AvailableBytes > 500) return; NetDataWriter writer = new(); if (info.Type == PacketType.UserInfo) { user.Username = dataReader.GetString(); if (user.Username.Length > 40) user.Username = user.Username[0..40]; //Clamp username length user.Nekoname = dataReader.GetString(); if (user.Nekoname.Length > 40) user.Nekoname = user.Nekoname[0..40]; //Clamp nekoname length user.SpriteSheet = dataReader.GetBytesWithLength(); if (!user.Initialized) { //Send ID writer.ResetWithInfo(new PacketInfo(PacketType.UserId, -1)); writer.Put(user.Id); fromPeer.Send(writer, DeliveryMethod.ReliableUnordered); user.Initialized = true; Console.WriteLine($"{fromPeer.EndPoint} is {user.Username}!"); foreach (ServerUser toSend in users.Values) { if (!toSend.Initialized || toSend == user) continue; //Send all current users spritesheets to this user. writer.ResetWithInfo(new PacketInfo(PacketType.UserInfo, toSend.Id)); writer.Put(toSend.Username); writer.Put(toSend.Nekoname); writer.PutBytesWithLength(toSend.SpriteSheet); fromPeer.Send(writer, DeliveryMethod.ReliableUnordered); //Send all current users this users spritesheet writer.ResetWithInfo(new PacketInfo(PacketType.UserInfo, user.Id)); writer.Put(user.Username); writer.Put(user.Nekoname); writer.PutBytesWithLength(user.SpriteSheet); toSend.Peer.Send(writer, DeliveryMethod.ReliableUnordered); } } } if (info.Type == PacketType.OnekoState || info.Type == PacketType.MouseState) { writer.ResetWithInfo(new PacketInfo(info.Type, fromPeer.Id)); writer.Put(dataReader.GetRemainingBytes()); foreach (ServerUser toSend in users.Values) { if (!toSend.Initialized || toSend == user) continue; toSend.Peer.Send(writer, DeliveryMethod); } } }; Console.WriteLine("Server Initialized!"); } public void Poll() { NetServer.PollEvents(); } public void Disconnect() { NetServer.DisconnectAll(); } } class ServerUser(int id, NetPeer peer) : User(id), IDisposable { //Network public readonly NetPeer Peer = peer; public void Dispose() { Peer.Disconnect(); } }