summaryrefslogtreecommitdiff
path: root/NetClient.cs
blob: db62337c1ca57ab590c808384d38efe713ad8692 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
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<int, ClientUser> users = [];
    public ReadOnlyDictionary<int, ClientUser> Users => users.AsReadOnly();

    static public Action<ClientUser>? UserConnected;
    static public Action<ClientUser>? UserDisconnected;
    static public Action<NetDataReader, ClientUser, PacketType>? 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;
}