<|start_filename|>CelesteNet.Server/Channels.cs<|end_filename|>
﻿using Celeste.Mod.CelesteNet.DataTypes;
using Mono.Options;
using MonoMod.Utils;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Celeste.Mod.CelesteNet.Server {
    public class Channels : IDisposable {

        public const string NameDefault = "main";
        public const string NamePrivate = "!<private>";
        public const string PrefixPrivate = "!";

        public readonly CelesteNetServer Server;

        public readonly Channel Default;
        public readonly List<Channel> All = new();
        public readonly Dictionary<uint, Channel> ByID = new();
        public readonly Dictionary<string, Channel> ByName = new();
        public uint NextID = (uint) (DateTime.UtcNow.Ticks / TimeSpan.TicksPerSecond);

        public Channels(CelesteNetServer server) {
            Server = server;

            Server.Data.RegisterHandlersIn(this);

            Default = new(this, NameDefault, 0);

            Server.OnSessionStart += OnSessionStart;
        }

        public void Start() {
            Logger.Log(LogLevel.INF, "channels", "Startup");
        }

        public void Dispose() {
            Logger.Log(LogLevel.INF, "channels", "Shutdown");
        }

        private void OnSessionStart(CelesteNetPlayerSession session) {
            if (Server.UserData.TryLoad(session.UID, out LastChannelUserInfo last) &&
                last.Name != NameDefault) {
                Move(session, last.Name);
            } else {
                Default.Add(session);
                BroadcastList();
            }
        }

        public void SendListTo(CelesteNetPlayerSession session) {
            Channel own = session.Channel;

            List<DataChannelList.Channel> channels;
            lock (All) {
                channels = new(All.Count);
                foreach (Channel c in All) {
                    if (c.IsPrivate && c != own)
                        continue;
                    uint[] players;
                    using (c.Lock.R()) {
                        players = new uint[c.Players.Count];
                        int i = 0;
                        foreach (CelesteNetPlayerSession p in c.Players)
                            players[i++] = p.SessionID;
                    }
                    channels.Add(new DataChannelList.Channel {
                        Name = c.Name,
                        ID = c.ID,
                        Players = players
                    });
                }
            }

            session.Con.Send(new DataChannelList {
                List = channels.ToArray()
            });
        }

        public Action<Channels>? OnBroadcastList;

        public void BroadcastList() {
            using (ListSnapshot<Channel> snapshot = All.ToSnapshot())
                foreach (Channel c in snapshot)
                    c.RemoveStale();

            OnBroadcastList?.Invoke(this);

            lock (All)
                using (Server.ConLock.R())
                    foreach (CelesteNetPlayerSession session in Server.Sessions)
                        SendListTo(session);
        }

        public Tuple<Channel, Channel> Move(CelesteNetPlayerSession session, string name) {
            name = name.Sanitize();
            if (name.Length > Server.Settings.MaxChannelNameLength)
                name = name.Substring(0, Server.Settings.MaxChannelNameLength);
            if (name == NamePrivate)
                throw new Exception("Invalid private channel name.");

            lock (All) {
                Channel prev = session.Channel;

                Channel c;

                if (ByName.TryGetValue(name, out Channel? existing)) {
                    c = existing;
                    if (prev == c)
                        return Tuple.Create(c, c);

                } else {
                    c = new(this, name, NextID++);
                }

                prev.Remove(session);

                if (session.PlayerInfo != null)
                    c.Add(session);

                DataInternalBlob move = new(Server.Data, new DataChannelMove {
                    Player = session.PlayerInfo
                });
                session.Con.Send(move);
                using (prev.Lock.R())
                    foreach (CelesteNetPlayerSession other in prev.Players)
                        other.Con.Send(move);

                BroadcastList();

                session.ResendPlayerStates();

                if (!Server.UserData.GetKey(session.UID).IsNullOrEmpty()) {
                    Server.UserData.Save(session.UID, new LastChannelUserInfo {
                        Name = name
                    });
                }

                return Tuple.Create(prev, c);
            }
        }

    }

    public class Channel : IDisposable {
        public readonly Channels Ctx;
        public readonly string Name;
        public readonly uint ID;
        public readonly RWLock Lock = new();
        public readonly HashSet<CelesteNetPlayerSession> Players = new();

        public readonly bool IsPrivate;
        public readonly string PublicName;

        public Channel(Channels ctx, string name, uint id) {
            Ctx = ctx;
            Name = name;
            ID = id;

            if (IsPrivate = name.StartsWith(Channels.PrefixPrivate)) {
                PublicName = Channels.NamePrivate;

            } else {
                PublicName = name;
            }

            lock (Ctx.All) {
                Ctx.All.Add(this);
                Ctx.ByName[Name] = this;
                Ctx.ByID[ID] = this;
            }
        }

        public void RemoveStale() {
            using (Lock.W()) {
                List<CelesteNetPlayerSession> stale = new();
                foreach (CelesteNetPlayerSession session in Players)
                    if (session.PlayerInfo == null)
                        stale.Add(session);
                foreach (CelesteNetPlayerSession session in stale)
                    Remove(session);
            }
        }

        public void Add(CelesteNetPlayerSession session) {
            using (Lock.W())
                if (!Players.Add(session))
                    return;

            session.Channel = this;
            session.OnEnd += RemoveByDC;

            if (session.PlayerInfo == null)
                Remove(session);
        }

        public void Remove(CelesteNetPlayerSession session) {
            using (Lock.W())
                if (!Players.Remove(session))
                    return;

            // Hopefully nobody will get stuck in channel limbo...
            session.OnEnd -= RemoveByDC;

            if (ID == 0)
                return;

            lock (Ctx.All) {
                if (Players.Count > 0)
                    return;

                Ctx.All.Remove(this);
                Ctx.ByName.Remove(Name);
                Ctx.ByID.Remove(ID);
            }
        }

        private void RemoveByDC(CelesteNetPlayerSession session, DataPlayerInfo? lastInfo) {
            Remove(session);
            Ctx.BroadcastList();
        }

        public void Dispose() {
            Lock.Dispose();
        }
    }

    public class LastChannelUserInfo {
        public string Name = "main";
    }
}


<|start_filename|>CelesteNet.Shared/MetaTypes/MetaRequest.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Celeste.Mod.CelesteNet.DataTypes {
    public class MetaRequest : MetaType<MetaRequest> {

        static MetaRequest() {
            MetaID = "req";
        }

        public uint ID;

        public MetaRequest() {
        }
        public MetaRequest(uint id) {
            ID = id;
        }

        public override void Read(CelesteNetBinaryReader reader) {
            ID = reader.Read7BitEncodedUInt();
        }

        public override void Write(CelesteNetBinaryWriter writer) {
            writer.Write7BitEncodedUInt(ID);
        }

        public static implicit operator uint(MetaRequest meta)
            => meta.ID;

    }
}


<|start_filename|>CelesteNet.Shared/CelesteNetBinaryWriter.cs<|end_filename|>
﻿using Celeste.Mod.CelesteNet.DataTypes;
using Microsoft.Xna.Framework;
using Monocle;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.ObjectFactories;

namespace Celeste.Mod.CelesteNet {
    public class CelesteNetBinaryWriter : BinaryWriter {

        public readonly DataContext Data;

        public OptMap<string>? Strings;
        public OptMap<Type>? SlimMap;

        protected ConcurrentStack<Tuple<long, int>> SizeDummyStack = new();

        public CelesteNetBinaryWriter(DataContext ctx, OptMap<string>? strings, OptMap<Type>? slimMap, Stream output)
            : base(output, CelesteNetUtils.UTF8NoBOM) {
            Data = ctx;
            Strings = strings;
            SlimMap = slimMap;
        }

        public CelesteNetBinaryWriter(DataContext ctx, OptMap<string>? strings, OptMap<Type>? slimMap, Stream output, bool leaveOpen)
            : base(output, CelesteNetUtils.UTF8NoBOM, leaveOpen) {
            Data = ctx;
            Strings = strings;
            SlimMap = slimMap;
        }

        public virtual void WriteSizeDummy(int size) {
            Flush();
            long pos = BaseStream.Position;

            if (size == 1)
                Write((byte) 0);
            else if (size == 2)
                Write((ushort) 0);
            else if (size == 4)
                Write((uint) 0);
            else
                throw new ArgumentException($"Invalid size dummy size {size}");

            SizeDummyStack.Push(new(pos, size));
        }

        public virtual void UpdateSizeDummy() {
            if (!SizeDummyStack.TryPop(out Tuple<long, int>? dummy))
                throw new InvalidOperationException("No size dummy on the stack");
            long dummyPos = dummy.Item1;
            int dummySize = dummy.Item2;

            Flush();
            long end = BaseStream.Position;
            long length = end - (dummyPos + dummySize);

            BaseStream.Seek(dummyPos, SeekOrigin.Begin);

            if (dummySize == 1)
                Write((byte) length);
            else if (dummySize == 2)
                Write((ushort) length);
            else if (dummySize == 4)
                Write((uint) length);

            Flush();
            BaseStream.Seek(end, SeekOrigin.Begin);
        }

        public new void Write7BitEncodedInt(int value)
            => base.Write7BitEncodedInt(value);
        public void Write7BitEncodedUInt(uint value)
            => Write7BitEncodedInt(unchecked((int) value));

        public virtual void Write(Vector2 value) {
            Write(value.X);
            Write(value.Y);
        }

        public virtual void Write(Color value) {
            Write(value.R);
            Write(value.G);
            Write(value.B);
            Write(value.A);
        }

        public virtual void WriteNoA(Color value) {
            Write(value.R);
            Write(value.G);
            Write(value.B);
        }

        public virtual void Write(DateTime value) {
            Write(value.ToBinary());
        }

        public virtual unsafe void WriteNetString(string? text) {
            if (text != null) {
                if (text.Length > 4096)
                    throw new Exception("String too long.");
                fixed (char* src = text) {
                    char* ptr = src;
                    for (int i = text.Length; i > 0; i--) {
                        Write(*ptr++);
                    }
                }
            }
            Write('\0');
        }

        public virtual void WriteNetMappedString(string? text) {
            if (Strings == null || !Strings.TryMap(text, out int id)) {
                WriteNetString(text);
                return;
            }

            Write((byte) 0xFF);
            Write7BitEncodedInt(id);
        }

        public virtual bool TryGetSlimID(Type type, out int slimID) {
            slimID = -1;
            return SlimMap != null && SlimMap.TryMap(type, out slimID);
        }

        public void WriteRef<T>(T? data) where T : DataType<T>
            => Write7BitEncodedUInt((data ?? throw new ArgumentException($"Expected {Data.DataTypeToID[typeof(T)]} to write, got null")).Get<MetaRef>(Data) ?? uint.MaxValue);

        public void WriteOptRef<T>(T? data) where T : DataType<T>
            => Write7BitEncodedUInt(data?.GetOpt<MetaRef>(Data) ?? uint.MaxValue);

    }
}


<|start_filename|>CelesteNet.Shared/ConnectionFeatures/ConnectionFeature.cs<|end_filename|>
using System.Threading.Tasks;

namespace Celeste.Mod.CelesteNet {
    public interface IConnectionFeature {
        void Register(CelesteNetConnection con, bool isClient);
        Task DoHandshake(CelesteNetConnection con, bool isClient);
    }
}

<|start_filename|>CelesteNet.Client/CelesteNetClient.cs<|end_filename|>
﻿using Celeste.Mod.CelesteNet.DataTypes;
using MonoMod.Utils;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Celeste.Mod.CelesteNet.Client {
    public class CelesteNetClient : IDisposable {

        public readonly CelesteNetClientSettings Settings;

        public readonly DataContext Data;

        public CelesteNetConnection Con;
        public readonly IConnectionFeature[] ConFeatures;

        private bool _IsAlive;
        public bool IsAlive {
            get => _IsAlive;
            set {
                if (_IsAlive == value)
                    return;

                _IsAlive = value;
            }
        }

        public bool IsReady { get; protected set; }
        private readonly ManualResetEventSlim _ReadyEvent;

        public DataPlayerInfo PlayerInfo;

        private readonly object StartStopLock = new();

        private System.Timers.Timer HeartbeatTimer;

        public CelesteNetClient()
            : this(new()) {
        }

        public CelesteNetClient(CelesteNetClientSettings settings) {
            Settings = settings;

            Data = new();
            Data.RegisterHandlersIn(this);

            _ReadyEvent = new(false);

            // Find connection features
            List<IConnectionFeature> conFeatures = new();
            foreach (Type type in CelesteNetUtils.GetTypes()) {
                if (!typeof(IConnectionFeature).IsAssignableFrom(type) || type.IsAbstract)
                    continue;

                IConnectionFeature feature = (IConnectionFeature) Activator.CreateInstance(type);
                if (feature == null)
                    throw new Exception($"Cannot create instance of connection feature {type.FullName}");
                Logger.Log(LogLevel.VVV, "main", $"Found connection feature: {type.FullName}");
                conFeatures.Add(feature);
            }
            ConFeatures = conFeatures.ToArray();
        }

        public void Start(CancellationToken token) {
            if (IsAlive)
                return;
            IsAlive = true;

            lock (StartStopLock) {
                if (IsReady)
                    return;
                Logger.Log(LogLevel.CRI, "main", $"Startup");

                switch (Settings.ConnectionType) {
                    case ConnectionType.Auto:
                    case ConnectionType.TCPUDP:
                    case ConnectionType.TCP:
                        Logger.Log(LogLevel.INF, "main", $"Connecting via TCP/UDP to {Settings.Host}:{Settings.Port}");

                        // Create a TCP connection
                        Socket sock = new(SocketType.Stream, ProtocolType.Tcp);
                        try {
                            uint conToken;
                            IConnectionFeature[] conFeatures;
                            CelesteNetTCPUDPConnection.Settings settings;
                            using (token.Register(() => sock.Close())) {
                                sock.ReceiveTimeout = sock.SendTimeout = 5000;
                                sock.Connect(Settings.Host, Settings.Port);

                                // Do the teapot handshake
                                Tuple<uint, IConnectionFeature[], CelesteNetTCPUDPConnection.Settings> teapotRes = Handshake.DoTeapotHandshake<CelesteNetClientTCPUDPConnection.Settings>(sock, ConFeatures, Settings.Name);
                                conToken = teapotRes.Item1;
                                conFeatures = teapotRes.Item2;
                                settings = teapotRes.Item3;
                                Logger.Log(LogLevel.INF, "main", $"Teapot handshake success: token {conToken} conFeatures '{conFeatures.Select(f => f.GetType().FullName).Aggregate((string) null, (a, f) => (a == null) ? f : $"{a}, {f}")}'");
                                sock.ReceiveTimeout = sock.SendTimeout = -1;
                            }

                            // Create a connection and start the heartbeat timer
                            CelesteNetClientTCPUDPConnection con = new(Data, conToken, settings, sock);
                            con.OnDisconnect += _ => {
                                CelesteNetClientModule.Instance.Context?.Dispose();
                                Dispose();
                                CelesteNetClientModule.Instance.Context?.Status?.Set("Server disconnected", 3f);
                            };
                            if (Settings.ConnectionType == ConnectionType.TCP)
                                con.UseUDP = false;

                            // Initialize the heartbeat timer
                            HeartbeatTimer = new(settings.HeartbeatInterval);
                            HeartbeatTimer.AutoReset = true;
                            HeartbeatTimer.Elapsed += (_, _) => {
                                string disposeReason = con.DoHeartbeatTick();
                                if (disposeReason != null) {
                                    Logger.Log(LogLevel.CRI, "main", disposeReason);
                                    CelesteNetClientContext ctx = CelesteNetClientModule.Instance.Context;
                                    Dispose();
                                }
                            };
                            HeartbeatTimer.Start();

                            // Do the regular connection handshake
                            Handshake.DoConnectionHandshake(Con, conFeatures, token);
                            Logger.Log(LogLevel.INF, "main", $"Connection handshake success");

                            Con = con;
                        } catch (Exception) {
                            Con?.Dispose();
                            try {
                                sock.ShutdownSafe(SocketShutdown.Both);
                                sock.Close();
                            } catch (Exception) {}
                            sock.Dispose();
                            Con = null;
                            throw;
                        }

                        break;

                    default:
                        throw new NotSupportedException($"Unsupported connection type {Settings.ConnectionType}");
                }
            }

            SendFilterList();

            // Wait until the server sent the ready packet
            _ReadyEvent.Wait(token);

            Logger.Log(LogLevel.INF, "main", "Ready");
            IsReady = true;
        }

        public void Dispose() {
            if (!IsAlive)
                return;
            IsAlive = false;

            lock (StartStopLock) {
                if (Con == null)
                    return;
                Logger.Log(LogLevel.CRI, "main", "Shutdown");
                IsReady = false;

                HeartbeatTimer?.Dispose();
                Con?.Dispose();
                Con = null;

                Data.Dispose();
                _ReadyEvent?.Dispose();
            }
        }


        public void SendFilterList() {
            Logger.Log(LogLevel.INF, "main", "Sending filter list");
            Con?.Send(new DataNetFilterList {
                List = Data.DataTypeToSource.Values.Distinct().ToArray()
            });
        }


        public DataType Send(DataType data) {
            Con?.Send(data);
            return data;
        }

        public T Send<T>(T data) where T : DataType<T> {
            Con?.Send(data);
            return data;
        }

        public DataType SendAndHandle(DataType data) {
            Con?.Send(data);
            Data.Handle(null, data);
            return data;
        }

        public T SendAndHandle<T>(T data) where T : DataType<T> {
            Con?.Send(data);
            Data.Handle(null, data);
            return data;
        }

        public void Handle(CelesteNetConnection con, DataPlayerInfo info) {
            // The first DataPlayerInfo sent from the server is our own
            if (PlayerInfo == null || PlayerInfo.ID == info.ID)
                PlayerInfo = info;
        }

        public void Handle(CelesteNetConnection con, DataReady ready) {
            _ReadyEvent.Set();
        }

    }
}


<|start_filename|>CelesteNet.Shared/ConnectionTypes/CelesteNetConnection.cs<|end_filename|>
﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Celeste.Mod.CelesteNet.DataTypes;
using Celeste.Mod.Helpers;
using Monocle;

namespace Celeste.Mod.CelesteNet {
    public abstract class CelesteNetConnection : IDisposable {

        public readonly string Creator = "Unknown";
        public readonly DataContext Data;

        protected readonly object DisposeLock = new();

        private Action<CelesteNetConnection>? _OnDisconnect;
        public event Action<CelesteNetConnection> OnDisconnect {
            add {
                lock (DisposeLock) {
                    _OnDisconnect += value;
                    if (!IsAlive)
                        value?.Invoke(this);
                }
            }
            remove {
                lock (DisposeLock) {
                    _OnDisconnect -= value;
                }
            }
        }

        private readonly object SendFilterLock = new();
        private DataFilter? _OnSendFilter;
        public event DataFilter OnSendFilter {
            add {
                lock (SendFilterLock) {
                    _OnSendFilter += value;
                }
            }
            remove {
                lock (SendFilterLock) {
                    _OnSendFilter -= value;
                }
            }
        }

        private readonly object ReceiveFilterLock = new();
        private DataFilter? _OnReceiveFilter;
        public event DataFilter OnReceiveFilter {
            add {
                lock (ReceiveFilterLock) {
                    _OnReceiveFilter += value;
                }
            }
            remove {
                lock (ReceiveFilterLock) {
                    _OnReceiveFilter -= value;
                }
            }
        }

        public virtual bool IsAlive { get; protected set; } = true;
        public abstract bool IsConnected { get; }
        public abstract string ID { get; }
        public abstract string UID { get; }

        public CelesteNetConnection(DataContext data) {
            Data = data;

            StackTrace trace = new();
            foreach (StackFrame? frame in trace.GetFrames()) {
                MethodBase? method = frame?.GetMethod();
                if (method == null || method.IsConstructor)
                    continue;

                string? type = method.DeclaringType?.Name;
                Creator = (type == null ? "" : type + "::") + method.Name;
                break;
            }
        }

        public virtual void Send(DataType? data) {
            if (data == null)
                return;
            if (data is DataInternalLoopbackMessage msg) {
                LoopbackReceive(msg);
                return;
            }
            if (data is DataInternalLoopend end) {
                LoopbackReceive(end);
                return;
            }
            if (data is not DataInternalBlob)
                data.Meta = data.GenerateMeta(Data);
            if (!data.FilterSend(Data))
                return;
            if (!IsAlive)
                return;
            lock (SendFilterLock)
                if (!(_OnSendFilter?.InvokeWhileTrue(this, data) ?? true))
                    return;
            CelesteNetSendQueue? queue = GetQueue(data);
            if (queue != null)
                queue.Enqueue(data);
            else
                SendNoQueue(data);
        }

        protected abstract CelesteNetSendQueue? GetQueue(DataType data);
        protected virtual void SendNoQueue(DataType data) {
        }

        protected virtual void Receive(DataType data) {
            lock (ReceiveFilterLock)
                if (!(_OnReceiveFilter?.InvokeWhileTrue(this, data) ?? true))
                    return;
            Data.Handle(this, data);
        }

        protected virtual void LoopbackReceive(DataInternalLoopbackMessage msg) {
            Receive(msg);
        }

        protected virtual void LoopbackReceive(DataInternalLoopend end) {
            end.Action();
        }

        public virtual void LogCreator(LogLevel level) {
            Logger.Log(level, "con", $"Creator: {Creator}");
        }

        protected virtual void Dispose(bool disposing) {
            IsAlive = false;
            _OnDisconnect?.Invoke(this);
        }

        public void Dispose() {
            lock (DisposeLock) {
                if (!IsAlive)
                    return;
                Dispose(true);
            }
        }

        public virtual void DisposeSafe() => Dispose();

        public override string ToString() => $"{GetType()}({ID})";

    }

    public class CelesteNetSendQueue : IDisposable {

        private volatile bool _Alive = true;
        public bool Alive => _Alive;

        public readonly CelesteNetConnection Con;
        public readonly string Name;
        public readonly int MaxSize;
        public readonly float MergeWindow;

        private ConcurrentQueue<DataType> _FrontQueue = new();
        private ConcurrentQueue<DataType> _BackQueue = new();
        public ConcurrentQueue<DataType> FrontQueue => _FrontQueue;
        public ConcurrentQueue<DataType> BackQueue => _BackQueue;

        private readonly Action<CelesteNetSendQueue> QueueFlushCB;
        private int InMergeWindow;
        private readonly System.Timers.Timer Timer;
        private volatile bool SwapQueues;

        public CelesteNetSendQueue(CelesteNetConnection con, string name, int maxSize, float mergeWindow, Action<CelesteNetSendQueue> queueFlusher) {
            Con = con;
            Name = name;
            MaxSize = maxSize;
            MergeWindow = mergeWindow;

            QueueFlushCB = queueFlusher;
            InMergeWindow = 0;
            Timer = new(mergeWindow);
            Timer.AutoReset = false;
            Timer.Elapsed += TimerElapsed;
        }

        public void Dispose() {
            lock (Timer) {
                _Alive = false;
                Timer.Dispose();
            }
        }

        public void Enqueue(DataType data) {
            if (!Alive)
                return;
            if (_FrontQueue.Count >= MaxSize) {
                Logger.Log(LogLevel.WRN, "sendqueue", $"Connection {Con}'s send queue '{Name}' is at maximum size");
                Con.DisposeSafe();
                Dispose();
                return;
            }

            _FrontQueue.Enqueue(data);
            Flush();
        }

        public void Clear() {
            // FIXME: This was _FrontQueue.Clear() in the past, but has been replaced with new(). What about clear race condition behavior?
            _FrontQueue = new();
        }

        public void Flush() {
            if (Interlocked.CompareExchange(ref InMergeWindow, 1, 0) == 0) {
                lock (Timer) {
                    if (!Alive)
                        return;
                    SwapQueues = true;
                    Timer.Interval = MergeWindow;
                    Timer.Start();
                }
            }
        }

        public void DelayFlush(float delay, bool dropUnreliable) {
            if (Volatile.Read(ref InMergeWindow) != 1)
                throw new InvalidOperationException($"Not currently flushing queue '{Name}'");

            lock (Timer) {
                if (dropUnreliable) {
                    ConcurrentQueue<DataType> newBackQueue = new();
                    foreach (DataType data in BackQueue) {
                        if ((data.DataFlags & DataFlags.Unreliable) == 0)
                            newBackQueue.Enqueue(data);
                    }
                    _BackQueue = newBackQueue;
                }

                SwapQueues = false;
                Timer.Interval = delay;
                Timer.Start();
            }
        }

        public void SignalFlushed() {
            if (Volatile.Read(ref InMergeWindow) != 1)
                throw new InvalidOperationException($"Not currently flushing queue '{Name}'");

            // TODO For whatever reason, MonoKickstart can't clear concurrent queues
            _BackQueue = new();
            InMergeWindow = 0;
            Interlocked.MemoryBarrier();
            if (FrontQueue.Count > 0)
                Flush();
        }

        private void TimerElapsed(object? s, EventArgs a) {
            if (Volatile.Read(ref InMergeWindow) != 1)
                throw new InvalidOperationException($"Not currently flushing queue '{Name}'");

            lock (Timer) {
                Timer.Stop();
                if (SwapQueues)
                    _BackQueue = Interlocked.Exchange(ref _FrontQueue, BackQueue);
            }

            try {
                QueueFlushCB.Invoke(this);
            } catch (Exception e) {
                Logger.Log(LogLevel.WRN, "sendqueue", $"Error flushing connection {Con}'s send queue '{Name}': {e}");
                Con.DisposeSafe();
                Dispose();
            }
        }

    }
}


<|start_filename|>CelesteNet.Shared/TokenGenerator.cs<|end_filename|>
using System;
using System.Linq;
using System.Threading;

namespace Celeste.Mod.CelesteNet {
    /*
    This class generates pseudo-random tokens, which are primarly used as
    connection tokens for the server. The reason for not just using an
    incrementing counter is that those tokens would be predictable, and a
    malicious client could use those predicted token to hijack new UDP
    connections by spamming token datagrams. The tokens by this generator are
    generated via a Galois LFSR with a random polynomial, which is stepped a
    random number of times to prevent correlation attacks, whose output bits
    are randomly shuffled, and then XORed with a random key.
    -Popax21
    */
    public class TokenGenerator {

        public const int MaxLFSRSteps = 4;

        private readonly object lfsrLock = new();
        private readonly Random lfsrStepRNG;
        private uint lfsrState;
        private readonly uint lfsrMask;

        private readonly uint[] shuffleMasks;
        private readonly uint xorKey;

        public TokenGenerator() : this(new()) {}
        public TokenGenerator(Random rng) {
            uint RandomUInt() {
                return ((uint) rng.Next(1 << 30)) << 2 | ((uint) rng.Next(1 << 2));
            }

            lfsrStepRNG = rng;

            // Initialize the LFSR to a random non-zero state
            while (lfsrState == 0)
                lfsrState = RandomUInt();

            // Generate a random LFSR mask with a period of 2^32-1 (the maximum)
            // We just use x^(rand) + 1, which is guaranteed to be of maximum length
            lfsrMask = 1u | (1u << rng.Next(0, 31));

            // Determine the new locations for each bit by shuffeling a bit index array
            int[] newBitLocs = Enumerable.Range(0, 32).ToArray();
            for (int i = 31; i > 0; i--) {
                int n = rng.Next(0, i+1);

                int t = newBitLocs[n];
                newBitLocs[n] = newBitLocs[i];
                newBitLocs[i] = t;
            }

            // Generate shuffle masks
            shuffleMasks = new uint[32];
            for (int b = 0; b < 31; b++) {
                int rotateAmount = (32 + newBitLocs[b] - b) % 32;
                shuffleMasks[rotateAmount] |= 1u << b;
            }

            // Generate a random XOR key
            xorKey = RandomUInt();
        }

        public uint GenerateToken() {
            uint val = 0;

            // Step the (Galois) LFSR a random number of times
            lock (lfsrLock)
            for (int i = lfsrStepRNG.Next(MaxLFSRSteps); i >= 0; i--) {
                val = lfsrState;
                lfsrState <<= 1;
                if ((val & (1u << 31)) != 0) lfsrState ^= lfsrMask;
            }

            // Shuffle the bits around
            // Each shuffleMasks[s] has a 1 for all bits which should be rotated left by s
            uint sVal = 0;
            for (int s = 0; s < 32; s++)
                sVal |= ((val & shuffleMasks[s]) << s) | ((val & shuffleMasks[s]) >> (32 - s));
            val = sVal;

            // XOR the value with the random XOR key
            val ^= xorKey;

            return val;
        }

    }
}


<|start_filename|>CelesteNet.Server/ConPlus/HandshakerRole.cs<|end_filename|>
using Celeste.Mod.CelesteNet.DataTypes;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Celeste.Mod.CelesteNet.Server {
    /*
    This role creates a task scheduler, whose tasks are run on all threads
    assigned to the role. The connection acceptor role creates new tasks for
    that scheduler, which perform the handshake, before passing control of the
    connection to the regular send/recv roles.
    -Popax21
    */
    public partial class HandshakerRole : NetPlusThreadRole {

        public const int TeapotTimeout = 5000;
        public const int TeapotVersion = 1;

        private class TaskWorkerScheduler : TaskScheduler, IDisposable {

            private readonly BlockingCollection<Task> TaskQueue = new();
            private readonly ThreadLocal<bool> ExecutingTasks = new();

            public void Dispose() {
                ExecutingTasks.Dispose();
                TaskQueue.Dispose();
            }

            public void ExecuteTasks(Worker worker, CancellationToken token) {
                ExecutingTasks.Value = true;
                foreach (Task t in TaskQueue.GetConsumingEnumerable(token)) {
                    worker.EnterActiveZone();
                    try {
                        TryExecuteTask(t);
                    } finally {
                        worker.ExitActiveZone();
                    }
                }
                ExecutingTasks.Value = false;
            }

            protected override IEnumerable<Task> GetScheduledTasks() => TaskQueue;
            protected override void QueueTask(Task task) => TaskQueue.Add(task);

            protected override bool TryExecuteTaskInline(Task task, bool prevQueued) {
                if (prevQueued || !ExecutingTasks.Value)
                    return false;
                return TryExecuteTask(task);
            }

        }

        private class Worker : RoleWorker {

            public Worker(HandshakerRole role, NetPlusThread thread) : base(role, thread) {}

            protected internal override void StartWorker(CancellationToken token) => Role.Scheduler.ExecuteTasks(this, token);

            public new void EnterActiveZone() => base.EnterActiveZone();
            public new void ExitActiveZone() => base.ExitActiveZone();

            public new HandshakerRole Role => (HandshakerRole) base.Role;

        }

        public override int MinThreads => 1;
        public override int MaxThreads => int.MaxValue;

        public CelesteNetServer Server { get; }
        public TaskFactory Factory { get; }

        private readonly TaskWorkerScheduler Scheduler;
        private readonly List<(string, IConnectionFeature)> ConFeatures;

        public HandshakerRole(NetPlusThreadPool pool, CelesteNetServer server) : base(pool) {
            Server = server;
            Scheduler = new();
            Factory = new(Scheduler);

            // Find connection features
            ConFeatures = new List<(string, IConnectionFeature)>();
            foreach (Type type in CelesteNetUtils.GetTypes()) {
                if (!typeof(IConnectionFeature).IsAssignableFrom(type) || type.IsAbstract || string.IsNullOrEmpty(type.FullName))
                    continue;

                IConnectionFeature? feature = (IConnectionFeature?) Activator.CreateInstance(type);
                if (feature == null)
                    throw new Exception($"Cannot create instance of connection feature {type.FullName}");
                Logger.Log(LogLevel.VVV, "handshake", $"Found connection feature: {type.FullName}");
                ConFeatures.Add((type.FullName, feature));
            }
        }

        public override void Dispose() {
            Scheduler.Dispose();
            base.Dispose();
        }

        public override RoleWorker CreateWorker(NetPlusThread thread) => new Worker(this, thread);

        public async Task DoTCPUDPHandshake(Socket sock, CelesteNetTCPUDPConnection.Settings settings, TCPReceiverRole tcpReceiver, UDPReceiverRole udpReceiver, TCPUDPSenderRole sender) {
            EndPoint? remoteEP = sock.RemoteEndPoint;
            if (remoteEP == null) {
                Logger.Log(LogLevel.WRN, "tcpudphs", $"Handshake for connection without remote endpoint???");
                sock.Dispose();
                return;
            }
            ConPlusTCPUDPConnection? con = null;
            try {
                // Obtain a connection token
                uint conToken = Server.ConTokenGenerator.GenerateToken();

                // Do the teapot handshake
                IConnectionFeature[]? conFeatures = null;
                string? playerUID = null, playerName = null;
                using (CancellationTokenSource tokenSrc = new()) {
                    // .NET is completly stupid, you can't cancel async socket operations
                    // We literally have to kill the socket for the handshake to be able to timeout
                    tokenSrc.CancelAfter(TeapotTimeout);
                    tokenSrc.Token.Register(() => sock.Close());
                    try {
                        (IConnectionFeature[] conFeatures, string playerUID, string playerName)? teapotRes =
                            await TeapotHandshake(
                                sock, conToken, settings,
                                $"fb-tpcudp-{BitConverter.ToString(((IPEndPoint?) sock.RemoteEndPoint)?.Address.MapToIPv6().GetAddressBytes() ?? new byte[0])}"
                            );
                        if (teapotRes != null)
                            (conFeatures, playerUID, playerName) = teapotRes.Value;
                    } catch (Exception) {
                        if (tokenSrc.IsCancellationRequested) {
                            Logger.Log(LogLevel.VVV, "tcpudphs", $"Handshake for connection {remoteEP} timed out, maybe an old client?");
                            sock.Dispose();
                            return;
                        }
                        throw;
                    }
                }

                if (conFeatures == null || playerUID.IsNullOrEmpty() || playerName.IsNullOrEmpty()) {
                    Logger.Log(LogLevel.VVV, "tcpudphs", $"Connection from {remoteEP} failed teapot handshake");
                    sock.ShutdownSafe(SocketShutdown.Both);
                    sock.Close();
                    return;
                }
                Logger.Log(LogLevel.VVV, "tcpudphs", $"Connection {remoteEP} teapot handshake success: connection features '{conFeatures.Aggregate((string?) null, (a, f) => ((a == null) ? $"{f}" : $"{a}, {f}"))}' player UID {playerUID} player name {playerName}");

                // Create the connection, do the generic connection handshake and create a session
                Server.HandleConnect(con = new(Server, conToken, settings, sock, tcpReceiver, udpReceiver, sender));
                await DoConnectionHandshake(con, conFeatures);
                Server.CreateSession(con, playerUID, playerName);
            } catch(Exception) {
                con?.Dispose();
                sock.Dispose();
                throw;
            }
        }

        // Let's mess with web crawlers even more ;)
        // Also: I'm a Teapot
        private async Task<(IConnectionFeature[] conFeatures, string playerUID, string playerName)?> TeapotHandshake<T>(Socket sock, uint conToken, T settings, string fbUID) where T : struct {
            using NetworkStream netStream = new(sock, false);
            using BufferedStream bufStream = new(netStream);
            using StreamReader reader = new(bufStream);
            using StreamWriter writer = new(bufStream);
            async Task<(IConnectionFeature[], string, string)?> Send500() {
                await writer.WriteAsync(
@"HTTP/1.1 500 Internal Server Error
Connection: close

The server encountered an internal error while handling the request"
                    .Trim().Replace("\r\n", "\n").Replace("\n", "\r\n")
                );
                return null;
            }

            // Parse the "HTTP" request line
            string? reqLine = await reader.ReadLineAsync();
            if (reqLine == null)
                return await Send500();

            string[] reqLineSegs = reqLine.Split(' ').Where(s => !string.IsNullOrEmpty(s)).ToArray();
            if (
                reqLineSegs.Length != 3 ||
                reqLineSegs[0] != "CONNECT" ||
                reqLineSegs[1] != "/teapot"
            )
                return await Send500();

            // Parse the headers
            Dictionary<string, string> headers = new();
            for (string? line = await reader.ReadLineAsync(); !string.IsNullOrEmpty(line); line = await reader.ReadLineAsync()) {
                int split = line.IndexOf(':');
                if (split == -1)
                    return await Send500();
                headers[line.Substring(0, split).Trim()] = line.Substring(split + 1).Trim();
            }
            bufStream.Flush();

            // Check teapot version
            if (!headers.TryGetValue("CelesteNet-TeapotVersion", out string? teapotVerHeader) || !int.TryParse(teapotVerHeader, out int teapotVer))
                return await Send500();

            if (teapotVer != TeapotVersion) {
                Logger.Log(LogLevel.VVV, "teapot", $"Teapot version mismatch for connection {sock.RemoteEndPoint}: {teapotVer} [client] != {TeapotVersion} [server]");
                await writer.WriteAsync(
$@"HTTP/1.1 409 Version Mismatch
Connection: close

{string.Format(Server.Settings.MessageTeapotVersionMismatch, teapotVer, TeapotVersion)}"
                    .Trim().Replace("\r\n", "\n").Replace("\n", "\r\n")
                );
                return null;
            }

            // Get the list of supported connection features
            HashSet<string> conFeatures;
            if (headers.TryGetValue("CelesteNet-ConnectionFeatures", out string? conFeaturesRaw))
                conFeatures = new(conFeaturesRaw.Split(',').Select(f => f.Trim().ToLower()));
            else
                conFeatures = new();

            // Match connection features
            List<(string name, IConnectionFeature feature)> matchedFeats = new();
            foreach ((string name, IConnectionFeature feature) feat in ConFeatures) {
                if (conFeatures.Contains(feat.name.ToLower()))
                    matchedFeats.Add((feat.name, feat.feature));
            }

            // Get the player name-key
            if (!headers.TryGetValue("CelesteNet-PlayerNameKey", out string? playerNameKey))
                return await Send500();

            // Authenticate name-key
            string? errorReason = AuthenticatePlayerNameKey(playerNameKey, fbUID, out string? playerUID, out string? playerName);
            if (playerUID == null)
                errorReason ??= "No UID";
            if (playerName == null)
                errorReason ??= "No name";
            if (errorReason != null || playerUID == null || playerName == null) {
                Logger.Log(LogLevel.VVV, "teapot", $"Error authenticating name-key '{playerNameKey}' for connection {sock.RemoteEndPoint}: {errorReason}");
                await writer.WriteAsync(
$@"HTTP/1.1 403 Access Denied
Connection: close

{errorReason}"
                    .Trim().Replace("\r\n", "\n").Replace("\n", "\r\n")
                );
                return null;
            }

            // Answer with the almighty teapot
            StringBuilder settingsBuilder = new();
            object boxedSettings = settings;
            foreach (FieldInfo field in typeof(T).GetFields(BindingFlags.Public | BindingFlags.Instance)) {
                switch (Type.GetTypeCode(field.FieldType)) {
                    case TypeCode.Int16:
                    case TypeCode.Int32:
                    case TypeCode.Int64:
                    case TypeCode.UInt16:
                    case TypeCode.UInt32:
                    case TypeCode.UInt64:
                    case TypeCode.Single:
                    case TypeCode.Double: {
                        settingsBuilder.AppendLine($"CelesteNet-Settings-{field.Name}: {field.GetValue(boxedSettings)}");
                    } break;
                }
            }

            await writer.WriteAsync(
$@"HTTP/1.1 418 I'm a teapot
Connection: close
CelesteNet-TeapotVersion: {TeapotVersion}
CelesteNet-ConnectionToken: {conToken:X}
CelesteNet-ConnectionFeatures: {matchedFeats.Aggregate((string) null!, (a, f) => ((a == null) ? f.name : $"{a}, {f.name}"))}
{settingsBuilder.ToString().Trim()}

Who wants some tea?"
                .Trim().Replace("\r\n", "\n").Replace("\n", "\r\n") + "\r\n" + "\r\n"
            );

            return (matchedFeats.Select(f => f.feature).ToArray(), playerUID, playerName);
        }

        public async Task DoConnectionHandshake(CelesteNetConnection con, IConnectionFeature[] features) {
            // Handshake connection features
            foreach (IConnectionFeature feature in features)
                feature.Register(con, false);
            foreach (IConnectionFeature feature in features)
                await feature.DoHandshake(con, false);

            // Send the current tick rate
            con.Send(new DataTickRate {
                TickRate = Server.CurrentTickRate
            });
        }

        public string? AuthenticatePlayerNameKey(string nameKey, string fbUID, out string? playerUID, out string? playerName) {
            // Get the player UID and name from the player name-key
            playerUID = playerName = null;
            if (nameKey.Length > 1 && nameKey[0] == '#') {
                playerUID = Server.UserData.GetUID(nameKey.Substring(1));
                if (playerUID != null && Server.UserData.TryLoad(playerUID, out BasicUserInfo info))
                    playerName = info.Name;
                else
                    return string.Format(Server.Settings.MessageInvalidKey, nameKey);
            } else if (!Server.Settings.AuthOnly) {
                playerName = nameKey;
                playerUID = fbUID;
            } else
                return string.Format(Server.Settings.MessageAuthOnly, nameKey);

            // Check if the player's banned
            BanInfo? ban = null;
            if (Server.UserData.TryLoad(playerUID, out BanInfo banInfo) && (banInfo.From == null || banInfo.From <= DateTime.Now) && (banInfo.To == null || DateTime.Now <= banInfo.To))
                ban = banInfo;
            if (Server.UserData.TryLoad(fbUID, out BanInfo conBanInfo) && (conBanInfo.From == null || conBanInfo.From <= DateTime.Now) && (conBanInfo.To == null || DateTime.Now <= conBanInfo.To))
                ban = conBanInfo;
            if (ban != null)
                return string.Format(Server.Settings.MessageBan, playerUID, playerName, ban.Reason);

            // Sanitize the player's name
            playerName = playerName.Sanitize(CelesteNetPlayerSession.IllegalNameChars);
            if (playerName.Length > Server.Settings.MaxNameLength)
                playerName = playerName.Substring(0, Server.Settings.MaxNameLength);
            if (playerName.IsNullOrEmpty())
                playerName = "Guest";

            return null;
        }

    }
}


<|start_filename|>CelesteNet.Client/Components/CelesteNetEmojiComponent.cs<|end_filename|>
﻿using Celeste.Mod.CelesteNet.Client.Entities;
using Celeste.Mod.CelesteNet.DataTypes;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Monocle;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MDraw = Monocle.Draw;

namespace Celeste.Mod.CelesteNet.Client.Components {
    public class CelesteNetEmojiComponent : CelesteNetGameComponent {

        public ConcurrentDictionary<string, FileStream> Pending = new();
        public HashSet<string> Registered = new();
        public HashSet<string> RegisteredFiles = new();

        public CelesteNetEmojiComponent(CelesteNetClientContext context, Game game)
            : base(context, game) {

            UpdateOrder = 10000;
            Visible = false;
        }

        public void Handle(CelesteNetConnection con, DataNetEmoji netemoji) {
            if (!Pending.TryGetValue(netemoji.ID, out FileStream cacheStream)) {
                // Create a new cache stream for the emoji
                string dir = Path.Combine(Path.GetTempPath(), "CelesteNetClientEmojiCache");
                if (!Directory.Exists(dir))
                    Directory.CreateDirectory(dir);

                string path = Path.Combine(dir, $"{netemoji.ID}-{netemoji.GetHashCode():X8}.png");
                Pending.TryAdd(netemoji.ID, cacheStream = File.OpenWrite(path));
            }

            // Add fragment data to the emoji
            cacheStream.Write(netemoji.Data, 0, netemoji.Data.Length);

            if (!netemoji.MoreFragments) {
                Pending.TryRemove(netemoji.ID, out _);
                string path = cacheStream.Name;
                cacheStream.Close();

                // Register the emoji
                RunOnMainThread(() => {
                    Logger.Log(LogLevel.VVV, "netemoji", $"Registering {netemoji.ID}");

                    bool registered = false;

                    try {
                        VirtualTexture vt = VirtualContent.CreateTexture(path);
                        MTexture mt = new(vt);
                        if (vt.Texture_Safe == null) // Needed to trigger lazy loading.
                            throw new Exception($"Couldn't load emoji {netemoji.ID}");

                        Registered.Add(netemoji.ID);
                        RegisteredFiles.Add(path);
                        Emoji.Register(netemoji.ID, mt);
                        Emoji.Fill(CelesteNetClientFont.Font);
                        registered = true;

                    } finally {
                        if (!registered)
                            File.Delete(path);
                    }
                });
            }
        }

        protected override void Dispose(bool disposing) {
            base.Dispose(disposing);

            foreach (Stream s in Pending.Values)
                s.Dispose();
            Pending.Clear();

            foreach (string id in Registered)
                Emoji.Register(id, GFX.Misc["whiteCube"]);

            Emoji.Fill(CelesteNetClientFont.Font);

            foreach (string path in RegisteredFiles)
                if (File.Exists(path))
                    File.Delete(path);
        }

    }
}


<|start_filename|>CelesteNet.Server/CelesteNetServer.cs<|end_filename|>
﻿using Celeste.Mod.CelesteNet.DataTypes;
using Mono.Cecil;
using Mono.Options;
using MonoMod.RuntimeDetour;
using MonoMod.Utils;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Celeste.Mod.CelesteNet.Server {
    public class CelesteNetServer : IDisposable {

        public readonly DateTime StartupTime;
        public readonly long Timestamp;

        public readonly CelesteNetServerSettings Settings;

        public readonly DataContext Data;
        public readonly NetPlusThreadPool ThreadPool;

        public UserData UserData;

        public readonly Channels Channels;

        public bool Initialized = false;
        public readonly List<CelesteNetServerModuleWrapper> ModuleWrappers = new();
        public readonly List<CelesteNetServerModule> Modules = new();
        public readonly Dictionary<Type, CelesteNetServerModule> ModuleMap = new();
        public readonly FileSystemWatcher ModulesFSWatcher;

        public readonly DetourModManager DetourModManager;

        public uint PlayerCounter = 0;
        public readonly TokenGenerator ConTokenGenerator = new();
        public readonly RWLock ConLock = new();
        public readonly HashSet<CelesteNetConnection> Connections = new();
        public readonly HashSet<CelesteNetPlayerSession> Sessions = new();
        public readonly ConcurrentDictionary<CelesteNetConnection, CelesteNetPlayerSession> PlayersByCon = new();
        public readonly ConcurrentDictionary<uint, CelesteNetPlayerSession> PlayersByID = new();
        private readonly System.Timers.Timer HeartbeatTimer;

        public float CurrentTickRate { get; private set; }
        private float NextTickRate;

        private readonly ManualResetEvent ShutdownEvent = new(false);

        private bool _IsAlive;
        public bool IsAlive {
            get => _IsAlive;
            set {
                if (_IsAlive == value)
                    return;

                _IsAlive = value;
                if (value)
                    ShutdownEvent.Reset();
                else
                    ShutdownEvent.Set();
            }
        }

        public CelesteNetServer()
            : this(new()) {
        }

        public CelesteNetServer(CelesteNetServerSettings settings) {
            StartupTime = DateTime.UtcNow;
            Timestamp = StartupTime.Ticks / TimeSpan.TicksPerMillisecond;

            Settings = settings;

            DetourModManager = new();

            AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => {
                if (args.Name == null)
                    return null;

                AssemblyName name = new(args.Name);
                if (ModuleWrappers.Any(wrapper => wrapper.ID == name.Name))
                    return null;

                string path = Path.Combine(Path.GetFullPath(Settings.ModuleRoot), name.Name + ".dll");
                if (File.Exists(path))
                    return Assembly.LoadFrom(path);

                return null;
            };

            foreach (string file in Directory.GetFiles(Path.GetFullPath(Settings.ModuleRoot)))
                if (Path.GetFileName(file).StartsWith("CelesteNet.Server.") && file.EndsWith("Module.dll"))
                    RegisterModule(file);

            Data = new();
            Data.RegisterHandlersIn(this);

            Channels = new(this);

            UserData = new FileSystemUserData(this);

            Initialized = true;
            lock (Modules) {
                foreach (CelesteNetServerModuleWrapper wrapper in ModuleWrappers) {
                    Logger.Log(LogLevel.INF, "module", $"Initializing {wrapper.ID}");
                    wrapper.Module?.Init(wrapper);
                }
            }

            ModulesFSWatcher = new() {
                Path = Path.GetFullPath(Settings.ModuleRoot),
                Filter = "*.dll",
                NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite | NotifyFilters.CreationTime
            };

            ModulesFSWatcher.Error += (sender, args) => {
                Logger.Log(LogLevel.ERR, "module", $"Module file watcher error:\n{args.GetException()}");
            };

            ModulesFSWatcher.Created += OnModuleFileUpdate;
            ModulesFSWatcher.Renamed += OnModuleFileUpdate;
            ModulesFSWatcher.Changed += OnModuleFileUpdate;

            ModulesFSWatcher.EnableRaisingEvents = true;

            ThreadPool = new((Settings.NetPlusThreadPoolThreads <= 0) ? Environment.ProcessorCount : Settings.NetPlusThreadPoolThreads, Settings.NetPlusMaxThreadRestarts, Settings.HeuristicSampleWindow, Settings.NetPlusSchedulerInterval, Settings.NetPlusSchedulerUnderloadThreshold, Settings.NetPlusSchedulerOverloadThreshold, Settings.NetPlusSchedulerStealThreshold);

            HeartbeatTimer = new(Settings.HeartbeatInterval);
            HeartbeatTimer.Elapsed += (_, _) => DoHeartbeatTick();
        }

        private void OnModuleFileUpdate(object sender, FileSystemEventArgs args) {
            Logger.Log(LogLevel.VVV, "module", $"Module file changed: {args.FullPath}, {args.ChangeType}");
            QueuedTaskHelper.Do("ReloadModuleAssembly:" + args.FullPath, () => {
                lock (Modules)
                    foreach (CelesteNetServerModuleWrapper wrapper in ModuleWrappers)
                        if (args.FullPath == wrapper.AssemblyPath)
                            wrapper.Reload();
            });
        }

        public void Start() {
            if (IsAlive)
                return;

            Logger.Log(LogLevel.CRI, "main", "Startup");
            IsAlive = true;

            lock (Modules) {
                foreach (CelesteNetServerModule module in Modules) {
                    Logger.Log(LogLevel.INF, "module", $"Starting {module.Wrapper?.ID ?? module.ToString()}");
                    module.Start();
                }
            }

            Channels.Start();

            IPEndPoint serverEP = new(IPAddress.IPv6Any, Settings.MainPort);
            IPEndPoint udpRecvEP = new(IPAddress.IPv6Any, Settings.UDPReceivePort);
            IPEndPoint udpSendEP = new(IPAddress.IPv6Any, Settings.UDPSendPort);

            CelesteNetTCPUDPConnection.Settings tcpUdpConSettings = new() {
                UDPReceivePort = Settings.UDPReceivePort,
                UDPSendPort = Settings.UDPSendPort,
                MaxPacketSize = Settings.MaxPacketSize,
                MaxQueueSize = Settings.MaxQueueSize,
                MergeWindow = Settings.MergeWindow,
                MaxHeartbeatDelay = Settings.MaxHeartbeatDelay,
                HeartbeatInterval = Settings.HeartbeatInterval,
                UDPAliveScoreMax = Settings.UDPAliveScoreMax,
                UDPDowngradeScoreMin = Settings.UDPDowngradeScoreMin,
                UDPDowngradeScoreMax = Settings.UDPDowngradeScoreMax,
                UDPDeathScoreMin = Settings.UDPDeathScoreMin,
                UDPDeathScoreMax = Settings.UDPDeathScoreMax
            };

            Logger.Log(LogLevel.INF, "server", $"Starting server on {serverEP}");
            ThreadPool.Scheduler.AddRole(new HandshakerRole(ThreadPool, this));
            ThreadPool.Scheduler.AddRole(new TCPReceiverRole(ThreadPool, this, (PlatformHelper.Is(MonoMod.Utils.Platform.Linux) && Settings.TCPRecvUseEPoll) ? new TCPEPollPoller() : new TCPFallbackPoller()));
            ThreadPool.Scheduler.AddRole(new UDPReceiverRole(ThreadPool, this, udpRecvEP));
            ThreadPool.Scheduler.AddRole(new TCPUDPSenderRole(ThreadPool, this, udpSendEP));
            ThreadPool.Scheduler.AddRole(new TCPAcceptorRole(ThreadPool, this, serverEP, ThreadPool.Scheduler.FindRole<HandshakerRole>()!, ThreadPool.Scheduler.FindRole<TCPReceiverRole>()!, ThreadPool.Scheduler.FindRole<UDPReceiverRole>()!, ThreadPool.Scheduler.FindRole<TCPUDPSenderRole>()!, tcpUdpConSettings));

            HeartbeatTimer.Start();
            CurrentTickRate = NextTickRate = Settings.MaxTickRate;
            ThreadPool.Scheduler.OnPreScheduling += AdjustTickRate;

            Logger.Log(LogLevel.CRI, "main", "Ready");
        }

        public void Wait() {
            WaitHandle.WaitAny(new WaitHandle[] { ShutdownEvent });
            ShutdownEvent.Dispose();
        }

        public void Dispose() {
            if (!IsAlive)
                return;

            Logger.Log(LogLevel.CRI, "main", "Shutdown");
            IsAlive = false;

            HeartbeatTimer.Dispose();

            ModulesFSWatcher.Dispose();

            lock (Modules) {
                foreach (CelesteNetServerModuleWrapper wrapper in ModuleWrappers.ToArray()) {
                    wrapper.Unload();
                }
            }

            Channels.Dispose();
            ThreadPool.Dispose();
            ConLock.Dispose();

            UserData.Dispose();

            Data.Dispose();
        }


        public void RegisterModule(string path) {
            ModuleWrappers.Add(new(this, path));

            Reload:
            foreach (CelesteNetServerModuleWrapper wrapper in ModuleWrappers) {
                if (wrapper.Module != null ||
                    !wrapper.References.All(ModuleWrappers.Where(other => other.Module != null).Select(other => other.ID).Contains))
                    continue;

                wrapper.Load();
                goto Reload;
            }
        }

        public T Get<T>() where T : class
            => TryGet(out T? module) ? module : throw new Exception($"Invalid module type: {typeof(T).FullName}");

        public bool TryGet<T>([NotNullWhen(true)] out T? moduleT) where T : class {
            lock (Modules) {
                if (ModuleMap.TryGetValue(typeof(T), out CelesteNetServerModule? module)) {
                    moduleT = module as T ?? throw new Exception($"Incompatible types: Requested {typeof(T).FullName}, got {module.GetType().FullName}");
                    return true;
                }

                foreach (CelesteNetServerModule other in Modules)
                    if (other is T otherT) {
                        ModuleMap[typeof(T)] = other;
                        moduleT = otherT;
                        return true;
                    }
            }

            moduleT = null;
            return false;
        }


        public event Action<CelesteNetServer, CelesteNetConnection>? OnConnect;

        public void HandleConnect(CelesteNetConnection con) {
            Logger.Log(LogLevel.INF, "main", $"New connection: {con}");
            using (ConLock.W())
                Connections.Add(con);
            OnConnect?.Invoke(this, con);
            con.OnDisconnect += HandleDisconnect;
        }

        public event Action<CelesteNetServer, CelesteNetConnection, CelesteNetPlayerSession?>? OnDisconnect;

        public void HandleDisconnect(CelesteNetConnection con) {
            Logger.Log(LogLevel.INF, "main", $"Disconnecting: {con}");

            using (ConLock.W())
                Connections.Remove(con);

            Logger.Log(LogLevel.VVV, "main", $"Loopend send {con}");
            con.Send(new DataInternalLoopend(() => {
                Logger.Log(LogLevel.VVV, "main", $"Loopend run {con}");

                PlayersByCon.TryGetValue(con, out CelesteNetPlayerSession? session);
                if (session != null) {
                    using (ConLock.W()) {
                        Sessions.Remove(session);
                        PlayersByCon.TryRemove(con, out _);
                        PlayersByID.TryRemove(session.SessionID, out _);
                        session?.Dispose();
                    }
                }

                OnDisconnect?.Invoke(this, con, session);
            }));
        }

        public event Action<CelesteNetPlayerSession>? OnSessionStart;

        private int nextSesId = 0;
        public CelesteNetPlayerSession CreateSession(CelesteNetConnection con, string playerUID, string playerName) {
            CelesteNetPlayerSession ses;
            using (ConLock.W()) {
                ses = new CelesteNetPlayerSession(this, con, unchecked ((uint) Interlocked.Increment(ref nextSesId)), playerUID, playerName);
                Sessions.Add(ses);
                PlayersByCon[con] = ses;
                PlayersByID[ses.SessionID] = ses;
            }
            ses.Start();
            OnSessionStart?.Invoke(ses);
            return ses;
        }

        public void Broadcast(DataType data) {
            DataInternalBlob blob = new(Data, data);
            using (ConLock.R())
                foreach (CelesteNetConnection con in Connections) {
                    try {
                        con.Send(blob);
                    } catch (Exception e) {
                        // Whoops, it probably wasn't important anyway.
                        Logger.Log(LogLevel.DEV, "main", $"Broadcast (sync) failed:\n{data}\n{con}\n{e}");
                    }
                };
        }

        public void BroadcastAsync(DataType data) {
            DataInternalBlob blob = new(Data, data);
            using (ConLock.R())
                foreach (CelesteNetConnection con in Connections) {
                    Task.Run(() => {
                        try {
                            con.Send(blob);
                        } catch (Exception e) {
                            // Whoops, it probably wasn't important anyway.
                            Logger.Log(LogLevel.DEV, "main", $"Broadcast (async) failed:\n{data}\n{con}\n{e}");
                        }
                    });
                }
        }

        private void DoHeartbeatTick() {
            HashSet<(CelesteNetConnection con, string reason)> disposeCons = new();
            using (ConLock.R())
                foreach (CelesteNetConnection con in new HashSet<CelesteNetConnection>(Connections)) {
                    try {
                        switch (con) {
                            case CelesteNetTCPUDPConnection tcpUdpCon: {
                                string? disposeReason = tcpUdpCon.DoHeartbeatTick();
                                if (disposeReason != null)
                                    disposeCons.Add((tcpUdpCon, disposeReason));
                            } break;
                        }
                    } catch (Exception e) {
                        disposeCons.Add((con, $"Error in heartbeat tick of connection {con}: {e}"));
                    }
                }
            foreach ((CelesteNetConnection con, string reason) in disposeCons) {
                Logger.Log(LogLevel.WRN, "main", reason);
                try {
                    con.Dispose();
                } catch (Exception e) {
                    Logger.Log(LogLevel.WRN, "main", $"Error disposing connection {con}: {e}");
                }
            }
        }

        private void AdjustTickRate() {
            CurrentTickRate = NextTickRate;

            TCPUDPSenderRole sender = ThreadPool.Scheduler.FindRole<TCPUDPSenderRole>()!;
            float actvRate = ThreadPool.ActivityRate, tcpByteRate = sender.TCPByteRate, udpByteRate = sender.UDPByteRate;
            if (actvRate < Settings.TickRateLowActivityThreshold && tcpByteRate < Settings.TickRateLowTCPUplinkBpSThreshold && udpByteRate < Settings.TickRateLowUDPUplinkBpSThreshold) {
                if (CurrentTickRate < Settings.MaxTickRate) {
                    // Increase the tick rate
                    NextTickRate = Math.Min(Settings.MaxTickRate, CurrentTickRate * 2);
                    Logger.Log(LogLevel.INF, "main", $"Increased the tick rate {CurrentTickRate} TpS -> {NextTickRate} TpS");
                    CurrentTickRate = NextTickRate;
                } else {
                    return;
                }
            } else if (actvRate > Settings.TickRateHighActivityThreshold && tcpByteRate > Settings.TickRateHighTCPUplinkBpSThreshold && udpByteRate > Settings.TickRateHighUDPUplinkBpSThreshold) {
                // Decrease the tick rate
                Logger.Log(LogLevel.INF, "main", $"Decreased the tick rate {CurrentTickRate} TpS -> {NextTickRate} TpS");
                NextTickRate = CurrentTickRate / 2;
            } else {
                return;
            }

            // Broadcast the new tick rate
            BroadcastAsync(new DataTickRate {
                TickRate = NextTickRate
            });
        }

        #region Handlers

        #endregion

    }
}


<|start_filename|>CelesteNet.Shared/DataTypes/DataNetEmoji.cs<|end_filename|>
﻿using Microsoft.Xna.Framework;
using Monocle;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Celeste.Mod.CelesteNet.DataTypes {
    public class DataNetEmoji : DataType<DataNetEmoji> {

        static DataNetEmoji() {
            DataID = "netemoji";
        }

        public string ID = "";
        public bool MoreFragments;
        public byte[] Data = Dummy<byte>.EmptyArray;

        protected override void Read(CelesteNetBinaryReader reader) {
            ID = reader.ReadNetString();
            MoreFragments = reader.ReadBoolean();
            Data = reader.ReadBytes(reader.ReadInt32());
        }

        protected override void Write(CelesteNetBinaryWriter writer) {
            writer.WriteNetString(ID);
            writer.Write(MoreFragments);
            writer.Write(Data.Length);
            writer.Write(Data);
        }

    }
}


<|start_filename|>CelesteNet.Shared/DataTypes/DataMapModInfo.cs<|end_filename|>
﻿using Microsoft.Xna.Framework;
using Monocle;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Celeste.Mod.CelesteNet.DataTypes {
    public class DataMapModInfo : DataType<DataMapModInfo>, IDataRequestable<DataMapModInfoRequest> {

        static DataMapModInfo() {
            DataID = "mapModInfo";
        }

        public override DataFlags DataFlags => DataFlags.Taskable;

        public uint RequestID = uint.MaxValue;

        public string MapSID = "";
        public string MapName = "";
        public string ModID = "";
        public string ModName = "";
        public Version ModVersion = new();

        public override MetaType[] GenerateMeta(DataContext ctx)
            => new MetaType[] {
                new MetaRequestResponse(RequestID)
            };

        public override void FixupMeta(DataContext ctx) {
            RequestID = Get<MetaRequestResponse>(ctx);
        }

        protected override void Read(CelesteNetBinaryReader reader) {
            MapSID = reader.ReadNetString();
            MapName = reader.ReadNetString();
            ModID = reader.ReadNetString();
            ModName = reader.ReadNetString();
            ModVersion = new(reader.ReadNetString());
        }

        protected override void Write(CelesteNetBinaryWriter writer) {
            writer.WriteNetString(MapSID);
            writer.WriteNetString(MapName);
            writer.WriteNetString(ModID);
            writer.WriteNetString(ModName);
            writer.WriteNetString(ModVersion.ToString());
        }

    }

    public class DataMapModInfoRequest : DataType<DataMapModInfoRequest> {

        static DataMapModInfoRequest() {
            DataID = "mapModInfoReq";
        }

        public uint ID = uint.MaxValue;

        public string MapSID = "";

        public override MetaType[] GenerateMeta(DataContext ctx)
            => new MetaType[] {
                new MetaRequest(ID)
            };

        public override void FixupMeta(DataContext ctx) {
            ID = Get<MetaRequest>(ctx);
        }

        protected override void Read(CelesteNetBinaryReader reader) {
            MapSID = reader.ReadNetString();
        }

        protected override void Write(CelesteNetBinaryWriter writer) {
            writer.WriteNetString(MapSID);
        }

    }
}


<|start_filename|>CelesteNet.Shared/DataTypes/DataChannelMove.cs<|end_filename|>
﻿using Microsoft.Xna.Framework;
using Monocle;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Celeste.Mod.CelesteNet.DataTypes {
    public class DataChannelMove : DataType<DataChannelMove> {

        static DataChannelMove() {
            DataID = "channelMove";
        }

        public DataPlayerInfo? Player;

        protected override void Read(CelesteNetBinaryReader reader) {
            Player = reader.ReadRef<DataPlayerInfo>();
        }

        protected override void Write(CelesteNetBinaryWriter writer) {
            writer.WriteRef(Player);
        }

    }
}


<|start_filename|>CelesteNet.Server/NetPlus/ThreadRole.cs<|end_filename|>
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;

namespace Celeste.Mod.CelesteNet.Server {
    /*
    A thread role represents something a thread could be doing, e.g. waiting for
    TCP connections, polling sockets, flushing send queues, or just ideling. The
    thread pool contains a list of thread roles, and it will monitor their
    activity rates and heuristicaly distribute roles among all threads.
    -Popax21
    */
    public abstract class NetPlusThreadRole : IDisposable {

        public abstract class RoleWorker : IDisposable {
            private readonly RWLock ActivityLock;
            internal int ActiveZoneCounter = 0;
            private long LastActivityUpdate = long.MinValue;
            private float LastActivityRate = 0f;

            protected RoleWorker(NetPlusThreadRole role, NetPlusThread thread) {
                Role = role;
                Thread = thread;

                // Init heuristic stuff
                ActivityLock = new();

                using (Role.WorkerLock.W())
                    role.Workers.Add(this);
            }

            public virtual void Dispose() {
                using (Role.WorkerLock.W())
                    Role.Workers.Remove(this);
                ActivityLock.Dispose();
            }

            protected internal abstract void StartWorker(CancellationToken token);

            protected void EnterActiveZone() {
                using (ActivityLock.W())
                    Thread.Pool.IterateSteadyHeuristic(ref LastActivityRate, ref LastActivityUpdate, (ActiveZoneCounter++ > 0) ? 1f : 0f, true);
            }

            protected void ExitActiveZone() {
                using (ActivityLock.W()) {
                    if (ActiveZoneCounter <= 0)
                        throw new InvalidOperationException("Not in an active zone");
                    Thread.Pool.IterateSteadyHeuristic(ref LastActivityRate, ref LastActivityUpdate, (ActiveZoneCounter-- > 0) ? 1f : 0f, true);
                }
            }

            public NetPlusThread Thread { get; }
            public NetPlusThreadRole Role { get; }

            public float ActivityRate {
                get {
                    using (ActivityLock.R())
                        return Thread.Pool.IterateSteadyHeuristic(ref LastActivityRate, ref LastActivityUpdate, (ActiveZoneCounter > 0) ? 1f : 0f);
                }
            }
        }

        public NetPlusThreadPool Pool { get; }
        public float ActivityRate => EnumerateWorkers().Aggregate(0f, (a, w) => a + w.ActivityRate / Workers.Count);

        public abstract int MinThreads { get; }
        public abstract int MaxThreads { get; }

        private bool Disposed = false;
        private readonly RWLock WorkerLock = new();
        private readonly List<RoleWorker> Workers = new();

        protected NetPlusThreadRole(NetPlusThreadPool pool) {
            Pool = pool;
        }

        public virtual void Dispose() {
            if (Disposed)
                return;
            Disposed = true;

            using (WorkerLock.W()) {
                Workers.Clear();
                WorkerLock.Dispose();
            }
        }

        public virtual void InvokeSchedular() {}

        public IEnumerable<RoleWorker> EnumerateWorkers() {
            using (WorkerLock.R())
            foreach (RoleWorker worker in Workers)
                yield return worker;
        }

        public RoleWorker? FindWorker(Func<RoleWorker, bool> filter){
            foreach (RoleWorker worker in EnumerateWorkers()) {
                if (filter(worker))
                    return worker;
            }
            return null;
        }

        public abstract RoleWorker CreateWorker(NetPlusThread thread);

    }

    public sealed class IdleThreadRole : NetPlusThreadRole {

        private sealed class Worker : RoleWorker {
            public Worker(IdleThreadRole role, NetPlusThread thread) : base(role, thread) {}

            protected internal override void StartWorker(CancellationToken token) => token.WaitHandle.WaitOne();
        }

        public override int MinThreads => 0;
        public override int MaxThreads => int.MaxValue;

        public IdleThreadRole(NetPlusThreadPool pool) : base(pool) {}

        public override NetPlusThreadRole.RoleWorker CreateWorker(NetPlusThread thread) => new Worker(this, thread);

    }
}

<|start_filename|>CelesteNet.Shared/DataTypes/DataLowLevelUDPInfo.cs<|end_filename|>
namespace Celeste.Mod.CelesteNet.DataTypes {
    /*
    While debugging the old UDP protocol, I got so fed up with it that I created
    this overengineered protocol instead. However, as it seems way more stable
    and predictable than the old one, I decided to keep it.

    A UDP connection has two properties which are of interest to us here:
    -> UDP connections can be "initialized"
        This means the connection knows its peer's endpoint, it's receiving and
        sending infrastructure is up and running and is activly beeing monitored
        for heartbeats and keepalives. However, just beeing initialized isn't enough
        for the connection to actually enqueue anything onto it's send queue.
    -> UDP connections can be "established"
        This means the connection has an actual UDP connection ID in addition to
        beeing initialized (if it's not established, it has the ID -1), and data
        packets are actually sent over it.
    If UDP's disabled (!UseUDP), then connections can neither be initialized nor
    established. Also note that UDP scoring mechanisms are outside the scope of
    this protocol.

    An UDP connection is said to have "died" when it switches from initialized
    to unestablished. This protocol only notifies the peer if it happens for an
    established connection, however unestablished connections can die too.

    The client initially starts its UDP connection initialized, but
    unestablished. It remains in this "limbo"-state until the server tells it to
    establish the connection. This is used to prevent sending packets before the
    server's ready to receive them, while still handling timeouts and
    keep-alives like usual.

    This packet is used to coordinate established connections, and has a few
    different meanings, depending on the values in it:
    - ConnectionID < 0
        Disable UDP for this client's connection. This is the only packet
        handled even when no connection is initialized.
    - ConnectionID >= 0 && MaxDatagramSize < 1+MaxPacketSize
        Nofication that the connection died (similarly to how the downgrade
        mechanism determines connection death)
    - ConnectionID >= 0 && MaxDatagramSize >= 1+MaxPacketSize
        New MAXIMUM datagram size. Note that once a connection is initially
        established, it's max datagram size can only go down. If the other peer
        has a different datagram size than here requested BEFORE the change,
        it'll respond with an info packet containing it's new maximum datagram size.

    If a peer (in the current implementation, only the client) has an
    initialized but unestablished connection, when it receives a packet falling
    into the last case, and it doesn't refer to an old connection
    (ConnectionID <= udpLastConnectionID), it'll establish it's connection with
    the parameters specified in the packet. This mechanism is used by the server
    to notify the client when it established the UDP connection, and it's ready
    to receive data.

    Example packet exchange
                    CLIENT                                                        SERVER
            <connect via UDP>
    <initialize unestablished connection>
                                            -> (connection token)
                                                                            <establish connection>
                                            <- UDPInfo(0, 4096)
            <establish connection>


            <downgrade connection>
                                            -> UDPInfo(0, 2048)
                                            <- UDPInfo(0, 2048)

                                                                            <connection death>
                                            <- UDPInfo(0, 0)
            <connection death>
        <client decides to disable UDP>
                                            -> UDPInfo(-1, 0)
                                                                            <won't accept UDP connections from client>

    -Popax21
    */
    public class DataLowLevelUDPInfo : DataType<DataLowLevelUDPInfo> {

        static DataLowLevelUDPInfo() {
            DataID = "udpInfo";
        }

        public override DataFlags DataFlags => DataFlags.CoreType | DataFlags.Small;

        public int ConnectionID;
        public int MaxDatagramSize;

        protected override void Read(CelesteNetBinaryReader reader) {
            ConnectionID = reader.Read7BitEncodedInt();
            MaxDatagramSize = reader.Read7BitEncodedInt();
        }

        protected override void Write(CelesteNetBinaryWriter writer) {
            writer.Write7BitEncodedInt(ConnectionID);
            writer.Write7BitEncodedInt(MaxDatagramSize);
        }

    }
}

<|start_filename|>CelesteNet.Server/ConPlus/MultipleSocketBinderRole.cs<|end_filename|>
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace Celeste.Mod.CelesteNet.Server {
    public abstract class MultipleSocketBinderRole : NetPlusThreadRole {

        public new abstract class RoleWorker : NetPlusThreadRole.RoleWorker {

            protected RoleWorker(NetPlusThreadRole role, NetPlusThread thread) : base(role, thread) {}

            public new MultipleSocketBinderRole Role => (MultipleSocketBinderRole) base.Role;

            protected internal override void StartWorker(CancellationToken token) {
                using Socket sock = Role.CreateSocket();
                StartWorker(sock, token);
            }

            protected abstract void StartWorker(Socket socket, CancellationToken token);

        }

        public override int MinThreads => 1;
        public override int MaxThreads => MonoMod.Utils.PlatformHelper.Is(MonoMod.Utils.Platform.Linux) ? int.MaxValue : 1;

        public ProtocolType Protocol { get; }
        public EndPoint EndPoint { get; }

        protected MultipleSocketBinderRole(NetPlusThreadPool pool, ProtocolType protocol, EndPoint endPoint) : base(pool) {
            Protocol = protocol;
            EndPoint = endPoint;
        }

        private Socket CreateSocket() {
            Socket? socket = null;
            try {
                // Create socket and perform socket options magic
                socket = new(EndPoint.AddressFamily, Protocol switch {
                    ProtocolType.Tcp => SocketType.Stream,
                    ProtocolType.Udp => SocketType.Dgram,
                    _ => throw new InvalidOperationException($"Unknown protocol type {Protocol}")
                }, Protocol);
                socket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, false);
                if (MaxThreads > 1)
                    socket.EnableEndpointReuse();

                // Bind the socket
                socket.Bind(EndPoint);

                return socket;
            } catch (Exception) {
                socket?.Dispose();
                throw;
            }
        }

    }
}

<|start_filename|>CelesteNet.Server/ConPlus/TCPUDPSenderRole.cs<|end_filename|>
using Celeste.Mod.CelesteNet.DataTypes;
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace Celeste.Mod.CelesteNet.Server {
    /*
    The TCP/UDP sender role gets specified as the queue flusher for the TCP/UDP
    send queues. When a queue needs to be flushed, it get's added to a queue of
    all waiting queues (queueception), which workers pull from to actually send
    packets. It also does some fancy buffering.
    -Popax21
    */
    public class TCPUDPSenderRole : NetPlusThreadRole {

        private enum QueueType {
            TCP, UDP
        }

        private class Worker : RoleWorker {

            private readonly BufferedSocketStream SockStream;
            private readonly BinaryWriter SockWriter;
            private readonly MemoryStream PacketStream;
            private readonly CelesteNetBinaryWriter PacketWriter;

            private readonly Socket UDPSocket;
            private readonly byte[] UDPBuffer;

            private readonly RWLock TCPMetricsLock;
            private long LastTCPByteRateUpdate, LastTCPPacketRateUpdate;
            private float _TCPByteRate, _TCPPacketRate;

            private readonly RWLock UDPMetricsLock;
            private long LastUDPByteRateUpdate, LastUDPPacketRateUpdate;
            private float _UDPByteRate, _UDPPacketRate;

            public Worker(TCPUDPSenderRole role, NetPlusThread thread) : base(role, thread) {
                SockStream = new(role.Server.Settings.TCPBufferSize);
                SockWriter = new(SockStream);
                PacketStream = new(role.Server.Settings.MaxPacketSize);
                PacketWriter = new(role.Server.Data, null, null, PacketStream);

                UDPSocket = new(SocketType.Dgram, ProtocolType.Udp);
                UDPSocket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, false);
                UDPSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveBuffer, 0);
                UDPSocket.EnableEndpointReuse();
                UDPSocket.Bind(role.UDPEndPoint);
                UDPBuffer = new byte[role.Server.Settings.UDPMaxDatagramSize];

                TCPMetricsLock = new();
                UDPMetricsLock = new();
            }

            public override void Dispose() {
                base.Dispose();
                TCPMetricsLock.Dispose();
                UDPMetricsLock.Dispose();
                SockStream.Dispose();
                SockWriter.Dispose();
                PacketStream.Dispose();
                PacketWriter.Dispose();
                UDPSocket.Dispose();
            }

            protected internal override void StartWorker(CancellationToken token) {
                // Handle queues from the queue
                foreach ((QueueType queueType, CelesteNetSendQueue queue) in Role.QueueQueue.GetConsumingEnumerable(token)) {
                    ConPlusTCPUDPConnection con = (ConPlusTCPUDPConnection) queue.Con;
                    EnterActiveZone();
                    try {
                        using (con.Utilize(out bool alive)) {
                            // Maybe the connection got closed while it was in the queue
                            if (!alive || !con.IsConnected)
                                continue;

                            switch (queueType) {
                                case QueueType.TCP: {
                                    FlushTCPQueue(con, queue, token);
                                } break;
                                case QueueType.UDP: {
                                    lock (con.UDPLock) {
                                        // If there's no established UDP connection, just drop all packets
                                        if (con.UDPEndpoint == null)
                                            queue.SignalFlushed();
                                        else
                                            FlushUDPQueue(con, queue, token);
                                    }
                                } break;
                            }
                        }
                    } catch (Exception e) {
                        switch (queueType) {
                            case QueueType.TCP: {
                                if (e is SocketException se && se.IsDisconnect()) {
                                    Logger.Log(LogLevel.INF, "tcpsend", $"Remote of connection {con} closed the connection");
                                    con.DisposeSafe();
                                    continue;
                                }

                                Logger.Log(LogLevel.WRN, "tcpsend", $"Error flushing connection {con} TCP queue '{queue.Name}': {e}");
                                con.DisposeSafe();
                            } break;
                            case QueueType.UDP: {
                                Logger.Log(LogLevel.DBG, "udpsend", $"Error flushing connection {con} UDP queue '{queue.Name}': {e}");
                                con.DecreaseUDPScore();
                            } break;
                        }
                    } finally {
                        ExitActiveZone();
                    }
                }
            }

            private void FlushTCPQueue(ConPlusTCPUDPConnection con, CelesteNetSendQueue queue, CancellationToken token) {
                // Check if the connection's capped
                if (con.TCPSendCapped) {
                    Logger.Log(LogLevel.WRN, "tcpsend", $"Connection {con} hit TCP uplink cap: {con.TCPSendRate.ByteRate} BpS {con.TCPSendRate.PacketRate} PpS {con.Server.CurrentTickRate * con.Server.Settings.PlayerTCPUplinkBpTCap} cap BpS {con.Server.CurrentTickRate * con.Server.Settings.PlayerTCPUplinkPpTCap} cap PpS");

                    // Requeue the queue to be flushed later
                    queue.DelayFlush(con.TCPSendCapDelay, true);
                    return;
                }

                SockStream.Socket = con.TCPSocket;
                PacketWriter.Strings = con.Strings;
                PacketWriter.SlimMap = con.SlimMap;

                // Write all packets
                int byteCounter = 0, packetCounter = 0;
                while (queue.BackQueue.TryDequeue(out DataType? packet)) {
                    // Write the packet onto the temporary packet stream
                    PacketStream.Position = 0;
                    con.Data.Write(PacketWriter, packet!);
                    PacketWriter.Flush();
                    int packLen = (int) PacketStream.Position;

                    // Write size and raw packet data into the actual stream
                    SockWriter.Write((ushort) packLen);
                    SockStream.Write(PacketStream.GetBuffer(), 0, packLen);

                    // Update connection metrics and check if we hit the connection cap
                    con.TCPSendRate.UpdateRate(2 + packLen, 1);
                    if (con.TCPSendCapped)
                        break;

                    byteCounter += 2 + packLen;
                    packetCounter++;

                    if (packet is not DataLowLevelKeepAlive)
                        con.SurpressTCPKeepAlives();
                }
                SockStream.Flush();
                SockStream.Socket = null;

                // Signal the queue that it's flushed if we didn't hit a cap
                // Else requeue it to be flushed again later
                if (queue.BackQueue.Count <= 0)
                    queue.SignalFlushed();
                else
                    queue.DelayFlush(con.TCPSendCapDelay, true);

                // Iterate metrics
                using (TCPMetricsLock.W()) {
                    Role.Pool.IterateEventHeuristic(ref _TCPByteRate, ref LastTCPByteRateUpdate, byteCounter, true);
                    Role.Pool.IterateEventHeuristic(ref _TCPPacketRate, ref LastTCPPacketRateUpdate, packetCounter, true);
                }
            }

            private void FlushUDPQueue(ConPlusTCPUDPConnection con, CelesteNetSendQueue queue, CancellationToken token) {
                // TODO This could be optimized with sendmmsg

                // Check if the connection's capped
                if (con.UDPSendCapped) {
                    Logger.Log(LogLevel.WRN, "udpsend", $"Connection {con} hit UDP uplink cap: {con.UDPSendRate.ByteRate} BpS {con.UDPSendRate.PacketRate} PpS {con.Server.CurrentTickRate * con.Server.Settings.PlayerUDPUplinkBpTCap} cap BpS {con.Server.CurrentTickRate * con.Server.Settings.PlayerUDPUplinkPpTCap} cap PpS");

                    // UDP's unreliable, just drop the excess packets
                    queue.SignalFlushed();
                    return;
                }

                PacketWriter.Strings = con.Strings;
                PacketWriter.SlimMap = con.SlimMap;

                // Write all packets
                UDPBuffer[0] = con.NextUDPContainerID();
                int bufOff = 1;

                int byteCounter = 0, packetCounter = 0;
                foreach (DataType packet in queue.BackQueue) {
                    // Write the packet onto the temporary packet stream
                    PacketStream.Position = 0;
                    con.Data.Write(PacketWriter, packet);
                    PacketWriter.Flush();
                    int packLen = (int) PacketStream.Position;

                    // Copy packet data to the container buffer
                    if (bufOff + packLen > UDPBuffer.Length) {
                        // Send container & start a new one
                        UDPSocket.SendTo(UDPBuffer, bufOff, SocketFlags.None, con.UDPEndpoint!);
                        UDPBuffer[0] = con.NextUDPContainerID();
                        bufOff = 1;

                        // Update connection metrics and check if we hit the connection cap
                        con.UDPSendRate.UpdateRate(bufOff, 1);
                        if (con.UDPSendCapped)
                            break;
                    }

                    Buffer.BlockCopy(PacketStream.GetBuffer(), 0, UDPBuffer, bufOff, packLen);
                    bufOff += packLen;

                    byteCounter += packLen;
                    packetCounter++;

                    if (packet is not DataLowLevelKeepAlive)
                        con.SurpressUDPKeepAlives();
                }

                // Send the last container
                if (bufOff > 1) {
                    UDPSocket.SendTo(UDPBuffer, bufOff, SocketFlags.None, con.UDPEndpoint!);
                    con.UDPSendRate.UpdateRate(bufOff, 1);
                }

                // Signal the queue that it's flushed
                queue.SignalFlushed();

                // Iterate metrics
                using (UDPMetricsLock.W()) {
                    Role.Pool.IterateEventHeuristic(ref _UDPByteRate, ref LastTCPByteRateUpdate, byteCounter, true);
                    Role.Pool.IterateEventHeuristic(ref _UDPPacketRate, ref LastTCPPacketRateUpdate, packetCounter, true);
                }
            }

            public new TCPUDPSenderRole Role => (TCPUDPSenderRole) base.Role;

            public float TCPByteRate {
                get {
                    using (TCPMetricsLock.R())
                        return Role.Pool.IterateEventHeuristic(ref _TCPByteRate, ref LastTCPByteRateUpdate);
                }
            }

            public float TCPPacketRate {
                get {
                    using (TCPMetricsLock.R())
                        return Role.Pool.IterateEventHeuristic(ref _TCPPacketRate, ref LastTCPPacketRateUpdate);
                }
            }

            public float UDPByteRate {
                get {
                    using (UDPMetricsLock.R())
                        return Role.Pool.IterateEventHeuristic(ref _UDPByteRate, ref LastUDPByteRateUpdate);
                }
            }

            public float UDPPacketRate {
                get {
                    using (UDPMetricsLock.R())
                        return Role.Pool.IterateEventHeuristic(ref _UDPPacketRate, ref LastUDPPacketRateUpdate);
                }
            }

        }

        public override int MinThreads => 1;
        public override int MaxThreads => int.MaxValue;

        public CelesteNetServer Server { get; }
        public EndPoint UDPEndPoint { get; }

        public float TCPByteRate => EnumerateWorkers().Aggregate(0f, (r, w) => r + ((Worker) w).TCPByteRate);
        public float TCPPacketRate => EnumerateWorkers().Aggregate(0f, (r, w) => r + ((Worker) w).TCPPacketRate);

        public float UDPByteRate => EnumerateWorkers().Aggregate(0f, (r, w) => r + ((Worker) w).UDPByteRate);
        public float UDPPacketRate => EnumerateWorkers().Aggregate(0f, (r, w) => r + ((Worker) w).UDPPacketRate);

        private readonly BlockingCollection<(QueueType, CelesteNetSendQueue)> QueueQueue = new();

        public TCPUDPSenderRole(NetPlusThreadPool pool, CelesteNetServer server, EndPoint udpEP) : base(pool) {
            Server = server;
            UDPEndPoint = udpEP;
        }

        public override void Dispose() {
            QueueQueue.Dispose();
            base.Dispose();
        }

        public override RoleWorker CreateWorker(NetPlusThread thread) => new Worker(this, thread);

        public void TriggerTCPQueueFlush(CelesteNetSendQueue queue) => QueueQueue.Add((QueueType.TCP, queue));
        public void TriggerUDPQueueFlush(CelesteNetSendQueue queue) => QueueQueue.Add((QueueType.UDP, queue));

    }
}

<|start_filename|>CelesteNet.Server/ConPlus/TCPUDPConnection.cs<|end_filename|>
using Celeste.Mod.CelesteNet.DataTypes;
using System;
using System.IO;
using System.Net.Sockets;
using System.Threading;

namespace Celeste.Mod.CelesteNet.Server {
    public class ConPlusTCPUDPConnection : CelesteNetTCPUDPConnection {

        public const int DownlinkCapCounterMax = 8;

        public class RateMetric : IDisposable {

            public readonly ConPlusTCPUDPConnection Con;
            private readonly RWLock RateLock;
            private long LastByteRateUpdate, LastPacketRateUpdate;
            private float _ByteRate, _PacketRate;

            public float ByteRate {
                get {
                    using (RateLock.R())
                        return Con.Server.ThreadPool.IterateEventHeuristic(ref _ByteRate, ref LastByteRateUpdate);
                }
            }

            public float PacketRate {
                get {
                    using (RateLock.R())
                        return Con.Server.ThreadPool.IterateEventHeuristic(ref _PacketRate, ref LastPacketRateUpdate);
                }
            }

            internal RateMetric(ConPlusTCPUDPConnection con) {
                Con = con;
                RateLock = new();
                LastByteRateUpdate = LastPacketRateUpdate = 0;
                _ByteRate = _PacketRate = 0;
            }

            public void Dispose() {
                RateLock.Dispose();
            }

            public void UpdateRate(int byteCount, int packetCount) {
                using (RateLock.W()) {
                    Con.Server.ThreadPool.IterateEventHeuristic(ref _ByteRate, ref LastByteRateUpdate, byteCount, true);
                    Con.Server.ThreadPool.IterateEventHeuristic(ref _PacketRate, ref LastPacketRateUpdate, packetCount, true);
                }
            }

        }

        public readonly CelesteNetServer Server;
        public readonly TCPReceiverRole TCPReceiver;
        public readonly UDPReceiverRole UDPReceiver;
        public readonly TCPUDPSenderRole Sender;

        private readonly RWLock UsageLock;
        private int DownlinkCapCounter = 0;
        private readonly object TCPLock = new();
        private readonly byte[] TCPBuffer;
        private int TCPBufferOff;
        public int UDPNextConnectionID = 0;

        public readonly RateMetric TCPRecvRate, TCPSendRate;
        public readonly RateMetric UDPRecvRate, UDPSendRate;

        public bool TCPSendCapped => TCPSendRate.ByteRate > Server.CurrentTickRate * Server.Settings.PlayerTCPUplinkBpTCap || TCPSendRate.PacketRate > Server.CurrentTickRate * Server.Settings.PlayerTCPUplinkPpTCap;
        public float TCPSendCapDelay => Math.Max(Server.Settings.HeuristicSampleWindow * Math.Max(1 - Server.Settings.PlayerTCPUplinkBpTCap / TCPSendRate.ByteRate, 1 - Server.Settings.PlayerTCPUplinkPpTCap / TCPSendRate.PacketRate), 0);
        public bool UDPSendCapped => UDPSendRate.ByteRate > Server.CurrentTickRate * Server.Settings.PlayerUDPUplinkBpTCap || UDPSendRate.PacketRate > Server.CurrentTickRate * Server.Settings.PlayerUDPUplinkPpTCap;

        public ConPlusTCPUDPConnection(
            CelesteNetServer server,
            uint token,
            Settings settings,
            Socket tcpSock,
            TCPReceiverRole tcpReceiver,
            UDPReceiverRole udpReceiver,
            TCPUDPSenderRole sender
        ) : base(server.Data, token, settings, tcpSock, sender.TriggerTCPQueueFlush, sender.TriggerUDPQueueFlush) {
            Server = server;
            TCPReceiver = tcpReceiver;
            UDPReceiver = udpReceiver;
            Sender = sender;
            UsageLock = new();
            TCPRecvRate = new(this);
            TCPSendRate = new(this);
            UDPRecvRate = new(this);
            UDPSendRate = new(this);

            // Initialize TCP receiving
            tcpSock.Blocking = false;
            TCPBuffer = new byte[Math.Max(server.Settings.TCPBufferSize, 2 + server.Settings.MaxPacketSize)];
            TCPBufferOff = 0;
            tcpReceiver.Poller.AddConnection(this);

            // Initialize UDP receiving
            udpReceiver.AddConnection(this);
        }

        // The usage lock could still be used after we dispose
        // So keep it alive as long as possible
        ~ConPlusTCPUDPConnection() => UsageLock.Dispose();

        protected override void Dispose(bool disposing) {
            TCPReceiver.Poller.RemoveConnection(this);
            UDPReceiver.RemoveConnection(this);
            using (UsageLock.W()) {
                base.Dispose(disposing);
                TCPRecvRate.Dispose();
                TCPSendRate.Dispose();
                UDPRecvRate.Dispose();
                UDPSendRate.Dispose();
            }
        }

        public IDisposable? Utilize(out bool alive) {
            if (!IsAlive) {
                alive = false;
                return null;
            }

            IDisposable dis = UsageLock.R();

            // Detect race conditions with Dispose
            if (!IsAlive) {
                dis.Dispose();
                alive = false;
                return null;
            }

            alive = true;
            return dis;
        }

        public override string? DoHeartbeatTick() {
            string? disposeReason = base.DoHeartbeatTick();
            if (disposeReason != null)
                return disposeReason;

            // Decrement the amount of times we hit the downlink cap
            if (Volatile.Read(ref DownlinkCapCounter) > 0)
                Interlocked.Decrement(ref DownlinkCapCounter);

            return null;
        }

        public void HandleTCPData() {
            lock (TCPLock) {
                using (Utilize(out bool alive)) {
                    if (!alive || !IsConnected)
                        return;

                    do {
                        // Receive data into the buffer
                        int numRead = TCPSocket.Receive(TCPBuffer, TCPBufferOff, TCPBuffer.Length - TCPBufferOff, SocketFlags.None);
                        if (numRead <= 0) {
                            // The remote closed the connection
                            Logger.Log(LogLevel.INF, "tcpudpcon", $"Remote of connection {this} closed the connection");
                            goto closeConnection;
                        }
                        TCPBufferOff += numRead;

                        // Let the connection know we got a heartbeat
                        TCPHeartbeat();

                        // Update metrics and check if we hit the cap
                        TCPRecvRate.UpdateRate(numRead, 0);
                        if (TCPRecvRate.ByteRate > Server.CurrentTickRate * Server.Settings.PlayerTCPDownlinkBpTCap && Interlocked.Increment(ref DownlinkCapCounter) >= DownlinkCapCounterMax) {
                            Logger.Log(LogLevel.WRN, "tcpudpcon", $"Connection {this} hit TCP downlink byte cap: {TCPRecvRate.ByteRate} BpS {Server.CurrentTickRate * Server.Settings.PlayerTCPDownlinkBpTCap} cap BpS");
                            goto closeConnection;
                        }

                        while (true) {
                            // Check if we have read the first two length bytes
                            if (2 <= TCPBufferOff) {
                                // Get the packet length
                                int packetLen = BitConverter.ToUInt16(TCPBuffer, 0);
                                if (packetLen < 0 || packetLen > Server.Settings.MaxPacketSize) {
                                    Logger.Log(LogLevel.WRN, "tcpudpcon", $"Connection {this} sent packet over the size limit: {packetLen} > {Server.Settings.MaxPacketSize}");
                                    goto closeConnection;
                                }

                                // Did we receive the entire packet already?
                                if (2 + packetLen <= TCPBufferOff) {
                                    // Update metrics and check if we hit the cap
                                    TCPRecvRate.UpdateRate(0, 1);
                                    if (TCPRecvRate.PacketRate > Server.CurrentTickRate * Server.Settings.PlayerTCPDownlinkPpTCap) {
                                        Logger.Log(LogLevel.WRN, "tcpudpcon", $"Connection {this} hit TCP downlink packet cap: {TCPRecvRate.PacketRate} PpS {Server.CurrentTickRate * Server.Settings.PlayerTCPDownlinkPpTCap} cap PpS");
                                        goto closeConnection;
                                    }

                                    // Read the packet
                                    DataType packet;
                                    using (MemoryStream mStream = new(TCPBuffer, 2, packetLen))
                                    using (CelesteNetBinaryReader reader = new(Server.Data, Strings, SlimMap, mStream))
                                        packet = Server.Data.Read(reader);

                                    // Handle the packet
                                    switch (packet) {
                                        case DataLowLevelUDPInfo udpInfo: {
                                            HandleUDPInfo(udpInfo);
                                        } break;
                                        case DataLowLevelStringMap strMap: {
                                            Strings.RegisterWrite(strMap.String, strMap.ID);
                                        } break;
                                        case DataLowLevelSlimMap slimMap: {
                                            if (slimMap.PacketType != null)
                                                SlimMap.RegisterWrite(slimMap.PacketType, slimMap.ID);
                                        } break;
                                        default: {
                                            Receive(packet);
                                        } break;
                                    }

                                    // Remove the packet data from the buffer
                                    TCPBufferOff -= 2 + packetLen;
                                    Buffer.BlockCopy(TCPBuffer, 2 + packetLen, TCPBuffer, 0, TCPBufferOff);
                                    continue;
                                }
                            }
                            break;
                        }
                    } while (TCPSocket.Available > 0);

                    // Promote optimizations
                    PromoteOptimizations();
                }

                return;
                closeConnection:
                Dispose();
            }
        }

        public void HandleUDPDatagram(byte[] buffer, int dgSize) {
            lock (UDPLock) {
                using (Utilize(out bool alive)) {
                    if (!alive || !IsConnected)
                        return;

                    if (UDPEndpoint == null || dgSize <= 0)
                        return;

                    // Update metrics
                    UDPRecvRate.UpdateRate(dgSize, 0);
                    if (UDPRecvRate.ByteRate > Server.CurrentTickRate * Server.Settings.PlayerUDPDownlinkBpTCap && Interlocked.Increment(ref DownlinkCapCounter) >= DownlinkCapCounterMax) {
                        Logger.Log(LogLevel.WRN, "tcpudpcon", $"Connection {this} hit UDP downlink byte cap: {UDPRecvRate.ByteRate} BpS {Server.CurrentTickRate * Server.Settings.PlayerUDPDownlinkBpTCap} cap BpS");
                        goto closeConnection;
                    }

                    // Let the connection know we received a container
                    ReceivedUDPContainer(buffer[0]);

                    using (MemoryStream mStream = new(buffer, 0, dgSize))
                    using (CelesteNetBinaryReader reader = new(Server.Data, Strings, SlimMap, mStream)) {
                        // Get the container ID
                        byte containerID = reader.ReadByte();

                        // Read packets until we run out data
                        while (mStream.Position < dgSize-1) {
                            DataType packet = Server.Data.Read(reader);

                            // Update metrics
                            UDPRecvRate.UpdateRate(0, 1);
                            if (UDPRecvRate.PacketRate > Server.CurrentTickRate * Server.Settings.PlayerUDPDownlinkPpTCap) {
                                Logger.Log(LogLevel.WRN, "tcpudpcon", $"Connection {this} hit UDP downlink packet cap: {UDPRecvRate.PacketRate} PpS {Server.CurrentTickRate * Server.Settings.PlayerUDPDownlinkPpTCap} cap PpS");
                                goto closeConnection;
                            }

                            if (packet.TryGet<MetaOrderedUpdate>(Server.Data, out MetaOrderedUpdate? orderedUpdate))
                                orderedUpdate.UpdateID = containerID;
                            Receive(packet);
                        }
                    }

                    // Promote optimizations
                    PromoteOptimizations();
                }

                return;
                closeConnection:
                Dispose();
            }
        }

    }
}

<|start_filename|>CelesteNet.Server/NetPlus/ThreadPool.cs<|end_filename|>
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;

namespace Celeste.Mod.CelesteNet.Server {
    public class NetPlusThreadPool : IDisposable {

        private bool Disposed = false;
        private readonly RWLock _PoolLock = new();
        private readonly Stopwatch RuntimeWatch = new();

        private readonly NetPlusThread[] Threads;
        private readonly int[] ThreadRestarts;
        private readonly CancellationTokenSource TokenSrc;

        private readonly RWLock _RoleLock = new();
        private readonly IdleThreadRole _IdleRole;
        private readonly NetPlusRoleScheduler _Scheduler;

        public RWLock PoolLock => _PoolLock;
        public RWLock RoleLock => _RoleLock;

        public int NumThreads => Threads.Length;
        public int MaxThreadsRestart { get; }
        public IdleThreadRole IdleRole => _IdleRole;
        public NetPlusRoleScheduler Scheduler => _Scheduler;
        public CancellationToken Token => TokenSrc.Token;

        public int HeuristicSampleWindow { get; }
        public float ActivityRate => EnumerateThreads().Aggregate(0f, (a, t) => a + t.ActivityRate) / Threads.Length;

        public NetPlusThreadPool(int numThreads, int maxThreadRestarts, int heuristicSampleWindow, float schedulerInterval, float underloadThreshold, float overloadThreshold, float stealThreshold) {
            MaxThreadsRestart = maxThreadRestarts;
            HeuristicSampleWindow = heuristicSampleWindow;

            // Start the runtime watch
            RuntimeWatch.Start();

            using (_PoolLock.W())
            using (_RoleLock.W()) {
                // Create threads
                TokenSrc = new();
                _IdleRole = new(this);
                Logger.Log(LogLevel.INF, "netplus", $"Creating thread pool with {numThreads} threads");
                ThreadRestarts = new int[numThreads];
                Threads = Enumerable.Range(0, numThreads).Select(idx => new NetPlusThread(this, idx, _IdleRole)).ToArray();
            }

            // Create the schedular
            _Scheduler = new(this, schedulerInterval, underloadThreshold, overloadThreshold, stealThreshold);
        }

        public void Dispose() {
            if (Disposed)
                return;
            Disposed = true;

            // Stop the scheduler
            Scheduler.Dispose();

            using (_PoolLock.W())
            using (_RoleLock.W()) {
                // Stop threads
                TokenSrc.Dispose();
                foreach (NetPlusThread thread in Threads)
                    thread.Thread.Join();

                RuntimeWatch.Stop();
                _RoleLock.Dispose();
                _PoolLock.Dispose();
            }
        }

        public float IterateSteadyHeuristic(ref float lastVal, ref long lastUpdate, float curVal, bool update=false) {
            long curMs = RuntimeWatch.ElapsedMilliseconds;
            long cutoffMs = curMs - HeuristicSampleWindow;
            float newVal = (lastUpdate < cutoffMs) ? curVal : ((lastVal * (lastUpdate - cutoffMs) + curVal * (curMs - lastUpdate)) / HeuristicSampleWindow);
            if (update) {
                lastVal = newVal;
                lastUpdate = curMs;
            }
            return newVal;
        }

        public float IterateEventHeuristic(ref float lastVal, ref long lastUpdate, int numEvents=0, bool update=false) {
            long curMs = RuntimeWatch.ElapsedMilliseconds;
            long cutoffMs = curMs - HeuristicSampleWindow;
            float newVal = (((lastUpdate < cutoffMs) ? 0 : (lastVal * (lastUpdate - cutoffMs) / 1000f)) + numEvents) / HeuristicSampleWindow * 1000f;
            if (update) {
                lastVal = newVal;
                lastUpdate = curMs;
            }
            return newVal;
        }

        public IEnumerable<NetPlusThread> EnumerateThreads() {
            using (PoolLock.R())
            foreach (NetPlusThread thread in Threads)
                yield return thread;
        }

        public NetPlusThread? FindThread(Func<NetPlusThread, bool> filter){
            foreach (NetPlusThread thread in EnumerateThreads()) {
                if (filter(thread))
                    return thread;
            }
            return null;
        }

        internal void ReportThreadError(NetPlusThread thread, Exception ex) {
            // Log the exception
            Logger.Log(LogLevel.CRI, "netplus", $"Error in thread pool thread {thread.Index}: {ex}");

            // It's OK, we can start a new one... right?
            if (!Disposed) {
                using (PoolLock.W()) {
                    if (++ThreadRestarts[thread.Index] < MaxThreadsRestart) {
                            Logger.Log(LogLevel.DBG, "netplus", $"Restarting thread pool thread {thread.Index}");
                            Threads[thread.Index] = new(this, thread.Index, thread.Role);
                    } else throw new InvalidOperationException($"Too many restarts for thread pool thread {thread.Index}", ex);
                }
            }
        }

        internal void IndicateThreadStable(NetPlusThread thread) {
            // Decrement the thread's restart count
            if (!Disposed) {
                using (PoolLock.W()) {
                    if (ThreadRestarts[thread.Index] > 0)
                        ThreadRestarts[thread.Index]--;
                }
            }
        }

    }

    public class NetPlusThread {

        private readonly Thread _Thread;
        private NetPlusThreadRole _Role;
        private readonly SemaphoreSlim RoleSwitchSem;
        private NetPlusThreadRole.RoleWorker? _RoleWorker;
        private CancellationTokenSource? RoleWorkerTokenSrc;
        private float LastActivityRate = 0f;

        public NetPlusThreadPool Pool { get; }

        public int Index { get; }

        public Thread Thread => _Thread;

        public NetPlusThreadRole Role {
            get => _Role;
            set {
                if (_Role == value)
                    return;
                RoleSwitchSem.Wait();
                using (Pool.RoleLock.R()) {
                    _Role = value;
                    RoleWorkerTokenSrc?.Cancel();
                }
            }
        }

        public NetPlusThreadRole.RoleWorker? RoleWorker => _RoleWorker;

        public float ActivityRate => LastActivityRate = _RoleWorker?.ActivityRate ?? LastActivityRate;

        internal NetPlusThread(NetPlusThreadPool pool, int idx, NetPlusThreadRole initRole) {
            Pool = pool;
            Index = idx;
            _Role = initRole;
            RoleSwitchSem = new(0, 1);

            // Start the thread
            _Thread = new(ThreadLoop);
            _Thread.Name = $"CelesteNet.Server Thread Pool {pool} Thread {idx}";
            _Thread.Start();
        }

        private void ThreadLoop() {
            CancellationToken? lastWorkerToken = null;
            try {
                CancellationToken poolToken = Pool.Token;
                poolToken.Register(() => RoleWorkerTokenSrc?.Cancel());
                while (!poolToken.IsCancellationRequested) {
                    Logger.Log(LogLevel.DBG, "netplus", $"Thread pool thread {Index} starting role worker for role {_Role}");

                    // Start the worker
                    using (_RoleWorker = _Role.CreateWorker(this))
                    using (RoleWorkerTokenSrc = new()) {
                        lastWorkerToken = RoleWorkerTokenSrc.Token;
                        RoleSwitchSem.Release();
                        try {
                            while (!RoleWorkerTokenSrc.IsCancellationRequested) {
                                _RoleWorker.StartWorker(RoleWorkerTokenSrc.Token);
                                if (!RoleWorkerTokenSrc.IsCancellationRequested)
                                    Logger.Log(LogLevel.WRN, "netplus", $"Thread pool thread {Index} worker {_RoleWorker} exited prematurely!");
                            }
                        } finally {
                            _RoleWorker.ActiveZoneCounter = 0;
                            _RoleWorker = null;
                            RoleWorkerTokenSrc = null;
                        }
                    }
                }
            } catch (Exception e) {
                if (e is OperationCanceledException ce && ce.CancellationToken == lastWorkerToken) return;

                // Report error to the pool
                Pool.ReportThreadError(this, e);
            } finally {
                // Dispose stuff
                RoleSwitchSem.Dispose();
            }
        }

    }
}

<|start_filename|>CelesteNet.Shared/CelesteNetBinaryReader.cs<|end_filename|>
﻿using Celeste.Mod.CelesteNet.DataTypes;
using Microsoft.Xna.Framework;
using Monocle;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.ObjectFactories;

namespace Celeste.Mod.CelesteNet {
    public class CelesteNetBinaryReader : BinaryReader {

        public readonly DataContext Data;

        public OptMap<string>? Strings;
        public OptMap<Type>? SlimMap;

        public CelesteNetBinaryReader(DataContext ctx, OptMap<string>? strings, OptMap<Type>? slimMap, Stream input)
            : base(input, CelesteNetUtils.UTF8NoBOM) {
            Data = ctx;
            Strings = strings;
            SlimMap = slimMap;
        }

        public CelesteNetBinaryReader(DataContext ctx, OptMap<string>? strings, OptMap<Type>? slimMap, Stream input, bool leaveOpen)
            : base(input, CelesteNetUtils.UTF8NoBOM, leaveOpen) {
            Data = ctx;
            Strings = strings;
            SlimMap = slimMap;
        }

        public new int Read7BitEncodedInt()
            => base.Read7BitEncodedInt();
        public uint Read7BitEncodedUInt()
            => unchecked((uint) Read7BitEncodedInt());

        public virtual Vector2 ReadVector2()
            => new(ReadSingle(), ReadSingle());
        public virtual Vector2 ReadVector2Scale()
            => new(Calc.Clamp(ReadSingle(), -3f, 3f), Calc.Clamp(ReadSingle(), -3f, 3f));

        public virtual Color ReadColor()
            => new(ReadByte(), ReadByte(), ReadByte(), ReadByte());

        public virtual Color ReadColorNoA()
            => new(ReadByte(), ReadByte(), ReadByte(), 255);

        public virtual DateTime ReadDateTime()
            => DateTime.FromBinary(ReadInt64());

        [ThreadStatic]
        private static char[]? charsShared;
        private unsafe void ReadNetStringCore(char c, out char[] chars, out int i) {
            const int buffer = 8;
            chars = charsShared ?? new char[128];
            int length = chars.Length;
            i = 0;
            goto Read;

            Resize:
            length *= 2;
            Array.Resize(ref chars, length);

            Read:
            fixed (char* ptr = chars) {
                ptr[i++] = c;
                while ((c = ReadChar()) != '\0') {
                    if (i > 4096)
                        throw new Exception("String too long.");
                    if ((i + buffer) >= length)
                        goto Resize;
                    ptr[i++] = c;
                }
                for (int j = 0; j < buffer; j++)
                    ptr[i++] = '\0';
                i -= buffer;
            }

            charsShared = chars;
        }

        public virtual string ReadNetString() {
            char c = (char) ReadByte();
            if (c == 0xFF)
                throw new Exception("Trying to read a mapped string as a non-mapped string!");

            if (c == 0x00)
                return "";

            ReadNetStringCore(c, out char[] chars, out int i);
            return chars.ToDedupedString(i);
        }

        public virtual string ReadNetMappedString() {
            string value;

            char c = (char) ReadByte();
            if (c == 0xFF) {
                if (Strings == null)
                    throw new Exception("Trying to read a mapped string without a string map!");
                value = Strings.Get(Read7BitEncodedInt());
                return value;
            }

            if (c == 0x00)
                return "";

            ReadNetStringCore(c, out char[] chars, out int i);
            value = chars.ToDedupedString(i);

            Strings?.CountRead(value);
            return value;
        }

        public T? ReadRef<T>() where T : DataType<T> {
            uint id = Read7BitEncodedUInt();
            if (id == uint.MaxValue)
                throw new InvalidDataException($"Expected reference to '{Data.DataTypeToID[typeof(T)]}', but didn't get one");
            return Data.GetRef<T>(id);
        }

        public T? ReadOptRef<T>() where T : DataType<T>
            => Data.TryGetRef(Read7BitEncodedUInt(), out T? value) ? value : null;

    }
}


<|start_filename|>CelesteNet.Server/ConPlus/UDPReceiverRole.cs<|end_filename|>
using Celeste.Mod.CelesteNet.DataTypes;
using System;
using System.Collections.Concurrent;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace Celeste.Mod.CelesteNet.Server {
    public class UDPReceiverRole : MultipleSocketBinderRole {

        private class Worker : RoleWorker {

            public new UDPReceiverRole Role => (UDPReceiverRole) base.Role;

            public Worker(UDPReceiverRole role, NetPlusThread thread) : base(role, thread) { }

            protected override void StartWorker(Socket socket, CancellationToken token) {
                // Receive packets as long as the token isn't canceled
                // TODO We can optimize this with recvmmsg
                byte[] dgBuffer = new byte[Role.Server.Settings.UDPMaxDatagramSize];
                int dgSize;

                EnterActiveZone();
                token.Register(() => socket.Close());
                while (!token.IsCancellationRequested) {
                    EndPoint dgramSender = socket.LocalEndPoint!;
                    ExitActiveZone();
                    try {
                        dgSize = socket.ReceiveFrom(dgBuffer, ref dgramSender);
                    } catch (SocketException) {
                        // There's no better way to do this, as far as I know...
                        if (token.IsCancellationRequested)
                            return;
                        throw;
                    }
                    EnterActiveZone();

                    if (dgSize == 5 && dgBuffer[0] == 0xff) {
                        // Get the connection token
                        uint conToken = BitConverter.ToUInt32(dgBuffer, 1);

                        // Get the connection from the token
                        if (!Role.conTokenMap.TryGetValue(conToken, out ConPlusTCPUDPConnection? tokCon) || tokCon == null)
                            continue;

                        // Initialize the connection
                        lock (tokCon.UDPLock) {
                            if (!tokCon.UseUDP) {
                                tokCon.Send(new DataLowLevelUDPInfo {
                                    ConnectionID = -1,
                                    MaxDatagramSize = 0
                                });
                                continue;
                            }

                            if (tokCon.UDPEndpoint != null) {
                                // This makes hijacking possible, but is also
                                // how a client with changing IP addresses can
                                // reconnect it's UDP connection :(
                                Logger.Log(LogLevel.INF, "udprecv", $"Connection {tokCon} UDP endpoint changed: {tokCon.UDPEndpoint} -> {dgramSender}");
                                Role.endPointMap.TryRemove(tokCon.UDPEndpoint, out _);
                            }

                            // Initialize and establish the UDP connection
                            tokCon.InitUDP(dgramSender, tokCon.UDPNextConnectionID++, Role.Server.Settings.UDPMaxDatagramSize);

                            // Add the connection to the map
                            Role.endPointMap[dgramSender] = tokCon;
                        }
                        continue;
                    }

                    // Get the associated connection
                    if (!Role.endPointMap.TryGetValue(dgramSender, out ConPlusTCPUDPConnection? con) || con == null)
                        continue;

                    try {
                        // Handle the UDP datagram
                        con.HandleUDPDatagram(dgBuffer, dgSize);
                    } catch (Exception e) {
                        Logger.Log(LogLevel.WRN, "udprecv", $"Error while reading from connection {con}: {e}");
                        con.DecreaseUDPScore();
                    }
                }
                ExitActiveZone();
            }

        }

        private readonly ConcurrentDictionary<uint, ConPlusTCPUDPConnection> conTokenMap = new();
        private readonly ConcurrentDictionary<EndPoint, ConPlusTCPUDPConnection> endPointMap = new();

        public UDPReceiverRole(NetPlusThreadPool pool, CelesteNetServer server, EndPoint endPoint) : base(pool, ProtocolType.Udp, endPoint) {
            Server = server;
        }

        public override NetPlusThreadRole.RoleWorker CreateWorker(NetPlusThread thread) => new Worker(this, thread);

        public void AddConnection(ConPlusTCPUDPConnection con) {
            conTokenMap.TryAdd(con.ConnectionToken, con);
            con.OnUDPDeath += UDPDeath;
        }

        public void RemoveConnection(ConPlusTCPUDPConnection con) {
            con.OnUDPDeath -= UDPDeath;
            conTokenMap.TryRemove(con.ConnectionToken, out _);

            EndPoint? udpEP = con.UDPEndpoint;
            if (udpEP != null)
                endPointMap.TryRemove(udpEP!, out _);
        }

        private void UDPDeath(CelesteNetTCPUDPConnection con, EndPoint ep) {
            endPointMap.TryRemove(ep, out _);
        }

        public CelesteNetServer Server { get; }

    }
}

<|start_filename|>CelesteNet.Shared/DataTypes/DataLowLevelSlimMap.cs<|end_filename|>
using System;

namespace Celeste.Mod.CelesteNet.DataTypes {
    public class DataLowLevelSlimMap : DataType<DataLowLevelSlimMap> {

        static DataLowLevelSlimMap() {
            DataID = "slimMap";
        }

        public override DataFlags DataFlags => DataFlags.Small;

        public Type? PacketType = null;
        public int ID;

        protected override void Read(CelesteNetBinaryReader reader) {
            PacketType = Type.GetType(reader.ReadNetString(), false);
            ID = reader.Read7BitEncodedInt();
        }

        protected override void Write(CelesteNetBinaryWriter writer) {
            writer.WriteNetString(PacketType?.FullName);
            writer.Write7BitEncodedInt(ID);
        }

    }
}

<|start_filename|>CelesteNet.Server.SqliteModule/SqliteUserData.cs<|end_filename|>
﻿using MessagePack;
using Microsoft.Data.Sqlite;
using Microsoft.Xna.Framework;
using Monocle;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Transactions;

namespace Celeste.Mod.CelesteNet.Server.Sqlite {
    public sealed class SqliteUserData : UserData {

        public static readonly HashSet<char> Illegal = new("`´'\"^[]\\//");

        public readonly object GlobalLock = new();

        public readonly SqliteModule Module;

        public string UserRoot => Path.Combine(Module.Settings.UserDataRoot, "User");
        public string DBPath => Path.Combine(Module.Settings.UserDataRoot, "main.db");

        private readonly ThreadLocal<BatchContext> _Batch = new();
        public BatchContext Batch => _Batch.Value ??= new(this);

        public SqliteUserData(SqliteModule module)
            : base(module.Server) {
            Module = module;

            if (!File.Exists(DBPath)) {
                using MiniCommand mini = new(this) {
                    SqliteOpenMode.ReadWriteCreate,
                    @"
                        -- sqlite can only into one database.
                        -- CREATE DATABASE main;

                        CREATE TABLE [meta] (
                            iid INTEGER PRIMARY KEY AUTOINCREMENT,
                            uid VARCHAR(255) UNIQUE,
                            key VARCHAR(255),
                            keyfull VARCHAR(255),
                            registered BOOLEAN
                        );

                        CREATE TABLE [data] (
                            iid INTEGER PRIMARY KEY AUTOINCREMENT,
                            name VARCHAR(255) UNIQUE,
                            real VARCHAR(255) UNIQUE,
                            type VARCHAR(255) UNIQUE
                        );

                        CREATE TABLE [file] (
                            iid INTEGER PRIMARY KEY AUTOINCREMENT,
                            name VARCHAR(255) UNIQUE,
                            real VARCHAR(255) UNIQUE
                        );
                    "
                };
                mini.Run();
            }
        }

        public override void Dispose() {
        }

        public override UserDataBatchContext OpenBatch() {
            BatchContext batch = Batch;
            batch.Count++;
            return batch;
        }

        public SqliteConnection Open(SqliteOpenMode mode) {
            SqliteConnection con = new(new SqliteConnectionStringBuilder() {
                DataSource = DBPath,
                Mode = mode,
            }.ToString());
            con.Open();
            return con;
        }

        public List<string> GetAllTables() {
            using MiniCommand mini = new(this) {
                SqliteOpenMode.ReadOnly,
                @"
                    SELECT name
                    FROM sqlite_master
                    WHERE type = 'table';
                ",
            };
            (SqliteConnection con, SqliteCommand cmd, SqliteDataReader reader) = mini.Read();

            List<string> names = new();
            while (reader.Read())
                names.Add(reader.GetString(0));

            return names;
        }

        public string GetDataTable(Type type, bool create) {
            type ??= typeof(object);
            string real = type.FullName ?? throw new Exception($"Type without full name: {type}");

            // FIXME: AAAAAAAAAAAAAAAAAAAAAAAAAA
            string name = $"data.{real}".Sanitize(Illegal);

            if (create) {
                string typename = type.AssemblyQualifiedName ?? throw new Exception($"Type without assembly qualified name: {type}");
                using MiniCommand mini = new(this) {
                    SqliteOpenMode.ReadWrite,
                    @$"
                        CREATE TABLE IF NOT EXISTS [{name}] (
                            iid INTEGER PRIMARY KEY AUTOINCREMENT,
                            uid VARCHAR(255) UNIQUE,
                            format INTEGER,
                            value BLOB
                        );

                        INSERT OR IGNORE INTO data (name, real, type)
                        VALUES ($name, $real, $type);

                        SELECT name
                        FROM sqlite_master
                        WHERE type = 'table'
                        AND name = $name;
                    ",
                    { "$name", name },
                    { "$real", real },
                    { "$type", typename },
                };
                return mini.Run<string>().value;

            } else {
                using MiniCommand mini = new(this) {
                    SqliteOpenMode.ReadOnly,
                    @"
                        SELECT name
                        FROM sqlite_master
                        WHERE type = 'table'
                        AND name = $name;
                    ",
                    { "$name", name }
                };
                return mini.Run<string>().value ?? "";
            }
        }

        public string GetDataTable(string shortname, bool create) {
            // FIXME: AAAAAAAAAAAAAAAAAAAAAAAAAA
            string name = $"data.{shortname}".Sanitize(Illegal);

            if (create) {
                using MiniCommand mini = new(this) {
                    SqliteOpenMode.ReadWrite,
                    @$"
                        CREATE TABLE IF NOT EXISTS [{name}] (
                            iid INTEGER PRIMARY KEY AUTOINCREMENT,
                            uid VARCHAR(255) UNIQUE,
                            format INTEGER,
                            value BLOB
                        );

                        -- The data table name -> type name mapping will be set once GetDataTable(Type) is called.
                        -- INSERT OR IGNORE INTO data (name, real, type)
                        -- VALUES ($name, $real, $type);

                        SELECT name
                        FROM sqlite_master
                        WHERE type = 'table'
                        AND name = $name;
                    ",
                    { "$name", name },
                };
                return mini.Run<string>().value;

            } else {
                using MiniCommand mini = new(this) {
                    SqliteOpenMode.ReadOnly,
                    @"
                        SELECT name
                        FROM sqlite_master
                        WHERE type = 'table'
                        AND name = $name;
                    ",
                    { "$name", name },
                };
                return mini.Run<string>().value ?? "";
            }
        }

        public string GetFileTable(string name, bool create) {
            string real = name;

            // FIXME: AAAAAAAAAAAAAAAAAAAAAAAAAA
            name = $"file.{real}".Sanitize(Illegal);

            if (create) {
                using MiniCommand mini = new(this) {
                    SqliteOpenMode.ReadWrite,
                    @$"
                        CREATE TABLE IF NOT EXISTS [{name}] (
                            iid INTEGER PRIMARY KEY AUTOINCREMENT,
                            uid VARCHAR(255) UNIQUE,
                            value BLOB
                        );

                        INSERT OR IGNORE INTO file (name, real)
                        VALUES ($name, $real);

                        SELECT name
                        FROM sqlite_master
                        WHERE type = 'table'
                        AND name = $name;
                    ",
                    { "$name", name },
                    { "$real", real },
                };
                return mini.Run<string>().value;

            } else {
                using MiniCommand mini = new(this) {
                    SqliteOpenMode.ReadOnly,
                    @"
                        SELECT name
                        FROM sqlite_master
                        WHERE type = 'table'
                        AND name = $name;
                    ",
                    { "$name", name },
                };
                return mini.Run<string>().value ?? "";
            }
        }

        public override string GetUID(string key) {
            if (key.IsNullOrEmpty())
                return "";
            using MiniCommand mini = new(this) {
                SqliteOpenMode.ReadOnly,
                @"
                    SELECT uid
                    FROM meta
                    WHERE key = $key
                    LIMIT 1;
                ",
                { "$key", key },
            };
            (SqliteConnection con, SqliteCommand cmd, SqliteDataReader reader) = mini.Read();
            return reader.Read() ? reader.GetString(0) : "";
        }

        public override string GetKey(string uid) {
            if (uid.IsNullOrEmpty())
                return "";
            using MiniCommand mini = new(this) {
                SqliteOpenMode.ReadOnly,
                @"
                    SELECT key
                    FROM meta
                    WHERE uid = $uid
                    LIMIT 1;
                ",
                { "$uid", uid },
            };
            (SqliteConnection con, SqliteCommand cmd, SqliteDataReader reader) = mini.Read();
            return reader.Read() ? reader.GetString(0) : "";
        }

        public override bool TryLoad<T>(string uid, out T value) {
            using UserDataBatchContext batch = OpenBatch();
            string table = GetDataTable(typeof(T), false);
            if (table.IsNullOrEmpty()) {
                value = new();
                return false;
            }
            using MiniCommand mini = new(this) {
                SqliteOpenMode.ReadOnly,
                @$"
                    SELECT format, value
                    FROM [{table}]
                    WHERE uid = $uid
                    LIMIT 1;
                ",
                { "$uid", uid },
            };
            (SqliteConnection con, SqliteCommand cmd, SqliteDataReader reader) = mini.Read();

            if (!reader.Read()) {
                value = new();
                return false;
            }

            switch ((DataFormat) reader.GetInt32(0)) {
                case DataFormat.MessagePack:
                default: {
                    using Stream stream = reader.GetStream(1);
                    value = MessagePackSerializer.Deserialize<T>(stream, MessagePackHelper.Options) ?? new();
                    return true;
                }

                case DataFormat.Yaml: {
                    using Stream stream = reader.GetStream(1);
                    using StreamReader streamReader = new(stream);
                    value = YamlHelper.Deserializer.Deserialize<T>(streamReader) ?? new();
                    return true;
                }
            }
        }

        public override Stream? ReadFile(string uid, string name) {
            using UserDataBatchContext batch = OpenBatch();
            string table = GetFileTable(name, false);
            if (table.IsNullOrEmpty())
                return null;
            using MiniCommand mini = new(this) {
                SqliteOpenMode.ReadOnly,
                @$"
                    SELECT value
                    FROM [{table}]
                    WHERE uid = $uid
                    LIMIT 1;
                ",
                { "$uid", uid },
            };
            (SqliteConnection con, SqliteCommand cmd, SqliteDataReader reader) = mini.Read();

            if (!reader.Read())
                return null;

            Stream stream = reader.GetStream(0);
            if (stream is MemoryStream ms)
                return ms;

            ms = new();
            using (stream)
                stream.CopyTo(ms);
            ms.Seek(0, SeekOrigin.Begin);
            return ms;
        }

        public override void Save<T>(string uid, T value) {
            using UserDataBatchContext batch = OpenBatch();
            using MemoryStream ms = new();
            MessagePackSerializer.Serialize(typeof(T), ms, value, MessagePackHelper.Options);

            ms.Seek(0, SeekOrigin.Begin);

            string table = GetDataTable(typeof(T), true);
            using MiniCommand mini = new(this) {
                SqliteOpenMode.ReadWrite,
                EnsureExistsQuery + @$"
                    REPLACE INTO [{table}] (uid, format, value)
                    VALUES ($uid, $format, zeroblob($length));

                    SELECT _ROWID_
                    FROM [{table}]
                    WHERE uid = $uid;
                ",
                { "$uid", uid },
                { "$format", (int) DataFormat.MessagePack },
                { "$length", ms.Length },
            };
            (SqliteConnection con, SqliteCommand cmd) = mini.Run(out long rowid);

            using SqliteBlob blob = new(con, table, "value", rowid);
            ms.CopyTo(blob);
            blob.Dispose();
        }

        public override Stream WriteFile(string uid, string name) {
            MemoryStream ms = new();
            return new DisposeActionStream(ms, () => {
                ms.Seek(0, SeekOrigin.Begin);

                using UserDataBatchContext batch = OpenBatch();
                string table = GetFileTable(name, true);
                using MiniCommand mini = new(this) {
                    SqliteOpenMode.ReadWrite,
                    EnsureExistsQuery + @$"
                        REPLACE INTO [{table}] (uid, value)
                        VALUES ($uid, zeroblob($length));

                        SELECT _ROWID_
                        FROM [{table}]
                        WHERE uid = $uid;
                    ",
                    { "$uid", uid },
                    { "$length", ms.Length },
                };
                (SqliteConnection con, SqliteCommand cmd) = mini.Run(out long rowid);

                using SqliteBlob blob = new(con, table, "value", rowid);
                ms.CopyTo(blob);
                blob.Dispose();
            });
        }

        public override void Delete<T>(string uid) {
            using UserDataBatchContext batch = OpenBatch();
            using MiniCommand mini = new(this) {
                SqliteOpenMode.ReadWrite,
                @$"
                    DELETE
                    FROM [{GetDataTable(typeof(T), true)}]
                    WHERE uid = $uid;
                ",
                { "$uid", uid },
            };
            mini.Run();

            CheckCleanup(uid);
        }

        public override void DeleteFile(string uid, string name) {
            using UserDataBatchContext batch = OpenBatch();
            using MiniCommand mini = new(this) {
                SqliteOpenMode.ReadWrite,
                @$"
                    DELETE
                    FROM [{GetFileTable(name, true)}]
                    WHERE uid = $uid;
                ",
                { "$uid", uid },
            };
            mini.Run();

            CheckCleanup(uid);
        }

        private void CheckCleanup(string uid) {
            using UserDataBatchContext batch = OpenBatch();
            using MiniCommand mini = new(this);

            mini.Next(new() {
                SqliteOpenMode.ReadOnly,
                @$"
                    SELECT registered
                    FROM meta
                    WHERE uid = $uid
                    LIMIT 1;
                ",
                { "$uid", uid },
            });
            (SqliteConnection con, SqliteCommand cmd, SqliteDataReader reader) = mini.Read();

            if (!reader.Read() || reader.GetBoolean(0))
                return;

            foreach (string table in GetAllTables()) {
                if (table == "meta")
                    continue;

                mini.Next(new() {
                SqliteOpenMode.ReadOnly,
                    $@"
                        SELECT iid
                        FROM [{table}]
                        WHERE uid = $uid
                        LIMIT 1;
                    ",
                    { "$uid", uid },
                });
                (con, cmd, reader) = mini.Read();

                while (reader.Read())
                    if (!reader.IsDBNull(0))
                        return;
            }

            Wipe(uid);
        }

        public override void Wipe(string uid) {
            using UserDataBatchContext batch = OpenBatch();
            using MiniCommand mini = new(this) {
                SqliteOpenMode.ReadWrite,
                string.Join('\n', GetAllTables().Select(table => $@"
                    DELETE
                    FROM [{table}]
                    WHERE uid = $uid;
                ")),
                { "$uid", uid },
            };
            mini.Run();
        }

        public override T[] LoadRegistered<T>() {
            using UserDataBatchContext batch = OpenBatch();
            string table = GetDataTable(typeof(T), false);
            if (table.IsNullOrEmpty())
                return Dummy<T>.EmptyArray;

            using MiniCommand mini = new(this) {
                SqliteOpenMode.ReadOnly,
                @$"
                    SELECT D.format, D.value
                    FROM [{table}] D
                    INNER JOIN meta M ON D.uid = M.uid
                    WHERE M.registered = 1;
                ",
            };
            (SqliteConnection con, SqliteCommand cmd, SqliteDataReader reader) = mini.Read();

            List<T> values = new();
            while (reader.Read()) {
                switch ((DataFormat) reader.GetInt32(0)) {
                    case DataFormat.MessagePack:
                    default: {
                        using Stream stream = reader.GetStream(1);
                        values.Add(MessagePackSerializer.Deserialize<T>(stream, MessagePackHelper.Options) ?? new());
                        break;
                    }

                    case DataFormat.Yaml: {
                        using Stream stream = reader.GetStream(1);
                        using StreamReader streamReader = new(stream);
                        values.Add(YamlHelper.Deserializer.Deserialize<T>(streamReader) ?? new());
                        break;
                    }
                }
            }

            return values.ToArray();
        }

        public override T[] LoadAll<T>() {
            using UserDataBatchContext batch = OpenBatch();
            string table = GetDataTable(typeof(T), false);
            if (table.IsNullOrEmpty())
                return Dummy<T>.EmptyArray;

            using MiniCommand mini = new(this) {
                SqliteOpenMode.ReadOnly,
                @$"
                    SELECT format, value
                    FROM [{table}];
                ",
            };
            (SqliteConnection con, SqliteCommand cmd, SqliteDataReader reader) = mini.Read();

            List<T> values = new();
            while (reader.Read()) {
                switch ((DataFormat) reader.GetInt32(0)) {
                    case DataFormat.MessagePack:
                    default: {
                        using Stream stream = reader.GetStream(1);
                        values.Add(MessagePackSerializer.Deserialize<T>(stream, MessagePackHelper.Options) ?? new());
                        break;
                    }

                    case DataFormat.Yaml: {
                        using Stream stream = reader.GetStream(1);
                        using StreamReader streamReader = new(stream);
                        values.Add(YamlHelper.Deserializer.Deserialize<T>(streamReader) ?? new());
                        break;
                    }
                }
            }

            return values.ToArray();
        }

        public override string[] GetRegistered() {
            using UserDataBatchContext batch = OpenBatch();
            string[] uids = new string[GetRegisteredCount()];
            using MiniCommand mini = new(this) {
                SqliteOpenMode.ReadOnly,
                @"
                    SELECT uid
                    FROM meta
                    WHERE registered = 1;
                ",
            };
            (SqliteConnection con, SqliteCommand cmd, SqliteDataReader reader) = mini.Read();
            for (int i = 0; i < uids.Length && reader.Read(); i++)
                uids[i] = reader.GetString(0);
            return uids;
        }

        public override string[] GetAll() {
            using UserDataBatchContext batch = OpenBatch();
            string[] uids = new string[GetAllCount()];
            using MiniCommand mini = new(this) {
                SqliteOpenMode.ReadOnly,
                @"
                    SELECT uid
                    FROM meta;
                ",
            };
            (SqliteConnection con, SqliteCommand cmd, SqliteDataReader reader) = mini.Read();
            for (int i = 0; i < uids.Length && reader.Read(); i++)
                uids[i] = reader.GetString(0);
            return uids;
        }

        public override int GetRegisteredCount() {
            using MiniCommand mini = new(this) {
                SqliteOpenMode.ReadOnly,
                @"
                    SELECT COUNT(uid)
                    FROM meta
                    WHERE registered = 1;
                ",
            };
            return (int) mini.Run<long>().value;
        }

        public override int GetAllCount() {
            using MiniCommand mini = new(this) {
                SqliteOpenMode.ReadOnly,
                @"
                    SELECT COUNT(uid)
                    FROM meta;
                ",
            };
            return (int) mini.Run<long>().value;
        }

        public static readonly string EnsureExistsQuery = @"
            INSERT OR IGNORE INTO meta (uid, key, keyfull, registered)
            VALUES ($uid, '', '', 0);
        ";
        public void EnsureExists(string uid) {
            lock (GlobalLock) {
                using MiniCommand mini = new(this) {
                    EnsureExistsQuery,
                    { "$uid", uid },
                };
                mini.Run();
            }
        }

        public override string Create(string uid) {
            lock (GlobalLock) {
                string key = GetKey(uid);
                if (!key.IsNullOrEmpty())
                    return key;

                string keyFull;
                do {
                    keyFull = Guid.NewGuid().ToString().Replace("-", "");
                    key = keyFull.Substring(0, 16);
                } while (!GetUID(key).IsNullOrEmpty());

                using MiniCommand mini = new(this) {
                    SqliteOpenMode.ReadWrite,
                    @"
                        REPLACE INTO meta (uid, key, keyfull, registered)
                        VALUES ($uid, $key, $keyfull, 1);
                    ",
                    { "$uid", uid },
                    { "$key", key },
                    { "$keyfull", keyFull },
                };
                mini.Run();

                return key;
            }
        }

        public override void RevokeKey(string key) {
            using MiniCommand mini = new(this) {
                SqliteOpenMode.ReadWrite,
                @"
                    UPDATE meta
                    SET key = ''
                    WHERE key = $key;
                ",
                { "$key", key },
            };
            mini.Run();
        }

        public override void CopyTo(UserData other) {
            using UserDataBatchContext batchOther = other.OpenBatch();
            using UserDataBatchContext batch = OpenBatch();
            lock (GlobalLock) {
                MiniCommand mini = new(this);

                mini.Next(new() {
                    SqliteOpenMode.ReadOnly,
                    $@"
                        SELECT uid, key, keyfull, registered
                        FROM meta;
                    "
                });
                (SqliteConnection con, SqliteCommand cmd, SqliteDataReader reader) = mini.Read();
                List<string> uids = new();
                while (reader.Read())
                    other.Insert(reader.GetString(0), reader.GetString(1), reader.GetString(2), reader.GetBoolean(3));

                foreach (string table in GetAllTables()) {
                    bool data = table.StartsWith("data.");
                    bool file = table.StartsWith("file.");
                    if (!data && !file)
                        continue;

                    mini.Next(new() {
                        SqliteOpenMode.ReadOnly,
                        $@"
                            SELECT uid, value
                            FROM [{table}];
                        ",
                    });
                    (con, cmd, reader) = mini.Read();

                    if (data) {
                        using MiniCommand miniName = new(this) {
                            mini.Connection,
                            SqliteOpenMode.ReadOnly,
                            @"
                                SELECT real, format, type
                                FROM data
                                WHERE name = $name;
                            ",
                            { "$name", table }
                        };
                        (_, _, SqliteDataReader readerName) = miniName.Read();
                        miniName.Add((SqliteConnection?) null);
                        if (!readerName.Read())
                            continue;
                        string name = readerName.GetString(0);
                        string typeName = readerName.GetString(1);
                        Type? type = Type.GetType(typeName);

                        while (reader.Read()) {
                            string uid = reader.GetString(0);
                            switch ((DataFormat) reader.GetInt32(1)) {
                                case DataFormat.MessagePack:
                                default: {
                                    if (type == null) {
                                        // TODO: Cannot transform data from MessagePack to Yaml!
                                    } else {
                                        using Stream stream = reader.GetStream(2);
                                        object? value = MessagePackSerializer.Deserialize(type, stream, MessagePackHelper.Options);
                                        using MemoryStream ms = new();
                                        using StreamWriter msWriter = new(ms);
                                        YamlHelper.Serializer.Serialize(msWriter, value);
                                        ms.Seek(0, SeekOrigin.Begin);
                                        other.InsertData(uid, name, type, ms);
                                    }
                                    break;
                                }

                                case DataFormat.Yaml: {
                                    using Stream stream = reader.GetStream(2);
                                    other.InsertData(uid, name, type, stream);
                                    break;
                                }
                            }
                        }
                    } else if (file) {
                        using MiniCommand miniName = new(this) {
                            mini.Connection,
                            SqliteOpenMode.ReadOnly,
                            @"
                                SELECT real
                                FROM file
                                WHERE name = $name;
                            ",
                            { "$name", table }
                        };
                        (_, _, SqliteDataReader readerName) = miniName.Read();
                        miniName.Add((SqliteConnection?) null);
                        if (!readerName.Read())
                            continue;
                        string name = readerName.GetString(0);

                        while (reader.Read()) {
                            string uid = reader.GetString(0);
                            using Stream stream = reader.GetStream(1);
                            other.InsertFile(uid, name, stream);
                        }

                    } else {
                        // ??
                    }
                }
            }
        }

        public override void Insert(string uid, string key, string keyFull, bool registered) {
            lock (GlobalLock) {
                using MiniCommand mini = new(this) {
                    SqliteOpenMode.ReadWrite,
                    @"
                        REPLACE INTO meta (uid, key, keyfull, registered)
                        VALUES ($uid, $key, $keyfull, $registered);
                    ",
                    { "$uid", uid },
                    { "$key", key },
                    { "$keyfull", keyFull },
                    { "$registered", registered },
                };
                mini.Run();
            }
        }

        public override void InsertData(string uid, string name, Type? type, Stream stream) {
            using MemoryStream ms = new();
            DataFormat format;

            if (type == null) {
                format = DataFormat.Yaml;
                stream.CopyTo(ms);

            } else {
                format = DataFormat.MessagePack;
                using StreamReader reader = new(stream);
                object? value = YamlHelper.Deserializer.Deserialize(reader, type);
                MessagePackSerializer.Serialize(type, ms, value, MessagePackHelper.Options);
            }

            ms.Seek(0, SeekOrigin.Begin);

            using UserDataBatchContext batch = OpenBatch();
            string table = type != null ? GetDataTable(type, true) : GetDataTable(name, true);
            using MiniCommand mini = new(this) {
                SqliteOpenMode.ReadWrite,
                EnsureExistsQuery + @$"
                    REPLACE INTO [{table}] (uid, format, value)
                    VALUES ($uid, $format, zeroblob($length));

                    SELECT _ROWID_
                    FROM [{table}]
                    WHERE uid = $uid;
                ",
                { "$uid", uid },
                { "$format", (int) format },
                { "$length", ms.Length },
            };
            (SqliteConnection con, SqliteCommand cmd) = mini.Run(out long rowid);

            using SqliteBlob blob = new(con, table, "value", rowid);
            ms.CopyTo(blob);
            blob.Dispose();
        }

        public override void InsertFile(string uid, string name, Stream stream) {
            using MemoryStream ms = new();
            stream.CopyTo(ms);

            ms.Seek(0, SeekOrigin.Begin);

            using UserDataBatchContext batch = OpenBatch();
            string table = GetFileTable(name, true);
            using MiniCommand mini = new(this) {
                SqliteOpenMode.ReadWrite,
                EnsureExistsQuery + @$"
                    REPLACE INTO [{table}] (uid, value)
                    VALUES ($uid, zeroblob($length));

                    SELECT _ROWID_
                    FROM [{table}]
                    WHERE uid = $uid;
                ",
                { "$uid", uid },
                { "$length", ms.Length },
            };
            (SqliteConnection con, SqliteCommand cmd) = mini.Run(out long rowid);

            using SqliteBlob blob = new(con, table, "value", rowid);
            ms.CopyTo(blob);
            blob.Dispose();
        }

        public struct MiniCommand : IDisposable, IEnumerable<(string, object)> {
            public SqliteUserData UserData;
            public string CommandText;
            public List<(string, object)>? Parameters;
            public SqliteOpenMode PreferredMode;

            public BatchContext? Batch;

            public SqliteOpenMode CurrentMode;
            public SqliteConnection? Connection;
            public SqliteCommand? Command;
            public SqliteDataReader? Reader;

            public MiniCommand(SqliteUserData userData) {
                UserData = userData;
                CommandText = "";
                Parameters = default;
                PreferredMode = default;
                Batch = default;
                CurrentMode = default;
                Connection = default;
                Command = default;
                Reader = default;
            }

            public void Next(MiniCommand cmd) {
                CommandText = cmd.CommandText;
                Parameters = cmd.Parameters;
                PreferredMode = cmd.PreferredMode;
            }

            public void Add(SqliteOpenMode mode) => PreferredMode = mode;
            public void Add(SqliteConnection? connection) => Connection = connection;
            public void Add(string text) => CommandText = text;
            public void Add(string key, object value) => (Parameters ??= new()).Add((key, value));

            IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

            public IEnumerator<(string, object)> GetEnumerator() {
                yield return ("", CommandText);
                if (Parameters != null)
                    foreach ((string, object) param in Parameters)
                        yield return param;
            }

            public (SqliteConnection, SqliteCommand) Prepare() {
                if (Batch == null) {
                    BatchContext batch = UserData.Batch;
                    if (batch.Count > 0)
                        Batch = batch;
                }

                Reader?.Close();
                ((IDisposable?) Reader)?.Dispose();
                Reader = null;

                if (CurrentMode > PreferredMode) {
                    Command?.Dispose();
                    Command = null;
                    Connection?.Dispose();
                    Connection = null;
                    if (Batch != null) {
                        Batch.Command = null;
                        Batch.Connection = null;
                    }
                }

                SqliteConnection con = Connection ??= (Batch?.Open(PreferredMode) ?? UserData.Open(PreferredMode));

                SqliteCommand cmd = Command ??= (Batch?.Command ?? con.CreateCommand());
                cmd.CommandText = CommandText;
                if (Parameters != null) {
                    cmd.Parameters.Clear();
                    foreach ((string Key, object Value) param in Parameters)
                        cmd.Parameters.AddWithValue(param.Key, param.Value);
                }

                return (con, cmd);
            }

            public (SqliteConnection, SqliteCommand, SqliteDataReader) Read() {
                (SqliteConnection con, SqliteCommand cmd) = Prepare();
                SqliteDataReader reader = Reader = cmd.ExecuteReader();
                return (con, cmd, reader);
            }

            public (SqliteConnection con, SqliteCommand cmd, int value) Run() {
                (SqliteConnection con, SqliteCommand cmd) = Prepare();
                return (con, cmd, cmd.ExecuteNonQuery());
            }

            public (SqliteConnection con, SqliteCommand cmd, T value) Run<T>() {
                (SqliteConnection con, SqliteCommand cmd) = Prepare();
                return (con, cmd, (T) cmd.ExecuteScalar());
            }

            public (SqliteConnection con, SqliteCommand cmd) Run<T>(out T value) {
                (SqliteConnection con, SqliteCommand cmd) = Prepare();
                value = (T) cmd.ExecuteScalar();
                return (con, cmd);
            }

            public void Dispose() {
                Reader?.Close();
                ((IDisposable?) Reader)?.Dispose();
                Reader = null;
                if (Batch == null) {
                    Command?.Dispose();
                    Command = null;
                    Connection?.Dispose();
                    Connection = null;
                }
            }
        }

        public enum DataFormat {
            MessagePack,
            Yaml
        }

        public sealed class BatchContext : UserDataBatchContext {

            public SqliteUserData UserData;
            public SqliteOpenMode Mode;
            public SqliteConnection? Connection;
            public SqliteCommand? Command;

            public int Count;

            internal BatchContext(SqliteUserData userData) {
                UserData = userData;
            }

            [MemberNotNull(nameof(Connection))]
            [MemberNotNull(nameof(Command))]
            public SqliteConnection Open(SqliteOpenMode mode) {
                if (Connection != null && Command != null && Mode <= mode)
                    return Connection;

                if (Connection != null) {
                    Command?.Dispose();
                    Command = null;
                    Connection?.Dispose();
                    Connection = null;
                }

                Mode = mode;
                Connection = UserData.Open(mode);
                Command = Connection.CreateCommand();
                return Connection;
            }

            public override void Dispose() {
                Count--;
                if (Count <= 0) {
                    Command?.Dispose();
                    Command = null;
                    Connection?.Dispose();
                    Connection = null;
                }
            }

        }

    }
}


<|start_filename|>CelesteNet.Server.FrontendModule/netdata/CelesteNet.node.js<|end_filename|>
// @ts-check
 
/**
@typedef {{
    GlobalRate: number,
    MinConRate: number,
    MaxConRate: number,
    AvgConRate: number
}} BandwithRate

@typedef {{
    Alive: boolean,
    StartupTime: number,
    GCMemory: number,
    Modules: number,
    TickRate: number,
    PlayerCounter: number,
    Registered: number,
    Banned: number,
    // Connections: number?,
    // Sessions: number?,
    // PlayersByCon: number?,
    // PlayersByID: number?,
    PlayerRefs: number,
    
    TCPUplinkBpS: BandwithRate, TCPUplinkPpS: BandwithRate,
    TCPDownlinkBpS: BandwithRate, TCPDownlinkPpS: BandwithRate,
    UDPUplinkBpS: BandwithRate, UDPUplinkPpS: BandwithRate,
    UDPDownlinkBpS: BandwithRate, UDPDownlinkPpS: BandwithRate
}} Status

@typedef {{
    ActivityRate : number,
    Role : string
}} NetPlusThread

@typedef {{
    Role : string,
    ActivityRate : number,
    NumThreads : number
}} NetPlusRole

@typedef {{
    PoolActivityRate : number,
    PoolNumThreads : number,
    PoolThreads : NetPlusThread[]?,
    PoolRoles : NetPlusRole[]?,
    SchedulerExecDuration : number,
    SchedulerNumThreadsReassigned : number,
    SchedulerNumThreadsIdled : number,
}} NetPlusStatus
 */
 
const { services } = require("netdata");
var netdata = require("netdata");
 
if (netdata.options.DEBUG === true)
    netdata.debug(`loaded plugin: ${__filename}`);
 
let CelesteNet = {
    name: __filename,
    enable_autodetect: true,
    update_every: 1,
    base_priority: 10100,
 
    charts: {},
 
    getChart: function(service, key, template) {
        const id = template.id || `${service.name}.${key}`;
        let chart = CelesteNet.charts[id];
        if (chart)
            return chart;
 
        chart = {
            id,
            name: template.name || "",
            title: template.title ? template.title.replace("$", service.name) : service.name,
            units: template.units,
            family: template.family || template.units,
            context: template.context || `${service.type}.${key}`,
            type: template.type || netdata.chartTypes.line,
            priority: service.base_priority + (template.priority || 0),
            update_every: service.update_every,
            dimensions: {},
        };
 
        for (let did in template.dimensions) {
            const dim = template.dimensions[did]
            chart.dimensions[did] = {
                id: did,
                name: dim.name || did,
                algorithm: dim.algorithm || netdata.chartAlgorithms.absolute,
                multiplier: dim.multiplier || 1,
                divisor: dim.divisor || 1,
                hidden: false,
            };
        }
 
        if (netdata.options.DEBUG === true)
            netdata.debug(`new chart: ${JSON.stringify(chart)}`);
        chart = service.chart(id, chart);
        CelesteNet.charts[id] = chart;
        return chart;
    },
 
    updateChart: function(service, key, template) {
        const chart = CelesteNet.getChart(service, key, template);
        if (netdata.options.DEBUG === true)
            netdata.debug(`updating chart ${service.name} ${key}`);
        service.begin(chart);
        for (let did in template.dimensions) {
            const dim = template.dimensions[did]
            if (netdata.options.DEBUG === true)
                netdata.debug(`updating value: ${did} = ${dim.value}`);
            service.set(did, dim.value);
        }
        service.end();
    },
 
    updateCharts: function(service, templates) {
        let i = 0;
        for (let tid in templates) {
            const template = templates[tid];
            if (!template.priority)
                template.priority = i++;
                CelesteNet.updateChart(service, tid, template);
        }
    },
 
    processResponse: function(service, dataRaw) {
        if (!dataRaw)
            return;
 
        if (service.type === "status") {
            if (netdata.options.DEBUG === true)
                netdata.debug(`received data for status: ${service.url} = ${dataRaw}`);
 
            /** @type {Status} */
            const data = JSON.parse(dataRaw);
 
            service.commit();
 
            var sCharts = {
                uptime: {
                    title: "$ uptime",
                    units: "seconds",
                    family: "uptime",
                    dimensions: {
                        time: {
                            value: (new Date().valueOf() - data.StartupTime) / 1000
                        },
                    },
                },
 
                gcmemory: {
                    title: "$ GC memory",
                    units: "bytes",
                    family: "memory",
                    type: netdata.chartTypes.area,
                    dimensions: {
                        memory: {
                            value: data.GCMemory
                        },
                    },
                },

                tickRate: {
                    title: "$ tick rate",
                    units: "TpS",
                    family: "ticker",
                    dimensions: {
                        tickRate: {
                            value: data.TickRate * 1000,
                            divisor: 1000
                        },
                    },
                },
 
                counted: {
                    title: "$ player counter",
                    units: "players",
                    family: "players",
                    dimensions: {
                        players: {
                            value: data.PlayerCounter
                        },
                    },
                },
 
                registered: {
                    title: "$ players registered",
                    units: "players",
                    family: "players",
                    dimensions: {
                        players: {
                            value: data.Registered
                        },
                    },
                },
 
                banned: {
                    title: "$ players banned",
                    units: "players",
                    family: "players",
                    dimensions: {
                        players: {
                            value: data.Banned
                        },
                    },
                },
 
                online: {
                    title: "$ players online",
                    units: "players",
                    family: "players",
                    dimensions: {
                        players: {
                            value: data.PlayerRefs
                        },
                    },
                },

                tcpDownlinkBpS: {
                    title: "$ TCP BpS downlink",
                    units: "bytes per second",
                    family: "bandwith",
                    type: netdata.chartTypes.area,
                    dimensions: {
                        global: { value: data.TCPDownlinkBpS.GlobalRate * 1000, divisor: 1000 },
                        minCon: { value: data.TCPDownlinkBpS.MinConRate * 1000, divisor: 1000 },
                        maxCon: { value: data.TCPDownlinkBpS.MaxConRate * 1000, divisor: 1000 },
                        avgCon: { value: data.TCPDownlinkBpS.AvgConRate * 1000, divisor: 1000 }
                    }
                },

                tcpDownlinkPpS: {
                    title: "$ TCP PpS downlink",
                    units: "packets per second",
                    family: "bandwith",
                    type: netdata.chartTypes.area,
                    dimensions: {
                        global: { value: data.TCPDownlinkPpS.GlobalRate * 1000, divisor: 1000 },
                        minCon: { value: data.TCPDownlinkPpS.MinConRate * 1000, divisor: 1000 },
                        maxCon: { value: data.TCPDownlinkPpS.MaxConRate * 1000, divisor: 1000 },
                        avgCon: { value: data.TCPDownlinkPpS.AvgConRate * 1000, divisor: 1000 }
                    }
                },

                udpDownlinkBpS: {
                    title: "$ UDP BpS downlink",
                    units: "bytes per second",
                    family: "bandwith",
                    type: netdata.chartTypes.area,
                    dimensions: {
                        global: { value: data.UDPDownlinkBpS.GlobalRate * 1000, divisor: 1000 },
                        minCon: { value: data.UDPDownlinkBpS.MinConRate * 1000, divisor: 1000 },
                        maxCon: { value: data.UDPDownlinkBpS.MaxConRate * 1000, divisor: 1000 },
                        avgCon: { value: data.UDPDownlinkBpS.AvgConRate * 1000, divisor: 1000 }
                    }
                },

                udpDownlinkPpS: {
                    title: "$ UDP PpS downlink",
                    units: "packets per second",
                    family: "bandwith",
                    type: netdata.chartTypes.area,
                    dimensions: {
                        global: { value: data.UDPDownlinkPpS.GlobalRate * 1000, divisor: 1000 },
                        minCon: { value: data.UDPDownlinkPpS.MinConRate * 1000, divisor: 1000 },
                        maxCon: { value: data.UDPDownlinkPpS.MaxConRate * 1000, divisor: 1000 },
                        avgCon: { value: data.UDPDownlinkPpS.AvgConRate * 1000, divisor: 1000 }
                    }
                },
                
                tcpUplinkBpS: {
                    title: "$ TCP BpS uplink",
                    units: "bytes per second",
                    family: "bandwith",
                    type: netdata.chartTypes.area,
                    dimensions: {
                        global: { value: data.TCPUplinkBpS.GlobalRate * 1000, divisor: 1000 },
                        minCon: { value: data.TCPUplinkBpS.MinConRate * 1000, divisor: 1000 },
                        maxCon: { value: data.TCPUplinkBpS.MaxConRate * 1000, divisor: 1000 },
                        avgCon: { value: data.TCPUplinkBpS.AvgConRate * 1000, divisor: 1000 }
                    }
                },

                udpUplinkBpS: {
                    title: "$ TCP PpS uplink",
                    units: "bytes per second",
                    family: "bandwith",
                    type: netdata.chartTypes.area,
                    dimensions: {
                        global: { value: data.TCPUplinkPpS.GlobalRate * 1000, divisor: 1000 },
                        minCon: { value: data.TCPUplinkPpS.MinConRate * 1000, divisor: 1000 },
                        maxCon: { value: data.TCPUplinkPpS.MaxConRate * 1000, divisor: 1000 },
                        avgCon: { value: data.TCPUplinkPpS.AvgConRate * 1000, divisor: 1000 }
                    }
                },

                tcpUplinkPpS: {
                    title: "$ UDP TpS uplink",
                    units: "packets per second",
                    family: "bandwith",
                    type: netdata.chartTypes.area,
                    dimensions: {
                        global: { value: data.UDPUplinkBpS.GlobalRate * 1000, divisor: 1000 },
                        minCon: { value: data.UDPUplinkBpS.MinConRate * 1000, divisor: 1000 },
                        maxCon: { value: data.UDPUplinkBpS.MaxConRate * 1000, divisor: 1000 },
                        avgCon: { value: data.UDPUplinkBpS.AvgConRate * 1000, divisor: 1000 }
                    }
                },

                udpUplinkPpS: {
                    title: "$ UDP PpS uplink",
                    units: "packets per second",
                    family: "bandwith",
                    type: netdata.chartTypes.area,
                    dimensions: {
                        global: { value: data.UDPUplinkPpS.GlobalRate * 1000, divisor: 1000 },
                        minCon: { value: data.UDPUplinkPpS.MinConRate * 1000, divisor: 1000 },
                        maxCon: { value: data.UDPUplinkPpS.MaxConRate * 1000, divisor: 1000 },
                        avgCon: { value: data.UDPUplinkPpS.AvgConRate * 1000, divisor: 1000 }
                    }
                },
            };
            
            CelesteNet.updateCharts(service, sCharts);
        } else if (service.type === "netplus") {
            if (netdata.options.DEBUG === true)
                netdata.debug(`received data for netplus: ${service.url} = ${dataRaw}`);
 
            /** @type {NetPlusStatus} */
            const data = JSON.parse(dataRaw);
 
            service.commit();
 
            var nCharts = {
                poolActivity: {
                    title: "$ Total pool activity rate",
                    units: "percent",
                    family: "activity",
                    type: netdata.chartTypes.area,
                    dimensions: {
                        activity: {
                            value: data.PoolActivityRate * 10000,
                            divisor: 100
                        },
                    },
                },
                schedulerExecDuration: {
                    title: "$ Last scheduler execution duration",
                    units: "ms",
                    family: "scheduler",
                    type: netdata.chartTypes.area,
                    dimensions: {
                        time: {
                            value: data.SchedulerExecDuration * 10000,
                            divisor: 100
                        },
                    },
                },
                schedulerNumThreadsReassigned: {
                    title: "$ Number of reassigned threads in last scheduler execution",
                    units: "threads",
                    family: "scheduler",
                    dimensions: {
                        numReassigned: {
                            value: data.SchedulerNumThreadsReassigned
                        },
                    },
                },
                schedulerNumThreadsIdled: {
                    title: "$ Number of threads sent ideling in last scheduler execution",
                    units: "threads",
                    family: "scheduler",
                    dimensions: {
                        numIdled: {
                            value: data.SchedulerNumThreadsIdled
                        },
                    },
                },
            }

            if (data.PoolThreads != null) {
                var threadActvDims = {}
                for (var i = 0; i < data.PoolThreads.length; i++) {
                    threadActvDims["Thread" + i] = { value: data.PoolThreads[i].ActivityRate * 10000, divisor: 100 };
                }
                nCharts["threadActivity"] = {
                    title: "$ Thread activity rates",
                    units: "percent",
                    family: "activity",
                    type: netdata.chartTypes.area,
                    dimensions: threadActvDims
                };
            }

            if (data.PoolRoles != null) {
                var roleActvDims = {}, roleThreadsDims = {}
                for (var i = 0; i < data.PoolRoles.length; i++) {
                    roleActvDims[data.PoolRoles[i].Role] = { value: data.PoolRoles[i].ActivityRate * 10000, divisor: 100 };
                    roleThreadsDims[data.PoolRoles[i].Role] = { value: data.PoolRoles[i].NumThreads };
                }
                nCharts["roleActivity"] = {
                    title: "$ Role activity rates",
                    units: "percent",
                    family: "activity",
                    type: netdata.chartTypes.area,
                    dimensions: roleActvDims
                };
                nCharts["roleThreadCount"] = {
                    title: "$ Role thread counts",
                    units: "threads",
                    family: "activity",
                    type: netdata.chartTypes.stacked,
                    dimensions: roleThreadsDims
                };
            }

            CelesteNet.updateCharts(service, nCharts);
        }
    },
 
    configure: function(config) {
        let added = 0;
 
        if (typeof(config.servers) !== "undefined") {
            for (let server of config.servers) {
                if (netdata.options.DEBUG === true)
                    netdata.debug(`adding server: ${JSON.stringify(server)}`);
                netdata.service({
                    name: server.name,
                    type: "status",
                    url: server.url + "/status",
                    request: netdata.requestFromURL(server.url + "/status"),
                    base_priority: this.base_priority + (server.priority || 0),
                    update_every: server.update_every || this.update_every,
                    module: this,
                }).execute(this.processResponse);
                netdata.service({
                    name: server.name,
                    type: "netplus",
                    url: server.url + "/netplus",
                    request: netdata.requestFromURL(server.url + "/netplus"),
                    base_priority: this.base_priority + (server.priority || 0),
                    update_every: server.update_every || this.update_every,
                    module: this,
                }).execute(this.processResponse);
                added++;
            }
        }
 
        if (netdata.options.DEBUG === true)
            netdata.debug(`added servers: ${added}`);
        return added;
    },
 
    update: function(service, callback) {
        if (netdata.options.DEBUG === true)
            netdata.debug(`update`);
        service.execute(function(serv, data) {
            if (netdata.options.DEBUG === true)
                netdata.debug(`execute`);
            service.module.processResponse(serv, data);
            if (netdata.options.DEBUG === true)
                netdata.debug(`callback`);
            callback();
            if (netdata.options.DEBUG === true)
                netdata.debug(`end?`);
        });
        if (netdata.options.DEBUG === true)
            netdata.debug(`end.`);
    },
};
 
module.exports = CelesteNet;


<|start_filename|>CelesteNet.Client/Handshake.cs<|end_filename|>
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using System.Reflection;
using System.Threading;

namespace Celeste.Mod.CelesteNet.Client {
    public static class Handshake {

        public const int TeapotVersion = 1;

        // TODO MonoKickstart is so stupid, it can't even handle string.Split(char)...
        public static Tuple<uint, IConnectionFeature[], T> DoTeapotHandshake<T>(Socket sock, IConnectionFeature[] features, string nameKey) where T : struct {
            // Find connection features
            // We don't buffer, as we could read actual packet data
            using NetworkStream netStream = new(sock, false);
            using StreamReader reader = new(netStream);
            using StreamWriter writer = new(netStream);
            // Send the "HTTP" request
            writer.Write($@"
CONNECT /teapot HTTP/1.1
CelesteNet-TeapotVersion: {TeapotVersion}
CelesteNet-ConnectionFeatures: {features.Select(f => f.GetType().FullName).Aggregate((string) null, (a, f) => (a == null) ? f : $"{a}, {f}")}
CelesteNet-PlayerNameKey: {nameKey}

Can I have some tea?
".Trim().Replace("\r\n", "\n").Replace("\n", "\r\n") + "\r\n");
            writer.Flush();

            // Read the "HTTP" response
            string statusLine = reader.ReadLine();
            string[] statusSegs = statusLine.Split(new[] { ' ' }, 3);
            if (statusSegs.Length != 3)
                throw new InvalidDataException($"Invalid HTTP response status line: '{statusLine}'");
            int statusCode = int.Parse(statusSegs[1]);

            Dictionary<string, string> headers = new();
            for (string line = reader.ReadLine(); !string.IsNullOrEmpty(line); line = reader.ReadLine()) {
                string[] lineSegs = (line!).Split(new[] { ':' }, 2).Select(s => s.Trim()).ToArray()!;
                if (lineSegs.Length < 2)
                    throw new InvalidDataException($"Invalid HTTP header: '{line}'");
                headers[lineSegs[0]] = lineSegs[1];
            }

            string content = "";
            for (string line = reader.ReadLine(); !string.IsNullOrEmpty(line); line = reader.ReadLine())
                content += line + "\n";

            // Parse the "HTTP response"
            if (statusCode != 418)
                throw new ConnectionErrorException($"Server rejected teapot handshake (status {statusCode})", content.Trim());

            uint conToken = uint.Parse(headers["CelesteNet-ConnectionToken"], NumberStyles.HexNumber);
            IConnectionFeature[] conFeatures = headers["CelesteNet-ConnectionFeatures"].Split(new[] { ',' }).Select(n => features.FirstOrDefault(f => f.GetType().FullName == n)).Where(f => f != null).ToArray();

            object boxedSettings = default(T);
            foreach (FieldInfo field in typeof(T).GetFields(BindingFlags.Public | BindingFlags.Instance)) {
                string headerName = $"CelesteNet-Settings-{field.Name}";
#pragma warning disable IDE0049 // Simplify Names
                switch (Type.GetTypeCode(field.FieldType)) {
                    case TypeCode.Int16:  field.SetValue(boxedSettings,  Int16.Parse(headers[headerName])); break;
                    case TypeCode.Int32:  field.SetValue(boxedSettings,  Int32.Parse(headers[headerName])); break;
                    case TypeCode.Int64:  field.SetValue(boxedSettings,  Int64.Parse(headers[headerName])); break;
                    case TypeCode.UInt16: field.SetValue(boxedSettings, UInt16.Parse(headers[headerName])); break;
                    case TypeCode.UInt32: field.SetValue(boxedSettings, UInt32.Parse(headers[headerName])); break;
                    case TypeCode.UInt64: field.SetValue(boxedSettings, UInt64.Parse(headers[headerName])); break;
                    case TypeCode.Single: field.SetValue(boxedSettings, Single.Parse(headers[headerName])); break;
                    case TypeCode.Double: field.SetValue(boxedSettings, Double.Parse(headers[headerName])); break;
                }
#pragma warning restore IDE0049
            }

            return new(conToken, conFeatures, (T) boxedSettings);
        }

        public static void DoConnectionHandshake(CelesteNetConnection con, IConnectionFeature[] features, CancellationToken token) {
            // Handshake connection features
            foreach (IConnectionFeature feature in features)
                feature.Register(con, true);
            foreach (IConnectionFeature feature in features)
                feature.DoHandshake(con, true).Wait(token);
        }

    }
}

<|start_filename|>CelesteNet.Server/CelesteNetServerSettings.cs<|end_filename|>
﻿using Microsoft.Xna.Framework;
using Mono.Options;
using Monocle;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using YamlDotNet.Serialization;

namespace Celeste.Mod.CelesteNet.Server {
    public class CelesteNetServerSettings : CelesteNetServerModuleSettings {

        [YamlIgnore]
        [JsonIgnore]
        public static string DefaultFilePath = "celestenet-config.yaml";

        public override void Load(string path = "") {
            base.Load(path.Nullify() ?? FilePath.Nullify() ?? DefaultFilePath);
        }

        public override void Save(string path = "") {
            base.Save(path.Nullify() ?? FilePath.Nullify() ?? DefaultFilePath);
        }

        public string ModuleRoot { get; set; } = "Modules";
        public string ModuleConfigRoot { get; set; } = "ModuleConfigs";
        public string UserDataRoot { get; set; } = "UserData";

        public int HeuristicSampleWindow { get; set; } = 800;

        public int NetPlusThreadPoolThreads { get; set; } = -1;
        public int NetPlusMaxThreadRestarts { get; set; } = 5;
        public float NetPlusSchedulerInterval { get; set; } = 10000;
        public float NetPlusSchedulerUnderloadThreshold { get; set; } = 0.1f;
        public float NetPlusSchedulerOverloadThreshold { get; set; } = 0.8f;
        public float NetPlusSchedulerStealThreshold { get; set; } = 0.7f;

        public int MainPort { get; set; } = 3802;
        public int UDPReceivePort { get; set; } = 3803;
        public int UDPSendPort { get; set; } = 3804;

        public int MaxTickRate { get; set; } = 60;
        public float TickRateLowActivityThreshold { get; set; } = 0.3f;
        public float TickRateLowTCPUplinkBpSThreshold { get; set; } = 8388608; // 8 MBpS
        public float TickRateLowUDPUplinkBpSThreshold { get; set; } = 8388608; // 8 MBpS
        public float TickRateHighActivityThreshold { get; set; } = 0.85f;
        public float TickRateHighTCPUplinkBpSThreshold { get; set; } = 33554432; // 32 MBpS
        public float TickRateHighUDPUplinkBpSThreshold { get; set; } = 33554432; // 32 MBpS

        public int MaxPacketSize { get; set; } = 2048;
        public int MaxQueueSize { get; set; } = 256;
        public float MergeWindow { get; set; } = 15;
        public int MaxHeartbeatDelay { get; set; } = 20;
        public float HeartbeatInterval { get; set; } = 250f;

        public bool TCPRecvUseEPoll { get; set; } = true;
        public int TCPPollMaxEvents { get; set; } = 16;
        public int TCPBufferSize { get; set; } = 16384;
        public int TCPSockSendBufferSize { get; set; } = 65536;
        public int UDPMaxDatagramSize { get; set; } = 4096;

        public int UDPAliveScoreMax { get; set; } = 70;
        public int UDPDowngradeScoreMin { get; set; } = -2;
        public int UDPDowngradeScoreMax { get; set; } = 2;
        public int UDPDeathScoreMin { get; set; } = -2;
        public int UDPDeathScoreMax { get; set; } = 2;

        public float PlayerTCPDownlinkBpTCap { get; set; } = 1024;
        public float PlayerTCPDownlinkPpTCap { get; set; } = 64;
        public float PlayerTCPUplinkBpTCap { get; set; } = 4096;
        public float PlayerTCPUplinkPpTCap { get; set; } = 128;
        public float PlayerUDPDownlinkBpTCap { get; set; } = 1024;
        public float PlayerUDPDownlinkPpTCap { get; set; } = 64;
        public float PlayerUDPUplinkBpTCap { get; set; } = 4096;
        public float PlayerUDPUplinkPpTCap { get; set; } = 128;

        public LogLevel LogLevel {
            get => Logger.Level;
            set => Logger.Level = value;
        }

        public int MaxNameLength { get; set; } = 30;
        public int MaxEmoteValueLength { get; set; } = 2048;
        public int MaxChannelNameLength { get; set; } = 16;

        public byte MaxHairLength { get; set; } = 12;
        public byte MaxFollowers { get; set; } = 12;

        public bool AuthOnly { get; set; } = false;

        public string MessageTeapotVersionMismatch { get; set; } = "Teapot version mismatch";
        public string MessageAuthOnly { get; set; } = "Server supports only authenticated clients";
        public string MessageInvalidKey { get; set; } = "Invalid key";
        public string MessageBan { get; set; } = "Banned: {3}";

    }
}


<|start_filename|>CelesteNet.Shared/MetaTypes/MetaRequestResponse.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Celeste.Mod.CelesteNet.DataTypes {
    public class MetaRequestResponse : MetaType<MetaRequestResponse> {

        static MetaRequestResponse() {
            MetaID = "reqRes";
        }

        public uint ID;

        public MetaRequestResponse() {
        }
        public MetaRequestResponse(uint id) {
            ID = id;
        }

        public override void Read(CelesteNetBinaryReader reader) {
            ID = reader.Read7BitEncodedUInt();
        }

        public override void Write(CelesteNetBinaryWriter writer) {
            writer.Write7BitEncodedUInt(ID);
        }

        public static implicit operator uint(MetaRequestResponse meta)
            => meta.ID;

    }
}


<|start_filename|>CelesteNet.Shared/MetaTypes/MetaUnparsed.cs<|end_filename|>
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Celeste.Mod.CelesteNet.DataTypes {
    public class MetaUnparsed : MetaType<MetaUnparsed> {

        public string InnerID = "";
        public byte[] InnerData = Dummy<byte>.EmptyArray;

        public override string GetTypeID(DataContext ctx)
            => InnerID;

        public override void Read(CelesteNetBinaryReader reader) {
            throw new InvalidOperationException("Can't read unparsed MetaTypes");
        }

        public override void Write(CelesteNetBinaryWriter writer) {
            writer.Write(InnerData);
        }

    }
}


<|start_filename|>CelesteNet.Client/Components/CelesteNetChatComponent.cs<|end_filename|>
﻿using Celeste.Mod.CelesteNet.Client.Entities;
using Celeste.Mod.CelesteNet.DataTypes;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Monocle;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MDraw = Monocle.Draw;

namespace Celeste.Mod.CelesteNet.Client.Components {
    public class CelesteNetChatComponent : CelesteNetGameComponent {

        protected float _Time;

        public float Scale => Settings.UIScale;

        protected Overlay _DummyOverlay = new PauseUpdateOverlay();

        public List<DataChat> Log = new();
        public List<DataChat> LogSpecial = new();
        public Dictionary<string, DataChat> Pending = new();
        public string Typing = "";

        public ChatMode Mode => Active ? ChatMode.All : Settings.ShowNewMessages;

        public enum ChatMode {
            All,
            Special,
            Off
        }

        public List<string> Repeat = new() {
            ""
        };

        protected int _RepeatIndex = 0;
        public int RepeatIndex {
            get => _RepeatIndex;
            set {
                if (_RepeatIndex == value)
                    return;

                value = (value + Repeat.Count) % Repeat.Count;

                if (_RepeatIndex == 0 && value != 0)
                    Repeat[0] = Typing;
                Typing = Repeat[value];
                _RepeatIndex = value;
                _CursorIndex = Typing.Length;
            }
        }

        protected int _CursorIndex = 0;
        public int CursorIndex {
            get => _CursorIndex;
            set {
                if (value < 0)
                    value = 0;

                // This deliberately exceeds the Typing string's indices since the cursor
                // 1. ... at index 0 is before the first char,
                // 2. ... at Typing.Length-1 is before the last char,
                // and at Typing.Length is _after_ the last char.
                if (value > Typing.Length)
                    value = Typing.Length;

                _CursorIndex = value;
            }
        }

        protected bool _ControlHeld = false;
        protected bool _CursorMoveFast = false;

        protected float _TimeSinceCursorMove = 0;
        protected float _CursorInitialMoveDelay = 0.4f;
        protected float _CursorMoveDelay = 0.1f;

        protected bool _SceneWasPaused;
        protected int _ConsumeInput;
        protected bool _Active;
        public bool Active {
            get => _Active;
            set {
                if (_Active == value)
                    return;

                if (value) {
                    _SceneWasPaused = Engine.Scene.Paused;
                    Engine.Scene.Paused = true;
                    // If we're in a level, add a dummy overlay to prevent the pause menu from handling input.
                    if (Engine.Scene is Level level)
                        level.Overlay = _DummyOverlay;

                    _RepeatIndex = 0;
                    _Time = 0;
                    TextInput.OnInput += OnTextInput;

                } else {
                    Typing = "";
                    CursorIndex = 0;
                    Engine.Scene.Paused = _SceneWasPaused;
                    _ConsumeInput = 2;
                    if (Engine.Scene is Level level && level.Overlay == _DummyOverlay)
                        level.Overlay = null;
                    TextInput.OnInput -= OnTextInput;
                }

                _Active = value;
            }
        }

        public CelesteNetChatComponent(CelesteNetClientContext context, Game game)
            : base(context, game) {

            UpdateOrder = 10000;
            DrawOrder = 10100;
        }

        public void Send(string text) {
            text = text?.Trim();
            if (string.IsNullOrEmpty(text))
                return;

            lock (Log) {
                if (Pending.ContainsKey(text))
                    return;
                DataChat msg = new() {
                    Player = Client.PlayerInfo,
                    Text = text
                };
                Pending[text] = msg;
                Log.Add(msg);
                LogSpecial.Add(msg);
                Client.Send(msg);
            }
        }

        public void Handle(CelesteNetConnection con, DataChat msg) {
            lock (Log) {
                if (msg.Player?.ID == Client.PlayerInfo?.ID) {
                    foreach (DataChat pending in Pending.Values) {
                        Log.Remove(pending);
                        LogSpecial.Remove(pending);
                    }
                    Pending.Clear();
                }

                int index = Log.FindLastIndex(other => other.ID == msg.ID);
                if (index != -1) {
                    Log[index] = msg;
                } else {
                    Log.Add(msg);
                }

                if (msg.Color != Color.White) {
                    index = LogSpecial.FindLastIndex(other => other.ID == msg.ID);
                    if (index != -1) {
                        LogSpecial[index] = msg;
                    } else {
                        LogSpecial.Add(msg);
                    }
                }
            }
        }

        public override void Update(GameTime gameTime) {
            base.Update(gameTime);

            _Time += Engine.RawDeltaTime;
            _TimeSinceCursorMove += Engine.RawDeltaTime;

            bool isRebinding = Engine.Scene == null ||
                Engine.Scene.Entities.FindFirst<KeyboardConfigUI>() != null ||
                Engine.Scene.Entities.FindFirst<ButtonConfigUI>() != null;

            if (!(Engine.Scene?.Paused ?? true) || isRebinding) {
                string typing = Typing;
                Active = false;
                Typing = typing;
            }

            if (!Active && !isRebinding && Settings.ButtonChat.Button.Pressed) {
                Active = true;

            } else if (Active) {
                Engine.Commands.Open = false;

                _ControlHeld = MInput.Keyboard.Check(Keys.LeftControl) || MInput.Keyboard.Check(Keys.RightControl);

                if (!MInput.Keyboard.Check(Keys.Left) && !MInput.Keyboard.Check(Keys.Right)) {
                    _CursorMoveFast = false;
                    _TimeSinceCursorMove = 0;
                }

                // boolean to determine if Left or Right were already held on previous frame
                bool _directionHeldLast = MInput.Keyboard.PreviousState[Keys.Left] == KeyState.Down
                                       || MInput.Keyboard.PreviousState[Keys.Right] == KeyState.Down;

                bool _cursorCanMove = true;
                // conditions for the cursor to be moving:
                // 1. Don't apply delays on first frame Left/Right is pressed
                if (_directionHeldLast) {
                    // 2. Delay time depends on whether this is the initial delay or subsequent "scrolling" left or right
                    _cursorCanMove = _TimeSinceCursorMove > (_CursorMoveFast ? _CursorMoveDelay : _CursorInitialMoveDelay);
                }

                if (MInput.Keyboard.Pressed(Keys.Enter)) {
                    if (!string.IsNullOrWhiteSpace(Typing))
                        Repeat.Insert(1, Typing);
                    Send(Typing);
                    Active = false;

                } else if (MInput.Keyboard.Pressed(Keys.Down) && RepeatIndex > 0) {
                    RepeatIndex--;

                } else if (MInput.Keyboard.Pressed(Keys.Up) && RepeatIndex < Repeat.Count - 1) {
                    RepeatIndex++;

                } else if (MInput.Keyboard.Check(Keys.Left) && _cursorCanMove && CursorIndex > 0) {
                    if (_ControlHeld) {
                        // skip over space right before the cursor, if there is one
                        if (Typing[_CursorIndex - 1] == ' ')
                            CursorIndex--;
                        if (CursorIndex > 0) {
                            int prevWord = Typing.LastIndexOf(" ", _CursorIndex - 1);
                            CursorIndex = (prevWord < 0) ? 0 : prevWord + 1;
                        }
                    } else {
                        CursorIndex--;
                    }
                    _TimeSinceCursorMove = 0;
                    _CursorMoveFast = _directionHeldLast;
                    _Time = 0;

                } else if (MInput.Keyboard.Check(Keys.Right) && _cursorCanMove && CursorIndex < Typing.Length) {
                    if (_ControlHeld) {
                        int nextWord = Typing.IndexOf(" ", _CursorIndex);
                        CursorIndex = (nextWord < 0) ? Typing.Length : nextWord + 1;
                    } else {
                        CursorIndex++;
                    }
                    _TimeSinceCursorMove = 0;
                    _CursorMoveFast = _directionHeldLast;
                    _Time = 0;

                } else if (MInput.Keyboard.Pressed(Keys.Home)) {
                    CursorIndex = 0;

                } else if (MInput.Keyboard.Pressed(Keys.End)) {
                    CursorIndex = Typing.Length;

                } else if (Input.ESC.Pressed) {
                    Active = false;
                }
            }

            // Prevent menus from reacting to player input after exiting chat.
            if (_ConsumeInput > 0) {
                Input.MenuConfirm.ConsumeBuffer();
                Input.MenuConfirm.ConsumePress();
                Input.ESC.ConsumeBuffer();
                Input.ESC.ConsumePress();
                Input.Pause.ConsumeBuffer();
                Input.Pause.ConsumePress();
                _ConsumeInput--;
            }

        }

        public void OnTextInput(char c) {
            if (!Active)
                return;

            if (c == (char) 13) {
                // Enter - send.
                // Handled in Update.

            } else if (c == (char) 8 && _CursorIndex > 0) {
                // Backspace - trim.
                if (Typing.Length > 0) {
                    int trim = 1;

                    // extra CursorIndex check since at index=1 using trim=1 is fine
                    if (_ControlHeld && _CursorIndex > 1) {
                        // adjust Ctrl+Backspace for having a space right before cursor
                        int _adjustedCursor = CursorIndex;
                        if (Typing[_CursorIndex - 1] == ' ')
                            _adjustedCursor--;
                        int prevWord = Typing.LastIndexOf(" ", _adjustedCursor - 1);
                        // if control is held and a space is found, trim from cursor back to space
                        if (prevWord >= 0)
                            trim = _adjustedCursor - prevWord;
                        // otherwise trim whole input back from cursor as it is one word
                        else
                            trim = _adjustedCursor;
                    }
                    // remove <trim> amount of characters before cursor
                    Typing = Typing.Remove(_CursorIndex - trim, trim);
                    _CursorIndex -= trim;
                }
                _RepeatIndex = 0;
                _Time = 0;

            } else if (c == (char) 127 && CursorIndex < Typing.Length) {
                // Delete - remove character after cursor.
                if (_ControlHeld && Typing[_CursorIndex] != ' ') {
                    int nextWord = Typing.IndexOf(" ", _CursorIndex);
                    // if control is held and a space is found, remove from cursor to space
                    if (nextWord >= 0) {
                        // include the found space in removal
                        nextWord++;
                        Typing = Typing.Remove(_CursorIndex, nextWord - _CursorIndex);
                    } else {
                        // otherwise remove everything after cursor
                        Typing = Typing.Substring(0, _CursorIndex);
                    }
                } else {
                    // just remove single char
                    Typing = Typing.Remove(_CursorIndex, 1);
                }
                _RepeatIndex = 0;
                _Time = 0;

            } else if (!char.IsControl(c)) {
                if (CursorIndex == Typing.Length) {
                    // Any other character - append.
                    Typing += c;
                } else {
                    // insert into string if cursor is not at the end
                    Typing = Typing.Insert(_CursorIndex, c.ToString());
                }
                _CursorIndex++;
                _RepeatIndex = 0;
                _Time = 0;
            }
        }

        protected override void Render(GameTime gameTime, bool toBuffer) {
            float scale = Scale;
            Vector2 fontScale = Vector2.One * scale;

            if (Active) {
                Context.RenderHelper.Rect(25f * scale, UI_HEIGHT - 125f * scale, UI_WIDTH - 50f * scale, 100f * scale, Color.Black * 0.8f);

                CelesteNetClientFont.Draw(
                    ">",
                    new(50f * scale, UI_HEIGHT - 105f * scale),
                    Vector2.Zero,
                    fontScale * new Vector2(0.5f, 1f),
                    Color.White * 0.5f
                );
                float offs = CelesteNetClientFont.Measure(">").X * scale;

                string text = Typing;
                CelesteNetClientFont.Draw(
                    text,
                    new(50f * scale + offs, UI_HEIGHT - 105f * scale),
                    Vector2.Zero,
                    fontScale,
                    Color.White
                );

                if (!Calc.BetweenInterval(_Time, 0.5f)) {

                    if (CursorIndex == Typing.Length) {
                        offs += CelesteNetClientFont.Measure(text).X * scale;
                        CelesteNetClientFont.Draw(
                            "_",
                            new(50f * scale + offs, UI_HEIGHT - 105f * scale),
                            Vector2.Zero,
                            fontScale,
                            Color.White * 0.5f
                        );
                    } else {
                        // draw cursor at correct location, but move back half a "." width to not overlap following char
                        offs += CelesteNetClientFont.Measure(Typing.Substring(0, CursorIndex)).X * scale;
                        offs -= CelesteNetClientFont.Measure(".").X / 2f * scale;

                        CelesteNetClientFont.Draw(
                               "|",
                               new(50f * scale + offs, UI_HEIGHT - 110f * scale),
                               Vector2.Zero,
                               fontScale * new Vector2(.5f, 1.2f),
                               Color.White * 0.6f
                           );
                    }
                }
            }

            lock (Log) {
                List<DataChat> log = Mode switch {
                    ChatMode.Special => LogSpecial,
                    ChatMode.Off => Dummy<DataChat>.EmptyList,
                    _ => Log,
                };

                int count = log.Count;
                if (count > 0) {
                    DateTime now = DateTime.UtcNow;

                    float y = UI_HEIGHT - 50f * scale;
                    if (Active)
                        y -= 105f * scale;

                    float logLength = Settings.ChatLogLength;
                    for (int i = 0; i < count && i < logLength; i++) {
                        DataChat msg = log[count - 1 - i];

                        float alpha = 1f;
                        float delta = (float) (now - msg.ReceivedDate).TotalSeconds;
                        if (!Active && delta > 3f)
                            alpha = 1f - Ease.CubeIn(delta - 3f);
                        if (alpha <= 0f)
                            continue;

                        string time = msg.Date.ToLocalTime().ToLongTimeString();

                        string text = msg.ToString(true, false);
                        logLength -= Math.Max(0, text.Count(c => c == '\n') - 1) * 0.75f;

                        int lineScaleTry = 0;
                        float lineScale = scale;
                        RetryLineScale:
                        Vector2 lineFontScale = Vector2.One * lineScale;

                        Vector2 sizeTime = CelesteNetClientFontMono.Measure(time) * lineFontScale;
                        Vector2 sizeText = CelesteNetClientFont.Measure(text) * lineFontScale;
                        Vector2 size = new(sizeTime.X + 25f * scale + sizeText.X, Math.Max(sizeTime.Y - 5f * scale, sizeText.Y));

                        if ((size.X + 100f * scale) > UI_WIDTH && lineScaleTry < 4) {
                            lineScaleTry++;
                            lineScale -= scale * 0.1f;
                            goto RetryLineScale;
                        }

                        float height = 50f * scale + size.Y;

                        y -= height;

                        Context.RenderHelper.Rect(25f * scale, y, size.X + 50f * scale, height, Color.Black * 0.8f * alpha);
                        CelesteNetClientFontMono.Draw(
                            time,
                            new(50f * scale, y + 20f * scale),
                            Vector2.Zero,
                            lineFontScale,
                            msg.Color * alpha * (msg.ID == uint.MaxValue ? 0.8f : 1f)
                        );
                        CelesteNetClientFont.Draw(
                            text,
                            new(75f * scale + sizeTime.X, y + 25f * scale),
                            Vector2.Zero,
                            lineFontScale,
                            msg.Color * alpha * (msg.ID == uint.MaxValue ? 0.8f : 1f)
                        );
                    }
                }
            }
        }

        protected override void Dispose(bool disposing) {
            if (Active)
                Active = false;

            base.Dispose(disposing);
        }

    }
}


<|start_filename|>CelesteNet.Shared/DataTypes/DataPlayerGrabPlayer.cs<|end_filename|>
﻿using Microsoft.Xna.Framework;
using Monocle;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Celeste.Mod.CelesteNet.DataTypes {
    public class DataPlayerGrabPlayer : DataType<DataPlayerGrabPlayer> {

        static DataPlayerGrabPlayer() {
            DataID = "playerGrabPlayer";
        }

        // Too many too quickly to make tasking worth it.
        public override DataFlags DataFlags => DataFlags.Unreliable | DataFlags.CoreType | DataFlags.Small | DataFlags.NoStandardMeta;

        public DataPlayerInfo? Player, Grabbing;
        public Vector2 Position;
        public Vector2? Force;

        public override bool FilterHandle(DataContext ctx)
            => Player != null && Grabbing != null; // Can be RECEIVED BY CLIENT TOO EARLY because UDP is UDP.

        public override MetaType[] GenerateMeta(DataContext ctx)
            => new MetaType[] {
                new MetaPlayerUpdate(Player),
                new MetaOrderedUpdate(Player?.ID ?? uint.MaxValue)
            };

        public override void FixupMeta(DataContext ctx) {
            MetaPlayerUpdate playerUpd = Get<MetaPlayerUpdate>(ctx);
            MetaOrderedUpdate order = Get<MetaOrderedUpdate>(ctx);

            order.ID = playerUpd;
            Player = playerUpd;
        }

        public override void ReadAll(CelesteNetBinaryReader reader) {
            Player = reader.ReadRef<DataPlayerInfo>();
            Grabbing = reader.ReadOptRef<DataPlayerInfo>();
            Position = reader.ReadVector2();
            Force = reader.ReadBoolean() ? reader.ReadVector2() : null;

            Meta = GenerateMeta(reader.Data);
        }

        public override void WriteAll(CelesteNetBinaryWriter writer) {
            FixupMeta(writer.Data);

            writer.WriteRef(Player);
            writer.WriteOptRef(Grabbing);
            writer.Write(Position);
            writer.Write(Force.HasValue);
            if (Force.HasValue)
                writer.Write(Force.Value);
        }

    }
}


<|start_filename|>CelesteNet.Shared/DataTypes/DataTickRate.cs<|end_filename|>
namespace Celeste.Mod.CelesteNet.DataTypes {
    public class DataTickRate : DataType<DataTickRate> {

        static DataTickRate() {
            DataID = "tickRate";
        }

        public float TickRate;

        public override DataFlags DataFlags => DataFlags.Small;

        protected override void Read(CelesteNetBinaryReader reader) {
            TickRate = reader.ReadSingle();
        }

        protected override void Write(CelesteNetBinaryWriter writer) {
            writer.Write(TickRate);
        }

    }
}

<|start_filename|>CelesteNet.Client/CelesteNetClientTCPUDPConnection.cs<|end_filename|>
using Celeste.Mod.CelesteNet.DataTypes;
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace Celeste.Mod.CelesteNet.Client {
    public class CelesteNetClientTCPUDPConnection : CelesteNetTCPUDPConnection {

        public const int TCPBufferSize = 65536;
        public const int UDPBufferSize = 65536;
        public const int UDPEstablishDelay = 2;
        private static readonly byte[] UDPHolePunchMessage = new byte[] { 42 };

        private readonly BufferedSocketStream TCPStream;
        private readonly Socket UDPRecvSocket, UDPSendSocket;
        private readonly BlockingCollection<DataType> TCPSendQueue, UDPSendQueue;

        private readonly CancellationTokenSource TokenSrc;
        private readonly Thread TCPRecvThread, UDPRecvThread, TCPSendThread, UDPSendThread;

        public CelesteNetClientTCPUDPConnection(DataContext data, uint token, Settings settings, Socket tcpSock) : base(data, token, settings, tcpSock, FlushTCPQueue, FlushUDPQueue) {
            // Initialize networking
            tcpSock.Blocking = false;
            TCPStream = new(TCPBufferSize) { Socket = tcpSock };
            IPAddress serverAddr = ((IPEndPoint) tcpSock.RemoteEndPoint).Address;

            UDPRecvSocket = new(SocketType.Dgram, ProtocolType.Udp);
            UDPRecvSocket.EnableEndpointReuse();
            UDPRecvSocket.Connect(serverAddr, settings.UDPSendPort);

            UDPSendSocket = new(SocketType.Dgram, ProtocolType.Udp);
            UDPSendSocket.EnableEndpointReuse();
            UDPSendSocket.Bind(UDPRecvSocket.LocalEndPoint);
            UDPSendSocket.Connect(serverAddr, settings.UDPReceivePort);

            OnUDPDeath += (_, _) => {
                CelesteNetClientModule.Instance?.Context?.Status?.Set(UseUDP ? "UDP connection died" : "Switching to TCP only", 3);
            };

            TCPSendQueue = new();
            UDPSendQueue = new();

            // Start threads
            TokenSrc = new();
            TCPRecvThread = new(TCPRecvThreadFunc) { Name = "CelesteNet.Client TCP Send Thread" };
            UDPRecvThread = new(UDPRecvThreadFunc) { Name = "CelesteNet.Client UDP Send Thread" };
            TCPSendThread = new(TCPSendThreadFunc) { Name = "CelesteNet.Client TCP Recv Thread" };
            UDPSendThread = new(UDPSendThreadFunc) { Name = "CelesteNet.Client UDP Recv Thread" };

            TCPRecvThread.Start();
            UDPRecvThread.Start();
            TCPSendThread.Start();
            UDPSendThread.Start();
        }

        protected override void Dispose(bool disposing) {
            // Wait for threads
            TokenSrc.Cancel();
            TCPSocket.ShutdownSafe(SocketShutdown.Both);
            UDPRecvSocket.Close();
            UDPSendSocket.Close();

            if (Thread.CurrentThread != TCPRecvThread)
                TCPRecvThread.Join();
            if (Thread.CurrentThread != UDPRecvThread)
                UDPRecvThread.Join();
            if (Thread.CurrentThread != TCPSendThread)
                TCPSendThread.Join();
            if (Thread.CurrentThread != UDPSendThread)
                UDPSendThread.Join();

            base.Dispose(disposing);

            // Dispose stuff
            TokenSrc.Dispose();
            TCPStream.Dispose();
            UDPRecvSocket.Dispose();
            UDPSendSocket.Dispose();
            TCPSendQueue.Dispose();
            UDPSendQueue.Dispose();
        }

        public override string DoHeartbeatTick() {
            string disposeReason = base.DoHeartbeatTick();
            if (disposeReason != null)
                return disposeReason;

            lock (UDPLock) {
                if (UseUDP && UDPEndpoint == null) {
                    // Initialize an unestablished connection and send the token
                    InitUDP(UDPSendSocket.RemoteEndPoint, -1, 0);
                    UDPSendQueue.Add(null);
                }

                if (UDPEndpoint != null) {
                    // Punch a hole so that the server's packets can reach us
                    UDPRecvSocket?.Send(UDPHolePunchMessage);
                }
            }

            return null;
        }

        private void TCPRecvThreadFunc() {
            try {
                byte[] packetBuffer = new byte[ConnectionSettings.MaxPacketSize];
                using BinaryReader tcpReader = new(TCPStream, Encoding.UTF8, true);
                while (!TokenSrc.IsCancellationRequested) {
                    // Read the packet
                    ushort packetSize = tcpReader.ReadUInt16();
                    if (packetSize > ConnectionSettings.MaxPacketSize)
                        throw new InvalidDataException("Peer sent packet over maximum size");
                    if (tcpReader.Read(packetBuffer, 0, packetSize) < packetSize)
                        throw new EndOfStreamException();

                    // Let the connection now we got a TCP heartbeat
                    TCPHeartbeat();

                    // Read the packet
                    DataType packet;
                    using (MemoryStream packetStream = new(packetBuffer, 0, packetSize))
                    using (CelesteNetBinaryReader packetReader = new(Data, Strings, SlimMap, packetStream))
                        packet = Data.Read(packetReader);

                    // Handle the packet
                    switch (packet) {
                        case DataLowLevelUDPInfo udpInfo: {
                            HandleUDPInfo(udpInfo);
                            break;
                        }
                        case DataLowLevelStringMap strMap: {
                            Strings.RegisterWrite(strMap.String, strMap.ID);
                            break;
                        }
                        case DataLowLevelSlimMap slimMap: {
                            if (slimMap.PacketType != null)
                                SlimMap.RegisterWrite(slimMap.PacketType, slimMap.ID);
                            break;
                        }
                        default: {
                            Receive(packet);
                            break;
                        }
                    }

                    // Promote optimizations
                    PromoteOptimizations();
                }

            } catch (EndOfStreamException) {
                if (!TokenSrc.IsCancellationRequested) {
                    Logger.Log(LogLevel.WRN, "tcprecv", "Remote closed the connection");
                    DisposeSafe();
                }
                return;

            } catch (Exception e) {
                if (e is OperationCanceledException oe && oe.CancellationToken == TokenSrc.Token)
                    return;

                if ((e is IOException || e is SocketException) && TokenSrc.IsCancellationRequested)
                    return;

                Logger.Log(LogLevel.WRN, "tcprecv", $"Error in TCP receiving thread: {e}");
                DisposeSafe();
            }
        }

        private void UDPRecvThreadFunc() {
            try {
                byte[] buffer = new byte[UDPBufferSize];
                while (!TokenSrc.IsCancellationRequested) {
                    try {
                        // Receive a datagram
                        int dgSize = UDPRecvSocket.Receive(buffer);
                        if (dgSize <= 0)
                            continue;

                        // Ignore it if we don't actually have an established connection
                        lock (UDPLock)
                        if (UDPConnectionID < 0)
                            continue;

                        // Let the connection know we received a container
                        ReceivedUDPContainer(buffer[0]);

                        using (MemoryStream mStream = new(buffer, 0, dgSize))
                        using (CelesteNetBinaryReader reader = new(Data, Strings, SlimMap, mStream)) {
                            // Get the container ID
                            byte containerID = reader.ReadByte();

                            // Read packets until we run out data
                            while (mStream.Position < dgSize - 1) {
                                DataType packet = Data.Read(reader);
                                if (packet.TryGet<MetaOrderedUpdate>(Data, out MetaOrderedUpdate orderedUpdate))
                                    orderedUpdate.UpdateID = containerID;
                                Receive(packet);
                            }
                        }

                        // Promote optimizations
                        PromoteOptimizations();

                    } catch (Exception e) {
                        if (e is SocketException se && TokenSrc.IsCancellationRequested)
                            return;

                        Logger.Log(LogLevel.WRN, "udprecv", $"Error in UDP receiving thread: {e}");
                        DecreaseUDPScore();
                    }
                }

            } catch (Exception e) {
                if (e is OperationCanceledException oe && oe.CancellationToken == TokenSrc.Token)
                    return;

                if (e is SocketException && TokenSrc.IsCancellationRequested)
                    return;

                Logger.Log(LogLevel.WRN, "udprecv", $"Error in UDP receiving thread: {e}");
                DisposeSafe();
            }
        }

        private void TCPSendThreadFunc() {
            try {
                using BinaryWriter tcpWriter = new(TCPStream, Encoding.UTF8, true);
                using MemoryStream mStream = new(ConnectionSettings.MaxPacketSize);
                using CelesteNetBinaryWriter bufWriter = new(Data, Strings, SlimMap, mStream);
                foreach (DataType p in TCPSendQueue.GetConsumingEnumerable(TokenSrc.Token)) {
                    // Try to send as many packets as possible
                    for (DataType packet = p; packet != null; packet = TCPSendQueue.TryTake(out packet) ? packet : null) {
                        mStream.Position = 0;
                        Data.Write(bufWriter, packet);
                        bufWriter.Flush();
                        int packLen = (int) mStream.Position;

                        tcpWriter.Write((ushort) packLen);
                        tcpWriter.Write(mStream.GetBuffer(), 0, packLen);

                        if (packet is not DataLowLevelKeepAlive)
                            SurpressTCPKeepAlives();
                    }
                    tcpWriter.Flush();
                }

            } catch (Exception e) {
                if (e is OperationCanceledException oe && oe.CancellationToken == TokenSrc.Token)
                    return;

                Logger.Log(LogLevel.WRN, "tcpsend", $"Error in TCP sending thread: {e}");
                DisposeSafe();
            }
        }

        private void UDPSendThreadFunc() {
            try {
                byte[] dgBuffer = new byte[UDPBufferSize];
                using MemoryStream mStream = new(ConnectionSettings.MaxPacketSize);
                using CelesteNetBinaryWriter bufWriter = new(Data, Strings, SlimMap, mStream);
                foreach (DataType p in UDPSendQueue.GetConsumingEnumerable(TokenSrc.Token)) {
                    try {
                        lock (UDPLock) {
                            if (!UseUDP)
                                return;

                            if (UDPEndpoint != null && UDPConnectionID < 0) {
                                // Try to establish the UDP connection
                                // If it succeeeds, we'll receive a UDPInfo packet with our connection parameters
                                if (p == null) {
                                    UDPRecvSocket?.Send(UDPHolePunchMessage);
                                    UDPSendSocket.Send(new byte[] { 0xff }.Concat(BitConverter.GetBytes(ConnectionToken)).ToArray());
                                }
                                continue;
                            } else if (UDPEndpoint == null || p == null)
                                continue;

                            // Try to send as many packets as possible
                            dgBuffer[0] = NextUDPContainerID();
                            int bufOff = 1;
                            for (DataType packet = p; packet != null; packet = UDPSendQueue.TryTake(out packet, 0) ? packet : null) {
                                mStream.Position = 0;
                                Data.Write(bufWriter, packet);
                                bufWriter.Flush();
                                int packLen = (int) mStream.Position;

                                // Copy packet data to the container buffer
                                if (bufOff + packLen > dgBuffer.Length) {
                                    // Send container & start a new one
                                    UDPSendSocket.Send(dgBuffer, bufOff, SocketFlags.None);
                                    dgBuffer[0] = NextUDPContainerID();
                                    bufOff = 1;
                                }

                                Buffer.BlockCopy(mStream.GetBuffer(), 0, dgBuffer, bufOff, packLen);
                                bufOff += packLen;

                                if (packet is not DataLowLevelKeepAlive)
                                    SurpressUDPKeepAlives();
                            }

                            // Send the last container
                            if (bufOff > 1)
                                UDPSendSocket.Send(dgBuffer, bufOff, SocketFlags.None);
                        }

                    } catch (Exception e) {
                        Logger.Log(LogLevel.WRN, "udpsend", $"Error in UDP sending thread: {e}");
                        DecreaseUDPScore();
                    }
                }

            } catch (Exception e) {
                if (e is OperationCanceledException oe && oe.CancellationToken == TokenSrc.Token)
                    return;

                Logger.Log(LogLevel.WRN, "udpsend", $"Error in UDP sending thread: {e}");
                DisposeSafe();
            }
        }

        private static void FlushTCPQueue(CelesteNetSendQueue queue) {
            CelesteNetClientTCPUDPConnection con = (CelesteNetClientTCPUDPConnection) queue.Con;
            foreach (DataType packet in queue.BackQueue)
                con.TCPSendQueue.Add(packet);
            queue.SignalFlushed();
        }

        private static void FlushUDPQueue(CelesteNetSendQueue queue) {
            CelesteNetClientTCPUDPConnection con = (CelesteNetClientTCPUDPConnection) queue.Con;
            foreach (DataType packet in queue.BackQueue)
                con.UDPSendQueue.Add(packet);
            queue.SignalFlushed();
        }

    }
}


<|start_filename|>CelesteNet.Client/Entities/GhostEmote.cs<|end_filename|>
﻿using Microsoft.Xna.Framework;
using Monocle;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Celeste.Mod.CelesteNet.Client.Entities {
    // TODO: This is taken mostly as is from GhostNet and can be improved.
    public class GhostEmote : Entity {

        private static readonly char[] IconPathsSeperator = new[] { ' ' };

        public static float Size = 256f;

        public Entity Tracking;

        public string Value;

        public float Alpha = 1f;

        public bool PopIn = false;
        public bool FadeOut = false;
        public bool PopOut = false;
        public float AnimationTime;

        public bool Float = false;

        public float Time;

        public float PopupAlpha = 1f;
        public float PopupScale = 1f;

        protected GhostEmote(Entity tracking)
            : base(Vector2.Zero) {
            Tracking = tracking;

            Tag = TagsExt.SubHUD | Tags.Persistent | Tags.PauseUpdate | Tags.TransitionUpdate;
        }

        public GhostEmote(Entity tracking, string value)
            : this(tracking) {
            Value = value;
        }

        public override void Update() {
            base.Update();

            if (Scene is not Level level)
                return;

            if (Tracking?.Scene != Scene)
                PopOut = true;

            if (PopIn || FadeOut || PopOut) {
                AnimationTime += Engine.RawDeltaTime;
                if (AnimationTime < 0.1f && PopIn) {
                    float t = AnimationTime / 0.1f;
                    // Pop in.
                    PopupAlpha = Ease.CubeOut(t);
                    PopupScale = Ease.ElasticOut(t);

                } else if (AnimationTime < 1f) {
                    // Stay.
                    PopupAlpha = 1f;
                    PopupScale = 1f;

                } else if (FadeOut) {
                    // Fade out.
                    if (AnimationTime < 2f) {
                        float t = AnimationTime - 1f;
                        PopupAlpha = 1f - Ease.CubeIn(t);
                        PopupScale = 1f - 0.4f * Ease.CubeIn(t);

                    } else {
                        RemoveSelf();
                        return;
                    }

                } else if (PopOut) {
                    // Pop out.
                    if (AnimationTime < 1.1f) {
                        float t = (AnimationTime - 1f) / 0.1f;
                        PopupAlpha = 1f - Ease.CubeIn(t);
                        PopupAlpha *= PopupAlpha;
                        PopupScale = 1f - 0.4f * Ease.BounceIn(t);

                    } else {
                        RemoveSelf();
                        return;
                    }

                } else {
                    AnimationTime = 1f;
                    PopupAlpha = 1f;
                    PopupScale = 1f;
                }
            }

            Time += Engine.RawDeltaTime;

            if (Tracking == null)
                return;

            Position = Tracking.Position;
            // - name offset - popup offset
            Position.Y -= 16f + 6f;
        }

        public override void Render() {
            base.Render();

            if (Scene is not Level level)
                return;

            float popupScale = PopupScale * level.GetScreenScale();

            MTexture icon = null;
            string text = null;

            if (IsIcon(Value))
                icon = GetIcon(Value, Time);
            else
                text = Value;

            float alpha = Alpha * PopupAlpha;

            if (alpha <= 0f || (icon == null && string.IsNullOrWhiteSpace(text)))
                return;

            if (Tracking == null)
                return;

            Vector2 pos = level.WorldToScreen(Position);

            if (Float)
                pos.Y -= (float) Math.Sin(Time * 2f) * 4f;

            if (icon != null) {
                Vector2 size = new(icon.Width, icon.Height);
                float scale = (Size / Math.Max(size.X, size.Y)) * 0.5f * popupScale;
                size *= scale;

                pos = pos.Clamp(
                    0f + size.X * 0.5f + 64f, 0f + size.Y * 1f + 64f,
                    1920f - size.X * 0.5f - 64f, 1080f - 64f
                );

                icon.DrawJustified(
                    pos,
                    new(0.5f, 1f),
                    Color.White * alpha,
                    Vector2.One * scale
                );

            } else {
                Vector2 size = CelesteNetClientFont.Measure(text);
                float scale = (Size / Math.Max(size.X, size.Y)) * 0.5f * popupScale;
                size *= scale;

                pos = pos.Clamp(
                    0f + size.X * 0.5f, 0f + size.Y * 1f,
                    1920f - size.X * 0.5f, 1080f
                );

                CelesteNetClientFont.DrawOutline(
                    text,
                    pos,
                    new(0.5f, 1f),
                    Vector2.One * scale,
                    Color.White * alpha,
                    2f,
                    Color.Black * alpha * alpha * alpha
                );
            }
        }

        public static bool IsText(string emote) {
            return !IsIcon(emote);
        }

        public static bool IsIcon(string emote) {
            return GetIconAtlas(ref emote) != null;
        }

        private static Atlas FallbackIconAtlas = new();
        public static Atlas GetIconAtlas(ref string emote) {
            if (emote.StartsWith("i:")) {
                emote = emote.Substring(2);
                return GFX.Gui ?? FallbackIconAtlas;
            }

            if (emote.StartsWith("g:")) {
                emote = emote.Substring(2);
                return GFX.Game ?? FallbackIconAtlas;
            }

            if (emote.StartsWith("p:")) {
                emote = emote.Substring(2);
                return GFX.Portraits ?? FallbackIconAtlas;
            }

            return null;
        }

        public static MTexture GetIcon(string emote, float time) {
            Atlas atlas;
            if ((atlas = GetIconAtlas(ref emote)) == null)
                return null;

            List<string> iconPaths = new(emote.Split(IconPathsSeperator));
            if (iconPaths.Count > 1 && int.TryParse(iconPaths[0], out int fps)) {
                iconPaths.RemoveAt(0);
            } else {
                fps = 7; // Default FPS.
            }

            List<MTexture> icons = iconPaths.SelectMany(iconPath => {
                iconPath = iconPath.Trim();
                List<MTexture> subs = atlas.orig_GetAtlasSubtextures(iconPath);
                if (subs.Count != 0)
                    return subs;
                if (atlas.Has(iconPath))
                    return new List<MTexture>() { atlas[iconPath] };
                if (iconPath.ToLowerInvariant() == "end")
                    return new List<MTexture>() { null };
                return new List<MTexture>();
            }).ToList();

            if (icons.Count == 0)
                return null;

            if (icons.Count == 1)
                return icons[0];

            int index = (int) Math.Floor(time * fps);

            if (index >= icons.Count - 1 && icons[icons.Count - 1] == null)
                return icons[icons.Count - 2];

            return icons[index % icons.Count];
        }

    }
}


<|start_filename|>CelesteNet.Server/NetPlus/RoleScheduler.cs<|end_filename|>
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Timers;

namespace Celeste.Mod.CelesteNet.Server {
    /*
    The role scheduler is invoked at a regular interval, and everytime the Roles
    change. It's job is it to make sure all Roles are balanced, and to
    distribute threads among Roles, so that individual threads aren't overloaded
    and aren't inactive
    */
    public class NetPlusRoleScheduler : IDisposable {

        private class RoleMetadata {

            public NetPlusThreadRole Role;
            public float ActvRate;
            public int NumThreads;
            public int NumStolenThreads;

            public RoleMetadata(NetPlusThreadRole role) {
                Role = role;
                ActvRate = 0;
                NumThreads = 0;
                NumStolenThreads = 0;
            }

        }

        private readonly RWLock _RoleLock = new();
        private readonly List<NetPlusThreadRole> Roles = new();

        private Timer? SchedulerTimer;
        private readonly object SchedulerLock = new();
        private long _LastSchedulerExecDuration;
        private int _LastSchedulerExecNumThreadsReassigned, _LastSchedulerExecNumThreadsIdeling;

        internal NetPlusRoleScheduler(NetPlusThreadPool pool, float timerInterval, float underloadThreshold, float overloadThreshold, float stealThreshold) {
            Pool = pool;
            UnderloadThreshold = underloadThreshold;
            OverloadThreshold = overloadThreshold;
            StealThreshold = stealThreshold;

            if (timerInterval > 0) {
                // Create scheduler timer
                SchedulerTimer = new(timerInterval);
                SchedulerTimer.Elapsed += (_, _) => InvokeScheduler();
                SchedulerTimer.Enabled = true;
            }
        }

        public void Dispose() {
            // Stop the scheduler timer
            lock (SchedulerLock) {
                if (SchedulerTimer != null) {
                    SchedulerTimer.Dispose();
                    SchedulerTimer = null;
                }
            }

            // Dispose Roles
            foreach (NetPlusThreadRole role in Roles)
                role.Dispose();
            Roles.Clear();
            _RoleLock.Dispose();
        }

        public void InvokeScheduler() {
            OnPreScheduling?.Invoke();
            lock (SchedulerLock) {
                using (Pool.PoolLock.R())
                using (Pool.RoleLock.W())
                using (_RoleLock.R()) {
                    Stopwatch watch = new();
                    Logger.Log(LogLevel.DBG, "netplus", "Invoking thread pool scheduler...");

                    // Collect metadata from Roles and threads
                    List<(NetPlusThreadRole role, RoleMetadata metadata)> roles = new();
                    Dictionary<NetPlusThreadRole, int> roleIdxs = new();
                    foreach (NetPlusThreadRole role in Roles) {
                        roles.Add((role, new(role)));
                        roleIdxs.Add(role, Roles.Count-1);
                    }

                    List<(NetPlusThread thread, float actvRate, RoleMetadata? role)> threads = new();
                    foreach (NetPlusThread thread in Pool.EnumerateThreads()) {
                        float actvRate = thread.ActivityRate;
                        RoleMetadata? md = null;
                        if (roleIdxs.TryGetValue(thread.Role, out int roleIdx)) {
                            md = roles[roleIdx].metadata;
                            md.ActvRate += actvRate;
                            md.NumThreads++;
                        }
                        threads.Add((thread, actvRate, md));
                    }

                    foreach ((_, RoleMetadata md) in roles)
                        md.ActvRate /= md.NumThreads;

                    // Sort Roles and threads by their activity rates
                    roles.Sort((r1, r2) => r1.metadata.ActvRate.CompareTo(r2.metadata.ActvRate));
                    threads.Sort((t1, t2) => t1.actvRate.CompareTo(t2.actvRate));

                    // Iterate over all overloaded Roles or Roles with too little threads, and assign them to underloaded threads
                    _LastSchedulerExecNumThreadsReassigned = 0;
                    for (int ri = roles.Count - 1; ri >= 0; ri--) {
                        bool needsThread = (roles[ri].metadata.ActvRate >= OverloadThreshold || roles[ri].metadata.NumThreads < roles[ri].role.MinThreads);
                        if (!needsThread || roles[ri].role.MaxThreads <= roles[ri].metadata.NumThreads)
                            continue;

                        // Find an underloaded thread
                        int threadIdx = -1;
                        for (int ti = 0; ti < threads.Count; ti++) {
                            if (threads[ti].actvRate > UnderloadThreshold)
                                break;

                            // Check the thread's role
                            RoleMetadata? md = threads[ti].role;
                            if (md != null && (md.NumThreads <= md.Role.MinThreads || md.ActvRate >= StealThreshold))
                                continue;

                            // We found an underloaded thread!
                            threadIdx = ti;
                            break;
                        }

                        if (threadIdx < 0)
                            break;

                        // Assign the thread to this role
                        Logger.Log(LogLevel.DBG, "netplus", $"Assigning thread pool thread {threads[threadIdx].thread.Index} to role {roles[ri].role}...");
                        threads[threadIdx].thread.Role = roles[ri].role;

                        roles[ri].metadata.ActvRate = roles[ri].metadata.ActvRate * roles[ri].metadata.NumThreads / (roles[ri].metadata.NumThreads+1);
                        roles[ri].metadata.NumThreads++;

                        RoleMetadata? metadata = threads[threadIdx].role;
                        if (metadata != null) {
                            metadata.ActvRate = metadata.ActvRate * metadata.NumThreads / (metadata.NumThreads-1);
                            metadata.NumThreads--;
                            metadata.NumStolenThreads++;
                        }

                        threads.RemoveAt(threadIdx);
                        _LastSchedulerExecNumThreadsReassigned++;
                    }

                    // Let the idle role steal all remaining underloaded threads
                    _LastSchedulerExecNumThreadsIdeling = 0;
                    for (int ti = 0; ti < threads.Count; ti++) {
                        if (threads[ti].actvRate > UnderloadThreshold)
                            break;

                        // Check the thread's role
                        RoleMetadata? md = threads[ti].role;
                        if (md == null || (md.NumThreads <= md.Role.MinThreads || md.ActvRate >= StealThreshold))
                            continue;

                        Logger.Log(LogLevel.DBG, "netplus", $"Letting thread pool thread {threads[ti].thread.Index} idle...");
                        threads[ti].thread.Role = Pool.IdleRole;

                        md.ActvRate = md.ActvRate * md.NumThreads / (md.NumThreads-1);
                        md.NumThreads--;
                        md.NumStolenThreads++;
                        _LastSchedulerExecNumThreadsIdeling++;
                    }

                    // Invoke the individual role schedulers
                    foreach (NetPlusThreadRole role in Roles)
                        role.InvokeSchedular();

                    // Indicate that threads have been running stable
                    foreach ((NetPlusThread thread, _, _) in threads)
                        Pool.IndicateThreadStable(thread);

                    Logger.Log(LogLevel.DBG, "netplus", $"Thread pool scheduler done in {watch.ElapsedMilliseconds}ms");
                    _LastSchedulerExecDuration = watch.ElapsedMilliseconds;

                    SchedulerTimer?.Start();
                }
            }
            OnPostScheduling?.Invoke();
        }

        public void AddRole(NetPlusThreadRole role) {
            using (_RoleLock.W()) {
                if (Roles.Aggregate(0, (a, r) => a + r.MinThreads) + role.MinThreads > Pool.NumThreads)
                    throw new InvalidOperationException("Maximum thread Roles reached");
                Roles.Add(role);
            }
            InvokeScheduler();
        }

        public IEnumerable<NetPlusThreadRole> EnumerateRoles(){
            using (_RoleLock.R()) {
                foreach(NetPlusThreadRole role in Roles)
                    yield return role;
            }
        }

        public T? FindRole<T>(Func<T, bool>? filter = null) where T : NetPlusThreadRole {
            foreach (NetPlusThreadRole role in EnumerateRoles()) {
                if (role is T t && (filter == null || filter(t)))
                    return t;
            }
            return null;
        }

        public RWLock RoleLock => _RoleLock;

        public NetPlusThreadPool Pool { get; }
        public float UnderloadThreshold { get; }
        public float OverloadThreshold { get; }
        public float StealThreshold { get; }

        public long LastSchedulerExecDuration { get { lock(SchedulerLock) return _LastSchedulerExecDuration; } }
        public int LastSchedulerExecNumThreadsReassigned { get { lock(SchedulerLock) return _LastSchedulerExecNumThreadsReassigned; } }
        public int LastSchedulerExecNumThreadsIdeling { get { lock(SchedulerLock) return _LastSchedulerExecNumThreadsIdeling; } }

        public event Action? OnPreScheduling, OnPostScheduling;

    }
}

<|start_filename|>CelesteNet.Shared/MetaTypes/MetaRef.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Celeste.Mod.CelesteNet.DataTypes {
    public class MetaRef : MetaType<MetaRef> {

        static MetaRef() {
            MetaID = "ref";
        }

        public uint ID;
        public bool IsAlive;

        public MetaRef() {
        }
        public MetaRef(uint id, bool isAlive) {
            ID = id;
            IsAlive = isAlive;
        }

        public override void Read(CelesteNetBinaryReader reader) {
            ID = reader.Read7BitEncodedUInt();
            IsAlive = reader.ReadBoolean();
        }

        public override void Write(CelesteNetBinaryWriter writer) {
            writer.Write7BitEncodedUInt(ID);
            writer.Write(IsAlive);
        }

        public static implicit operator uint(MetaRef meta)
            => meta.ID;

    }
}


<|start_filename|>CelesteNet.Server.FrontendModule/RCEPs/RCEPControl.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Celeste.Mod.CelesteNet.DataTypes;
using Celeste.Mod.CelesteNet.Server.Chat;
using Newtonsoft.Json;
using WebSocketSharp.Net;
using WebSocketSharp.Server;

#if NETCORE
using System.Runtime.Loader;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.Text;
#endif

namespace Celeste.Mod.CelesteNet.Server.Control {
    public static partial class RCEndpoints {

        [RCEndpoint(false, "/auth", null, null, "Authenticate", "Basic POST authentication endpoint.")]
        public static void Auth(Frontend f, HttpRequestEventArgs c) {
            string? key = c.Request.Cookies[Frontend.COOKIE_SESSION]?.Value;
            string? pass;

            try {
                using StreamReader sr = new(c.Request.InputStream, Encoding.UTF8, false, 1024, true);
                using JsonTextReader jtr = new(sr);
                pass = f.Serializer.Deserialize<string>(jtr);
            } catch (Exception e) {
                Logger.Log(LogLevel.DEV, "frontend-auth", e.ToString());
                pass = null;
            }

            if (pass.IsNullOrEmpty() && !key.IsNullOrEmpty()) {
                if (f.CurrentSessionKeys.Contains(key)) {
                    f.RespondJSON(c, new {
                        Key = key,
                        Info = "Resumed previous session based on cookies."
                    });
                    return;

                } else {
                    c.Response.StatusCode = (int) HttpStatusCode.Unauthorized;
                    f.RespondJSON(c, new {
                        Error = "Previous session expired."
                    });
                    return;
                }
            }

            if (pass == null) {
                c.Response.StatusCode = (int) HttpStatusCode.BadRequest;
                f.RespondJSON(c, new {
                    Error = "Invalid data."
                });
                return;
            }

            if (pass == f.Settings.PasswordExec) {
                do {
                    key = Guid.NewGuid().ToString();
                } while (!f.CurrentSessionKeys.Add(key) || !f.CurrentSessionExecKeys.Add(key));
                c.Response.SetCookie(new(Frontend.COOKIE_SESSION, key));
                f.RespondJSON(c, new {
                    Key = key
                });
                return;
            }

            if (pass == f.Settings.Password) {
                do {
                    key = Guid.NewGuid().ToString();
                } while (!f.CurrentSessionKeys.Add(key));
                c.Response.SetCookie(new(Frontend.COOKIE_SESSION, key));
                f.RespondJSON(c, new {
                    Key = key
                });
                return;
            }

            c.Response.StatusCode = (int) HttpStatusCode.Unauthorized;
            f.RespondJSON(c, new {
                Error = "Incorrect password."
            });
        }

        [RCEndpoint(false, "/ws", null, null, "WebSocket Connection", "Establish a WebSocket control panel connection.")]
        public static void WSPseudo(Frontend f, HttpRequestEventArgs c) {
            c.Response.StatusCode = (int) HttpStatusCode.BadRequest;
            f.RespondJSON(c, new {
                Error = "Connect to this endpoint using WebSockets, not plain HTTP."
            });
        }

        [RCEndpoint(true, "/shutdown", null, null, "Shutdown", "Shut the server down.")]
        public static void Shutdown(Frontend f, HttpRequestEventArgs c) {
            DateTime start = DateTime.UtcNow;

            using (f.Server.ConLock.R())
                foreach (CelesteNetConnection con in f.Server.Connections) {
                    con.Send(new DataDisconnectReason { Text = "Server shutting down" });
                    con.Send(new DataInternalDisconnect());
                }

            // This isn't perf critical and would require a heavily specialized event anyway.
            bool timeout;
            while ((timeout = (DateTime.UtcNow - start).TotalSeconds >= 3) || f.Server.Connections.Count > 0)
                Thread.Sleep(100);

            f.RespondJSON(c, new {
                Info = "OK",
                Timeout = timeout
            });
            f.Server.IsAlive = false;
        }

        [RCEndpoint(false, "/eps", null, null, "Endpoint List", "List of all registered endpoints.")]
        public static void EPs(Frontend f, HttpRequestEventArgs c) {
            f.RespondJSON(c, f.EndPoints);
        }

        [RCEndpoint(true, "/asms", null, null, "Assembly List", "List of all loaded assemblies.")]
        public static void ASMs(Frontend f, HttpRequestEventArgs c) {
            f.RespondJSON(c, AppDomain.CurrentDomain.GetAssemblies().Select(asm => new {
                asm.GetName().Name,
                Version = asm.GetName().Version?.ToString() ?? "",
                Context =
#if NETCORE
                    (AssemblyLoadContext.GetLoadContext(asm) ?? AssemblyLoadContext.Default)?.Name ?? "Unknown",
#else
                    AppDomain.CurrentDomain.FriendlyName
#endif
            }).ToList());
        }

        private static float NetPlusPoolActvRate;
        private static object[]? NetPlusThreadStats;
        private static object[]? NetPlusRoleStats;

        private struct BandwidthRate {

            public float GlobalRate, MinConRate, MaxConRate, AvgConRate;

            public void UpdateRate(CelesteNetServer server, Func<CelesteNetConnection, float?> cb) {
                GlobalRate = 0;
                MinConRate = float.MaxValue;
                MaxConRate = float.MinValue;
                AvgConRate = 0;
                int numCons = 0;
                foreach (CelesteNetConnection con in server.Connections) {
                    float? r = cb(con);
                    if (r == null)
                        continue;
                    float conRate = r.Value;

                    GlobalRate += conRate;
                    if (conRate < MinConRate)
                        MinConRate = conRate;
                    if (conRate > MaxConRate)
                        MaxConRate = conRate;
                    AvgConRate += conRate;
                    numCons++;
                }
                if (numCons > 0)
                    AvgConRate /= numCons;
                else
                    MinConRate = MaxConRate = AvgConRate = 0;
            }

        }

        private static readonly object StatLock = new();
        private static int NumCons, NumTCPCons, NumUDPCons;
        private static BandwidthRate TCPRecvBpSRate, TCPRecvPpSRate, TCPSendBpSRate, TCPSendPpSRate;
        private static BandwidthRate UDPRecvBpSRate, UDPRecvPpSRate, UDPSendBpSRate, UDPSendPpSRate;

        public static void UpdateStats(CelesteNetServer server) {
            lock (StatLock) {
                // Update connection stats
                NumCons = 0;
                NumTCPCons = 0;
                NumUDPCons = 0;
                using (server.ConLock.R())
                    foreach (CelesteNetConnection con in server.Connections) {
                        NumCons++;
                        if (con is CelesteNetTCPUDPConnection tcpUdpCon) {
                            NumTCPCons++;
                            if (tcpUdpCon.UDPConnectionID >= 0)
                                NumUDPCons++;
                        }
                    }

                // Update bandwith stats
                using (server.ConLock.R()) {
                    TCPRecvBpSRate.UpdateRate(server, c => (c is ConPlusTCPUDPConnection con) ? con.TCPRecvRate.ByteRate : null);
                    TCPRecvPpSRate.UpdateRate(server, c => (c is ConPlusTCPUDPConnection con) ? con.TCPRecvRate.PacketRate : null);
                    TCPSendBpSRate.UpdateRate(server, c => (c is ConPlusTCPUDPConnection con) ? con.TCPSendRate.ByteRate : null);
                    TCPSendPpSRate.UpdateRate(server, c => (c is ConPlusTCPUDPConnection con) ? con.TCPSendRate.PacketRate : null);
                    UDPRecvBpSRate.UpdateRate(server, c => (c is ConPlusTCPUDPConnection con) ? con.UDPRecvRate.ByteRate : null);
                    UDPRecvPpSRate.UpdateRate(server, c => (c is ConPlusTCPUDPConnection con) ? con.UDPRecvRate.PacketRate : null);
                    UDPSendBpSRate.UpdateRate(server, c => (c is ConPlusTCPUDPConnection con) ? con.UDPSendRate.ByteRate : null);
                    UDPSendPpSRate.UpdateRate(server, c => (c is ConPlusTCPUDPConnection con) ? con.UDPSendRate.PacketRate : null);
                }

                // Update NetPlus stats
                using (server.ThreadPool.PoolLock.R())
                using (server.ThreadPool.RoleLock.R())
                using (server.ThreadPool.Scheduler.RoleLock.R()) {
                    NetPlusPoolActvRate = server.ThreadPool.ActivityRate;
                    NetPlusThreadStats = server.ThreadPool.EnumerateThreads().Select(t => new {
                        t.ActivityRate,
                        Role = t.Role.ToString()
                    }).ToArray();
                    NetPlusRoleStats = server.ThreadPool.Scheduler.EnumerateRoles().Select(r => new {
                        r.ActivityRate,
                        Role = r.ToString(),
                        NumThreads = server.ThreadPool.EnumerateThreads().Count(t => t.Role == r)
                    }).ToArray();
                }
            }
        }

        [RCEndpoint(false, "/status", null, null, "Server Status", "Basic server status information.")]
        public static void Status(Frontend f, HttpRequestEventArgs c) {
            bool auth = f.IsAuthorized(c);
            lock (StatLock)
                f.RespondJSON(c, new {
                    StartupTime = f.Server.StartupTime.ToUnixTime(),
                    GCMemory = GC.GetTotalMemory(false),
                    Modules = f.Server.Modules.Count,
                    TickRate = f.Server.CurrentTickRate,

                    f.Server.PlayerCounter,
                    Registered = f.Server.UserData.GetRegisteredCount(),
                    Banned = f.Server.UserData.LoadAll<BanInfo>().GroupBy(ban => ban.UID).Select(g => g.First()).Count(ban => !ban.Reason.IsNullOrEmpty()),

                    Connections = auth ? NumCons : (int?) null,
                    TCPConnections = auth ? NumTCPCons : (int?) null,
                    UDPConnections = auth ? NumUDPCons : (int?) null,
                    Sessions = auth ? f.Server.Sessions.Count : (int?) null,
                    PlayersByCon = auth ? f.Server.PlayersByCon.Count : (int?) null,
                    PlayersByID = auth ? f.Server.PlayersByID.Count : (int?) null,
                    PlayerRefs = f.Server.Data.GetRefs<DataPlayerInfo>().Length,

                    TCPDownlinkBpS = TCPRecvBpSRate, TCPDownlinkPpS = TCPRecvPpSRate,
                    UDPDownlinkBpS = UDPRecvBpSRate, UDPDownlinkPpS = UDPRecvPpSRate,
                    TCPUplinkBpS = TCPSendBpSRate, TCPUplinkPpS = TCPSendPpSRate,
                    UDPUplinkBpS = UDPSendBpSRate, UDPUplinkPpS = UDPSendPpSRate,
                });
        }


        [RCEndpoint(false, "/netplus", null, null, "NetPlus Status", "Status information about NetPlus")]
        public static void NetPlus(Frontend f, HttpRequestEventArgs c) {
            lock (StatLock)
                f.RespondJSON(c, new {
                    PoolActivityRate = NetPlusPoolActvRate,
                    PoolNumThreads = f.Server.ThreadPool.NumThreads,
                    PoolThreads = NetPlusThreadStats,
                    PoolRoles = NetPlusRoleStats,

                    SchedulerExecDuration = f.Server.ThreadPool.Scheduler.LastSchedulerExecDuration,
                    SchedulerNumThreadsReassigned = f.Server.ThreadPool.Scheduler.LastSchedulerExecNumThreadsReassigned,
                    SchedulerNumThreadsIdled = f.Server.ThreadPool.Scheduler.LastSchedulerExecNumThreadsIdeling
                });
        }

        [RCEndpoint(true, "/userinfos", "?from={first}&count={count}", "?from=0&count=100", "User Infos", "Get some basic information about ALL users.")]
        public static void UserInfos(Frontend f, HttpRequestEventArgs c) {
            using UserDataBatchContext ctx = f.Server.UserData.OpenBatch();

            string[] uids = f.Server.UserData.GetAll();

            NameValueCollection args = f.ParseQueryString(c.Request.RawUrl);
            if (!int.TryParse(args["from"], out int from) || from <= 0)
                from = 0;
            if (!int.TryParse(args["count"], out int count) || count <= 0)
                count = 100;
            if (from + count > uids.Length)
                count = uids.Length - from;

            f.RespondJSON(c, uids.Skip(from).Take(count).Select(uid => {
                BasicUserInfo info = f.Server.UserData.Load<BasicUserInfo>(uid);
                BanInfo ban = f.Server.UserData.Load<BanInfo>(uid);
                KickHistory kicks = f.Server.UserData.Load<KickHistory>(uid);
                return new {
                    UID = uid,
                    info.Name,
                    info.Discrim,
                    info.Tags,
                    Key = f.Server.UserData.GetKey(uid),
                    Ban = ban.Reason.IsNullOrEmpty() ? null : new {
                        ban.Name,
                        ban.Reason,
                        From = ban.From?.ToUnixTime() ?? 0,
                        To = ban.To?.ToUnixTime() ?? 0
                    },
                    Kicks = kicks.Log.Select(e => new {
                        e.Reason,
                        From = e.From?.ToUnixTime() ?? 0
                    }).ToArray()
                };
            }).ToArray());
        }

        [RCEndpoint(false, "/players", null, null, "Player List", "Basic player list.")]
        public static void Players(Frontend f, HttpRequestEventArgs c) {
            bool auth = f.IsAuthorized(c);
            f.RespondJSON(c, f.Server.PlayersByID.Values.Select(p => new {
                p.SessionID,
                UID = auth ? p.UID : null,
                p.PlayerInfo?.Name,
                p.PlayerInfo?.FullName,
                p.PlayerInfo?.DisplayName,

                Connection = auth ? p.Con.ID : null,
                ConnectionUID = auth ? p.Con.UID : null,

                TCPDownlinkBpS = auth ? (p.Con as ConPlusTCPUDPConnection)?.TCPRecvRate.ByteRate : null,
                TCPDownlinkPpS = auth ? (p.Con as ConPlusTCPUDPConnection)?.TCPRecvRate.PacketRate : null,
                TCPUplinkBpS = auth ? (p.Con as ConPlusTCPUDPConnection)?.TCPSendRate.ByteRate : null,
                TCPUplinkPpS = auth ? (p.Con as ConPlusTCPUDPConnection)?.TCPSendRate.PacketRate : null,
                UDPDownlinkBpS = auth ? (p.Con as ConPlusTCPUDPConnection)?.UDPRecvRate.ByteRate : null,
                UDPDownlinkPpS = auth ? (p.Con as ConPlusTCPUDPConnection)?.UDPRecvRate.PacketRate : null,
                UDPUplinkBpS = auth ? (p.Con as ConPlusTCPUDPConnection)?.UDPSendRate.ByteRate : null,
                UDPUplinkPpS = auth ? (p.Con as ConPlusTCPUDPConnection)?.UDPSendRate.PacketRate : null,
            }).ToArray());
        }

        [RCEndpoint(false, "/channels", null, null, "Channel List", "Basic channel list.")]
        public static void Channels(Frontend f, HttpRequestEventArgs c) {
            IEnumerable<Channel> channels = f.Server.Channels.All;
            if (!f.IsAuthorized(c))
                channels = channels.Where(c => !c.IsPrivate);
            f.RespondJSON(c, channels.Select(c => new {
                c.ID, c.Name, c.IsPrivate,
                Players = c.Players.Select(p => p.SessionID).ToArray()
            }).ToArray());
        }

        [RCEndpoint(false, "/chatlog", "?count={count}&detailed={true|false}", "?count=20&detailed=false", "Chat Log", "Basic chat log.")]
        public static void ChatLog(Frontend f, HttpRequestEventArgs c) {
            bool auth = f.IsAuthorized(c);
            NameValueCollection args = f.ParseQueryString(c.Request.RawUrl);

            if (!int.TryParse(args["count"], out int count) || count <= 0)
                count = 20;
            if (!auth && count > 100)
                count = 100;

            if (!bool.TryParse(args["detailed"], out bool detailed))
                detailed = false;

            ChatModule chat = f.Server.Get<ChatModule>();
            List<object> log = new();
            RingBuffer<DataChat?> buffer = chat.ChatBuffer;
            lock (buffer) {
                for (int i = Math.Max(-buffer.Moved, -count); i < 0; i++) {
                    DataChat? msg = buffer[i];
                    if (msg != null && (msg.Target == null || auth))
                        log.Add(detailed ? msg.ToDetailedFrontendChat() : msg.ToFrontendChat());
                }
            }

            f.RespondJSON(c, log);
        }

        [RCEndpoint(true, "/settings", "?module={id}", "?module=CelesteNet.Server", "Server Settings", "Get the settings of any server module as YAML.")]
        public static void Settings(Frontend f, HttpRequestEventArgs c) {
            NameValueCollection args = f.ParseQueryString(c.Request.RawUrl);
            string? moduleID = args["module"];
            if (moduleID.IsNullOrEmpty()) {
                c.Response.StatusCode = (int) HttpStatusCode.BadRequest;
                f.RespondJSON(c, new {
                    Error = "No ID."
                });
                return;
            }

            CelesteNetServerModuleSettings? settings;
            if (moduleID == "CelesteNet.Server") {
                settings = f.Server.Settings;
            } else {
                lock (f.Server.Modules)
                    settings = f.Server.Modules.FirstOrDefault(m => m.Wrapper.ID == moduleID)?.GetSettings();
            }

            if (settings == null) {
                c.Response.StatusCode = (int) HttpStatusCode.NotFound;
                f.RespondJSON(c, new {
                    Error = $"Module {moduleID} not loaded or doesn't have settings."
                });
                return;
            }

            if (c.Request.HttpMethod == "POST") {
                try {
                    using (StreamReader sr = new(c.Request.InputStream, Encoding.UTF8, false, 1024, true))
                        settings.Load(sr);
                    settings.Save();
                    f.RespondJSON(c, new {
                        Info = "Success."
                    });
                    return;
                } catch (Exception e) {
                    f.RespondJSON(c, new {
                        Error = e.ToString()
                    });
                    return;
                }
            }

            StringBuilder sb = new();
            using (StringWriter sw = new(sb))
                settings.Save(sw);
            f.Respond(c, sb.ToString());
        }

        [RCEndpoint(true, "/notes", "", "", "Admin Notes", "Get or set some administrative notes.")]
        public static void Notes(Frontend f, HttpRequestEventArgs c) {
            string path = Path.ChangeExtension(f.Settings.FilePath, ".notes.txt");
            string text;

            if (c.Request.HttpMethod == "POST") {
                try {
                    using (StreamReader sr = new(c.Request.InputStream, Encoding.UTF8, false, 1024, true))
                        text = sr.ReadToEnd();
                    File.WriteAllText(path, text);
                    f.RespondJSON(c, new {
                        Info = "Success."
                    });
                    return;
                } catch (Exception e) {
                    f.RespondJSON(c, new {
                        Error = e.ToString()
                    });
                    return;
                }
            }

            if (!File.Exists(path)) {
                f.Respond(c, "");
                return;
            }

            try {
                text = File.ReadAllText(path);
                f.Respond(c, text);
            } catch (Exception e) {
                f.RespondJSON(c, new {
                    Error = e.ToString()
                });
                return;
            }
        }

#if NETCORE
        [RCEndpoint(true, "/exec", "", "", "Execute C#", "Run some C# code. Highly dangerous!")]
        public static void Exec(Frontend f, HttpRequestEventArgs c) {
            if (!f.IsAuthorizedExec(c))
                f.Respond(c, "Unauthorized!");

            ExecALC? alc = null;

            try {
                string name = $"CelesteNet.Server.FrontendModule.REPL.{Guid.NewGuid().ToString().Replace("-", "")}";

                string code;
                using (StreamReader sr = new(c.Request.InputStream, Encoding.UTF8, false, 1024, true))
                    code = sr.ReadToEnd();

                List<MetadataReference> refs = new();
                foreach (Assembly asmLoaded in AppDomain.CurrentDomain.GetAssemblies()) {
                    try {
                        string asmPath = asmLoaded.Location;
                        if (asmPath.IsNullOrEmpty() || !File.Exists(asmPath))
                            continue;
                        refs.Add(MetadataReference.CreateFromFile(asmPath));
                    } catch {
                    }
                }

                CSharpCompilation comp = CSharpCompilation.Create(
                    $"{name}.dll",
                    new SyntaxTree[] {
                        SyntaxFactory.ParseSyntaxTree(code, CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Latest))
                    },
                    refs,
                    new(
                        OutputKind.ConsoleApplication,
                        optimizationLevel: OptimizationLevel.Release,
                        assemblyIdentityComparer: DesktopAssemblyIdentityComparer.Default
                    )
                );

                using MemoryStream ms = new();
                EmitResult result = comp.Emit(ms);
                if (!result.Success) {
                    throw new Exception("Failed building:\n" +
                        string.Join('\n', result.Diagnostics
                            .Where(d => d.IsWarningAsError || d.Severity == DiagnosticSeverity.Error)
                            .Select(d => $"{d.Id}: {d.GetMessage()}"))
                    );
                }

                ms.Seek(0, SeekOrigin.Begin);

                alc = new(name);
                alc.Resolving += (ctx, name) => {
                    foreach (CelesteNetServerModuleWrapper wrapper in f.Server.ModuleWrappers)
                        if (wrapper.ID == name.Name)
                            return wrapper.Assembly;
                    return null;
                };

                Assembly asm = alc.LoadFromStream(ms);
                if (asm.EntryPoint == null)
                    throw new Exception("No entry point found");

                try {
                    object? rv = asm.EntryPoint.Invoke(null, new object[asm.EntryPoint.GetParameters().Length]);
                    f.Respond(c, rv?.ToString() ?? "null");
                } catch (TargetInvocationException tie) when (tie.InnerException is Exception e && e.TargetSite == asm.EntryPoint) {
                    f.Respond(c, e.Message);
                }

            } catch (Exception e) {
                Logger.Log(LogLevel.DEV, "frontend-exec", e.ToString());
                c.Response.StatusCode = (int) HttpStatusCode.Unauthorized;
                f.Respond(c, $"Error:\n{e}");

            } finally {
                alc?.Unload();
            }
        }

        private class ExecALC : AssemblyLoadContext {

            public ExecALC(string name)
                : base(name, isCollectible: true) {
            }

            protected override Assembly? Load(AssemblyName name) {
                return null;
            }

        }
#endif

    }
}


<|start_filename|>CelesteNet.Shared/DataTypes/DataLowLevelKeepAlive.cs<|end_filename|>
using System;

namespace Celeste.Mod.CelesteNet.DataTypes {
    public class DataLowLevelKeepAlive : DataType<DataLowLevelKeepAlive> {

        static DataLowLevelKeepAlive() {
            DataID = "keepAlive";
        }

        public override DataFlags DataFlags => DataFlags.CoreType | DataFlags.Small;

        protected override void Read(CelesteNetBinaryReader reader) {}
        protected override void Write(CelesteNetBinaryWriter writer) {}

    }
}

<|start_filename|>CelesteNet.Client/CelesteNetGameComponent.cs<|end_filename|>
﻿using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Monocle;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Remoting.Contexts;
using System.Text;
using System.Threading.Tasks;
using MDraw = Monocle.Draw;

namespace Celeste.Mod.CelesteNet.Client {
    public abstract class CelesteNetGameComponent : DrawableGameComponent, Components.ITickReceiver {

        public const int UI_WIDTH = 1920;
        public const int UI_HEIGHT = 1080;

        public static bool IsDrawingUI;

        public readonly CelesteNetClientContext Context;
        public CelesteNetClient Client => Context?.Client;
        public CelesteNetClientSettings ClientSettings => Context?.Client?.Settings ?? CelesteNetClientModule.Settings;
        public CelesteNetClientSettings Settings => CelesteNetClientModule.Settings;

        public bool AutoRemove = true;
        public bool AutoDispose = true;

        public CelesteNetGameComponent(CelesteNetClientContext context, Game game)
            : base(game) {

            Context = context;

            UpdateOrder = 10000;
            DrawOrder = 10000;

            Enabled = false;
        }

        public virtual void Init() {
            Enabled = true;
            Client.Data.RegisterHandlersIn(this);
        }

        public virtual void Start() {
        }

        public override void Update(GameTime gameTime) {
            base.Update(gameTime);

            if (AutoRemove && Context.Game == null)
                Dispose();
        }

        public virtual void Tick() {
        }

        protected virtual void Render(GameTime gameTime, bool toBuffer) {
        }

        protected virtual void RenderContentWrap(GameTime gameTime, bool toBuffer) {
            MDraw.SpriteBatch.Begin(
                SpriteSortMode.Deferred,
                BlendState.AlphaBlend,
                SamplerState.LinearClamp,
                DepthStencilState.None,
                RasterizerState.CullNone,
                null,
                toBuffer ? Matrix.Identity : Engine.ScreenMatrix
            );

            Render(gameTime, toBuffer);

            MDraw.SpriteBatch.End();
        }

        public override void Draw(GameTime gameTime) {
            if (IsDrawingUI) {
                RenderContentWrap(gameTime, true);
            } else if (!IsDrawingUI && (Context.IsDisposed || CelesteNetClientModule.Instance.UIRenderTarget == null)) {
                RenderContentWrap(gameTime, false);
            }
        }

        protected override void Dispose(bool disposing) {
            if (AutoRemove)
                Game.Components.Remove(this);
            base.Dispose(disposing);
            Client?.Data.UnregisterHandlersIn(this);
        }

        protected void RunOnMainThread(Action action, bool wait = false)
            => Context._RunOnMainThread(action, wait);

    }
}


<|start_filename|>CelesteNet.Shared/DataTypes/DataUnparsed.cs<|end_filename|>
﻿using Microsoft.Xna.Framework;
using Monocle;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Celeste.Mod.CelesteNet.DataTypes {
    public class DataUnparsed : DataType<DataUnparsed> {

        public override DataFlags DataFlags => InnerFlags & ~DataFlags.Small;

        public string InnerID = "";
        public string InnerSource = "";
        public DataFlags InnerFlags;
        public int InnerLength;
        private byte[] InnerData = Dummy<byte>.EmptyArray;

        public override string GetTypeID(DataContext ctx)
            => InnerID;

        public override string GetSource(DataContext ctx)
            => InnerSource;


        public override void ReadAll(CelesteNetBinaryReader reader) {
            long metaStart = reader.BaseStream.Position;
            if ((InnerFlags & DataFlags.NoStandardMeta) == 0)
                Meta = ReadMeta(reader);
            int metaLength = (int) (reader.BaseStream.Position - metaStart);
            InnerData = reader.ReadBytes(InnerLength - metaLength);
        }

        public override void WriteAll(CelesteNetBinaryWriter writer) {
            if ((InnerFlags & DataFlags.NoStandardMeta) == 0)
                WriteMeta(writer, Meta);
            writer.Write(InnerData);
        }

    }
}


<|start_filename|>CelesteNet.Server/ConPlus/TCPAcceptorRole.cs<|end_filename|>
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace Celeste.Mod.CelesteNet.Server {
    public partial class TCPAcceptorRole : MultipleSocketBinderRole {

        public const int MaxWorkerBacklog = 32;

        private class Worker : RoleWorker {

            public new TCPAcceptorRole Role => (TCPAcceptorRole) base.Role;

            public Worker(TCPAcceptorRole role, NetPlusThread thread) : base(role, thread) {}

            protected override void StartWorker(Socket socket, CancellationToken token) {
                EnterActiveZone();

                // Accept new connections as long as the token isn't canceled
                socket.Listen(MaxWorkerBacklog);
                token.Register(() => socket.Close());
                while (!token.IsCancellationRequested) {
                    Socket newConn;

                    ExitActiveZone();
                    try {
                        newConn = socket.Accept();
                    } catch (SocketException) {
                        // There's no better way to do this, as far as I know...
                        if (token.IsCancellationRequested)
                            return;
                        throw;
                    }
                    EnterActiveZone();

                    // Setup the new socket
                    newConn.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendBuffer, Role.Server.Settings.TCPSockSendBufferSize);

                    // Start the connection handshake
                    EndPoint remoteEP = newConn.RemoteEndPoint!;
                    Logger.Log(LogLevel.VVV, "tcpaccept", $"Incoming connection from {remoteEP} <-> {Role.EndPoint}");
                    Role.Handshaker.Factory.StartNew(() => {
                        Role.Handshaker.DoTCPUDPHandshake(newConn, Role.ConnectionSettings, Role.TCPReceiver, Role.UDPReceiver, Role.Sender).ContinueWith(t => {
                            if (t.IsFaulted)
                                Logger.Log(LogLevel.WRN, "tcpaccept", $"Handshake failed for connection {remoteEP}: {t.Exception}");
                        });
                    });
                }
                ExitActiveZone();
            }

        }

        public CelesteNetServer Server { get; }
        public HandshakerRole Handshaker { get; }
        public TCPReceiverRole TCPReceiver { get; }
        public UDPReceiverRole UDPReceiver { get; }
        public TCPUDPSenderRole Sender { get; }

        public CelesteNetTCPUDPConnection.Settings ConnectionSettings { get; }

        public TCPAcceptorRole(
            NetPlusThreadPool pool,
            CelesteNetServer server,
            EndPoint endPoint,
            HandshakerRole handshaker,
            TCPReceiverRole tcpReceiver,
            UDPReceiverRole udpReceiver,
            TCPUDPSenderRole sender,
            CelesteNetTCPUDPConnection.Settings conSettings
        ) : base(pool, ProtocolType.Tcp, endPoint) {
            Server = server;
            Handshaker = handshaker;
            TCPReceiver = tcpReceiver;
            UDPReceiver = udpReceiver;
            Sender = sender;
            ConnectionSettings = conSettings;
        }

        public override NetPlusThreadRole.RoleWorker CreateWorker(NetPlusThread thread) => new Worker(this, thread);

    }
}

<|start_filename|>CelesteNet.Shared/CelesteNetUtils.cs<|end_filename|>
﻿using Celeste.Mod.CelesteNet.DataTypes;
using Celeste.Mod.Helpers;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace Celeste.Mod.CelesteNet {
    public static partial class CelesteNetUtils {

        public const ushort Version = 2;
        public static readonly ushort LoadedVersion = Version;

        // Port MUST be fixed as the website expects it to be the same for everyone.
        public static readonly int ClientRCPort = 38038;

        public static readonly Encoding UTF8NoBOM = new UTF8Encoding(false);

        // See https://github.com/dotnet/runtime/blob/144e5145453ac3885ac20bc1f1f2641523c6fcea/src/libraries/System.Private.CoreLib/src/System/String.cs#L488
        public static bool IsNullOrEmpty([NotNullWhen(false)] this string? value)
            => string.IsNullOrEmpty(value);

        public static string? Nullify(this string? value)
            => string.IsNullOrEmpty(value) ? null : value;

        public static Type[] GetTypes() {
            if (Everest.Modules.Count != 0)
                return _GetTypes();

            Type[] typesPrev = _GetTypes();
            Retry:
            Type[] types = _GetTypes();
            if (typesPrev.Length != types.Length) {
                typesPrev = types;
                goto Retry;
            }
            return types;
        }

        private static IEnumerable<Assembly> _GetAssemblies()
            => (Everest.Modules?.Select(m => m.GetType().Assembly) ?? new Assembly[0])
            .Concat(AppDomain.CurrentDomain.GetAssemblies())
            .Distinct();

        private static Type[] _GetTypes()
            => _GetAssemblies().SelectMany(_GetTypes).ToArray();

        private static IEnumerable<Type> _GetTypes(Assembly asm) {
            try {
                return asm.GetTypes();
            } catch (ReflectionTypeLoadException e) {
#pragma warning disable CS8619 // Compiler thinks this could be <Type?> even though we check for t != null
                return e.Types.Where(t => t != null);
#pragma warning restore CS8619
            }
        }

        public static string ToHex(this Color c)
            => $"#{c.R:X2}{c.G:X2}{c.B:X2}";

        public static Type GetRequestType(this Type t) {
            Type[] interfaces = t.GetInterfaces();
            foreach (Type i in interfaces)
                if (i.IsConstructedGenericType && i.GetGenericTypeDefinition() == typeof(IDataRequestable<>))
                    return i.GetGenericArguments()[0];
            throw new Exception($"Invalid requested type: {t.FullName}");
        }

        [ThreadStatic]
        private static char[]? sanitizedShared;
        public static unsafe string Sanitize(this string? value, HashSet<char>? illegal = null, bool space = false) {
            const int buffer = 64;

            if (value.IsNullOrEmpty())
                return "";

            char[] sanitizedArray = sanitizedShared ?? new char[value.Length + buffer];
            if (sanitizedArray.Length < value.Length)
                sanitizedArray = new char[value.Length + buffer];
            sanitizedShared = sanitizedArray;

            fixed (char* sanitized = sanitizedArray)
            fixed (char* raw = value) {
                char* to = sanitized;
                char* last = (char*) IntPtr.Zero;
                char* from = raw;
                if (illegal != null && illegal.Count != 0) {
                    if (!space) {
                        for (int i = value.Length; i > 0; --i) {
                            char c = *from++;
                            if (illegal.Contains(c))
                                continue;
                            if (!EnglishFontCharsSet.Contains(c))
                                continue;
                            if (char.IsWhiteSpace(c))
                                continue;
                            else
                                last = to;
                            *to++ = c;
                        }
                    } else {
                        bool isStart = true;
                        for (int i = value.Length; i > 0; --i) {
                            char c = *from++;
                            if (illegal.Contains(c))
                                continue;
                            if (!EnglishFontCharsSet.Contains(c))
                                continue;
                            if (isStart && char.IsWhiteSpace(c))
                                continue;
                            else
                                last = to;
                            isStart = false;
                            *to++ = c;
                        }
                    }

                } else {
                     if (!space) {
                        for (int i = value.Length; i > 0; --i) {
                            char c = *from++;
                            if (!EnglishFontCharsSet.Contains(c))
                                continue;
                            if (char.IsWhiteSpace(c))
                                continue;
                            else
                                last = to;
                            *to++ = c;
                        }
                    } else {
                        bool isStart = true;
                        for (int i = value.Length; i > 0; --i) {
                            char c = *from++;
                            if (!EnglishFontCharsSet.Contains(c))
                                continue;
                            if (isStart && char.IsWhiteSpace(c))
                                continue;
                            else
                                last = to;
                            isStart = false;
                            *to++ = c;
                        }
                    }
                }

                if (last == (char*) IntPtr.Zero)
                    return "";

                return StringDedupeStaticContext.ToDedupedString(sanitized, (int) (last - sanitized) + 1);
            }
        }

        public static T Await<T>(this Task<T> task) {
            T? result = default;
            task.ContinueWith(_ => result = task.Result).Wait();
            if (result is null)
                throw new NullReferenceException("Task returned null: " + task);
            return result;
        }

        public static byte[] ToBytes(this Stream stream) {
            if (stream is MemoryStream ms)
                return ms.ToArray();

            long length;
            if (stream.CanSeek) {
                try {
                    length = stream.Length - stream.Position;
                } catch {
                    length = 0;
                }
            } else {
                length = 0;
            }

            if (length != 0) {
                byte[] data = new byte[length];
                using (ms = new(data, 0, (int) length, true, true)) {
                    stream.CopyTo(ms);
                }
                return data;
            }

            using (ms = new()) {
                stream.CopyTo(ms);
                length = ms.Position;
                ms.Seek(0, SeekOrigin.Begin);
                byte[] buffer = ms.GetBuffer();
                if (buffer.Length != length)
                    buffer = ms.ToArray();
                return buffer;
            }
        }

        public static void ShutdownSafe(this Socket sock, SocketShutdown shutdown) {
            if (!sock.Connected)
                return;
            try {
                sock.Shutdown(shutdown);
            } catch (SocketException se) {
                // Sometime the first check isn't enough
                if (se.IsDisconnect())
                    return;
                throw;
            }
        }

        private const int SOL_SOCKET = 1, SO_REUSEPORT = 15;
        [DllImport("libc", SetLastError = true)]
        private static extern int setsockopt(IntPtr socket, int level, int opt, [In, MarshalAs(UnmanagedType.LPArray)] int[] val, int len);

        public static void EnableEndpointReuse(this Socket sock) {
            // Set reuse address and port options (if available)
            // We have to set SO_REUSEPORT directly though
            sock.ExclusiveAddressUse = false;
            switch (sock.AddressFamily) {
                case AddressFamily.InterNetwork:
                    sock.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.ReuseAddress, true);
                    break;
                case AddressFamily.InterNetworkV6:
                    sock.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.ReuseAddress, true);
                    break;
            }

            //All Unix-ish systems should have SO_REUSEPORT
            if (MonoMod.Utils.PlatformHelper.Is(MonoMod.Utils.Platform.Unix)) {
                if (setsockopt(sock.Handle, SOL_SOCKET, SO_REUSEPORT, new[] { 1 }, sizeof(int)) < 0)
                    throw new SystemException($"Could not set SO_REUSEPORT: {Marshal.GetLastWin32Error()}");
            }
        }

        public static bool IsDisconnect(this SocketException se) {
            switch (se.SocketErrorCode) {
                case SocketError.NotConnected:
                case SocketError.ConnectionRefused:
                case SocketError.ConnectionAborted:
                case SocketError.ConnectionReset:
                    return true;
                default:
                    return false;
            }
        }

    }
}


<|start_filename|>CelesteNet.Shared/DataTypes/DataReady.cs<|end_filename|>
namespace Celeste.Mod.CelesteNet.DataTypes {
    public class DataReady : DataType<DataReady> {

        static DataReady() {
            DataID = "ready";
        }

        public override DataFlags DataFlags => DataFlags.Small;

    }
}

<|start_filename|>CelesteNet.Shared/MetaTypes/MetaOrderedUpdate.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Celeste.Mod.CelesteNet.DataTypes {
    public class MetaOrderedUpdate : MetaType<MetaOrderedUpdate> {

        static MetaOrderedUpdate() {
            MetaID = "ordered";
        }

        public uint ID;
        public byte? UpdateID;

        public MetaOrderedUpdate() {
        }
        public MetaOrderedUpdate(uint id) {
            ID = id;
        }

        public override void Read(CelesteNetBinaryReader reader) {
            ID = reader.Read7BitEncodedUInt();
        }

        public override void Write(CelesteNetBinaryWriter writer) {
            writer.Write7BitEncodedUInt(ID);
        }

    }
}


<|start_filename|>CelesteNet.Server/ConPlus/TCPReceiverRole.cs<|end_filename|>
using Celeste.Mod.CelesteNet.Server;
using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Threading;

namespace Celeste.Mod.CelesteNet {
    public class TCPReceiverRole : NetPlusThreadRole {

        public interface IPoller : IDisposable {

            void AddConnection(ConPlusTCPUDPConnection con);
            void RemoveConnection(ConPlusTCPUDPConnection con);
            IEnumerable<ConPlusTCPUDPConnection> StartPolling(TCPReceiverRole role, CancellationToken token);
            void ArmConnectionPoll(ConPlusTCPUDPConnection con);

        }

        private class Worker : RoleWorker {

            public new TCPReceiverRole Role => (TCPReceiverRole) base.Role;

            public Worker(TCPReceiverRole role, NetPlusThread thread) : base(role, thread) {}

            protected internal override void StartWorker(CancellationToken token) {
                // Poll for connections
                foreach (ConPlusTCPUDPConnection con in Role.Poller.StartPolling(Role, token)) {
                    EnterActiveZone();
                    try {
                        // Receive data from the connection
                        con.HandleTCPData();

                        // Arm the connection to be polled again
                        if (con.IsConnected)
                            Role.Poller.ArmConnectionPoll(con);
                    } catch (Exception e) {
                        if (e is SocketException se && se.IsDisconnect()) {
                            Logger.Log(LogLevel.INF, "tcprecv", $"Remote of connection {con} closed the connection");
                            con.DisposeSafe();
                            continue;
                        }

                        Logger.Log(LogLevel.WRN, "tcprecv", $"Error while reading from connection {con}: {e}");
                        con.DisposeSafe();
                    } finally {
                        ExitActiveZone();
                    }
                }
            }

        }

        public override int MinThreads => 1;
        public override int MaxThreads => int.MaxValue;

        public CelesteNetServer Server { get; }
        public IPoller Poller { get; }

        public TCPReceiverRole(NetPlusThreadPool pool, CelesteNetServer server, IPoller poller) : base(pool) {
            Server = server;
            Poller = poller;
        }

        public override void Dispose() {
            Poller.Dispose();
            base.Dispose();
        }

        public override RoleWorker CreateWorker(NetPlusThread thread) => new Worker(this, thread);

    }
}

<|start_filename|>CelesteNet.Server.FrontendModule/Frontend.cs<|end_filename|>
﻿#if NETCORE
using Microsoft.AspNetCore.StaticFiles;
#endif
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using WebSocketSharp.Server;
using Celeste.Mod.Helpers;
using Celeste.Mod.CelesteNet.DataTypes;
using Celeste.Mod.CelesteNet.Server.Chat;
using System.Timers;

namespace Celeste.Mod.CelesteNet.Server.Control {
    public class Frontend : CelesteNetServerModule<FrontendSettings> {

        public static readonly string COOKIE_SESSION = "celestenet-session";

        public readonly List<RCEndpoint> EndPoints = new();
        public readonly HashSet<string> CurrentSessionKeys = new();
        public readonly HashSet<string> CurrentSessionExecKeys = new();

        private HttpServer? HTTPServer;
        private WebSocketServiceHost? WSHost;

        private Timer? StatsTimer;

#if NETCORE
        private readonly FileExtensionContentTypeProvider ContentTypeProvider = new();
#endif

        public JsonSerializer Serializer = new() {
            Formatting = Formatting.Indented
        };

        public override void Init(CelesteNetServerModuleWrapper wrapper) {
            base.Init(wrapper);

            Logger.Log(LogLevel.VVV, "frontend", "Scanning for endpoints");
            foreach (Type t in CelesteNetUtils.GetTypes()) {
                foreach (MethodInfo m in t.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)) {
                    foreach (RCEndpointAttribute epa in m.GetCustomAttributes(typeof(RCEndpointAttribute), false)) {
                        RCEndpoint ep = epa.Data;
                        Logger.Log(LogLevel.VVV, "frontend", $"Found endpoint: {ep.Path} - {ep.Name} ({m.Name}::{t.FullName})");
                        ep.Handle = (f, c) => m.Invoke(null, new object[] { f, c });
                        EndPoints.Add(ep);
                    }
                }
            }

            Server.OnConnect += OnConnect;
            Server.OnSessionStart += OnSessionStart;
            using (Server.ConLock.R())
                foreach (CelesteNetPlayerSession session in Server.Sessions)
                    session.OnEnd += OnSessionEnd;
            Server.OnDisconnect += OnDisconnect;

            Server.Channels.OnBroadcastList += OnBroadcastChannels;

            ChatModule chat = Server.Get<ChatModule>();
            chat.OnReceive += OnChatReceive;
            chat.OnForceSend += OnForceSend;
        }

        public override void Start() {
            base.Start();

            Logger.Log(LogLevel.INF, "frontend", $"Startup on port {Settings.Port}");

            HTTPServer = new(Settings.Port);

            HTTPServer.OnGet += HandleRequestRaw;
            HTTPServer.OnPost += HandleRequestRaw;

            HTTPServer.WebSocketServices.AddService<FrontendWebSocket>("/ws", ws => ws.Frontend = this);

            HTTPServer.Start();

            HTTPServer.WebSocketServices.TryGetServiceHost("/ws", out WSHost);

            StatsTimer = new Timer(Settings.NetPlusStatsUpdateRate);
            StatsTimer.AutoReset = true;
            StatsTimer.Elapsed += (_, _) => RCEndpoints.UpdateStats(Server);
            StatsTimer.Enabled = true;
        }

        public override void Dispose() {
            base.Dispose();

            Logger.Log(LogLevel.INF, "frontend", "Shutdown");

            StatsTimer?.Dispose();

            try {
                HTTPServer?.Stop();
            } catch (Exception) {
            }
            HTTPServer = null;

            Server.OnConnect -= OnConnect;
            Server.OnSessionStart -= OnSessionStart;
            using (Server.ConLock.R())
                foreach (CelesteNetPlayerSession session in Server.Sessions)
                    session.OnEnd -= OnSessionEnd;
            Server.OnDisconnect -= OnDisconnect;

            Server.Channels.OnBroadcastList -= OnBroadcastChannels;

            if (Server.TryGet(out ChatModule? chat)) {
                chat.OnReceive -= OnChatReceive;
                chat.OnForceSend -= OnForceSend;
            }
        }

        private void OnConnect(CelesteNetServer server, CelesteNetConnection con) {
            BroadcastCMD(false, "update", "/status");
        }

        private void OnSessionStart(CelesteNetPlayerSession session) {
            BroadcastCMD(false, "update", "/status");
            BroadcastCMD(false, "update", "/players");
            session.OnEnd += OnSessionEnd;
        }

        private void OnSessionEnd(CelesteNetPlayerSession session, DataPlayerInfo? lastPlayerInfo) {
            BroadcastCMD(false, "update", "/status");
            BroadcastCMD(false, "update", "/players");
        }

        private void OnDisconnect(CelesteNetServer server, CelesteNetConnection con, CelesteNetPlayerSession? session) {
            if (session == null)
                BroadcastCMD(false, "update", "/status");
        }

        private void OnBroadcastChannels(Channels obj) {
            BroadcastCMD(false, "update", "/channels");
        }

        private bool OnChatReceive(ChatModule chat, DataChat msg) {
            BroadcastCMD(msg.Targets != null, "chat", msg.ToFrontendChat());
            return true;
        }

        private void OnForceSend(ChatModule chat, DataChat msg) {
            BroadcastCMD(msg.Targets != null, "chat", msg.ToFrontendChat());
        }

        private string? GetContentType(string path) {
#if NETCORE
            if (ContentTypeProvider.TryGetContentType(path, out string contentType))
                return contentType;
            return null;
#else
            return MimeMapping.GetMimeMapping(path);
#endif
        }

        public Stream? OpenContent(string path, out string pathNew, out DateTime? lastMod, out string? contentType) {
            pathNew = path;

            try {
                string dir = Path.GetFullPath(Settings.ContentRoot);
                string pathFS = Path.GetFullPath(Path.Combine(dir, path));
                if (pathFS.StartsWith(dir) && File.Exists(pathFS)) {
                    lastMod = File.GetLastWriteTimeUtc(pathFS);
                    contentType = GetContentType(pathFS);
                    return File.OpenRead(pathFS);
                }
            } catch {
            }

#if DEBUG
            try {
                string dir = Path.GetFullPath(Path.Combine("..", "..", "..", "Content"));
                string pathFS = Path.GetFullPath(Path.Combine(dir, path));
                if (pathFS.StartsWith(dir) && File.Exists(pathFS)) {
                    lastMod = File.GetLastWriteTimeUtc(pathFS);
                    contentType = GetContentType(pathFS);
                    return File.OpenRead(pathFS);
                }
            } catch {
            }

            try {
                string dir = Path.GetFullPath(Path.Combine("..", "..", "..", "..", "CelesteNet.Server.FrontendModule", "Content"));
                string pathFS = Path.GetFullPath(Path.Combine(dir, path));
                if (pathFS.StartsWith(dir) && File.Exists(pathFS)) {
                    lastMod = File.GetLastWriteTimeUtc(pathFS);
                    contentType = GetContentType(pathFS);
                    return File.OpenRead(pathFS);
                }
            } catch {
            }
#endif

            if (!path.EndsWith("/index.html")) {
                path = path.EndsWith("/") ? path : (path + "/");
                Stream? index = OpenContent(path + "index.html", out _, out lastMod, out contentType);
                if (index != null) {
                    pathNew = path;
                    return index;
                }
            }

            lastMod = null;
            contentType = GetContentType(path);
            return typeof(CelesteNetServer).Assembly.GetManifestResourceStream("Celeste.Mod.CelesteNet.Server.Content." + path.Replace("/", "."));
        }

        private void HandleRequestRaw(object? sender, HttpRequestEventArgs c) {
            try {
                using (c.Request.InputStream)
                using (c.Response) {
                    HandleRequest(c);
                }

            } catch (Exception e) {
                Logger.Log(LogLevel.ERR, "frontend", $"Frontend failed responding: {e}");
            }
        }

        private void HandleRequest(HttpRequestEventArgs c) {
            Logger.Log(LogLevel.VVV, "frontend", $"{c.Request.RemoteEndPoint} requested: {c.Request.RawUrl}");

            string url = c.Request.RawUrl;
            int indexOfSplit = url.IndexOf('?');
            if (indexOfSplit != -1)
                url = url.Substring(0, indexOfSplit);

            RCEndpoint? endpoint =
                EndPoints.FirstOrDefault(ep => ep.Path == c.Request.RawUrl) ??
                EndPoints.FirstOrDefault(ep => ep.Path == url) ??
                EndPoints.FirstOrDefault(ep => ep.Path.ToLowerInvariant() == c.Request.RawUrl.ToLowerInvariant()) ??
                EndPoints.FirstOrDefault(ep => ep.Path.ToLowerInvariant() == url.ToLowerInvariant());

            if (endpoint == null) {
                RespondContent(c, "frontend/" + url.Substring(1));
                return;
            }

            c.Response.Headers.Set("Cache-Control", "no-store, max-age=0, s-maxage=0, no-cache, no-transform");

            if (endpoint.Auth && !IsAuthorized(c)) {
                c.Response.StatusCode = (int) HttpStatusCode.Unauthorized;
                RespondJSON(c, new {
                    Error = "Unauthorized."
                });
                return;
            }

            endpoint.Handle(this, c);
        }

        public bool IsAuthorized(HttpRequestEventArgs c)
            => c.Request.Cookies[COOKIE_SESSION]?.Value is string session && CurrentSessionKeys.Contains(session);

#if NETCORE
        public bool IsAuthorizedExec(HttpRequestEventArgs c)
            => c.Request.Cookies[COOKIE_SESSION]?.Value is string session && CurrentSessionExecKeys.Contains(session);
#endif

        public void BroadcastRawString(bool authOnly, string data) {
            if (WSHost == null)
                return;

            foreach (FrontendWebSocket session in WSHost.Sessions.Sessions.ToArray())
                if (!authOnly || session.IsAuthorized)
                    try {
                        session.SendRawString(data);
                    } catch (Exception e) {
                        Logger.Log(LogLevel.VVV, "frontend", $"Failed broadcast:\n{session.CurrentEndPoint}\n{e}");
                    }
        }

        public void BroadcastRawObject(bool authOnly, object obj) {
            if (WSHost == null)
                return;

            using MemoryStream ms = new();
            using (StreamWriter sw = new(ms, CelesteNetUtils.UTF8NoBOM, 1024, true))
            using (JsonTextWriter jtw = new(sw))
                Serializer.Serialize(jtw, obj);

            ms.Seek(0, SeekOrigin.Begin);

            using StreamReader sr = new(ms, Encoding.UTF8, false, 1024, true);
            BroadcastRawString(authOnly, sr.ReadToEnd());
        }

        public void BroadcastCMD(bool authOnly, string id, object obj) {
            BroadcastRawString(authOnly, "cmd");
            BroadcastRawString(authOnly, id);
            BroadcastRawObject(authOnly, obj);
        }

        #region Read / Parse Helpers

        public NameValueCollection ParseQueryString(string url) {
            NameValueCollection nvc = new();

            int indexOfSplit = url.IndexOf('?');
            if (indexOfSplit == -1)
                return nvc;
            url = url.Substring(indexOfSplit + 1);

            string[] args = url.Split('&');
            foreach (string arg in args) {
                indexOfSplit = arg.IndexOf('=');
                if (indexOfSplit == -1)
                    continue;
                nvc[arg.Substring(0, indexOfSplit)] = arg.Substring(indexOfSplit + 1);
            }

            return nvc;
        }

        #endregion

        #region Write Helpers

        public void RespondContent(HttpRequestEventArgs c, string id) {
            using Stream? s = OpenContent(id, out string pathNew, out DateTime? lastMod, out string? contentType);
            if (s == null) {
                c.Response.StatusCode = (int) HttpStatusCode.NotFound;
                RespondJSON(c, new {
                    Error = "Resource not found."
                });
                return;
            }

            if (id != pathNew && pathNew.StartsWith("frontend/")) {
                // c.Response.Redirect($"http://{c.Request.UserHostName}/{pathNew.Substring(9)}");
                c.Response.StatusCode = (int) HttpStatusCode.Moved;
                c.Response.Headers.Set("Location", $"http://{c.Request.UserHostName}/{pathNew.Substring(9)}");
                Respond(c, $"Redirecting to /{pathNew.Substring(9)}");
                return;
            }

            if (lastMod != null)
                c.Response.Headers.Set("Last-Modified", lastMod.Value.ToString("r"));

            if (contentType != null)
                c.Response.ContentType = contentType;

            Respond(c, s.ToBytes());
        }

        public void RespondContent(HttpRequestEventArgs c, Stream s) {
            Respond(c, s.ToBytes());
        }

        public void RespondJSON(HttpRequestEventArgs c, object obj) {
            using MemoryStream ms = new();
            using (StreamWriter sw = new(ms, CelesteNetUtils.UTF8NoBOM, 1024, true))
            using (JsonTextWriter jtw = new(sw))
                Serializer.Serialize(jtw, obj);

            ms.Seek(0, SeekOrigin.Begin);

            c.Response.ContentType = "application/json";
            Respond(c, ms.ToArray());
        }

        public void Respond(HttpRequestEventArgs c, string str) {
            Respond(c, CelesteNetUtils.UTF8NoBOM.GetBytes(str));
        }

        public void Respond(HttpRequestEventArgs c, byte[] buf) {
            c.Response.ContentLength64 = buf.Length;
            c.Response.Close(buf, true);
        }

        #endregion

    }
}


<|start_filename|>CelesteNet.Server/ConPlus/TCPFallbackPoller.cs<|end_filename|>
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Threading;

namespace Celeste.Mod.CelesteNet.Server {
    /*
    This poller is slower, but it works on all platforms. Using the EPoll poller
    on Linux is recommended for optimal performance.
    -Popax21
    */
    public class TCPFallbackPoller : TCPReceiverRole.IPoller {

        private readonly byte[] PollBuffer = new byte[0];

        private readonly RWLock PollerLock = new();
        private readonly HashSet<ConPlusTCPUDPConnection> Cons = new();
        private readonly BlockingCollection<ConPlusTCPUDPConnection> ConQueue = new();

        public void Dispose() {
            using (PollerLock.W()) {
                Cons.Clear();
                ConQueue.Dispose();
                PollerLock.Dispose();
            }
        }

        public void AddConnection(ConPlusTCPUDPConnection con) {
            using (PollerLock.W())
                Cons.Add(con);
            ArmConnectionPoll(con);
        }

        public void RemoveConnection(ConPlusTCPUDPConnection con) {
            using (PollerLock.W())
                Cons.Remove(con);
        }

        public IEnumerable<ConPlusTCPUDPConnection> StartPolling(TCPReceiverRole role, CancellationToken token) {
            return ConQueue.GetConsumingEnumerable(token);
        }

        public void ArmConnectionPoll(ConPlusTCPUDPConnection con) {
            using (PollerLock.R()) {
                if (!Cons.Contains(con))
                    return;

                con.TCPSocket.BeginReceive(PollBuffer, 0, 0, SocketFlags.None, _ => {
                    using (PollerLock.R())
                        if (Cons.Contains(con))
                            ConQueue.Add(con);
                }, null);
            }
        }

    }
}

<|start_filename|>CelesteNet.Shared/CelesteNetBinaryBlobPartWriter.cs<|end_filename|>
﻿using Celeste.Mod.CelesteNet.DataTypes;
using Microsoft.Xna.Framework;
using Monocle;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.ObjectFactories;

namespace Celeste.Mod.CelesteNet {
    public class CelesteNetBinaryBlobPartWriter : CelesteNetBinaryWriter {

        public readonly DataInternalBlob Blob;
        public readonly MemoryStream Stream;

        public int LastSplitPosition;

        public CelesteNetBinaryBlobPartWriter(DataContext ctx, DataInternalBlob blob, MemoryStream output)
            : base(ctx, null, null, output) {
            Blob = blob;
            Stream = output;
        }

        public CelesteNetBinaryBlobPartWriter(DataContext ctx, DataInternalBlob blob, MemoryStream output, bool leaveOpen)
            : base(ctx, null, null, output, leaveOpen) {
            Blob = blob;
            Stream = output;
        }

        public void SplitBytes() {
            Flush();

            int pos = (int) Stream.Position;
            if (LastSplitPosition == pos)
                return;

            DataInternalBlob.Part part = Blob.PartNext();
            part.Bytes = Stream.GetBuffer();
            part.BytesIndex = LastSplitPosition;
            part.BytesCount = pos - LastSplitPosition;
            LastSplitPosition = pos;
        }

        public override void WriteSizeDummy(int size) {
            SplitBytes();
            Blob.PartNext().SizeDummy = size;
        }

        public override void UpdateSizeDummy() {
            SplitBytes();
            Blob.PartNext().SizeDummy = int.MaxValue;
        }

        public override void WriteNetMappedString(string? text) {
            SplitBytes();
            Blob.PartNext().String = text ?? "";
        }

    }
}

<|start_filename|>CelesteNet.Client/Components/CelesteNetTickerComponent.cs<|end_filename|>
using Celeste.Mod.CelesteNet.DataTypes;
using Microsoft.Xna.Framework;
using Monocle;
using System;
using System.Collections.Generic;

namespace Celeste.Mod.CelesteNet.Client.Components {
    // All Monocle entities/component which implement this must be tracked
    public interface ITickReceiver {
        void Tick();
    }

    public class CelesteNetTickerComponent : CelesteNetGameComponent {

        public const float MinTickDelay = 0.01f;

        public volatile float TickRate = 0f;
        private float TickDelay = 0f;

        public CelesteNetTickerComponent(CelesteNetClientContext context, Game game) : base(context, game) {}

        public override void Update(GameTime gameTime) {
            base.Update(gameTime);

            if (TickRate <= 0 || !(Client?.IsReady ?? false))
                return;

            TickDelay -= Engine.RawDeltaTime;
            if (TickRate >= 60 || TickDelay <= 0) {
                TickDelay += 1 / TickRate;
                if (TickDelay < MinTickDelay)
                    TickDelay = MinTickDelay;

                // Tick components
                foreach (ITickReceiver receiver in Context.Components.Values)
                    receiver.Tick();

                if (Engine.Scene?.Tracker != null) {
                    // Tick all tracked entities and components which implement the interface
                    foreach (KeyValuePair<Type, List<Entity>> ets in Engine.Scene.Tracker.Entities) {
                        if (typeof(ITickReceiver).IsAssignableFrom(ets.Key)) {
                            foreach (Entity et in ets.Value)
                                ((ITickReceiver) et).Tick();
                        }
                    }

                    foreach (KeyValuePair<Type, List<Component>> cps in Engine.Scene.Tracker.Components) {
                        if (typeof(ITickReceiver).IsAssignableFrom(cps.Key)) {
                            foreach (Component cp in cps.Value)
                                ((ITickReceiver) cp).Tick();
                        }
                    }
                }

                OnTick?.Invoke();
            }
        }

        public void Handle(CelesteNetConnection con, DataTickRate rate) {
            TickRate = rate.TickRate;
            Logger.Log(LogLevel.INF, "client-ticker", $"Changed tick rate to {rate.TickRate} TpS");
        }

        public event Action OnTick;

    }
}

<|start_filename|>CelesteNet.Shared/DataTypes/DataModRec.cs<|end_filename|>
﻿using Microsoft.Xna.Framework;
using Monocle;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Celeste.Mod.CelesteNet.DataTypes {
    public class DataModRec : DataType<DataModRec> {

        static DataModRec() {
            DataID = "modRec";
        }

        public string ModID = "";
        public string ModName = "";
        public Version ModVersion = new();

        protected override void Read(CelesteNetBinaryReader reader) {
            ModID = reader.ReadNetString();
            ModName = reader.ReadNetString();
            ModVersion = new(reader.ReadNetString());
        }

        protected override void Write(CelesteNetBinaryWriter writer) {
            writer.WriteNetString(ModID);
            writer.WriteNetString(ModName);
            writer.WriteNetString(ModVersion.ToString());
        }

    }
}


<|start_filename|>CelesteNet.Server/ConPlus/TCPEPollPoller.cs<|end_filename|>
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Runtime.InteropServices;
using System.Threading;
using System;

namespace Celeste.Mod.CelesteNet.Server {
    /*
    EPoll can be used on Linux to achieve a similar load-balancing effect for
    TCP polling as with port reusing. This means that we can get very good
    performance from utilizing it. It was already a topic of passionate debate
    if this should be included, but I decided to include it as the performance
    gains are to siginficant to ignore.
    Used sources:
        epoll: https://linux.die.net/man/7/epoll
        eventfd: https://linux.die.net/man/2/eventfd
    -Popax21
    */
    public class TCPEPollPoller : TCPReceiverRole.IPoller {

        // This structure marshals as struct epoll_event
        [StructLayout(LayoutKind.Sequential)]
        private struct epoll_event {

            public uint events;
            public long user;

        }

        private const int EINTR = 4, EBADF = 9;

        private const int EFD_SEMAPHORE = 1;
        [DllImport("libc", SetLastError = true)]
        private static extern int eventfd(uint initval, int flags);
        [DllImport("libc", SetLastError = true)]
        private static extern nint read(int fd, [In, MarshalAs(UnmanagedType.LPArray)] byte[] buf, nint size);
        [DllImport("libc", SetLastError = true)]
        private static extern nint write(int fd, [In, MarshalAs(UnmanagedType.LPArray)] byte[] buf, nint size);
        [DllImport("libc", SetLastError = true)]
        private static extern void close(int fd);

        private const int EPOLL_CTL_ADD = 1, EPOLL_CTL_DEL = 2, EPOLL_CTL_MOD = 3;
        private const uint EPOLLIN = 0x00000001u, EPOLLERR = 0x00000008u, EPOLLHUP = 0x00000010, EPOLLRDHUP = 0x00002000u, EPOLLET = 0x80000000u, EPOLLONESHOT = 0x40000000u;
        [DllImport("libc", SetLastError = true)]
        private static extern int epoll_create(int size);
        [DllImport("libc", SetLastError = true)]
        private static extern int epoll_ctl(int epfd, uint op, int fd, [In] in epoll_event evt);

        [DllImport("libc", SetLastError = true)]
        private static extern int epoll_wait(int epfd, [Out, MarshalAs(UnmanagedType.LPArray)] epoll_event[] evt, int maxevts, int timeout);

        private readonly RWLock PollerLock = new();
        private readonly int EpollFD, CancelFD;
        private readonly ConcurrentDictionary<ConPlusTCPUDPConnection, (int fd, int id)> Connections = new();

        private int NextConId;
        private readonly ConcurrentDictionary<int, ConPlusTCPUDPConnection> ConIds = new();

        public TCPEPollPoller() {
            // Create the EPoll FD
            EpollFD = epoll_create(21); // The documentation says "pass any number, it's unused on modern systems" :)
            if (EpollFD < 0)
                throw new SystemException($"Could not create the EPoll FD: {Marshal.GetLastWin32Error()}");

            // Create the cancel eventfd, and initialize it to be not triggered
            // (counter at zero) and behave like a semaphore (so reading it
            // causes to counter to get decremented, not reset to zero)
            CancelFD = eventfd(0, EFD_SEMAPHORE);
            if (CancelFD < 0)
                throw new SystemException($"Could not create the cancel eventfd: {Marshal.GetLastWin32Error()}");

            // Make the EPoll FD also listen for the cancel eventfd
            // A eventfd is read-ready when it's counter is above zero
            // We don't specifiy EPOLLET (edge triggered) and EPOLLONESHOT
            // (don't poll the FD after it's triggered once until renabled), as
            // we want to wake up all threads
            epoll_event evt = new() {
                events = EPOLLIN,
                user = int.MaxValue
            };
            if (epoll_ctl(EpollFD, EPOLL_CTL_ADD, CancelFD, in evt) < 0)
                throw new SystemException($"Could not add the cancel eventfd to the EPoll FD: {Marshal.GetLastWin32Error()}");

        }

        public void Dispose() {
            lock (PollerLock.W()) {
                // Close the EPoll and cancel eventfd
                close(EpollFD);
                close(CancelFD);

                Connections.Clear();
                ConIds.Clear();
                PollerLock.Dispose();
            }
        }

        public void AddConnection(ConPlusTCPUDPConnection con) {
            using (PollerLock.W()) {
                int fd = (int) con.TCPSocket.Handle;

                // Assign the connection an ID
                // Don't assing int.MaxValue, as it is used by the cancel eventfd
                int id = int.MaxValue;
                while (id == int.MaxValue)
                    id = Interlocked.Increment(ref NextConId);
                if (!Connections.TryAdd(con, (fd, id)))
                    throw new ArgumentException("Connection already part of poller");
                ConIds[id] = con;

                // Add the socket's FD to the EPoll FD
                // Flag breakdown:
                //  - EPOLLIN: listen for "read-ready"
                //  - EPOLLERR: listen for "an error occured"
                //  - EPOLLHUP: listen for "the connection was closed"
                //  - EPOLLRDHUP: listen for "remote closed the connection"
                //      (I don't know why there are two similar events for this)
                //  - EPOLLET: enable "edge triggering", which causes two thing:
                //    - the poll call only returns a socket when it's state
                //      changed, not when we forgot to clear the trigger
                //      condition (like not reading all bytes available)
                //    - the Linux kernel will only wake up one thread polling on
                //      the EPoll FD for a certain event (the load-balancing
                //      behaviour we want)
                //  - EPOLLONESHOT: disable the socket for further polling until
                //      we enable it again. This ensures that the same
                //      connection isn't handled by two threads at the same time
                //      when the connection receives additional data when a
                //      thread's already handling it
                epoll_event evt = new() {
                    events = EPOLLIN | EPOLLERR | EPOLLHUP | EPOLLRDHUP | EPOLLET | EPOLLONESHOT,
                    user = id
                };
                if (epoll_ctl(EpollFD, EPOLL_CTL_ADD, fd, in evt) < 0)
                    throw new SystemException($"Couldn't add connection socket to EPoll FD: {Marshal.GetLastWin32Error()}");
            }
        }

        public void RemoveConnection(ConPlusTCPUDPConnection con) {
            using (PollerLock.W()) {
                // Remove the connection from the ID table
                if (!Connections.TryRemove(con, out (int fd, int id) conData))
                    return;
                ConIds.TryRemove(conData.id, out _);

                // Remove the socket from the EPoll FD
                // Because of a bug in old Linux versions we still have to pass
                // something as evt, even though it isn't used
                // Maybe the socket was already closed, in which case it already
                // got removed from the EPoll FD and epoll_ctl will return EBADF
                epoll_event evt = default;
                int ret = epoll_ctl(EpollFD, EPOLL_CTL_DEL, conData.fd, in evt);
                if (ret < 0 && Marshal.GetLastWin32Error() != EBADF)
                    throw new SystemException($"Couldn't remove connection socket from EPoll FD: {Marshal.GetLastWin32Error()}");
            }
        }

        public IEnumerable<ConPlusTCPUDPConnection> StartPolling(TCPReceiverRole role, CancellationToken token) {
            // We somehow have to be able to cancel the epoll_wait call. The
            // cleanest way I found to do this is to create an eventfd
            // (basically a low level ManualResetEvent), and increment it
            // whenever a token is triggered, and decrement it when a thread exits
            // Triggering it causes all threads to cycle out of their poll
            // loops and check their tokens to determine if they should exit.
            epoll_event[] evts = new epoll_event[role.Server.Settings.TCPPollMaxEvents];
            using (token.Register(() => write(CancelFD, BitConverter.GetBytes(1UL), 8)))
            while (!token.IsCancellationRequested) {
                // Poll the EPoll FD
                int ret;
                do {
                    ret = epoll_wait(EpollFD, evts, 1, -1);
                } while (!token.IsCancellationRequested && ret == EINTR);
                if (ret < 0)
                    throw new SystemException($"Couldn't poll the EPoll FD: {Marshal.GetLastWin32Error()}");

                // Yield the Connections from the event
                for (int i = 0; i < ret; i++) {
                    int id = (int) evts[i].user;
                    if (id != int.MaxValue)
                        yield return ConIds[id];
                }
            }
            // The eventfd got incremented for us to exit, so decrement it
            read(CancelFD, new byte[8], 8);
        }

        public void ArmConnectionPoll(ConPlusTCPUDPConnection con) {
            using (PollerLock.R()) {
                if (!Connections.TryGetValue(con, out (int fd, int id) conData))
                    return;

                // Modify all flags to how they were originally. This causes the
                // EPoll FD to monitor the socket again
                // Maybe the socket was already closed, in which case it already
                // got removed from the EPoll FD and epoll_ctl will return EBADF
                epoll_event evt = new() {
                    events = EPOLLIN | EPOLLERR | EPOLLHUP | EPOLLRDHUP | EPOLLET | EPOLLONESHOT,
                    user = conData.id
                };
                int ret = epoll_ctl(EpollFD, EPOLL_CTL_MOD, conData.fd, in evt);
                if (ret < 0 && Marshal.GetLastWin32Error() != EBADF)
                    throw new SystemException($"Couldn't arm connection socket for EPoll FD: {Marshal.GetLastWin32Error()}");
            }
        }

    }
}

<|start_filename|>CelesteNet.Shared/DataTypes/DataServerStatus.cs<|end_filename|>
﻿using Microsoft.Xna.Framework;
using Monocle;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Celeste.Mod.CelesteNet.DataTypes {
    public class DataServerStatus : DataType<DataServerStatus> {

        static DataServerStatus() {
            DataID = "serverStatus";
        }

        public string Text = "";
        public float Time;
        public bool Spin = true;

        protected override void Read(CelesteNetBinaryReader reader) {
            Text = reader.ReadNetString();
            Time = reader.ReadSingle();
            Spin = reader.ReadBoolean();
        }

        protected override void Write(CelesteNetBinaryWriter writer) {
            writer.WriteNetString(Text);
            writer.Write(Time);
            writer.Write(Spin);
        }

    }
}


<|start_filename|>CelesteNet.Client/Entities/Ghost.cs<|end_filename|>
﻿using Celeste.Mod.CelesteNet.Client.Components;
using Celeste.Mod.CelesteNet.DataTypes;
using Microsoft.Xna.Framework;
using Monocle;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Celeste.Mod.CelesteNet.Client.Entities {
    [Tracked]
    public class Ghost : Actor, ITickReceiver {

        public CelesteNetClientContext Context;

        public DataPlayerInfo PlayerInfo;
        public volatile DataPlayerGraphics PlayerGraphics;

        public float Alpha = 0.875f;

        public Vector2 Speed;

        public PlayerSprite Sprite;
        public PlayerHair Hair;
        public Leader Leader;
        public Holdable Holdable;
        private bool HoldableAdded;

        public GhostNameTag NameTag;
        public GhostEmote IdleTag;

        public Color[] HairColors = new[] { Color.White };

        public bool? DashWasB;
        public Vector2? DashDir;

        public bool Dead;

        public List<GhostFollower> Followers = new();
        public GhostEntity Holding;

        public bool Interactive;

        public float GrabCooldown = 0f;
        public const float GrabCooldownMax = CelesteNetMainComponent.GrabCooldownMax;
        protected DataPlayerGrabPlayer GrabPacket;

        // TODO Revert this to Queue<> once MonoKickstart weirdness is fixed
        protected List<Action<Ghost>> UpdateQueue = new();
        protected bool IsUpdating;

        public Ghost(CelesteNetClientContext context, DataPlayerInfo playerInfo, PlayerSpriteMode spriteMode)
            : base(Vector2.Zero) {
            Context = context;
            PlayerInfo = playerInfo;

            Depth = 0;

            RetryPlayerSprite:
            try {
                Sprite = new(spriteMode | (PlayerSpriteMode) (1 << 31));
            } catch (Exception) {
                if (spriteMode != PlayerSpriteMode.Madeline) {
                    spriteMode = PlayerSpriteMode.Madeline;
                    goto RetryPlayerSprite;
                }
                throw;
            }

            Add(Hair = new(Sprite));
            Add(Sprite);
            Hair.Color = Player.NormalHairColor;
            Add(Leader = new(new(0f, -8f)));
            Holdable = new() {
                OnCarry = OnCarry,
                OnRelease = OnRelease
            };

            Collidable = true;
            Collider = new Hitbox(8f, 11f, -4f, -11f);
            Add(new PlayerCollider(OnPlayer));

            NameTag = new(this, "");
            NameTag.Alpha = 0.85f;

            Dead = false;
            AllowPushing = false;
            SquishCallback = null;

            Tag = Tags.Persistent | Tags.PauseUpdate | Tags.TransitionUpdate;
        }

        public override void Added(Scene scene) {
            base.Added(scene);

            Hair.Start();
            Scene.Add(NameTag);
        }

        public override void Removed(Scene scene) {
            base.Removed(scene);

            NameTag.RemoveSelf();
        }

        public void OnPlayer(Player player) {
            if (!Interactive || GrabCooldown > 0f || !CelesteNetClientModule.Settings.Interactions || Context?.Main.GrabbedBy == this)
                return;

            if (player.StateMachine.State == Player.StNormal &&
                player.Speed.Y > 0f && player.Bottom <= Top + 3f) {

                Dust.Burst(player.BottomCenter, -1.57079637f, 8);
                (Scene as Level)?.DirectionalShake(Vector2.UnitY, 0.05f);
                Input.Rumble(RumbleStrength.Light, RumbleLength.Medium);
                player.Bounce(Top + 2f);
                player.Play("event:/game/general/thing_booped");

            } else if (player.StateMachine.State != Player.StDash &&
                player.StateMachine.State != Player.StRedDash &&
                player.StateMachine.State != Player.StDreamDash &&
                player.StateMachine.State != Player.StBirdDashTutorial &&
                player.Speed.Y <= 0f && Bottom <= player.Top + 5f) {
                player.Speed.Y = Math.Max(player.Speed.Y, 16f);
            }
        }

        public void OnCarry(Vector2 position) {
            if (!Interactive || GrabCooldown > 0f || !CelesteNetClientModule.Settings.Interactions || IdleTag != null)
                return;

            Position = position;
            Collidable = false;

            CelesteNetClient client = Context?.Client;
            if (PlayerInfo == null || client == null)
                return;

            GrabPacket = new DataPlayerGrabPlayer {
                Player = client.PlayerInfo,
                Grabbing = PlayerInfo,
                Position = position,
                Force = null
            };
        }

        public void OnRelease(Vector2 force) {
            Collidable = true;

            if (!Interactive || GrabCooldown > 0f || !CelesteNetClientModule.Settings.Interactions || IdleTag != null)
                return;

            CelesteNetClient client = Context?.Client;
            if (PlayerInfo == null || client == null)
                return;

            GrabPacket = new DataPlayerGrabPlayer {
                Player = client.PlayerInfo,
                Grabbing = PlayerInfo,
                Position = Position,
                Force = force
            };
        }

        public override void Update() {
            lock (UpdateQueue) {
                IsUpdating = true;
                for(int i = 0; i < UpdateQueue.Count; i++)
                    UpdateQueue[i](this);
                UpdateQueue.Clear();
                IsUpdating = false;
            }

            if (string.IsNullOrEmpty(NameTag.Name) && Active) {
                RemoveSelf();
                return;
            }

            bool holdable = Interactive && GrabCooldown <= 0f && CelesteNetClientModule.Settings.Interactions && IdleTag == null;

            GrabCooldown -= Engine.RawDeltaTime;
            if (GrabCooldown < 0f)
                GrabCooldown = 0f;

            if (!holdable && Holdable.Holder != null) {
                Collidable = false;
                Holdable.Release(Vector2.Zero);
                Collidable = true;
            }

            if (!HoldableAdded && holdable) {
                HoldableAdded = true;
                Add(Holdable);
            } else if (HoldableAdded && !holdable && !Holdable.IsHeld) {
                HoldableAdded = false;
                Remove(Holdable);
            }
            if (CelesteNetClientModule.Settings.PlayerOpacity == 0) {
                Alpha = 0f;
            } else {
                Alpha = 0.875f * ((CelesteNetClientModule.Settings.PlayerOpacity + 2) / 6f);
            }

            if (NameTag.Scene != Scene)
                Scene.Add(NameTag);

            Visible = !Dead;

            base.Update();

            if (Scene is not Level level)
                return;

            if (!level.GetUpdateHair() || level.Overlay is PauseUpdateOverlay)
                Hair.AfterUpdate();

            foreach (GhostFollower gf in Followers)
                if (gf.Scene != level)
                    level.Add(gf);

            if (Holding != null && Holding.Scene != level)
                level.Add(Holding);

            // TODO: Get rid of this, sync particles separately!
            if (DashWasB != null && DashDir != null && level != null && Speed != Vector2.Zero && level.OnRawInterval(0.02f))
                level.ParticlesFG.Emit(DashWasB.Value ? Player.P_DashB : Player.P_DashA, Center + Calc.Random.Range(Vector2.One * -2f, Vector2.One * 2f), DashDir.Value.Angle());
        }

        public void Tick() {
            if (GrabPacket != null) {
                CelesteNetClient client = Context?.Client;
                if (client != null) {
                    try {
                        client.Send(GrabPacket);
                    } catch (Exception e) {
                        Logger.Log(LogLevel.INF, "client-ghost", $"Error sending grab packet: {e}");
                        Context.Dispose();
                    }
                }
            }
            GrabPacket = null;
        }

        public void RunOnUpdate(Action<Ghost> action, bool wait = false) {
            bool updating;
            lock (UpdateQueue)
                updating = IsUpdating;
            if (updating) {
                action(this);
                return;
            }

            using ManualResetEvent waiter = wait ? new ManualResetEvent(false) : null;
            if (wait) {
                Action<Ghost> real = action;
                action = g => {
                    try {
                        real(g);
                    } finally {
                        waiter.Set();
                    }
                };
            }

            lock (UpdateQueue)
                UpdateQueue.Add(action);

            if (wait)
                WaitHandle.WaitAny(new WaitHandle[] { waiter });
        }

        public void UpdateGraphics(DataPlayerGraphics graphics) {
            if (graphics.HairCount == 0) {
                graphics.HairCount = 1;
                graphics.HairScales = new[] { Vector2.One };
                graphics.HairTextures = new[] { "characters/player/hair00" };
            }

            PlayerGraphics = graphics;

            Depth = graphics.Depth + 1;
            Sprite.Color = graphics.SpriteColor * Alpha;
            Sprite.Rate = graphics.SpriteRate;

            Sprite.HairCount = graphics.HairCount;
            Hair.StepPerSegment = graphics.HairStepPerSegment;
            Hair.StepInFacingPerSegment = graphics.HairStepInFacingPerSegment;
            Hair.StepApproach = graphics.HairStepApproach;
            Hair.StepYSinePerSegment = graphics.HairStepYSinePerSegment;
            while (Hair.Nodes.Count < graphics.HairCount)
                Hair.Nodes.Add(Hair.Nodes.LastOrDefault());
            while (Hair.Nodes.Count > graphics.HairCount)
                Hair.Nodes.RemoveAt(Hair.Nodes.Count - 1);
            Hair.Alpha = Alpha;
        }

        public void UpdateAnimation(int animationID, int animationFrame) {
            if (PlayerGraphics == null)
                return;

            if (animationID < 0 || PlayerGraphics.SpriteAnimations.Length <= animationID)
                return;

            string strID = PlayerGraphics.SpriteAnimations[animationID];
            if (strID != null && Sprite.Animations.ContainsKey(strID)) {
                if (Sprite.CurrentAnimationID != strID)
                    Sprite.Play(strID);
                Sprite.SetAnimationFrame(animationFrame);
            }
        }

        public void UpdatePosition(Vector2 pos, Vector2 scale, Facings facing, Vector2 speed) {
            if (Holdable.Holder == null)
                Position = pos;
            Sprite.Scale = scale;
            Sprite.Scale.X *= (float) facing;
            Hair.Facing = facing;
            Speed = speed;
        }

        public void UpdateHair(Facings facing, Color[] colors, string texture0, bool simulateMotion) {
            if (PlayerGraphics == null)
                return;

            if (colors.Length <= 0)
                colors = new[] { Color.White };
            if (PlayerGraphics.HairCount < colors.Length)
                Array.Resize(ref colors, PlayerGraphics.HairCount);

            Hair.Facing = facing;
            HairColors = colors;
            PlayerGraphics.HairTextures[0] = texture0;
            Hair.SimulateMotion = simulateMotion;
        }

        public void UpdateDash(bool? wasB, Vector2? dir) {
            DashWasB = wasB;
            DashDir = dir;
        }

        public void UpdateDead(bool dead) {
            if (!Dead && dead)
                HandleDeath();
            Dead = dead;
        }

        public void UpdateFollowers(DataPlayerFrame.Entity[] followers) {
            for (int i = 0; i < followers.Length; i++) {
                DataPlayerFrame.Entity f = followers[i];
                GhostFollower gf;
                if (i >= Followers.Count) {
                    gf = new(this);
                    gf.Position = Position + Leader.Position;
                    Followers.Add(gf);
                } else {
                    gf = Followers[i];
                }
                gf.UpdateSprite(f.Scale, f.Depth, f.Color, f.SpriteRate, f.SpriteJustify, f.SpriteID, f.CurrentAnimationID, f.CurrentAnimationFrame);
            }

            while (Followers.Count > followers.Length) {
                GhostFollower gf = Followers[Followers.Count - 1];
                gf.Ghost = null;
                Followers.RemoveAt(Followers.Count - 1);
            }
        }

        public void UpdateHolding(DataPlayerFrame.Entity h) {
            if (h == null) {
                if (Holding != null)
                    Holding.Ghost = null;
                Holding = null;
                return;
            }

            if (Holding == null)
                Holding = new(this);
            Holding.Position = h.Position;
            Holding.UpdateSprite(h.Scale, h.Depth, h.Color, h.SpriteRate, h.SpriteJustify, h.SpriteID, h.CurrentAnimationID, h.CurrentAnimationFrame);
        }


        public void HandleDeath() {
            if (Scene is not Level level ||
                level.Paused || level.Overlay != null)
                return;
            level.Add(new GhostDeadBody(this, Vector2.Zero));
        }

    }
}


<|start_filename|>CelesteNet.Shared/BufferedSocketStream.cs<|end_filename|>
using System;
using System.IO;
using System.Net.Sockets;

namespace Celeste.Mod.CelesteNet {
    /*
    This is a helper class primarly used by the new connection layer ConPlus. It
    doesn't do much, it just buffers some data in a buffer and sends that data
    to the underlying socket when the buffer's full. However, the reason there's
    a custom class for this is that this allows for the socket to change, making
    it possible to only use one stream per ConPlus worker. Also MonoKickstart is
    extremly broken, and BufferedStream just kinda doesn't work...
    -Popax21
    */
    public class BufferedSocketStream : Stream {

        private Socket? _Socket = null;
        private readonly byte[] RecvBuffer, SendBuffer;
        private int RecvAvail = 0, RecvBufferOff = 0, SendBufferOff = 0;

        public BufferedSocketStream(int bufferSize) {
            RecvBuffer = new byte[bufferSize];
            SendBuffer = new byte[bufferSize];
        }

        public override int Read(byte[] buffer, int offset, int count) {
            lock (RecvBuffer) {
                if (_Socket == null)
                    return 0;

                int numRead = 0;
                while (numRead < count) {
                    if (RecvAvail <= 0 || RecvBufferOff >= RecvBuffer.Length) {
                        _Socket.Poll(-1, SelectMode.SelectRead);
                        RecvAvail = _Socket.Receive(RecvBuffer, RecvBuffer.Length, SocketFlags.None);
                        RecvBufferOff = 0;
                        if (RecvAvail <= 0)
                            break;
                    }

                    int n = Math.Min(RecvAvail, count-numRead);
                    Buffer.BlockCopy(RecvBuffer, RecvBufferOff, buffer, offset, n);
                    RecvBufferOff += n;
                    RecvAvail -= n;
                    offset += n;
                    numRead += n;
                }
                return numRead;
            }
        }

        public override void Write(byte[] buffer, int offset, int count) {
            lock (SendBuffer) {
                if (_Socket == null)
                    return;

                while (count > 0) {
                    int n = Math.Min(SendBuffer.Length - SendBufferOff, count);
                    Buffer.BlockCopy(buffer, offset, SendBuffer, SendBufferOff, n);
                    SendBufferOff += n;
                    count -= n;
                    if (SendBufferOff >= SendBuffer.Length)
                        Flush();
                }
            }
        }

        public override void WriteByte(byte val) {
            lock (SendBuffer) {
                if (_Socket == null)
                    return;
                SendBuffer[SendBufferOff++] = val;
                if (SendBufferOff >= SendBuffer.Length)
                    Flush();
            }
        }

        public override void Flush() {
            lock (SendBuffer) {
                if (SendBufferOff != 0 && _Socket != null) {
                    while (true) {
                        try {
                            _Socket.Send(SendBuffer, SendBufferOff, SocketFlags.None);
                            break;
                        } catch (SocketException se) {
                            if (se.SocketErrorCode == SocketError.TryAgain && _Socket.Poll(-1, SelectMode.SelectWrite))
                                continue;
                            throw;
                        }
                    }
                }
                SendBufferOff = 0;
            }
        }

        public Socket? Socket {
            get => _Socket;
            set {
                if (value?.Blocking ?? false)
                    throw new ArgumentException("Only non-blocking sockets are supported");
                lock (RecvBuffer)
                lock (SendBuffer)
                    RecvBufferOff = RecvAvail = SendBufferOff = 0;
                _Socket = value;
            }
        }

        public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
        public override void SetLength(long value) => throw new NotSupportedException();

        public override bool CanRead => true;
        public override bool CanWrite => true;
        public override bool CanSeek => false;
        public override long Length => throw new NotSupportedException();
        public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); }

    }
}

<|start_filename|>CelesteNet.Shared/DataTypes/DataLowLevelStringMap.cs<|end_filename|>
namespace Celeste.Mod.CelesteNet.DataTypes {
    public class DataLowLevelStringMap : DataType<DataLowLevelStringMap> {

        static DataLowLevelStringMap() {
            DataID = "stringMap";
        }

        public override DataFlags DataFlags => DataFlags.CoreType;

        public string String = string.Empty;
        public int ID;

        protected override void Read(CelesteNetBinaryReader reader) {
            String = reader.ReadNetString();
            ID = reader.ReadInt32();
        }

        protected override void Write(CelesteNetBinaryWriter writer) {
            writer.WriteNetString(String);
            writer.Write(ID);
        }

    }
}

<|start_filename|>CelesteNet.Shared/DataTypes/DataPlayerFrame.cs<|end_filename|>
﻿using Microsoft.Xna.Framework;
using Monocle;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Celeste.Mod.CelesteNet.DataTypes {
    public class DataPlayerFrame : DataType<DataPlayerFrame> {

        static DataPlayerFrame() {
            DataID = "playerFrame";
        }

        // Too many too quickly to make tasking worth it.
        public override DataFlags DataFlags => DataFlags.Unreliable | DataFlags.CoreType | DataFlags.Small | DataFlags.NoStandardMeta;

        public DataPlayerInfo? Player;

        public Vector2 Position;
        public Vector2 Scale;
        public Facings Facing;
        public Vector2 Speed;

        public int CurrentAnimationID;
        public int CurrentAnimationFrame;

        public Color[] HairColors = Dummy<Color>.EmptyArray;
        public string HairTexture0 = string.Empty;
        public bool HairSimulateMotion;

        public Entity[] Followers = Dummy<Entity>.EmptyArray;

        public Entity? Holding;

        // TODO: Get rid of this, sync particles separately!
        public bool? DashWasB;
        public Vector2? DashDir;

        public bool Dead;

        public override bool FilterHandle(DataContext ctx)
            => Player != null; // Can be RECEIVED BY CLIENT TOO EARLY because UDP is UDP.

        public override MetaType[] GenerateMeta(DataContext ctx)
            => new MetaType[] {
                new MetaPlayerUpdate(Player),
                new MetaOrderedUpdate(Player?.ID ?? uint.MaxValue)
            };

        public override void FixupMeta(DataContext ctx) {
            MetaPlayerUpdate playerUpd = Get<MetaPlayerUpdate>(ctx);
            MetaOrderedUpdate order = Get<MetaOrderedUpdate>(ctx);

            order.ID = playerUpd;
            Player = playerUpd;
        }

        public override void ReadAll(CelesteNetBinaryReader reader) {
            Player = reader.ReadRef<DataPlayerInfo>();

            Flags flags = (Flags) reader.ReadByte();

            Position = new(reader.Read7BitEncodedInt(), reader.Read7BitEncodedInt());
            Scale = new(reader.ReadSByte() / 16f, reader.ReadSByte() / 16f);
            Facing = ((flags & Flags.FacingLeft) != 0) ? Facings.Left : Facings.Right;
            Speed = new(reader.ReadInt16(), reader.ReadInt16());

            CurrentAnimationID = reader.Read7BitEncodedInt();
            CurrentAnimationFrame = reader.Read7BitEncodedInt();

            HairColors = new Color[reader.ReadByte()];
            Color lastHairCol = Color.White;
            for (int i = 0; i < HairColors.Length;) {
                // Read the first color byte
                byte firstColByte = reader.ReadByte();

                // If it has the highest bit set, it's a repeat count of the last color
                if ((firstColByte & (1<<7)) != 0) {
                    int repCount = firstColByte & ~(1<<7);
                    for (int j = 0; i < HairColors.Length && j < repCount; j++)
                        HairColors[i++] = lastHairCol;
                } else {
                    // Read the second color byte and combine them
                    int packedCol = (firstColByte << 8) | reader.ReadByte();

                    // Unpack the color (each component is 5 bits)
                    lastHairCol.R = (byte) (((packedCol >> 0) & 0b11111) << 3);
                    lastHairCol.G = (byte) (((packedCol >> 5) & 0b11111) << 3);
                    lastHairCol.B = (byte) (((packedCol >> 10) & 0b11111) << 3);
                    lastHairCol.A = 255;

                    HairColors[i++] = lastHairCol;
                }
            }

            HairTexture0 = reader.ReadNetMappedString();
            HairSimulateMotion = (flags & Flags.HairSimulateMotion) != 0;

            Followers = new Entity[reader.ReadByte()];
            for (int i = 0; i < Followers.Length; i++) {
                Entity f = new();
                f.Scale = new Vector2(reader.ReadSByte() / 16f, reader.ReadSByte() / 16f);
                f.Color = reader.ReadColor();
                f.Depth = reader.Read7BitEncodedInt();
                f.SpriteRate = reader.ReadSingle();
                f.SpriteJustify = reader.ReadBoolean() ? (Vector2?) reader.ReadVector2() : null;
                f.SpriteID = reader.ReadNetMappedString();
                if (string.IsNullOrEmpty(f.SpriteID)) {
                    Entity p = Followers[i - 1];
                    f.SpriteID = p.SpriteID;
                    f.CurrentAnimationID = p.CurrentAnimationID;
                } else {
                    f.CurrentAnimationID = reader.ReadNetMappedString();
                }
                f.CurrentAnimationFrame = reader.Read7BitEncodedInt();
                Followers[i] = f;
            }

            if ((flags & Flags.Holding) != 0)
                Holding = new() {
                    Position = Position + new Vector2(reader.Read7BitEncodedInt(), reader.Read7BitEncodedInt()),
                    Scale = new Vector2(reader.ReadSByte() / 16f, reader.ReadSByte() / 16f),
                    Color = reader.ReadColor(),
                    Depth = reader.Read7BitEncodedInt(),
                    SpriteRate = reader.ReadSingle(),
                    SpriteJustify = reader.ReadBoolean() ? (Vector2?) reader.ReadVector2() : null,
                    SpriteID = reader.ReadNetMappedString(),
                    CurrentAnimationID = reader.ReadNetMappedString(),
                    CurrentAnimationFrame = reader.Read7BitEncodedInt()
                };

            if ((flags & Flags.Dashing) != 0) {
                DashWasB = ((flags & Flags.DashB) != 0);
                DashDir = Calc.AngleToVector((float) (reader.ReadByte() / 256f * 2*Math.PI), 1);
            } else {
                DashWasB = null;
                DashDir = null;
            }

            Dead = (flags & Flags.Dead) != 0;

            Meta = GenerateMeta(reader.Data);
        }

        public override void WriteAll(CelesteNetBinaryWriter writer) {
            FixupMeta(writer.Data);
            writer.WriteRef(Player);

            Flags flags = 0;
            if (Facing == Facings.Left)
                flags |= Flags.FacingLeft;
            if (HairSimulateMotion)
                flags |= Flags.HairSimulateMotion;
            if (DashDir != null && DashWasB != null)
                flags |= Flags.Dashing;
            if (DashWasB ?? false)
                flags |= Flags.DashB;
            if (Holding != null)
                flags |= Flags.Holding;
            if (Dead)
                flags |= Flags.Dead;
            writer.Write((byte) flags);

            writer.Write7BitEncodedInt((int) Position.X);
            writer.Write7BitEncodedInt((int) Position.Y);
            writer.Write((sbyte) Calc.Clamp((int) (Scale.X * 16), sbyte.MinValue, sbyte.MaxValue));
            writer.Write((sbyte) Calc.Clamp((int) (Scale.Y * 16), sbyte.MinValue, sbyte.MaxValue));
            writer.Write((short) Calc.Clamp((int) Speed.X, short.MinValue, short.MaxValue));
            writer.Write((short) Calc.Clamp((int) Speed.Y, short.MinValue, short.MaxValue));

            writer.Write7BitEncodedInt(CurrentAnimationID);
            writer.Write7BitEncodedInt(CurrentAnimationFrame);

            writer.Write((byte) HairColors.Length);
            ushort[] packedHairCols = new ushort[HairColors.Length];
            for (int i = 0; i < HairColors.Length; i++)
                packedHairCols[i] = (ushort) (
                    (HairColors[i].R >> 3) << 0 |
                    (HairColors[i].G >> 3) << 5 |
                    (HairColors[i].B >> 3) << 10
                );
            for (int i = 0; i < packedHairCols.Length;) {
                int origIdx = i;
                writer.Write((byte) ((packedHairCols[i] >> 8) & 0xff));
                writer.Write((byte) ((packedHairCols[i] >> 0) & 0xff));
                for (i++; i < packedHairCols.Length && packedHairCols[i] == packedHairCols[origIdx]; i++);
                if (origIdx+1 < i)
                    writer.Write((byte) ((1<<7) | (i-origIdx-1)));
            }

            writer.WriteNetMappedString(HairTexture0);

            writer.Write((byte) Followers.Length);
            if (Followers.Length != 0) {
                for (int i = 0; i < Followers.Length; i++) {
                    Entity f = Followers[i];
                    writer.Write((sbyte) Calc.Clamp((int) (f.Scale.X * 16), sbyte.MinValue, sbyte.MaxValue));
                    writer.Write((sbyte) Calc.Clamp((int) (f.Scale.Y * 16), sbyte.MinValue, sbyte.MaxValue));
                    writer.Write(f.Color);
                    writer.Write7BitEncodedInt(f.Depth);
                    writer.Write(f.SpriteRate);
                    if (f.SpriteJustify == null) {
                        writer.Write(false);
                    } else {
                        writer.Write(true);
                        writer.Write(f.SpriteJustify.Value);
                    }
                    if (i >= 1 &&
                        f.SpriteID == Followers[i - 1].SpriteID &&
                        f.CurrentAnimationID == Followers[i - 1].CurrentAnimationID) {
                        writer.WriteNetMappedString(null);
                    } else {
                        writer.WriteNetMappedString(f.SpriteID);
                        writer.WriteNetMappedString(f.CurrentAnimationID);
                    }
                    writer.Write7BitEncodedInt(f.CurrentAnimationFrame);
                }
            }

            if (Holding != null) {
                Entity h = Holding;
                writer.Write7BitEncodedInt((int) (h.Position.X - Position.X));
                writer.Write7BitEncodedInt((int) (h.Position.Y - Position.Y));
                writer.Write((sbyte) Calc.Clamp((int) (h.Scale.X * 16), sbyte.MinValue, sbyte.MaxValue));
                writer.Write((sbyte) Calc.Clamp((int) (h.Scale.Y * 16), sbyte.MinValue, sbyte.MaxValue));
                writer.Write(h.Color);
                writer.Write7BitEncodedInt(h.Depth);
                writer.Write(h.SpriteRate);
                if (h.SpriteJustify == null) {
                    writer.Write(false);

                } else {
                    writer.Write(true);
                    writer.Write(h.SpriteJustify.Value);
                }
                writer.WriteNetMappedString(h.SpriteID);
                writer.WriteNetMappedString(h.CurrentAnimationID);
                writer.Write7BitEncodedInt(h.CurrentAnimationFrame);
            }

            if (DashDir != null && DashWasB != null)
                writer.Write((byte) ((DashDir.Value.Angle() / (2 * Math.PI) * 256f) % 256));
        }

        [Flags]
        private enum Flags {
            FacingLeft = 0b00000001,
            HairSimulateMotion = 0b00000010,
            Dashing = 0b00000100,
            DashB = 0b00001000,
            Holding = 0b00010000,
            Dead = 0b00100000
        }

        public class Entity {
            public Vector2 Position;
            public Vector2 Scale;
            public Color Color;
            public int Depth;
            public float SpriteRate;
            public Vector2? SpriteJustify;
            public string SpriteID = "";
            public string CurrentAnimationID = "";
            public int CurrentAnimationFrame;
        }

    }
}


<|start_filename|>CelesteNet.Shared/MetaTypes/Meta.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using MonoMod.Utils;

namespace Celeste.Mod.CelesteNet.DataTypes {
    public abstract class MetaType {

        public virtual string GetTypeID(DataContext ctx)
            => ctx.MetaTypeToID.TryGetValue(GetType(), out string? value) ? value : "";

        public abstract void Read(CelesteNetBinaryReader reader);
        public abstract void Write(CelesteNetBinaryWriter writer);

    }

    public abstract class MetaType<T> : MetaType where T : MetaType<T> {

        public static string MetaID;

        static MetaType() {
            MetaID = typeof(T).Name;
        }

        public T ReadT(CelesteNetBinaryReader reader) {
            Read(reader);
            return (T) this;
        }

    }
}


<|start_filename|>CelesteNet.Server.FrontendModule/WSCMDs/WSCMDMove.cs<|end_filename|>
﻿using Celeste.Mod.CelesteNet.DataTypes;
using Celeste.Mod.CelesteNet.Server.Chat;
using Mono.Cecil;
using Mono.Cecil.Cil;
using MonoMod.Utils;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Celeste.Mod.CelesteNet.Server.Control {
    public class WSCMDMove : WSCMD {
        public override bool Auth => true;
        public override object? Run(dynamic? input) {
            if (input == null)
                return null;

            uint id = (uint?) input.ID ?? uint.MaxValue;
            string to = (string?) input.To ?? (string?) input.Channel ?? Channels.NameDefault;

            Channels channels = Frontend.Server.Channels;

            lock (channels.All) {
                foreach (Channel c in channels.All) {
                    CelesteNetPlayerSession? p = c.Players.FirstOrDefault(p => p.SessionID == id);
                    if (p != null) {
                        channels.Move(p, to);
                        return null;
                    }
                }
            }

            return null;
        }
    }
}


<|start_filename|>CelesteNet.Shared/ConnectionTypes/CelesteNetTCPUDPConnection.cs<|end_filename|>
using Celeste.Mod.CelesteNet.DataTypes;
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace Celeste.Mod.CelesteNet {
    public class CelesteNetTCPUDPConnection : CelesteNetConnection {

        public struct Settings {

            public int UDPReceivePort, UDPSendPort;
            public int MaxPacketSize, MaxQueueSize;
            public float MergeWindow;
            public int MaxHeartbeatDelay;
            public float HeartbeatInterval;
            public int UDPAliveScoreMax;
            public int UDPDowngradeScoreMin, UDPDowngradeScoreMax;
            public int UDPDeathScoreMin, UDPDeathScoreMax;

        }

        private volatile bool SafeDisposed = false, SafeDisposeTriggered = false;
        public override bool IsConnected => IsAlive && !SafeDisposed && _TCPSock.Connected;
        public override string ID { get; }
        public override string UID { get; }
        public readonly OptMap<string> Strings = new("StringMap");
        public readonly OptMap<Type> SlimMap = new("SlimMap");

        public readonly uint ConnectionToken;
        public readonly Settings ConnectionSettings;

        public Socket TCPSocket => _TCPSock;

        private readonly Socket _TCPSock;
        private EndPoint? _UDPEndpoint;
        private readonly CelesteNetSendQueue TCPQueue, UDPQueue;

        public const int UDPPacketDropThreshold = 8;
        public readonly object UDPLock = new();
        private volatile int _UDPConnectionID, UDPLastConnectionID = -1;
        private volatile int _UDPMaxDatagramSize;
        private volatile int UDPAliveScore = 0, UDPDowngradeScore = 0, UDPDeathScore = 0;
        private byte UDPRecvLastContainerID = 0, UDPSendNextContainerID = 0;

        public virtual bool UseUDP {
            get => IsConnected && !(ConnectionSettings.UDPReceivePort <= 0 || ConnectionSettings.UDPSendPort <= 0) && UDPDeathScore < ConnectionSettings.UDPDeathScoreMax;
            set => UDPDeathScore = value ? 0 : ConnectionSettings.UDPDeathScoreMax;
        }

        public virtual EndPoint? UDPEndpoint => UseUDP ? _UDPEndpoint : null;

        public virtual int UDPConnectionID => UseUDP ? _UDPConnectionID : -1;

        public virtual int UDPMaxDatagramSize => UDPMaxDatagramSize;

        public readonly object HeartbeatLock = new();
        private int TCPLastHeartbeatDelay = 0, UDPLastHeartbeatDelay = 0, TCPSendKeepAlive = 0, UDPSendKeepAlive = 0;

        public event Action<CelesteNetTCPUDPConnection, EndPoint>? OnUDPDeath;

        public CelesteNetTCPUDPConnection(DataContext data, uint token, Settings settings, Socket tcpSock, Action<CelesteNetSendQueue> tcpQueueFlusher, Action<CelesteNetSendQueue> udpQueueFlusher) : base(data) {
            IPEndPoint serverEp = (IPEndPoint) tcpSock.RemoteEndPoint!;
            ID = $"TCP/UDP {serverEp.Address}:{serverEp.Port}";
            UID = $"con-tcpudp-{BitConverter.ToString(serverEp.Address.GetAddressBytes())}-{serverEp.Port}";

            ConnectionToken = token;
            ConnectionSettings = settings;

            // Initialize networking stuff
            _TCPSock = tcpSock;
            _UDPEndpoint = null;
            _UDPMaxDatagramSize = 0;
            TCPQueue = new(this, "TCP Queue", settings.MaxQueueSize, settings.MergeWindow, tcpQueueFlusher);
            UDPQueue = new(this, "UDP Queue", settings.MaxQueueSize, settings.MergeWindow, udpQueueFlusher);
        }

        protected override void Dispose(bool disposing) {
            base.Dispose(disposing);

            lock (UDPLock) {
                if (_UDPEndpoint != null) {
                    EndPoint ep = _UDPEndpoint!;
                    _UDPEndpoint = null;
                    UDPDeathScore = ConnectionSettings.UDPDeathScoreMax;
                    OnUDPDeath?.Invoke(this, ep);
                }
            }

            _UDPEndpoint = null;
            TCPQueue.Dispose();
            UDPQueue.Dispose();
            try {
                _TCPSock.ShutdownSafe(SocketShutdown.Both);
                _TCPSock.Close();
            } catch (Exception e) {
                Logger.Log(LogLevel.WRN, "tcpudpcon", $"Error while closing TCP socket: {e}");
            }
            _TCPSock.Dispose();
        }

        public override void DisposeSafe() {
            SafeDisposeTriggered = true;
        }

        protected override CelesteNetSendQueue? GetQueue(DataType data) => (UseUDP && UDPEndpoint != null && UDPConnectionID >= 0 && (data.DataFlags & DataFlags.Unreliable) != 0) ? UDPQueue : TCPQueue;

        public virtual void PromoteOptimizations() {
            foreach (Tuple<string, int>? promo in Strings.PromoteRead())
                Send(new DataLowLevelStringMap {
                    String = promo.Item1,
                    ID = promo.Item2
                });

            foreach (Tuple<Type, int>? promo in SlimMap.PromoteRead())
                Send(new DataLowLevelSlimMap {
                    PacketType = promo.Item1,
                    ID = promo.Item2
                });
        }

        public virtual void InitUDP(EndPoint endpoint, int conId, int maxDatagramSize) {
            lock (UDPLock) {
                // Can't initialize two connections at once
                if (!UseUDP || UDPEndpoint != null)
                    return;

                // Initialize a new connection
                _UDPEndpoint = endpoint;
                _UDPConnectionID = conId;
                _UDPMaxDatagramSize = maxDatagramSize;
                UDPAliveScore = UDPDowngradeScore = 0;
                UDPRecvLastContainerID = UDPSendNextContainerID = 0;

                // If the connection is already established, send a state update
                if (conId >= 0)
                    Send(new DataLowLevelUDPInfo {
                        ConnectionID = conId,
                        MaxDatagramSize = maxDatagramSize
                    });

                Logger.Log(LogLevel.INF, "tcpudpcon", $"Initialized UDP connection of {this} [{conId} / {UDPMaxDatagramSize} / {UDPAliveScore} / {UDPDowngradeScore} / {UDPDeathScore}]");
            }
        }

        public virtual void IncreaseUDPScore() {
            lock (UDPLock) {
                // Must have an initialized connection
                if (UDPEndpoint == null)
                    return;

                // Increment the alive score, then decrement the downgrade score, then the death score
                if (++UDPAliveScore > ConnectionSettings.UDPAliveScoreMax) {
                    UDPAliveScore = 0;
                    if (UDPDowngradeScore > ConnectionSettings.UDPDowngradeScoreMin)
                        UDPDowngradeScore--;
                    else if (UDPDeathScore > ConnectionSettings.UDPDeathScoreMin)
                        UDPDeathScore--;
                }
            }
        }

        public virtual void DecreaseUDPScore(bool downgradeImmediatly = false, string? reason = null) {
            lock (UDPLock) {
                // Must have an initialized connection
                if (UDPEndpoint == null)
                    return;

                // Reset the alive score, half the maximum datagram size, and increment the downgrade score
                // If it reaches it's maximum, the connection died
                UDPAliveScore = 0;
                if (downgradeImmediatly || ++UDPDowngradeScore >= ConnectionSettings.UDPDowngradeScoreMax) {
                    UDPDowngradeScore = 0;
                    Logger.Log(LogLevel.INF, "tcpudpcon", $"Downgrading UDP connection of {this}{((reason != null) ? $": {reason}" : string.Empty)} [{_UDPConnectionID} / {UDPMaxDatagramSize} / {UDPAliveScore} / {UDPDowngradeScore} / {UDPDeathScore}]");
                    if ((_UDPMaxDatagramSize /= 2) >= 1 + ConnectionSettings.MaxPacketSize) {
                        if (_UDPConnectionID >= 0)
                            Send(new DataLowLevelUDPInfo {
                                ConnectionID = _UDPConnectionID,
                                MaxDatagramSize = UDPMaxDatagramSize
                            });
                    } else {
                        UDPConnectionDeath(true, "Too many downgrades");
                    }
                } else {
                    Logger.Log(LogLevel.INF, "tcpudpcon", $"Decreased score of UDP connection of {this}{((reason != null) ? $": {reason}" : string.Empty)} [{_UDPConnectionID} / {UDPMaxDatagramSize} / {UDPAliveScore} / {UDPDowngradeScore} / {UDPDeathScore}]");
                }

            }
        }

        public virtual void HandleUDPInfo(DataLowLevelUDPInfo info) {
            lock (UDPLock) {
                if (!UseUDP)
                    return;

                // Handle connection ID
                // If the packet contains a negative connection ID, disable UDP
                // If we don't have a connection ID, try to establish the connection
                // Otherwise, the packet must contain our connection ID
                if (info.ConnectionID < 0) {
                    UDPAliveScore = UDPDowngradeScore = 0;
                    UDPDeathScore = ConnectionSettings.UDPDeathScoreMax;
                    Logger.Log(LogLevel.INF, "tcpudpcon", $"Remote disabled UDP for connection {this} [{_UDPConnectionID} / {UDPMaxDatagramSize} / {UDPAliveScore} / {UDPDowngradeScore} / {UDPDeathScore}]");
                    return;
                }

                // We need an initialized connection for the following branches
                if (UDPEndpoint == null)
                    return;

                if (_UDPConnectionID < 0) {
                    // If it referes to an old connection, just ignore it
                    if (info.ConnectionID <= UDPLastConnectionID)
                        return;
                    if (info.MaxDatagramSize < 1 + ConnectionSettings.MaxPacketSize)
                        return;
                    _UDPConnectionID = info.ConnectionID;
                    _UDPMaxDatagramSize = info.MaxDatagramSize;
                    Logger.Log(LogLevel.INF, "tcpudpcon", $"Established UDP connection of {this} [{_UDPConnectionID} / {UDPMaxDatagramSize} / {UDPAliveScore} / {UDPDowngradeScore} / {UDPDeathScore}]");
                    return;
                }

                if (info.ConnectionID != _UDPConnectionID)
                    return;

                // Check if the remote notified us of a connection death
                if (info.MaxDatagramSize < 1+ConnectionSettings.MaxPacketSize) {
                    UDPConnectionDeath(false, "Remote connection died");
                    return;
                }

                // Decrease the maximum datagram size
                if (info.MaxDatagramSize == UDPMaxDatagramSize)
                    return;
                _UDPMaxDatagramSize = Math.Min(UDPMaxDatagramSize, info.MaxDatagramSize);

                // Notify the remote of the change in our state
                Send(new DataLowLevelUDPInfo {
                    ConnectionID = _UDPConnectionID,
                    MaxDatagramSize = UDPMaxDatagramSize
                });

                Logger.Log(LogLevel.INF, "tcpudpcon", $"Reduced maximum UDP datagram size of connection {this} [{_UDPConnectionID} / {UDPMaxDatagramSize} / {UDPAliveScore} / {UDPDowngradeScore} / {UDPDeathScore}]");
            }
        }

        private void UDPConnectionDeath(bool increaseScore, string reason) {
            EndPoint ep = _UDPEndpoint!;

            // Uninitialize the connection
            if (_UDPConnectionID >= 0)
                UDPLastConnectionID = UDPConnectionID;
            _UDPEndpoint = null;
            _UDPMaxDatagramSize = UDPAliveScore = UDPDowngradeScore = 0;
            UDPQueue.Clear();

            // Increment the death score
            // If it exceeds the maximum, disable UDP
            if (increaseScore && UDPDeathScore < ConnectionSettings.UDPDeathScoreMax) {
                if (++UDPDeathScore >= ConnectionSettings.UDPDeathScoreMax)
                    Logger.Log(LogLevel.INF, "tcpudpcon", $"Disabling UDP for connection {this} [{_UDPConnectionID} / {UDPMaxDatagramSize} / {UDPAliveScore} / {UDPDowngradeScore} / {UDPDeathScore}]");
            }

            // Notify the remote
            if (UDPLastConnectionID >= 0)
                Send(new DataLowLevelUDPInfo {
                    ConnectionID = UseUDP ? UDPLastConnectionID : -1,
                    MaxDatagramSize = 0
                });

            Logger.Log(LogLevel.INF, "tcpudpcon", $"UDP connection of {this} died: {reason} [{_UDPConnectionID} / {UDPMaxDatagramSize} / {UDPAliveScore} / {UDPDowngradeScore} / {UDPDeathScore}]");
            OnUDPDeath?.Invoke(this, ep);
        }

        public virtual byte NextUDPContainerID() {
            lock (UDPLock) {
                // Must have an initialized connection
                if (UDPEndpoint == null)
                    throw new InvalidOperationException("No established UDP connection");

                byte id = 0xff;
                while (id == 0xff)
                    id = unchecked(UDPSendNextContainerID++);
                return id;
            }
        }

        protected virtual void ReceivedUDPContainer(byte containerID) {
            UDPHeartbeat();
            lock (UDPLock) {
                // Check if we dropped more packets than the threshold
                int skipCount = (containerID - UDPRecvLastContainerID + 256) % 256;
                if (skipCount <= 128) {
                    if (skipCount > UDPPacketDropThreshold)
                        DecreaseUDPScore(reason: $"Container ID jump over threshold: {UDPRecvLastContainerID} - > {containerID}");
                    UDPRecvLastContainerID = containerID;
                }
            }
        }

        public virtual string? DoHeartbeatTick() {
            if (!IsAlive || SafeDisposeTriggered)
                return null;

            if (SafeDisposeTriggered) {
                SafeDisposed = true;
                return $"Safe dispose triggered for connection {this}";
            }

            lock (HeartbeatLock) {
                // Check if we got a TCP heartbeat in the required timeframe
                if (Interlocked.Increment(ref TCPLastHeartbeatDelay) > ConnectionSettings.MaxHeartbeatDelay) {
                    DisposeSafe();
                    return $"Connection {this} timed out";
                }

                // Check if we need to send a TCP keep-alive
                if (Interlocked.Exchange(ref TCPSendKeepAlive, 1) > 0)
                    TCPQueue.Enqueue(new DataLowLevelKeepAlive());

                if (UDPEndpoint != null) {
                    // Check if we got a UDP heartbeat in the required timeframe
                    if (Interlocked.Increment(ref UDPLastHeartbeatDelay) > ConnectionSettings.MaxHeartbeatDelay) {
                        Volatile.Write(ref UDPLastHeartbeatDelay, 0);
                        DecreaseUDPScore(true);
                    }

                    // Check if we need to send a UDP keep-alive
                    if (UDPEndpoint != null && Interlocked.Exchange(ref UDPSendKeepAlive, 1) > 0)
                        UDPQueue.Enqueue(new DataLowLevelKeepAlive());
                }
            }

            return null;
        }

        public virtual void TCPHeartbeat() {
            Volatile.Write(ref TCPLastHeartbeatDelay, 0);
        }

        public virtual void UDPHeartbeat() {
            Volatile.Write(ref UDPLastHeartbeatDelay, 0);
        }

        public virtual void SurpressTCPKeepAlives() {
            Volatile.Write(ref TCPSendKeepAlive, 0);
        }

        public virtual void SurpressUDPKeepAlives() {
            Volatile.Write(ref UDPSendKeepAlive, 0);
        }

    }
}


<|start_filename|>CelesteNet.Client/CelesteNetClientRC.cs<|end_filename|>
﻿using MC = Mono.Cecil;
using CIL = Mono.Cecil.Cil;

using Celeste.Mod.CelesteNet.DataTypes;
using Celeste.Mod.Helpers;
using Microsoft.Xna.Framework;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Monocle;
using MonoMod.Utils;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Net;
using System.Collections.Specialized;

namespace Celeste.Mod.CelesteNet.Client {
    // Based off of Everest's own DebugRC.
    public static class CelesteNetClientRC {

        private static readonly char[] URLArgsSeperator = new[] { '&' };

        private static HttpListener Listener;
        private static Thread ListenerThread;

        public static void Initialize() {
            if (Listener != null)
                return;

            try {
                Listener = new();
                // Port MUST be fixed as the website expects it to be the same for everyone.
                Listener.Prefixes.Add($"http://localhost:{CelesteNetUtils.ClientRCPort}/");
                Listener.Start();
            } catch (Exception e) {
                e.LogDetailed();
                try {
                    Listener?.Stop();
                } catch { }
                return;
            }

            ListenerThread = new(ListenerLoop) {
                IsBackground = true,
                Priority = ThreadPriority.BelowNormal
            };
            ListenerThread.Start();
        }

        public static void Shutdown() {
            Listener?.Abort();
            ListenerThread?.Abort();
            Listener = null;
            ListenerThread = null;
        }

        private static void ListenerLoop() {
            Logger.Log(LogLevel.INF, "rc", $"Started ClientRC thread, available via http://localhost:{CelesteNetUtils.ClientRCPort}/");
            try {
                while (Listener?.IsListening ?? false) {
                    ThreadPool.QueueUserWorkItem(c => {
                        HttpListenerContext context = c as HttpListenerContext;

                        if (context.Request.HttpMethod == "OPTIONS") {
                            context.Response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With");
                            context.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST");
                            context.Response.AddHeader("Access-Control-Max-Age", "1728000");
                            return;
                        }
                        context.Response.AppendHeader("Access-Control-Allow-Origin", "*");

                        try {
                            using (context.Request.InputStream)
                            using (context.Response) {
                                HandleRequest(context);
                            }
                        } catch (ThreadAbortException) {
                            throw;
                        } catch (ThreadInterruptedException) {
                            throw;
                        } catch (Exception e) {
                            Logger.Log(LogLevel.INF, "rc", $"ClientRC failed responding: {e}");
                        }
                    }, Listener.GetContext());
                }
            } catch (ThreadAbortException) {
                throw;
            } catch (ThreadInterruptedException) {
                throw;
            } catch (HttpListenerException e) {
                // 500 = Listener closed.
                // 995 = I/O abort due to thread abort or application shutdown.
                if (e.ErrorCode != 500 &&
                    e.ErrorCode != 995) {
                    Logger.Log(LogLevel.INF, "rc", $"ClientRC failed listening ({e.ErrorCode}): {e}");
                }
            } catch (Exception e) {
                Logger.Log(LogLevel.INF, "rc", $"ClientRC failed listening: {e}");
            }
        }

        private static void HandleRequest(HttpListenerContext c) {
            Logger.Log(LogLevel.VVV, "rc", $"Requested: {c.Request.RawUrl}");

            string url = c.Request.RawUrl;
            int indexOfSplit = url.IndexOf('?');
            if (indexOfSplit != -1)
                url = url.Substring(0, indexOfSplit);

            RCEndPoint endpoint =
                EndPoints.FirstOrDefault(ep => ep.Path == c.Request.RawUrl) ??
                EndPoints.FirstOrDefault(ep => ep.Path == url) ??
                EndPoints.FirstOrDefault(ep => ep.Path.ToLowerInvariant() == c.Request.RawUrl.ToLowerInvariant()) ??
                EndPoints.FirstOrDefault(ep => ep.Path.ToLowerInvariant() == url.ToLowerInvariant()) ??
                EndPoints.FirstOrDefault(ep => ep.Path == "/404");
            endpoint.Handle(c);
        }


        #region Read / Parse Helpers

        public static NameValueCollection ParseQueryString(string url) {
            NameValueCollection nvc = new();

            int indexOfSplit = url.IndexOf('?');
            if (indexOfSplit == -1)
                return nvc;
            url = url.Substring(indexOfSplit + 1);

            string[] args = url.Split(URLArgsSeperator);
            foreach (string arg in args) {
                indexOfSplit = arg.IndexOf('=');
                if (indexOfSplit == -1)
                    continue;
                nvc[arg.Substring(0, indexOfSplit)] = arg.Substring(indexOfSplit + 1);
            }

            return nvc;
        }

        #endregion

        #region Write Helpers

        public static void WriteHTMLStart(HttpListenerContext c, StringBuilder builder) {
            builder.Append(
@"<!DOCTYPE html>
<html>
    <head>
        <meta charset=""utf-8"" />
        <meta name=""viewport"" content=""width=device-width, initial-scale=1, user-scalable=no"" />
        <title>CelesteNet ClientRC</title>
        <style>
@font-face {
    font-family: Renogare;
    src:
    url(""https://everestapi.github.io/fonts/Renogare-Regular.woff"") format(""woff""),
    url(""https://everestapi.github.io/fonts/Renogare-Regular.otf"") format(""opentype"");
}
body {
    color: rgba(0, 0, 0, 0.87);
    font-family: sans-serif;
    padding: 0;
    margin: 0;
    line-height: 1.5em;
}
header {
    background: #3b2d4a;
    color: white;
    font-family: Renogare, sans-serif;
    font-size: 32px;
    position: sticky;
    top: 0;
    left: 0;
    right: 0;
    height: 64px;
    line-height: 64px;
    padding: 8px 48px;
    z-index: 100;
}
#main {
    position: relative;
    margin: 8px;
    min-height: 100vh;
}
#endpoints li h3 {
    margin-bottom: 0;
}
#endpoints li p {
    margin-top: 0;
}
        </style>
    </head>
    <body>
"
            );

            builder.AppendLine(@"<header>CelesteNet ClientRC</header>");
            builder.AppendLine(@"<div id=""main"">");
        }

        public static void WriteHTMLEnd(HttpListenerContext c, StringBuilder builder) {
            builder.AppendLine(@"</div>");

            builder.Append(
@"
    </body>
</html>
"
            );
        }

        public static void Write(HttpListenerContext c, string str) {
            byte[] buf = CelesteNetUtils.UTF8NoBOM.GetBytes(str);
            c.Response.ContentLength64 = buf.Length;
            c.Response.OutputStream.Write(buf, 0, buf.Length);
        }

        #endregion

        #region Default RCEndPoint Handlers

        public static List<RCEndPoint> EndPoints = new() {

                new RCEndPoint {
                    Path = "/",
                    Name = "Info",
                    InfoHTML = "Basic CelesteNet ClientRC info.",
                    Handle = c => {
                        StringBuilder builder = new();

                        WriteHTMLStart(c, builder);

                        builder.AppendLine(
@"<ul>
<h2>Info</h2>
<li>
    This weird website exists so that the main CelesteNet website can give your clients special commands.<br>
    For example, clicking on the ""send key to client"" button will send it to /setname.<br>
    It's only accessible from your local machine, meaning that nobody else can see this.
</li>
</ul>"
                        );

                        builder.AppendLine(@"<ul id=""endpoints"">");
                        builder.AppendLine(@"<h2>Endpoints</h2>");
                        foreach (RCEndPoint ep in EndPoints) {
                            builder.AppendLine(@"<li>");
                            builder.AppendLine($@"<h3>{ep.Name}</h3>");
                            builder.AppendLine($@"<p><a href=""{ep.PathExample ?? ep.Path}""><code>{ep.PathHelp ?? ep.Path}</code></a><br>{ep.InfoHTML}</p>");
                            builder.AppendLine(@"</li>");
                        }
                        builder.AppendLine(@"</ul>");

                        WriteHTMLEnd(c, builder);

                        Write(c, builder.ToString());
                    }
                },

                new RCEndPoint {
                    Path = "/404",
                    Name = "404",
                    InfoHTML = "Basic 404.",
                    Handle = c => {
                        c.Response.StatusCode = (int) HttpStatusCode.NotFound;
                        Write(c, "ERROR: Endpoint not found.");
                    }
                },

                new RCEndPoint {
                    Path = "/setkey",
                    PathHelp = "/setkey?value={key} (Example: ?value=1a2b3d4e)",
                    PathExample = "/setkey?value=Guest",
                    Name = "Set Key",
                    InfoHTML = "Set the key as the client name for the next connection.",
                    Handle = c => {
                        NameValueCollection data = ParseQueryString(c.Request.RawUrl);

                        string name = data["value"].Trim();
                        if (string.IsNullOrEmpty(name)) {
                            c.Response.StatusCode = (int) HttpStatusCode.BadRequest;
                            Write(c, $"ERROR: No value given.");
                            return;
                        }

                        CelesteNetClientModule.Settings.Name = "#" + name;
                        CelesteNetClientModule.Instance.SaveSettings();
                        Write(c, "OK");
                    }
                },

            };

        #endregion

    }
}


<|start_filename|>CelesteNet.Shared/DataTypes/DataPlayerGraphics.cs<|end_filename|>
﻿using Microsoft.Xna.Framework;
using Monocle;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Celeste.Mod.CelesteNet.DataTypes {
    public class DataPlayerGraphics : DataType<DataPlayerGraphics> {

        static DataPlayerGraphics() {
            DataID = "playerGraphics";
        }

        public override DataFlags DataFlags => DataFlags.CoreType;

        public DataPlayerInfo? Player;

        public int Depth;
        public PlayerSpriteMode SpriteMode;
        public Color SpriteColor;
        public float SpriteRate;
        public string[] SpriteAnimations = Dummy<string>.EmptyArray;

        public byte HairCount;
        public Vector2 HairStepPerSegment;
        public float HairStepInFacingPerSegment;
        public float HairStepApproach;
        public float HairStepYSinePerSegment;
        public Vector2[] HairScales = Dummy<Vector2>.EmptyArray;
        public string[] HairTextures = Dummy<string>.EmptyArray;

        public override bool FilterHandle(DataContext ctx)
            => Player != null; // Can be RECEIVED BY CLIENT TOO EARLY because UDP is UDP.

        public override MetaType[] GenerateMeta(DataContext ctx)
            => new MetaType[] {
                new MetaPlayerPublicState(Player),
                new MetaBoundRef(DataPlayerInfo.DataID, Player?.ID ?? uint.MaxValue, true)
            };

        public override void FixupMeta(DataContext ctx) {
            Player = Get<MetaPlayerPublicState>(ctx);
            Get<MetaBoundRef>(ctx).ID = Player?.ID ?? uint.MaxValue;
        }

        protected override void Read(CelesteNetBinaryReader reader) {
            Depth = reader.ReadInt32();
            SpriteMode = (PlayerSpriteMode) reader.Read7BitEncodedInt();
            SpriteColor = reader.ReadColor();
            SpriteRate = reader.ReadSingle();
            SpriteAnimations = new string[reader.Read7BitEncodedInt()];
            for (int i = 0; i < SpriteAnimations.Length; i++)
                SpriteAnimations[i] = reader.ReadNetMappedString();

            HairCount = reader.ReadByte();
            HairStepPerSegment = reader.ReadVector2();
            HairStepInFacingPerSegment = reader.ReadSingle();
            HairStepApproach = reader.ReadSingle();
            HairStepYSinePerSegment = reader.ReadSingle();
            HairScales = new Vector2[HairCount];
            for (int i = 0; i < HairCount; i++)
                HairScales[i] = reader.ReadVector2Scale();
            HairTextures = new string[HairCount];
            for (int i = 0; i < HairCount; i++)
                HairTextures[i] = reader.ReadNetMappedString();
        }

        protected override void Write(CelesteNetBinaryWriter writer) {
            writer.Write(Depth);
            writer.Write7BitEncodedInt((int) SpriteMode);
            writer.Write(SpriteColor);
            writer.Write(SpriteRate);
            writer.Write7BitEncodedInt(SpriteAnimations.Length);
            for (int i = 0; i < SpriteAnimations.Length; i++)
                writer.WriteNetMappedString(SpriteAnimations[i]);

            writer.Write(HairCount);
            writer.Write(HairStepPerSegment);
            writer.Write(HairStepInFacingPerSegment);
            writer.Write(HairStepApproach);
            writer.Write(HairStepYSinePerSegment);
            for (int i = 0; i < HairCount; i++)
                writer.Write(HairScales[i]);
            for (int i = 0; i < HairCount; i++)
                writer.WriteNetMappedString(HairTextures[i]);
        }

    }
}
