using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Net; using System.Net.Sockets; using System.Numerics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Threading.Tasks; using BabyStepsNetworking.Bandwidth; using BabyStepsNetworking.Client; using BabyStepsNetworking.Extensions; using BabyStepsNetworking.Host; using BabyStepsNetworking.Packets; using BabyStepsNetworking.Shared; using BabyStepsNetworking.Transport; using LiteNetLib; using LiteNetLib.Layers; using LiteNetLib.Utils; using Microsoft.CodeAnalysis; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("BabyStepsNetworking")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("BabyStepsNetworking")] [assembly: AssemblyTitle("BabyStepsNetworking")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } internal static class IsExternalInit { } } namespace BabyStepsNetworking.Transport { public sealed class CompositeServerTransport : IServerTransport { private sealed class Entry { public IServerTransport Transport; public int Offset; public Entry(IServerTransport t, int offset) { Transport = t; Offset = offset; } } private const int Range = 100000; private readonly Entry[] _entries; public IEnumerable ConnectedPeerIds => _entries.SelectMany((Entry e) => e.Transport.ConnectedPeerIds.Select((int id) => id + e.Offset)); public bool IsRunning => _entries.Any((Entry e) => e.Transport.IsRunning); public string TransportName => string.Join("+", _entries.Select((Entry e) => e.Transport.TransportName)); public event Action? PeerConnected; public event Action? PeerDisconnected; public event Action? PacketReceived; public CompositeServerTransport(params IServerTransport[] transports) { _entries = transports.Select((IServerTransport t, int i) => new Entry(t, i * 100000)).ToArray(); Entry[] entries = _entries; foreach (Entry entry in entries) { Entry captured = entry; entry.Transport.PeerConnected += delegate(int id) { this.PeerConnected?.Invoke(id + captured.Offset); }; entry.Transport.PeerDisconnected += delegate(int id, string r) { this.PeerDisconnected?.Invoke(id + captured.Offset, r); }; entry.Transport.PacketReceived += delegate(int id, byte[] data) { this.PacketReceived?.Invoke(id + captured.Offset, data); }; } } public void StartListening(int port, string connectionKey) { } public void Stop() { Entry[] entries = _entries; foreach (Entry entry in entries) { entry.Transport.Stop(); } } public void Poll() { Entry[] entries = _entries; foreach (Entry entry in entries) { entry.Transport.Poll(); } } public void Send(int peerId, byte[] payload, PacketDelivery delivery) { var (serverTransport, peerId2) = Resolve(peerId); serverTransport?.Send(peerId2, payload, delivery); } public void Broadcast(byte[] payload, PacketDelivery delivery, int excludePeerId = -1) { Entry[] entries = _entries; foreach (Entry entry in entries) { int excludePeerId2 = ((excludePeerId >= entry.Offset && excludePeerId < entry.Offset + 100000) ? (excludePeerId - entry.Offset) : (-1)); entry.Transport.Broadcast(payload, delivery, excludePeerId2); } } public void Kick(int peerId, string reason = "") { var (serverTransport, peerId2) = Resolve(peerId); serverTransport?.Kick(peerId2, reason); } private (IServerTransport? Transport, int LocalId) Resolve(int peerId) { Entry[] entries = _entries; foreach (Entry entry in entries) { if (peerId >= entry.Offset && peerId < entry.Offset + 100000) { return (entry.Transport, peerId - entry.Offset); } } return (null, 0); } } public interface IClientTransport { bool IsConnected { get; } string TransportName { get; } event Action? Connected; event Action? Disconnected; event Action? PacketReceived; void Connect(string address, int port, string connectionKey); void Disconnect(); void Poll(); void Send(byte[] payload, PacketDelivery delivery); } public interface IServerTransport { IEnumerable ConnectedPeerIds { get; } bool IsRunning { get; } string TransportName { get; } event Action? PeerConnected; event Action? PeerDisconnected; event Action? PacketReceived; void StartListening(int port, string connectionKey); void Stop(); void Poll(); void Send(int peerId, byte[] payload, PacketDelivery delivery); void Broadcast(byte[] payload, PacketDelivery delivery, int excludePeerId = -1); void Kick(int peerId, string reason = ""); } public enum PacketDelivery { Unreliable, Reliable, ReliableOrdered } } namespace BabyStepsNetworking.Transport.Steam { public abstract class SteamClientTransport : IClientTransport { public abstract bool IsConnected { get; } public string TransportName => "Steam P2P"; public abstract event Action? Connected; public abstract event Action? Disconnected; public abstract event Action? PacketReceived; public abstract void Connect(string address, int port, string connectionKey); public abstract void Disconnect(); public abstract void Poll(); public abstract void Send(byte[] payload, PacketDelivery delivery); } public abstract class SteamServerTransport : IServerTransport { public abstract IEnumerable ConnectedPeerIds { get; } public abstract bool IsRunning { get; } public string TransportName => "Steam P2P"; public abstract event Action? PeerConnected; public abstract event Action? PeerDisconnected; public abstract event Action? PacketReceived; public event Action? LobbyCreated; public abstract void StartListening(int port, string connectionKey); public abstract void Stop(); public abstract void Poll(); public abstract void Send(int peerId, byte[] payload, PacketDelivery delivery); public abstract void Broadcast(byte[] payload, PacketDelivery delivery, int excludePeerId = -1); public abstract void Kick(int peerId, string reason = ""); protected void OnLobbyCreated(string lobbyId) { this.LobbyCreated?.Invoke(lobbyId); } } } namespace BabyStepsNetworking.Transport.LocalLoopback { public static class LocalLoopbackTransportPair { public static (LocalLoopbackServerTransport Server, LocalLoopbackClientTransport Client) Create() { Queue queue = new Queue(); Queue queue2 = new Queue(); LocalLoopbackServerTransport item = new LocalLoopbackServerTransport(queue2, queue); LocalLoopbackClientTransport item2 = new LocalLoopbackClientTransport(queue, queue2); return (item, item2); } } public sealed class LocalLoopbackServerTransport : IServerTransport { private readonly Queue _incoming; private readonly Queue _outgoing; private bool _clientConnected = false; private bool _running = false; public IEnumerable ConnectedPeerIds => _clientConnected ? ((IEnumerable)new int[1]) : ((IEnumerable)Array.Empty()); public bool IsRunning => _running; public string TransportName => "Loopback"; public event Action? PeerConnected; public event Action? PeerDisconnected; public event Action? PacketReceived; internal LocalLoopbackServerTransport(Queue incoming, Queue outgoing) { _incoming = incoming; _outgoing = outgoing; } public void StartListening(int port, string connectionKey) { _running = true; } public void Stop() { if (_running) { _running = false; if (_clientConnected) { _clientConnected = false; this.PeerDisconnected?.Invoke(0, "Server stopped"); } } } public void Poll() { if (!_running) { return; } if (!_clientConnected && _incoming.Count > 0 && _incoming.Peek() == null) { _incoming.Dequeue(); _clientConnected = true; this.PeerConnected?.Invoke(0); return; } while (_clientConnected && _incoming.Count > 0) { byte[] array = _incoming.Dequeue(); if (array == null) { _clientConnected = false; this.PeerDisconnected?.Invoke(0, "Client disconnected"); break; } this.PacketReceived?.Invoke(0, array); } } public void Send(int peerId, byte[] payload, PacketDelivery delivery) { if (_clientConnected && peerId == 0) { _outgoing.Enqueue(payload); } } public void Broadcast(byte[] payload, PacketDelivery delivery, int excludePeerId = -1) { if (_clientConnected && excludePeerId != 0) { _outgoing.Enqueue(payload); } } public void Kick(int peerId, string reason = "") { if (_clientConnected && peerId == 0) { _outgoing.Enqueue(null); _clientConnected = false; this.PeerDisconnected?.Invoke(0, reason); } } internal void SignalClientConnect() { _incoming.Enqueue(null); } internal void SignalClientDisconnect() { _incoming.Enqueue(null); } } public sealed class LocalLoopbackClientTransport : IClientTransport { private readonly Queue _incoming; private readonly Queue _outgoing; internal LocalLoopbackServerTransport? ServerSide; private bool _connected = false; public bool IsConnected => _connected; public string TransportName => "Loopback"; public event Action? Connected; public event Action? Disconnected; public event Action? PacketReceived; internal LocalLoopbackClientTransport(Queue incoming, Queue outgoing) { _incoming = incoming; _outgoing = outgoing; } public void Connect(string address, int port, string connectionKey) { if (!_connected) { _connected = true; _outgoing.Enqueue(null); this.Connected?.Invoke(); } } public void Disconnect() { if (_connected) { _connected = false; _outgoing.Enqueue(null); this.Disconnected?.Invoke("Disconnected"); } } public void Poll() { if (!_connected) { return; } while (_incoming.Count > 0) { byte[] array = _incoming.Dequeue(); if (array == null) { _connected = false; this.Disconnected?.Invoke("Server disconnected"); break; } this.PacketReceived?.Invoke(array); } } public void Send(byte[] payload, PacketDelivery delivery) { if (_connected) { _outgoing.Enqueue(payload); } } } } namespace BabyStepsNetworking.Transport.LiteNetLib { public sealed class LiteNetLibClientTransport : IClientTransport, INetEventListener { private NetManager? _netManager; private NetPeer? _server; private readonly NetDataWriter _writer = new NetDataWriter(); public bool IsConnected => _server != null; public string TransportName => "LiteNetLib"; public event Action? Connected; public event Action? Disconnected; public event Action? PacketReceived; public void Connect(string address, int port, string connectionKey) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected O, but got Unknown NetManager? netManager = _netManager; if (netManager != null) { netManager.Stop(); } _netManager = new NetManager((INetEventListener)(object)this, (PacketLayerBase)null) { AutoRecycle = true, DisconnectTimeout = 15000 }; _netManager.Start(); _netManager.Connect(address, port, connectionKey); } public void Disconnect() { NetManager? netManager = _netManager; if (netManager != null) { netManager.Stop(); } _netManager = null; _server = null; } public void Poll() { if (_netManager != null && !_netManager.UnsyncedEvents) { _netManager.PollEvents(0); } } public void Send(byte[] payload, PacketDelivery delivery) { //IL_005b: Unknown result type (might be due to invalid IL or missing references) if (_server == null) { return; } lock (_writer) { _writer.Reset(); _writer.Put((ushort)(payload.Length + 2)); _writer.Put(payload); _server.Send(_writer, ToLnl(delivery)); } } void INetEventListener.OnPeerConnected(NetPeer peer) { _server = peer; this.Connected?.Invoke(); } void INetEventListener.OnPeerDisconnected(NetPeer peer, DisconnectInfo info) { _server = null; this.Disconnected?.Invoke(((object)(DisconnectReason)(ref info.Reason)).ToString()); } void INetEventListener.OnConnectionRequest(ConnectionRequest request) { request.AcceptIfKey("cuzzillobochfoddy"); } void INetEventListener.OnNetworkReceive(NetPeer peer, NetPacketReader reader, byte channelNumber, DeliveryMethod deliveryMethod) { ReadPacket(reader); } void INetEventListener.OnNetworkReceiveUnconnected(IPEndPoint ep, NetPacketReader reader, UnconnectedMessageType type) { ReadPacket(reader); } private void ReadPacket(NetPacketReader reader) { byte[] remainingBytes = ((NetDataReader)reader).GetRemainingBytes(); reader.Recycle(); if (remainingBytes.Length >= 2) { ushort num = BitConverter.ToUInt16(remainingBytes, 0); if (num == remainingBytes.Length) { byte[] array = new byte[remainingBytes.Length - 2]; Buffer.BlockCopy(remainingBytes, 2, array, 0, array.Length); this.PacketReceived?.Invoke(array); } } } void INetEventListener.OnNetworkError(IPEndPoint ep, SocketError err) { } void INetEventListener.OnNetworkLatencyUpdate(NetPeer peer, int latency) { } private static DeliveryMethod ToLnl(PacketDelivery d) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) if (1 == 0) { } DeliveryMethod result = ((d != PacketDelivery.ReliableOrdered) ? ((DeliveryMethod)0) : ((DeliveryMethod)2)); if (1 == 0) { } return result; } } public sealed class LiteNetLibServerTransport : IServerTransport, INetEventListener { private readonly NetManager _netManager; private readonly NetDataWriter _writer = new NetDataWriter(); private string _connectionKey = string.Empty; private readonly Dictionary _peers = new Dictionary(); public IEnumerable ConnectedPeerIds => _peers.Keys; public bool IsRunning => _netManager.IsRunning; public string TransportName => "LiteNetLib"; public event Action? PeerConnected; public event Action? PeerDisconnected; public event Action? PacketReceived; public LiteNetLibServerTransport() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Expected O, but got Unknown _netManager = new NetManager((INetEventListener)(object)this, (PacketLayerBase)null) { AutoRecycle = true, IPv6Enabled = false, DisconnectTimeout = 15000 }; } public void StartListening(int port, string connectionKey) { _connectionKey = connectionKey; _netManager.Start(port); } public void Stop() { _netManager.Stop(); } public void Poll() { _netManager.PollEvents(0); } public void Send(int peerId, byte[] payload, PacketDelivery delivery) { //IL_005e: Unknown result type (might be due to invalid IL or missing references) if (!_peers.TryGetValue(peerId, out NetPeer value)) { return; } lock (_writer) { _writer.Reset(); _writer.Put((ushort)(payload.Length + 2)); _writer.Put(payload); value.Send(_writer, ToLnl(delivery)); } } public void Broadcast(byte[] payload, PacketDelivery delivery, int excludePeerId = -1) { //IL_0080: Unknown result type (might be due to invalid IL or missing references) lock (_writer) { _writer.Reset(); _writer.Put((ushort)(payload.Length + 2)); _writer.Put(payload); foreach (var (num2, val2) in _peers) { if (num2 != excludePeerId) { val2.Send(_writer, ToLnl(delivery)); } } } } public void Kick(int peerId, string reason = "") { if (_peers.TryGetValue(peerId, out NetPeer value)) { value.Disconnect(); } } void INetEventListener.OnConnectionRequest(ConnectionRequest request) { string text = default(string); if (request.Data.TryGetString(ref text) && text == _connectionKey) { request.Accept(); } else { request.Reject(); } } void INetEventListener.OnPeerConnected(NetPeer peer) { _peers[peer.Id] = peer; this.PeerConnected?.Invoke(peer.Id); } void INetEventListener.OnPeerDisconnected(NetPeer peer, DisconnectInfo info) { _peers.Remove(peer.Id); this.PeerDisconnected?.Invoke(peer.Id, ((object)(DisconnectReason)(ref info.Reason)).ToString()); } void INetEventListener.OnNetworkReceive(NetPeer peer, NetPacketReader reader, byte channelNumber, DeliveryMethod deliveryMethod) { byte[] remainingBytes = ((NetDataReader)reader).GetRemainingBytes(); reader.Recycle(); if (remainingBytes.Length >= 2) { ushort num = BitConverter.ToUInt16(remainingBytes, 0); if (num == remainingBytes.Length) { byte[] array = new byte[remainingBytes.Length - 2]; Buffer.BlockCopy(remainingBytes, 2, array, 0, array.Length); this.PacketReceived?.Invoke(peer.Id, array); } } } void INetEventListener.OnNetworkError(IPEndPoint ep, SocketError err) { } void INetEventListener.OnNetworkLatencyUpdate(NetPeer peer, int latency) { } void INetEventListener.OnNetworkReceiveUnconnected(IPEndPoint ep, NetPacketReader reader, UnconnectedMessageType type) { reader.Recycle(); } private static DeliveryMethod ToLnl(PacketDelivery d) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) if (1 == 0) { } DeliveryMethod result = ((d != PacketDelivery.ReliableOrdered) ? ((DeliveryMethod)0) : ((DeliveryMethod)2)); if (1 == 0) { } return result; } } } namespace BabyStepsNetworking.Shared { public struct RGBColor { public byte R; public byte G; public byte B; public RGBColor(byte r, byte g, byte b) { R = r; G = g; B = b; } public string GetString() { return $"[{R},{G},{B}]"; } } } namespace BabyStepsNetworking.ServerBrowser { public interface IServerBrowserSource { string SourceName { get; } event Action>? ServersFound; void Refresh(); } public sealed class LanServerBrowserSource : IServerBrowserSource { public string SourceName => "LAN"; public event Action>? ServersFound; public void Refresh() { this.ServersFound?.Invoke(Array.Empty()); } } public enum ServerType { LiteNetLib, SteamP2P } public sealed class ServerInfo { public string Name { get; set; } = string.Empty; public string Address { get; set; } = string.Empty; public int Port { get; set; } public int PlayerCount { get; set; } public int MaxPlayers { get; set; } = 255; public bool IsPasswordProtected { get; set; } public ServerType Type { get; set; } = ServerType.LiteNetLib; public int PingMs { get; set; } = -1; public string? SessionId { get; set; } public override string ToString() { return $"[{Type}] {Name} ({Address}:{Port}) {PlayerCount}/{MaxPlayers} {PingMs}ms"; } } public abstract class SteamLobbyBrowserSource : IServerBrowserSource { public string SourceName => "Steam Lobbies"; public abstract event Action>? ServersFound; public abstract void Refresh(); } } namespace BabyStepsNetworking.Packets { public sealed class ClientPacketRegistry { public delegate void ClientPacketHandler(byte[] data, NetworkClient client); private const byte ModRangeStart = 65; private const byte ModRangeEnd = 254; private readonly Dictionary _handlers = new Dictionary(); private byte _nextModOpcode = 65; public void RegisterCoreHandler(byte opcode, ClientPacketHandler handler) { _handlers[opcode] = handler; } public void RegisterCoreHandler(CoreServerToClientOpcode opcode, ClientPacketHandler handler) { RegisterCoreHandler((byte)opcode, handler); } public byte AllocateModOpcode(ClientPacketHandler handler) { if (_nextModOpcode > 254) { return 0; } byte b = _nextModOpcode++; _handlers[b] = handler; return b; } public void Dispatch(byte opcode, byte[] data, NetworkClient client) { if (_handlers.TryGetValue(opcode, out ClientPacketHandler value)) { value(data, client); } } } public enum CoreClientToServerOpcode : byte { BoneUpdate = 1, PlayerInfo = 2, WorldEvent = 3, AccessoryAdd = 4, AccessoryRemove = 5, JiminyRibbon = 6, CollisionToggle = 7, ChatMessage = 8, AudioFrame = 9, Ping = 10, RequestState = 11, ModRangeStart = 65, ModRangeEnd = 254 } public enum CoreServerToClientOpcode : byte { AssignUUID = 1, PlayerJoined = 2, PlayerLeft = 3, PlayerInfoUpdate = 4, BoneUpdate = 5, WorldEvent = 6, AccessoryAdd = 7, AccessoryRemove = 8, JiminyRibbon = 9, CollisionToggle = 10, ChatMessage = 11, AudioFrame = 12, ModRangeStart = 65, ModRangeEnd = 254 } public static class PacketBuilder { public static byte[] Build(byte opcode) { return new byte[1] { opcode }; } public static byte[] Build(byte opcode, byte[] payload) { byte[] array = new byte[1 + payload.Length]; array[0] = opcode; Buffer.BlockCopy(payload, 0, array, 1, payload.Length); return array; } public static byte[] Build(byte opcode, Action> buildPayload) { List list = new List { opcode }; buildPayload(list); return list.ToArray(); } public static byte[] Build(CoreServerToClientOpcode opcode) { return Build((byte)opcode); } public static byte[] Build(CoreServerToClientOpcode opcode, byte[] payload) { return Build((byte)opcode, payload); } public static byte[] Build(CoreServerToClientOpcode opcode, Action> buildPayload) { return Build((byte)opcode, buildPayload); } public static byte[] Build(CoreClientToServerOpcode opcode) { return Build((byte)opcode); } public static byte[] Build(CoreClientToServerOpcode opcode, byte[] payload) { return Build((byte)opcode, payload); } public static byte[] Build(CoreClientToServerOpcode opcode, Action> buildPayload) { return Build((byte)opcode, buildPayload); } public static byte[] PrependUuid(byte uuid, byte[] payload) { byte[] array = new byte[1 + payload.Length]; array[0] = uuid; Buffer.BlockCopy(payload, 0, array, 1, payload.Length); return array; } } public static class PacketReader { public static bool TryRead(byte[] raw, out byte opcode, out byte[] payload) { if (raw.Length < 1) { opcode = 0; payload = Array.Empty(); return false; } opcode = raw[0]; payload = new byte[raw.Length - 1]; if (payload.Length != 0) { Buffer.BlockCopy(raw, 1, payload, 0, payload.Length); } return true; } public static ushort ReadU16(byte[] data, int offset) { return BitConverter.ToUInt16(data, offset); } public static long ReadI64(byte[] data, int offset) { return BitConverter.ToInt64(data, offset); } } public sealed class ServerPacketRegistry { public delegate void ServerPacketHandler(ConnectedClient sender, byte[] data, NetworkHost host); private const byte ModRangeStart = 65; private const byte ModRangeEnd = 254; private readonly Dictionary _handlers = new Dictionary(); private byte _nextModOpcode = 65; public void RegisterCoreHandler(byte opcode, ServerPacketHandler handler) { _handlers[opcode] = handler; } public void RegisterCoreHandler(CoreClientToServerOpcode opcode, ServerPacketHandler handler) { RegisterCoreHandler((byte)opcode, handler); } public byte AllocateModOpcode(ServerPacketHandler handler) { if (_nextModOpcode > 254) { return 0; } byte b = _nextModOpcode++; _handlers[b] = handler; return b; } public void Dispatch(ConnectedClient sender, byte opcode, byte[] data, NetworkHost host) { ConnectedClient sender2 = sender; byte[] data2 = data; if (_handlers.TryGetValue(opcode, out ServerPacketHandler value)) { value(sender2, data2, host); } else if (opcode >= 65 && opcode <= 254) { byte[] packet = PacketBuilder.Build(opcode, delegate(List bytes) { bytes.Add(sender2.Uuid); bytes.AddRange(data2); }); host.Broadcast(packet, PacketDelivery.Reliable, sender2.PeerId); } } } } namespace BabyStepsNetworking.Host { public sealed class ConnectedClient { public int PeerId { get; init; } public byte Uuid { get; init; } public string? DisplayName { get; set; } public RGBColor? Color { get; set; } public bool CollisionsEnabled { get; set; } = false; public bool JiminyState { get; set; } = false; public byte LastBoneKickoffPoint { get; set; } = 0; public byte[]? LatestRawBoneData { get; set; } public Vector3 Position { get; set; } public List? DistantClientPeerIds { get; set; } public Dictionary LastTransmitTimes { get; } = new Dictionary(); public byte[]? InfoPacket { get; set; } public Dictionary SavedPackets { get; } = new Dictionary(); public bool IsInitialized => DisplayName != null && Color.HasValue; } public class HostSettings { public int Port { get; set; } = 7777; public string Password { get; set; } = "cuzzillobochfoddy"; public float DistanceCutoff { get; set; } = 10f; public float OuterDistanceCutoff { get; set; } = 500f; public float CullIntervalMs { get; set; } = 1000f; public float MaxBandwidthKbps { get; set; } = 512f; public bool TelemetryEnabled { get; set; } = false; public float TelemetryIntervalMs { get; set; } = 5000f; public bool VoiceChatEnabled { get; set; } = true; public string ServerVersion { get; set; } = "106"; public string ConnectionKey => ServerVersion + Password; } public sealed class NetworkHost { private readonly IServerTransport _transport; private readonly HostSettings _settings; private readonly ServerPacketRegistry _registry; private readonly BandwidthManager _bandwidth; private readonly Dictionary _peerToClient = new Dictionary(); private readonly Dictionary _uuidToClient = new Dictionary(); private readonly HashSet _usedUuids = new HashSet(); private readonly Dictionary _lastSeenSequence = new Dictionary(); private const byte MaxUuid = 254; private readonly Stopwatch _uptime = new Stopwatch(); private volatile bool _isCulling; private readonly object _cullLock = new object(); private float _timeSinceCullMs; private readonly int _targetFps; public long UptimeMs => _uptime.ElapsedMilliseconds; public bool IsRunning => _transport.IsRunning; public IReadOnlyDictionary Clients => _uuidToClient; public event Action? ClientConnected; public event Action? ClientDisconnected; public event Action? Log; public NetworkHost(IServerTransport transport, HostSettings settings, int targetFps = 60) { _transport = transport; _settings = settings; _targetFps = targetFps; _registry = new ServerPacketRegistry(); _bandwidth = new BandwidthManager(transport, settings, targetFps, _peerToClient); transport.PeerConnected += OnPeerConnected; transport.PeerDisconnected += OnPeerDisconnected; transport.PacketReceived += OnPacketReceived; } public void RegisterHandler(byte opcode, ServerPacketRegistry.ServerPacketHandler handler) { _registry.RegisterCoreHandler(opcode, handler); } public void RegisterHandler(CoreClientToServerOpcode opcode, ServerPacketRegistry.ServerPacketHandler handler) { _registry.RegisterCoreHandler(opcode, handler); } public byte AllocateModOpcode(ServerPacketRegistry.ServerPacketHandler handler) { return _registry.AllocateModOpcode(handler); } public void Start() { _transport.StartListening(_settings.Port, _settings.ConnectionKey); _uptime.Restart(); this.Log?.Invoke($"Server listening on port {_settings.Port} [{_transport.TransportName}]"); } public void StartWithoutListening() { _uptime.Restart(); this.Log?.Invoke("Server started [" + _transport.TransportName + "]"); } public void Stop() { _transport.Stop(); } public void Tick(float deltaMs) { _transport.Poll(); _bandwidth.ProcessQueues(); _timeSinceCullMs += deltaMs; if (_timeSinceCullMs >= _settings.CullIntervalMs && !_isCulling) { _timeSinceCullMs = 0f; _isCulling = true; Task.Run((Action)CullDistantClients); } } public void Send(int peerId, byte[] packet, PacketDelivery delivery) { _transport.Send(peerId, packet, delivery); } public void Broadcast(byte[] packet, PacketDelivery delivery, int excludePeerId = -1) { _transport.Broadcast(packet, delivery, excludePeerId); } public void EnqueueBoneUpdate(int senderPeerId, byte[] packet) { _bandwidth.EnqueueBoneUpdate(senderPeerId, packet); } public void EnqueueAudio(int senderPeerId, byte[] packet) { _bandwidth.EnqueueAudioFrame(senderPeerId, packet); } public void EnqueueHighPriority(int senderPeerId, byte[] packet) { _bandwidth.EnqueueHighPriority(senderPeerId, packet); } public void BroadcastModPacket(byte opcode, byte senderUuid, byte[] payload, PacketDelivery delivery, int excludePeerId = -1) { byte[] payload2 = payload; byte[] payload3 = PacketBuilder.Build(opcode, delegate(List bytes) { bytes.Add(senderUuid); bytes.AddRange(payload2); }); _transport.Broadcast(payload3, delivery, excludePeerId); } public bool IsNewerSequence(byte uuid, ushort seq) { if (!_lastSeenSequence.TryGetValue(uuid, out var value) || IsNewer(seq, value)) { _lastSeenSequence[uuid] = seq; return true; } return false; } private static bool IsNewer(ushort current, ushort previous) { return (ushort)(current - previous) < 32768; } private void OnPeerConnected(int peerId) { byte uuid = AllocateUuid(); if (uuid == byte.MaxValue) { _transport.Kick(peerId, "Server full"); this.Log?.Invoke("Client rejected: server full"); return; } ConnectedClient connectedClient = new ConnectedClient { PeerId = peerId, Uuid = uuid }; _peerToClient[peerId] = connectedClient; _uuidToClient[uuid] = connectedClient; byte[] payload = PacketBuilder.Build(CoreServerToClientOpcode.AssignUUID, delegate(List bytes) { bytes.Add(uuid); bytes.AddRange(BitConverter.GetBytes(UptimeMs)); }); _transport.Send(peerId, payload, PacketDelivery.ReliableOrdered); foreach (ConnectedClient value in _uuidToClient.Values) { if (value.PeerId == peerId || value.InfoPacket == null) { continue; } _transport.Send(peerId, PacketBuilder.Build(CoreServerToClientOpcode.PlayerJoined, new byte[1] { value.Uuid }), PacketDelivery.ReliableOrdered); _transport.Send(peerId, value.InfoPacket, PacketDelivery.ReliableOrdered); foreach (byte[] value2 in value.SavedPackets.Values) { if (value2 != null) { _transport.Send(peerId, value2, PacketDelivery.ReliableOrdered); } } } this.ClientConnected?.Invoke(connectedClient); } private void OnPeerDisconnected(int peerId, string reason) { if (_peerToClient.TryGetValue(peerId, out ConnectedClient value)) { _peerToClient.Remove(peerId); _uuidToClient.Remove(value.Uuid); _lastSeenSequence.Remove(value.Uuid); ReclaimUuid(value.Uuid); byte[] payload = PacketBuilder.Build(CoreServerToClientOpcode.PlayerLeft, new byte[1] { value.Uuid }); _transport.Broadcast(payload, PacketDelivery.ReliableOrdered, peerId); this.Log?.Invoke(string.Format("Player {0}[{1}] disconnected: {2}", value.DisplayName ?? "?", value.Uuid, reason)); this.ClientDisconnected?.Invoke(value); } } private void OnPacketReceived(int peerId, byte[] payload) { if (_peerToClient.TryGetValue(peerId, out ConnectedClient value) && payload.Length >= 1) { byte opcode = payload[0]; byte[] subArray = payload[1..]; _registry.Dispatch(value, opcode, subArray, this); } } private byte AllocateUuid() { for (byte b = 0; b <= 254; b++) { if (!_usedUuids.Contains(b)) { _usedUuids.Add(b); return b; } } this.Log?.Invoke("Maximum number of clients reached!"); return byte.MaxValue; } private void ReclaimUuid(byte uuid) { _usedUuids.Remove(uuid); } private void CullDistantClients() { lock (_cullLock) { foreach (ConnectedClient value in _peerToClient.Values) { byte[] latestRawBoneData = value.LatestRawBoneData; if (latestRawBoneData != null && latestRawBoneData.Length >= 12) { float z = BitConverter.ToSingle(latestRawBoneData, 8); value.Position = new Vector3(0f, 0f, z); } } foreach (ConnectedClient value2 in _peerToClient.Values) { List list = new List(); if (value2.LatestRawBoneData == null) { value2.DistantClientPeerIds = list; continue; } foreach (ConnectedClient value3 in _peerToClient.Values) { if (value2.PeerId != value3.PeerId && value3.LatestRawBoneData != null) { float num = Math.Abs(value2.Position.Z - value3.Position.Z); if (num > _settings.DistanceCutoff) { list.Add(value3.PeerId); } } } value2.DistantClientPeerIds = list; } } _isCulling = false; } } } namespace BabyStepsNetworking.Extensions { public interface IModChannel { string ModId { get; } byte Opcode { get; } event Action? DataReceived; void Send(byte[] payload, PacketDelivery delivery = PacketDelivery.Reliable); } public interface INetworkExtension { string ExtensionId { get; } void Initialize(NetworkExtensionContext ctx); } public sealed class NetworkExtensionContext { private readonly NetworkClient _client; public NetworkHost? Host { get; } public bool IsHosting => Host != null; internal NetworkExtensionContext(NetworkClient client, NetworkHost? host) { _client = client; Host = host; } public IModChannel? CreateChannel() { return _client.CreateModChannel("ext"); } } } namespace BabyStepsNetworking.Client { internal sealed class ModChannel : IModChannel { private readonly NetworkClient _client; public string ModId { get; } public byte Opcode { get; internal set; } public event Action? DataReceived; internal ModChannel(string modId, NetworkClient client) { ModId = modId; _client = client; } public void Send(byte[] payload, PacketDelivery delivery = PacketDelivery.Reliable) { _client.Send(PacketBuilder.Build(Opcode, payload), delivery); } internal void DispatchReceived(byte[] data) { if (data.Length >= 1) { byte arg = data[0]; byte[] array = new byte[data.Length - 1]; if (array.Length != 0) { Buffer.BlockCopy(data, 1, array, 0, array.Length); } this.DataReceived?.Invoke(arg, array); } } } public sealed class NetworkClient { private readonly IClientTransport _transport; private readonly ClientPacketRegistry _registry; public bool IsConnected => _transport.IsConnected; public string TransportName => _transport.TransportName; public IClientTransport Transport => _transport; public event Action? Connected; public event Action? Disconnected; public NetworkClient(IClientTransport transport) { _transport = transport; _registry = new ClientPacketRegistry(); transport.Connected += delegate { this.Connected?.Invoke(); }; transport.Disconnected += delegate(string reason) { this.Disconnected?.Invoke(reason); }; transport.PacketReceived += OnPacketReceived; } public void RegisterHandler(byte opcode, ClientPacketRegistry.ClientPacketHandler handler) { _registry.RegisterCoreHandler(opcode, handler); } public void RegisterHandler(CoreServerToClientOpcode opcode, ClientPacketRegistry.ClientPacketHandler handler) { _registry.RegisterCoreHandler(opcode, handler); } public void Connect(string address, int port, string connectionKey) { _transport.Connect(address, port, connectionKey); } public void Disconnect() { _transport.Disconnect(); } public void Tick() { _transport.Poll(); } public void Send(byte[] packet, PacketDelivery delivery) { _transport.Send(packet, delivery); } public void Send(byte opcode, byte[] payload, PacketDelivery delivery = PacketDelivery.Reliable) { _transport.Send(PacketBuilder.Build(opcode, payload), delivery); } public void Send(CoreClientToServerOpcode opcode, byte[] payload, PacketDelivery delivery = PacketDelivery.Reliable) { Send((byte)opcode, payload, delivery); } public IModChannel? CreateModChannel(string modId) { ModChannel channel = new ModChannel(modId, this); byte b = _registry.AllocateModOpcode(delegate(byte[] data, NetworkClient _) { channel.DispatchReceived(data); }); if (b == 0) { return null; } channel.Opcode = b; return channel; } public void RegisterExtension(INetworkExtension extension, NetworkHost? host = null) { NetworkExtensionContext ctx = new NetworkExtensionContext(this, host); extension.Initialize(ctx); } private void OnPacketReceived(byte[] payload) { if (payload.Length >= 1) { byte opcode = payload[0]; byte[] array = new byte[payload.Length - 1]; if (array.Length != 0) { Buffer.BlockCopy(payload, 1, array, 0, array.Length); } _registry.Dispatch(opcode, array, this); } } internal byte AllocateModOpcode(ClientPacketRegistry.ClientPacketHandler handler) { return _registry.AllocateModOpcode(handler); } } } namespace BabyStepsNetworking.Bandwidth { public sealed class BandwidthManager { private enum PacketPriority { High, Medium, Low } private sealed class QueuedPacket { public int FromPeerId; public byte[] Payload = Array.Empty(); public PacketPriority Priority; public long QueuedTick; } private const float AUDIO_MAX_DISTANCE = 60f; private readonly Queue _highQueue = new Queue(); private readonly Queue _mediumQueue = new Queue(); private readonly Queue _lowQueue = new Queue(); private readonly IServerTransport _transport; private readonly HostSettings _settings; private readonly int _targetFps; private readonly Dictionary _clients; private long _bytesSentThisInterval; private long _lastTelemetryTick; private readonly Stopwatch _telemetryWatch = Stopwatch.StartNew(); public int TotalQueued => _highQueue.Count + _mediumQueue.Count + _lowQueue.Count; public BandwidthManager(IServerTransport transport, HostSettings settings, int targetFps, Dictionary clients) { _transport = transport; _settings = settings; _targetFps = targetFps; _clients = clients; } public void EnqueueBoneUpdate(int fromPeerId, byte[] payload) { _mediumQueue.Enqueue(new QueuedPacket { FromPeerId = fromPeerId, Payload = payload, Priority = PacketPriority.Medium, QueuedTick = Stopwatch.GetTimestamp() }); } public void EnqueueAudioFrame(int fromPeerId, byte[] payload) { _lowQueue.Enqueue(new QueuedPacket { FromPeerId = fromPeerId, Payload = payload, Priority = PacketPriority.Low, QueuedTick = Stopwatch.GetTimestamp() }); } public void EnqueueHighPriority(int fromPeerId, byte[] payload) { _highQueue.Enqueue(new QueuedPacket { FromPeerId = fromPeerId, Payload = payload, Priority = PacketPriority.High, QueuedTick = Stopwatch.GetTimestamp() }); } public void ProcessQueues() { float num = _settings.MaxBandwidthKbps * 1024f / (float)_targetFps; int num2 = (int)num; long timestamp = Stopwatch.GetTimestamp(); double msPerTick = 1000.0 / (double)Stopwatch.Frequency; float updateIntervalMs = 1000f / (float)_targetFps; int num3 = 0; while (_highQueue.Count > 0) { QueuedPacket queuedPacket = _highQueue.Dequeue(); if (_clients.ContainsKey(queuedPacket.FromPeerId)) { num3 += BroadcastToAll(queuedPacket, PacketDelivery.ReliableOrdered); } } while (_mediumQueue.Count > 0 && num3 < num2) { QueuedPacket queuedPacket2 = _mediumQueue.Dequeue(); if (_clients.ContainsKey(queuedPacket2.FromPeerId)) { num3 += BroadcastBoneUpdate(queuedPacket2, timestamp, msPerTick, updateIntervalMs); } } while (_lowQueue.Count > 0 && num3 < num2) { QueuedPacket queuedPacket3 = _lowQueue.Dequeue(); if (_clients.ContainsKey(queuedPacket3.FromPeerId)) { num3 += BroadcastAudioFrame(queuedPacket3); } } _bytesSentThisInterval += num3; EmitTelemetry(); } private int BroadcastToAll(QueuedPacket pkt, PacketDelivery delivery) { int num = 0; foreach (var (num3, _) in _clients) { if (num3 != pkt.FromPeerId) { _transport.Send(num3, pkt.Payload, delivery); num += pkt.Payload.Length; } } return num; } private int BroadcastBoneUpdate(QueuedPacket pkt, long now, double msPerTick, float updateIntervalMs) { if (!_clients.TryGetValue(pkt.FromPeerId, out ConnectedClient value)) { return 0; } int num = 0; foreach (var (num3, connectedClient2) in _clients) { if (num3 == pkt.FromPeerId) { continue; } float num4 = 1f; List? distantClientPeerIds = value.DistantClientPeerIds; if (distantClientPeerIds != null && distantClientPeerIds.Contains(num3)) { float num5 = Math.Abs(value.Position.Z - connectedClient2.Position.Z); if (num5 >= _settings.DistanceCutoff) { float num6 = Math.Clamp((num5 - _settings.DistanceCutoff) / (_settings.OuterDistanceCutoff - _settings.DistanceCutoff), 0f, 1f); float num7 = updateIntervalMs / 2000f; num4 = (float)Math.Exp((double)num6 * Math.Log(num7)); } } float num8 = updateIntervalMs / num4; if (!value.LastTransmitTimes.TryGetValue(num3, out var value2)) { value2 = 0L; } double num9 = (double)(now - value2) * msPerTick; if (num9 >= (double)num8) { _transport.Send(num3, pkt.Payload, PacketDelivery.Unreliable); value.LastTransmitTimes[num3] = now; num += pkt.Payload.Length; } } return num; } private int BroadcastAudioFrame(QueuedPacket pkt) { if (!_clients.TryGetValue(pkt.FromPeerId, out ConnectedClient value)) { return 0; } int num = 0; foreach (var (num3, connectedClient2) in _clients) { if (num3 != pkt.FromPeerId && Vector3.Distance(value.Position, connectedClient2.Position) <= 60f) { _transport.Send(num3, pkt.Payload, PacketDelivery.Unreliable); num += pkt.Payload.Length; } } return num; } private void EmitTelemetry() { if (_settings.TelemetryEnabled) { long num = _telemetryWatch.ElapsedMilliseconds - _lastTelemetryTick; if (!((float)num < _settings.TelemetryIntervalMs)) { float num2 = (float)_bytesSentThisInterval / 1024f / ((float)num / 1000f); float num3 = num2 / _settings.MaxBandwidthKbps * 100f; Console.WriteLine($"[BWM] {num2:F2} KB/s ({num3:F1}%) | Q: Hi={_highQueue.Count} Med={_mediumQueue.Count} Lo={_lowQueue.Count}"); _bytesSentThisInterval = 0L; _lastTelemetryTick = _telemetryWatch.ElapsedMilliseconds; } } } } }