summaryrefslogtreecommitdiff
path: root/NetServer.cs
blob: cab6a00fe5775e23fafe3460c7131ad6fdbb7392 (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
using System.Buffers.Binary;
using System.Collections.Concurrent;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Text;
using System.Threading.Channels;

namespace OnekoOnline.Net;

static class Server
{
    public static bool ServerRunning = false;

    static readonly ConcurrentDictionary<int, ServerUser> users = [];

    public static async void Init(int port)
    {
        TcpListener listener = new(IPAddress.Any, port);
        listener.Start(10);
        ServerRunning = true;

        while (ServerRunning)
        {
            TcpClient tcpSocket = await listener.AcceptTcpClientAsync();
            UdpClient udpSocket = new((IPEndPoint)tcpSocket.Client.RemoteEndPoint!);

            ServerUser newUser = new(tcpSocket.GetHashCode(), tcpSocket, udpSocket);

            Console.WriteLine("Accepted a connection from: " + tcpSocket.Client.RemoteEndPoint);

            await Task.Factory.StartNew(() => SetupUser(newUser));
        }
    }

    static async Task SetupUser(ServerUser user)
    {
        await Task.WhenAny(ExchangeUserData(user), Task.Delay(7000));

        if (user.ExchangedData) {
            //Send current user data
            foreach (ServerUser otherUser in users.Values.Where(u => u.ExchangedData)) {
                //Send username and spritesheet
                await NetBase.SendReliableData(new Packet(PacketType.OnekoSpritesheet, otherUser.SpriteSheet!, otherUser.Id), user.Tcp.GetStream());
                await NetBase.SendReliableData(new Packet(PacketType.Username, Encoding.UTF8.GetBytes(otherUser.Username!), otherUser.Id), user.Tcp.GetStream());
                //Ask users to take your data
                await otherUser.ToSend.Writer.WriteAsync(new Packet(PacketType.Username, Encoding.UTF8.GetBytes(user.Username!), user.Id));
                await otherUser.ToSend.Writer.WriteAsync(new Packet(PacketType.OnekoSpritesheet, user.SpriteSheet!, user.Id));
                Console.WriteLine($"Sent {otherUser.Username}'s Data to {user.Username}");
            }

            users.GetOrAdd(user.Id, user);
            await Task.Factory.StartNew(() => UpdateUser(user));
        } else {
            Console.WriteLine($"{user.Tcp.Client.RemoteEndPoint} failed to send required data, terminating connection.");
            user.Dispose();
        }
    }

    static async Task ExchangeUserData(ServerUser user)
    {
        while (!user.ExchangedData)
        {
            Packet packet = await NetBase.GetReliableData(user.Tcp.GetStream());

            if (packet.Data.Length > 50000) break;

            if (packet.Type == PacketType.Username) {
                user.Username = Encoding.UTF8.GetString(packet.Data);
            } else if (packet.Type == PacketType.OnekoSpritesheet) {
                user.SpriteSheet = packet.Data;
            }
        }

        if (!user.ExchangedData) return;

        //Send ID
        byte[] idData = new byte[sizeof(int)];
        BinaryPrimitives.WriteInt32LittleEndian(idData, user.Id);
        await NetBase.SendReliableData(new Packet(PacketType.UserId, idData, 0), user.Tcp.GetStream());

        Console.WriteLine($"{user.Tcp.Client.RemoteEndPoint} is {user.Username}!");
    }

    static async Task UpdateUser(ServerUser user)
    {
        while (user.Tcp.Connected) {
            Packet toSend = await user.ToSend.Reader.ReadAsync(user.UpdateCancel.Token);
            await NetBase.SendReliableData(toSend, user.Tcp.GetStream(), user.UpdateCancel.Token);
        }
        //users.TryRemove(user.Id, out _);
        //user.Dispose();
    }
}

class ServerUser(int id, TcpClient tcp, UdpClient udp) : User(id), IDisposable
{
    //Network
    public readonly TcpClient Tcp = tcp;
    public readonly UdpClient Udp = udp;
    public readonly Channel<Packet> ToSend = Channel.CreateUnbounded<Packet>();
    public CancellationTokenSource UpdateCancel = new();

    public void Dispose()
    {
        UpdateCancel.Cancel();
        Tcp.Dispose();
        Udp.Dispose();
    }
}