using System.Collections.ObjectModel; using LiteNetLib; using LiteNetLib.Utils; namespace OnekoOnline.Net; class Client { public static readonly string UserName = OnekoOnline.Config.GetValue("UserName", "User"); public int Id {get; private set;} = -1; public bool Connected => NetClient?.ConnectedPeersCount > 0 && Id != -1; readonly EventBasedNetListener Listener; readonly NetManager NetClient; public NetPeer ConnectedServer => NetClient.FirstPeer; readonly Dictionary users = []; public ReadOnlyDictionary Users => users.AsReadOnly(); static public Action? UserConnected; static public Action? UserDisconnected; static public Action? PacketRecived; static public Action? ServerDisconnected; public Client(string ServerAddress, int port, string ServerPassword) { if (string.IsNullOrEmpty(ServerAddress)) throw new Exception("Server Address invalid!"); if (string.IsNullOrEmpty(UserName)) throw new Exception("Invalid Username!!"); if (UserName.Length > 40) throw new Exception("Username too long!"); Listener = new(); NetClient = new(Listener); NetClient.Start(); NetClient.Connect(ServerAddress, port, ServerPassword); Listener.PeerConnectedEvent += peer => { Console.WriteLine("Connected to the Server!"); NetDataWriter writer = new(); writer.Put(new PacketInfo(PacketType.UserInfo, Id)); writer.Put(UserName, 40); writer.PutBytesWithLength(MouseLocal.Instance!.Cursor.Serialize()); writer.Put(OnekoOnline.SpectatorMode); if (!OnekoOnline.SpectatorMode) { writer.Put(OnekoLocal.Instance!.Name, 40); writer.PutBytesWithLength(OnekoLocal.Instance!.SpriteSheet.Serialize()); } if (writer.Length > 50000) { peer.Disconnect(); Console.WriteLine("You have too much data to send, try reducing the complexity of your sprites."); } else { peer.Send(writer, DeliveryMethod.ReliableUnordered); } }; Listener.PeerDisconnectedEvent += (peer, info) => { ServerDisconnected?.Invoke(); users.Clear(); Console.WriteLine("Server Disconnected! You're offline!"); OnekoOnline.AppTitle = "Oneko Offline"; }; Listener.NetworkReceiveEvent += (fromPeer, reader, channel, DeliveryMethod) => { if (reader.AvailableBytes < PacketInfo.SizeOf || reader.AvailableBytes > 40000) return; PacketInfo info = reader.GetPacketInfo(); if (info.Type == PacketType.UserId) { Id = reader.GetInt(); return; } ClientUser? from; if (!users.TryGetValue(info.FromId, out from)) { from = new(info.FromId); users.Add(from.Id, from); } if (info.Type == PacketType.Disconnect) { //Disconnect user that has disconnected if (from.Username != null) Console.WriteLine($"User {from.Username} left."); users.Remove(info.FromId); UserDisconnected?.Invoke(from); return; } else if (info.Type == PacketType.UserInfo) { from.Username = reader.GetString(40); from.CursorSprite = reader.GetBytesWithLength(); from.SpectatorMode = reader.GetBool(); if (!from.SpectatorMode) { from.Nekoname = reader.GetString(40); from.SpriteSheet = reader.GetBytesWithLength(); Console.WriteLine($"User {from.Username} joined with {from.Nekoname}!"); } else { Console.WriteLine($"User {from.Username} joined as a Spectator!"); } from.Initialized = true; UserConnected?.Invoke(from); return; } if (reader.AvailableBytes > 500) return; PacketRecived?.Invoke(reader, from, info.Type); }; } public void Poll() { NetClient.PollEvents(); } public void Disconnect() { NetClient.DisconnectAll(); } } class ClientUser(int id) : User(id) { public bool SpectatorMode; public byte[]? SpriteSheet; public byte[]? CursorSprite; public string? Username; public string? Nekoname; }