diff --git a/.github/workflows/nuget.yml b/.github/workflows/nuget.yml index 7e6cec86..950cb644 100644 --- a/.github/workflows/nuget.yml +++ b/.github/workflows/nuget.yml @@ -4,7 +4,9 @@ on: branches: - master - dev - - dev-1.35 + - dev-1.35.0 + - dev-1.35.0-Restructured + jobs: nuget-1: diff --git a/BeatTogether.DedicatedServer.Ignorance/BeatTogether.DedicatedServer.Ignorance.csproj b/BeatTogether.DedicatedServer.Ignorance/BeatTogether.DedicatedServer.Ignorance.csproj new file mode 100644 index 00000000..4b847eb4 --- /dev/null +++ b/BeatTogether.DedicatedServer.Ignorance/BeatTogether.DedicatedServer.Ignorance.csproj @@ -0,0 +1,22 @@ + + + + net6.0 + enable + enable + + + + + Always + + + Always + + + + + + + + diff --git a/BeatTogether.DedicatedServer.Ignorance/ENet/ENet.cs b/BeatTogether.DedicatedServer.Ignorance/ENet/ENet.cs new file mode 100644 index 00000000..3be96392 --- /dev/null +++ b/BeatTogether.DedicatedServer.Ignorance/ENet/ENet.cs @@ -0,0 +1,1415 @@ +/* + * Managed C# wrapper for an extended version of ENet + * This is a fork from upstream and is available at http://github.com/SoftwareGuy/ENet-CSharp + * + * Copyright (c) 2019 Matt Coburn (SoftwareGuy/Coburn64), Chris Burns (c6burns) + * Copyright (c) 2013 James Bellinger, 2016 Nate Shoffner, 2018 Stanislav Denisov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +using System.Runtime.InteropServices; +using System.Security; +using System.Text; + +namespace BeatTogether.DedicatedServer.Ignorance.ENet +{ + [Flags] + public enum PacketFlags + { + None = 0, + Reliable = 1 << 0, + Unsequenced = 1 << 1, + NoAllocate = 1 << 2, + UnreliableFragmented = 1 << 3, + Instant = 1 << 4, + Unthrottled = 1 << 5, + Sent = 1 << 8 + } + + public enum EventType + { + None = 0, + Connect = 1, + Disconnect = 2, + Receive = 3, + Timeout = 4 + } + + public enum PeerState + { + Uninitialized = -1, + Disconnected = 0, + Connecting = 1, + AcknowledgingConnect = 2, + ConnectionPending = 3, + ConnectionSucceeded = 4, + Connected = 5, + DisconnectLater = 6, + Disconnecting = 7, + AcknowledgingDisconnect = 8, + Zombie = 9 + } + + [StructLayout(LayoutKind.Explicit, Size = 18)] + internal struct ENetAddress + { + [FieldOffset(16)] + public ushort port; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct ENetEvent + { + public EventType type; + public IntPtr peer; + public byte channelID; + public uint data; + public IntPtr packet; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct ENetCallbacks + { + public AllocCallback malloc; + public FreeCallback free; + public NoMemoryCallback noMemory; + } + + public delegate IntPtr AllocCallback(IntPtr size); + public delegate void FreeCallback(IntPtr memory); + public delegate void NoMemoryCallback(); + public delegate void PacketFreeCallback(Packet packet); + public delegate int InterceptCallback(ref Event @event, ref Address address, IntPtr receivedData, int receivedDataLength); + public delegate ulong ChecksumCallback(IntPtr buffers, int bufferCount); + + internal static class ArrayPool + { + [ThreadStatic] + private static byte[] byteBuffer; + [ThreadStatic] + private static IntPtr[] pointerBuffer; + + public static byte[] GetByteBuffer() + { + if (byteBuffer == null) + byteBuffer = new byte[64]; + + return byteBuffer; + } + + public static IntPtr[] GetPointerBuffer() + { + if (pointerBuffer == null) + pointerBuffer = new IntPtr[Library.maxPeers]; + + return pointerBuffer; + } + } + + public struct Address + { + private ENetAddress nativeAddress; + + internal ENetAddress NativeData + { + get + { + return nativeAddress; + } + + set + { + nativeAddress = value; + } + } + + internal Address(ENetAddress address) + { + nativeAddress = address; + } + + public ushort Port + { + get + { + return nativeAddress.port; + } + + set + { + nativeAddress.port = value; + } + } + + public string GetIP() + { + StringBuilder ip = new StringBuilder(1025); + + if (Native.enet_address_get_ip(ref nativeAddress, ip, (IntPtr)ip.Capacity) != 0) + return String.Empty; + + return ip.ToString(); + } + + public bool SetIP(string ip) + { + if (ip == null) + throw new ArgumentNullException("ip"); + + return Native.enet_address_set_ip(ref nativeAddress, ip) == 0; + } + + public string GetHost() + { + StringBuilder hostName = new StringBuilder(1025); + + if (Native.enet_address_get_hostname(ref nativeAddress, hostName, (IntPtr)hostName.Capacity) != 0) + return String.Empty; + + return hostName.ToString(); + } + + public bool SetHost(string hostName) + { + if (hostName == null) + throw new ArgumentNullException("hostName"); + + return Native.enet_address_set_hostname(ref nativeAddress, hostName) == 0; + } + } + + public struct Event + { + private ENetEvent nativeEvent; + + internal ENetEvent NativeData + { + get + { + return nativeEvent; + } + + set + { + nativeEvent = value; + } + } + + internal Event(ENetEvent @event) + { + nativeEvent = @event; + } + + public EventType Type + { + get + { + return nativeEvent.type; + } + } + + public Peer Peer + { + get + { + return new Peer(nativeEvent.peer); + } + } + + public byte ChannelID + { + get + { + return nativeEvent.channelID; + } + } + + public uint Data + { + get + { + return nativeEvent.data; + } + } + + public Packet Packet + { + get + { + return new Packet(nativeEvent.packet); + } + } + } + + public class Callbacks + { + private ENetCallbacks nativeCallbacks; + + internal ENetCallbacks NativeData + { + get + { + return nativeCallbacks; + } + + set + { + nativeCallbacks = value; + } + } + + public Callbacks(AllocCallback allocCallback, FreeCallback freeCallback, NoMemoryCallback noMemoryCallback) + { + nativeCallbacks.malloc = allocCallback; + nativeCallbacks.free = freeCallback; + nativeCallbacks.noMemory = noMemoryCallback; + } + } + + public struct Packet : IDisposable + { + private IntPtr nativePacket; + + internal IntPtr NativeData + { + get + { + return nativePacket; + } + + set + { + nativePacket = value; + } + } + + internal Packet(IntPtr packet) + { + nativePacket = packet; + } + + public void Dispose() + { + if (nativePacket != IntPtr.Zero) + { + Native.enet_packet_dispose(nativePacket); + nativePacket = IntPtr.Zero; + } + } + + public bool IsSet + { + get + { + return nativePacket != IntPtr.Zero; + } + } + + public IntPtr Data + { + get + { + ThrowIfNotCreated(); + + return Native.enet_packet_get_data(nativePacket); + } + } + + public IntPtr UserData + { + get + { + ThrowIfNotCreated(); + + return Native.enet_packet_get_user_data(nativePacket); + } + + set + { + ThrowIfNotCreated(); + + Native.enet_packet_set_user_data(nativePacket, value); + } + } + + public int Length + { + get + { + ThrowIfNotCreated(); + + return Native.enet_packet_get_length(nativePacket); + } + } + + public bool HasReferences + { + get + { + ThrowIfNotCreated(); + + return Native.enet_packet_check_references(nativePacket) != 0; + } + } + + internal void ThrowIfNotCreated() + { + if (nativePacket == IntPtr.Zero) + throw new InvalidOperationException("Packet not created"); + } + + public void SetFreeCallback(IntPtr callback) + { + ThrowIfNotCreated(); + + Native.enet_packet_set_free_callback(nativePacket, callback); + } + + public void SetFreeCallback(PacketFreeCallback callback) + { + ThrowIfNotCreated(); + + Native.enet_packet_set_free_callback(nativePacket, Marshal.GetFunctionPointerForDelegate(callback)); + } + + public void Create(byte[] data) + { + if (data == null) + throw new ArgumentNullException("data"); + + Create(data, data.Length); + } + + public void Create(byte[] data, int length) + { + Create(data, length, PacketFlags.None); + } + + public void Create(byte[] data, PacketFlags flags) + { + Create(data, data.Length, flags); + } + + public void Create(byte[] data, int length, PacketFlags flags) + { + if (data == null) + throw new ArgumentNullException("data"); + + if (length < 0 || length > data.Length) + throw new ArgumentOutOfRangeException("length"); + + nativePacket = Native.enet_packet_create(data, (IntPtr)length, flags); + } + + public void Create(IntPtr data, int length, PacketFlags flags) + { + if (data == IntPtr.Zero) + throw new ArgumentNullException("data"); + + if (length < 0) + throw new ArgumentOutOfRangeException("length"); + + nativePacket = Native.enet_packet_create(data, (IntPtr)length, flags); + } + + public void Create(byte[] data, int offset, int length, PacketFlags flags) + { + if (data == null) + throw new ArgumentNullException("data"); + + if (offset < 0) + throw new ArgumentOutOfRangeException("offset"); + + if (length < 0 || length > data.Length) + throw new ArgumentOutOfRangeException("length"); + + nativePacket = Native.enet_packet_create_offset(data, (IntPtr)length, (IntPtr)offset, flags); + } + + public void Create(IntPtr data, int offset, int length, PacketFlags flags) + { + if (data == IntPtr.Zero) + throw new ArgumentNullException("data"); + + if (offset < 0) + throw new ArgumentOutOfRangeException("offset"); + + if (length < 0) + throw new ArgumentOutOfRangeException("length"); + + nativePacket = Native.enet_packet_create_offset(data, (IntPtr)length, (IntPtr)offset, flags); + } + + public void CopyTo(byte[] destination, int startPos = 0) + { + if (destination == null) + throw new ArgumentNullException("destination"); + + // Fix by katori, prevents trying to copy a NULL + // from native world (ie. disconnect a client) + if (Data == null) + { + return; + } + + Marshal.Copy(Data, destination, startPos, Length); + } + } + + public class Host : IDisposable + { + private IntPtr nativeHost; + + internal IntPtr NativeData + { + get + { + return nativeHost; + } + + set + { + nativeHost = value; + } + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (nativeHost != IntPtr.Zero) + { + Native.enet_host_destroy(nativeHost); + nativeHost = IntPtr.Zero; + } + } + + ~Host() + { + Dispose(false); + } + + public bool IsSet + { + get + { + return nativeHost != IntPtr.Zero; + } + } + + public uint PeersCount + { + get + { + ThrowIfNotCreated(); + + return Native.enet_host_get_peers_count(nativeHost); + } + } + + public uint PacketsSent + { + get + { + ThrowIfNotCreated(); + + return Native.enet_host_get_packets_sent(nativeHost); + } + } + + public uint PacketsReceived + { + get + { + ThrowIfNotCreated(); + + return Native.enet_host_get_packets_received(nativeHost); + } + } + + public uint BytesSent + { + get + { + ThrowIfNotCreated(); + + return Native.enet_host_get_bytes_sent(nativeHost); + } + } + + public uint BytesReceived + { + get + { + ThrowIfNotCreated(); + + return Native.enet_host_get_bytes_received(nativeHost); + } + } + + internal void ThrowIfNotCreated() + { + if (nativeHost == IntPtr.Zero) + throw new InvalidOperationException("Host not created"); + } + + private static void ThrowIfChannelsExceeded(int channelLimit) + { + if (channelLimit < 0 || channelLimit > Library.maxChannelCount) + throw new ArgumentOutOfRangeException("channelLimit"); + } + + public void Create() + { + Create(null, 1, 0); + } + + public void Create(int bufferSize) + { + Create(null, 1, 0, 0, 0, bufferSize); + } + + public void Create(Address? address, int peerLimit) + { + Create(address, peerLimit, 0); + } + + public void Create(Address? address, int peerLimit, int channelLimit) + { + Create(address, peerLimit, channelLimit, 0, 0, 0); + } + + public void Create(int peerLimit, int channelLimit) + { + Create(null, peerLimit, channelLimit, 0, 0, 0); + } + + public void Create(int peerLimit, int channelLimit, uint incomingBandwidth, uint outgoingBandwidth) + { + Create(null, peerLimit, channelLimit, incomingBandwidth, outgoingBandwidth, 0); + } + + public void Create(Address? address, int peerLimit, int channelLimit, uint incomingBandwidth, uint outgoingBandwidth) + { + Create(address, peerLimit, channelLimit, incomingBandwidth, outgoingBandwidth, 0); + } + + public void Create(Address? address, int peerLimit, int channelLimit, uint incomingBandwidth, uint outgoingBandwidth, int bufferSize) + { + if (nativeHost != IntPtr.Zero) + throw new InvalidOperationException("Host already created"); + + if (peerLimit < 0 || peerLimit > Library.maxPeers) + throw new ArgumentOutOfRangeException("peerLimit"); + + ThrowIfChannelsExceeded(channelLimit); + + if (address != null) + { + var nativeAddress = address.Value.NativeData; + + nativeHost = Native.enet_host_create(ref nativeAddress, (IntPtr)peerLimit, (IntPtr)channelLimit, incomingBandwidth, outgoingBandwidth, bufferSize); + } + else + { + nativeHost = Native.enet_host_create(IntPtr.Zero, (IntPtr)peerLimit, (IntPtr)channelLimit, incomingBandwidth, outgoingBandwidth, bufferSize); + } + + if (nativeHost == IntPtr.Zero) + throw new InvalidOperationException("Host creation call failed"); + } + + public void PreventConnections(bool state) + { + ThrowIfNotCreated(); + + Native.enet_host_prevent_connections(nativeHost, (byte)(state ? 1 : 0)); + } + + public void Broadcast(byte channelID, ref Packet packet) + { + ThrowIfNotCreated(); + + packet.ThrowIfNotCreated(); + Native.enet_host_broadcast(nativeHost, channelID, packet.NativeData); + packet.NativeData = IntPtr.Zero; + } + + public void Broadcast(byte channelID, ref Packet packet, Peer excludedPeer) + { + ThrowIfNotCreated(); + + packet.ThrowIfNotCreated(); + Native.enet_host_broadcast_exclude(nativeHost, channelID, packet.NativeData, excludedPeer.NativeData); + packet.NativeData = IntPtr.Zero; + } + + public void Broadcast(byte channelID, ref Packet packet, Peer[] peers) + { + if (peers == null) + throw new ArgumentNullException("peers"); + + ThrowIfNotCreated(); + + packet.ThrowIfNotCreated(); + + if (peers.Length > 0) + { + IntPtr[] nativePeers = ArrayPool.GetPointerBuffer(); + int nativeCount = 0; + + for (int i = 0; i < peers.Length; i++) + { + if (peers[i].NativeData != IntPtr.Zero) + { + nativePeers[nativeCount] = peers[i].NativeData; + nativeCount++; + } + } + + Native.enet_host_broadcast_selective(nativeHost, channelID, packet.NativeData, nativePeers, (IntPtr)nativeCount); + } + + packet.NativeData = IntPtr.Zero; + } + + public int CheckEvents(out Event @event) + { + ThrowIfNotCreated(); + + ENetEvent nativeEvent; + + var result = Native.enet_host_check_events(nativeHost, out nativeEvent); + + if (result <= 0) + { + @event = default(Event); + + return result; + } + + @event = new Event(nativeEvent); + + return result; + } + + public Peer Connect(Address address) + { + return Connect(address, 0, 0); + } + + public Peer Connect(Address address, int channelLimit) + { + return Connect(address, channelLimit, 0); + } + + public Peer Connect(Address address, int channelLimit, uint data) + { + ThrowIfNotCreated(); + ThrowIfChannelsExceeded(channelLimit); + + var nativeAddress = address.NativeData; + var peer = new Peer(Native.enet_host_connect(nativeHost, ref nativeAddress, (IntPtr)channelLimit, data)); + + if (peer.NativeData == IntPtr.Zero) + throw new InvalidOperationException("Host connect call failed"); + + return peer; + } + + public int Service(int timeout, out Event @event) + { + if (timeout < 0) + throw new ArgumentOutOfRangeException("timeout"); + + ThrowIfNotCreated(); + + ENetEvent nativeEvent; + + var result = Native.enet_host_service(nativeHost, out nativeEvent, (uint)timeout); + + if (result <= 0) + { + @event = default(Event); + + return result; + } + + @event = new Event(nativeEvent); + + return result; + } + + public void SetBandwidthLimit(uint incomingBandwidth, uint outgoingBandwidth) + { + ThrowIfNotCreated(); + + Native.enet_host_bandwidth_limit(nativeHost, incomingBandwidth, outgoingBandwidth); + } + + public void SetChannelLimit(int channelLimit) + { + ThrowIfNotCreated(); + ThrowIfChannelsExceeded(channelLimit); + + Native.enet_host_channel_limit(nativeHost, (IntPtr)channelLimit); + } + + public void SetMaxDuplicatePeers(ushort number) + { + ThrowIfNotCreated(); + + Native.enet_host_set_max_duplicate_peers(nativeHost, number); + } + + public void SetInterceptCallback(IntPtr callback) + { + ThrowIfNotCreated(); + + Native.enet_host_set_intercept_callback(nativeHost, callback); + } + + public void SetInterceptCallback(InterceptCallback callback) + { + ThrowIfNotCreated(); + + Native.enet_host_set_intercept_callback(nativeHost, Marshal.GetFunctionPointerForDelegate(callback)); + } + + public void SetChecksumCallback(IntPtr callback) + { + ThrowIfNotCreated(); + + Native.enet_host_set_checksum_callback(nativeHost, callback); + } + + public void SetChecksumCallback(ChecksumCallback callback) + { + ThrowIfNotCreated(); + + Native.enet_host_set_checksum_callback(nativeHost, Marshal.GetFunctionPointerForDelegate(callback)); + } + + public void Flush() + { + ThrowIfNotCreated(); + + Native.enet_host_flush(nativeHost); + } + } + + public struct Peer + { + private IntPtr nativePeer; + private uint nativeID; + + internal IntPtr NativeData + { + get + { + return nativePeer; + } + + set + { + nativePeer = value; + } + } + + internal Peer(IntPtr peer) + { + nativePeer = peer; + nativeID = nativePeer != IntPtr.Zero ? Native.enet_peer_get_id(nativePeer) : 0; + } + + public bool IsSet + { + get + { + return nativePeer != IntPtr.Zero; + } + } + + public uint ID + { + get + { + return nativeID; + } + } + + public string IP + { + get + { + ThrowIfNotCreated(); + + byte[] ip = ArrayPool.GetByteBuffer(); + + if (Native.enet_peer_get_ip(nativePeer, ip, (IntPtr)ip.Length) == 0) + return Encoding.ASCII.GetString(ip, 0, ip.StringLength()); + else + return String.Empty; + } + } + + public ushort Port + { + get + { + ThrowIfNotCreated(); + + return Native.enet_peer_get_port(nativePeer); + } + } + + public uint MTU + { + get + { + ThrowIfNotCreated(); + + return Native.enet_peer_get_mtu(nativePeer); + } + } + + public PeerState State + { + get + { + return nativePeer == IntPtr.Zero ? PeerState.Uninitialized : Native.enet_peer_get_state(nativePeer); + } + } + + public uint RoundTripTime + { + get + { + ThrowIfNotCreated(); + + return Native.enet_peer_get_rtt(nativePeer); + } + } + + public uint LastRoundTripTime + { + get + { + ThrowIfNotCreated(); + + return Native.enet_peer_get_last_rtt(nativePeer); + } + } + + public uint LastSendTime + { + get + { + ThrowIfNotCreated(); + + return Native.enet_peer_get_lastsendtime(nativePeer); + } + } + + public uint LastReceiveTime + { + get + { + ThrowIfNotCreated(); + + return Native.enet_peer_get_lastreceivetime(nativePeer); + } + } + + public ulong PacketsSent + { + get + { + ThrowIfNotCreated(); + + return Native.enet_peer_get_packets_sent(nativePeer); + } + } + + public ulong PacketsLost + { + get + { + ThrowIfNotCreated(); + + return Native.enet_peer_get_packets_lost(nativePeer); + } + } + + public float PacketsThrottle + { + get + { + ThrowIfNotCreated(); + + return Native.enet_peer_get_packets_throttle(nativePeer); + } + } + + public ulong BytesSent + { + get + { + ThrowIfNotCreated(); + + return Native.enet_peer_get_bytes_sent(nativePeer); + } + } + + public ulong BytesReceived + { + get + { + ThrowIfNotCreated(); + + return Native.enet_peer_get_bytes_received(nativePeer); + } + } + + public IntPtr Data + { + get + { + ThrowIfNotCreated(); + + return Native.enet_peer_get_data(nativePeer); + } + + set + { + ThrowIfNotCreated(); + + Native.enet_peer_set_data(nativePeer, value); + } + } + + internal void ThrowIfNotCreated() + { + if (nativePeer == IntPtr.Zero) + throw new InvalidOperationException("Peer not created"); + } + + public void ConfigureThrottle(uint interval, uint acceleration, uint deceleration, uint threshold) + { + ThrowIfNotCreated(); + + Native.enet_peer_throttle_configure(nativePeer, interval, acceleration, deceleration, threshold); + } + + public int Send(byte channelID, ref Packet packet) + { + ThrowIfNotCreated(); + + packet.ThrowIfNotCreated(); + + return Native.enet_peer_send(nativePeer, channelID, packet.NativeData); + } + + public bool Receive(out byte channelID, out Packet packet) + { + ThrowIfNotCreated(); + + IntPtr nativePacket = Native.enet_peer_receive(nativePeer, out channelID); + + if (nativePacket != IntPtr.Zero) + { + packet = new Packet(nativePacket); + + return true; + } + + packet = default(Packet); + + return false; + } + + public void Ping() + { + ThrowIfNotCreated(); + + Native.enet_peer_ping(nativePeer); + } + + public void PingInterval(uint interval) + { + ThrowIfNotCreated(); + + Native.enet_peer_ping_interval(nativePeer, interval); + } + + public void Timeout(uint timeoutLimit, uint timeoutMinimum, uint timeoutMaximum) + { + ThrowIfNotCreated(); + + Native.enet_peer_timeout(nativePeer, timeoutLimit, timeoutMinimum, timeoutMaximum); + } + + public void Disconnect(uint data) + { + ThrowIfNotCreated(); + + Native.enet_peer_disconnect(nativePeer, data); + } + + public void DisconnectNow(uint data) + { + ThrowIfNotCreated(); + + Native.enet_peer_disconnect_now(nativePeer, data); + } + + public void DisconnectLater(uint data) + { + ThrowIfNotCreated(); + + Native.enet_peer_disconnect_later(nativePeer, data); + } + + public void Reset() + { + ThrowIfNotCreated(); + + Native.enet_peer_reset(nativePeer); + } + } + + public static class Extensions + { + public static int StringLength(this byte[] data) + { + if (data == null) + throw new ArgumentNullException("data"); + + int i; + + for (i = 0; i < data.Length && data[i] != 0; i++) ; + + return i; + } + } + + public static class Library + { + public const uint maxChannelCount = 0xFF; + public const uint maxPeers = 0xFFF; + public const uint maxPacketSize = 32 * 1024 * 1024; + public const uint throttleThreshold = 40; + public const uint throttleScale = 32; + public const uint throttleAcceleration = 2; + public const uint throttleDeceleration = 2; + public const uint throttleInterval = 5000; + public const uint timeoutLimit = 32; + public const uint timeoutMinimum = 5000; + public const uint timeoutMaximum = 30000; + public const uint version = (2 << 16) | (4 << 8) | (7); + + public static uint Time + { + get + { + return Native.enet_time_get(); + } + } + + public static bool Initialize() + { + if (Native.enet_linked_version() != version) + throw new InvalidOperationException("ENet native is out of date. Download the latest release from https://github.com/SoftwareGuy/ENet-CSharp/releases"); + + return Native.enet_initialize() == 0; + } + + public static bool Initialize(Callbacks callbacks) + { + if (callbacks == null) + throw new ArgumentNullException("callbacks"); + + if (Native.enet_linked_version() != version) + throw new InvalidOperationException("ENet native is out of date. Download the latest release from https://github.com/SoftwareGuy/ENet-CSharp/releases"); + + ENetCallbacks nativeCallbacks = callbacks.NativeData; + + return Native.enet_initialize_with_callbacks(version, ref nativeCallbacks) == 0; + } + + public static void Deinitialize() + { + Native.enet_deinitialize(); + } + + public static ulong CRC64(IntPtr buffers, int bufferCount) + { + return Native.enet_crc64(buffers, bufferCount); + } + } + + [SuppressUnmanagedCodeSecurity] + internal static class Native + { + // This should address Unity usage and bug #66: Platform specific Enet / libenet + // https://github.com/SoftwareGuy/Ignorance/issues/66 + + #region Editor Specific Native Library Names +#if UNITY_EDITOR +#if UNITY_EDITOR_OSX + // Unity Editor on macOS needs to use libenet. + private const string nativeLibrary = "libenet"; +#else + // All other platforms should be using "enet". + private const string nativeLibrary = "enet"; +#endif +#endif + #endregion + + + #region Standalone Specific Native Library Names +#if !UNITY_EDITOR +#if __APPLE__ && !(__IOS__ || UNITY_IOS) + // Use libenet on macOS. + private const string nativeLibrary = "libenet"; +#elif __IOS__ || UNITY_IOS + // We're building for a certain mobile fruity OS. + private const string nativeLibrary = "__Internal"; +#else + // Assume everything else, Windows et al. + private const string nativeLibrary = "enet"; +#endif +#endif + #endregion + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern int enet_initialize(); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern int enet_initialize_with_callbacks(uint version, ref ENetCallbacks inits); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern void enet_deinitialize(); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern uint enet_linked_version(); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern uint enet_time_get(); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern ulong enet_crc64(IntPtr buffers, int bufferCount); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern int enet_address_set_ip(ref ENetAddress address, string ip); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern int enet_address_set_hostname(ref ENetAddress address, string hostName); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern int enet_address_get_ip(ref ENetAddress address, StringBuilder ip, IntPtr ipLength); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern int enet_address_get_hostname(ref ENetAddress address, StringBuilder hostName, IntPtr nameLength); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr enet_packet_create(byte[] data, IntPtr dataLength, PacketFlags flags); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr enet_packet_create(IntPtr data, IntPtr dataLength, PacketFlags flags); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr enet_packet_create_offset(byte[] data, IntPtr dataLength, IntPtr dataOffset, PacketFlags flags); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr enet_packet_create_offset(IntPtr data, IntPtr dataLength, IntPtr dataOffset, PacketFlags flags); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern int enet_packet_check_references(IntPtr packet); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr enet_packet_get_data(IntPtr packet); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr enet_packet_get_user_data(IntPtr packet); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr enet_packet_set_user_data(IntPtr packet, IntPtr userData); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern int enet_packet_get_length(IntPtr packet); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern void enet_packet_set_free_callback(IntPtr packet, IntPtr callback); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern void enet_packet_dispose(IntPtr packet); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr enet_host_create(ref ENetAddress address, IntPtr peerLimit, IntPtr channelLimit, uint incomingBandwidth, uint outgoingBandwidth, int bufferSize); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr enet_host_create(IntPtr address, IntPtr peerLimit, IntPtr channelLimit, uint incomingBandwidth, uint outgoingBandwidth, int bufferSize); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr enet_host_connect(IntPtr host, ref ENetAddress address, IntPtr channelCount, uint data); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern void enet_host_broadcast(IntPtr host, byte channelID, IntPtr packet); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern void enet_host_broadcast_exclude(IntPtr host, byte channelID, IntPtr packet, IntPtr excludedPeer); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern void enet_host_broadcast_selective(IntPtr host, byte channelID, IntPtr packet, IntPtr[] peers, IntPtr peersLength); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern int enet_host_service(IntPtr host, out ENetEvent @event, uint timeout); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern int enet_host_check_events(IntPtr host, out ENetEvent @event); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern void enet_host_channel_limit(IntPtr host, IntPtr channelLimit); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern void enet_host_bandwidth_limit(IntPtr host, uint incomingBandwidth, uint outgoingBandwidth); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern uint enet_host_get_peers_count(IntPtr host); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern uint enet_host_get_packets_sent(IntPtr host); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern uint enet_host_get_packets_received(IntPtr host); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern uint enet_host_get_bytes_sent(IntPtr host); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern uint enet_host_get_bytes_received(IntPtr host); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern void enet_host_set_max_duplicate_peers(IntPtr host, ushort number); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern void enet_host_set_intercept_callback(IntPtr host, IntPtr callback); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern void enet_host_set_checksum_callback(IntPtr host, IntPtr callback); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern void enet_host_flush(IntPtr host); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern void enet_host_destroy(IntPtr host); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern void enet_host_prevent_connections(IntPtr host, byte state); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern void enet_peer_throttle_configure(IntPtr peer, uint interval, uint acceleration, uint deceleration, uint threshold); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern uint enet_peer_get_id(IntPtr peer); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern int enet_peer_get_ip(IntPtr peer, byte[] ip, IntPtr ipLength); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern ushort enet_peer_get_port(IntPtr peer); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern uint enet_peer_get_mtu(IntPtr peer); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern PeerState enet_peer_get_state(IntPtr peer); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern uint enet_peer_get_rtt(IntPtr peer); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern uint enet_peer_get_last_rtt(IntPtr peer); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern uint enet_peer_get_lastsendtime(IntPtr peer); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern uint enet_peer_get_lastreceivetime(IntPtr peer); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern ulong enet_peer_get_packets_sent(IntPtr peer); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern ulong enet_peer_get_packets_lost(IntPtr peer); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern float enet_peer_get_packets_throttle(IntPtr peer); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern ulong enet_peer_get_bytes_sent(IntPtr peer); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern ulong enet_peer_get_bytes_received(IntPtr peer); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr enet_peer_get_data(IntPtr peer); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern void enet_peer_set_data(IntPtr peer, IntPtr data); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern int enet_peer_send(IntPtr peer, byte channelID, IntPtr packet); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr enet_peer_receive(IntPtr peer, out byte channelID); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern void enet_peer_ping(IntPtr peer); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern void enet_peer_ping_interval(IntPtr peer, uint pingInterval); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern void enet_peer_timeout(IntPtr peer, uint timeoutLimit, uint timeoutMinimum, uint timeoutMaximum); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern void enet_peer_disconnect(IntPtr peer, uint data); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern void enet_peer_disconnect_now(IntPtr peer, uint data); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern void enet_peer_disconnect_later(IntPtr peer, uint data); + + [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern void enet_peer_reset(IntPtr peer); + +#if UNITY_EDITOR + public static string nativeLibraryName { get { return nativeLibrary; } } +#endif + + } +} diff --git a/BeatTogether.DedicatedServer.Ignorance/IgnoranceCore/IgnoranceDefinitions.cs b/BeatTogether.DedicatedServer.Ignorance/IgnoranceCore/IgnoranceDefinitions.cs new file mode 100644 index 00000000..ebf25d19 --- /dev/null +++ b/BeatTogether.DedicatedServer.Ignorance/IgnoranceCore/IgnoranceDefinitions.cs @@ -0,0 +1,108 @@ +// Ignorance 1.4.x LTS (Long Term Support) +// https://github.com/SoftwareGuy/Ignorance +// ----------------- +// Copyright (c) 2019 - 2021 Matt Coburn (SoftwareGuy/Coburn64) +// Ignorance is licensed under the MIT license. Refer +// to the LICENSE file for more information. + +using BeatTogether.DedicatedServer.Ignorance.ENet; + +namespace BeatTogether.DedicatedServer.Ignorance.IgnoranceCore +{ + // Snipped from the transport files, as this will help + // me keep things up to date. + [Serializable] + public enum IgnoranceChannelTypes + { + Reliable = PacketFlags.Reliable, // Reliable UDP (TCP-like emulation) + ReliableUnsequenced = PacketFlags.Reliable | PacketFlags.Unsequenced, // Reliable UDP (TCP-like emulation w/o sequencing) + Unreliable = PacketFlags.Unsequenced, // Pure UDP, high velocity packet action. + UnreliableFragmented = PacketFlags.UnreliableFragmented, // Pure UDP, but fragmented. + UnreliableSequenced = PacketFlags.None, // Pure UDP, but sequenced. + Unthrottled = PacketFlags.Unthrottled, // Pure UDP. Literally turbo mode. + } + + public class IgnoranceInternals + { + public const string Version = "1.4.0r2 (LTS)"; + public const string Scheme = "enet"; + public const string BindAnyAddress = "::0"; + } + + public enum IgnoranceLogType + { + Quiet, + Standard, + Verbose + } + + public struct IgnoranceIncomingPacket + { + public byte Channel; + public uint NativePeerId; + public Packet Payload; + } + + public struct IgnoranceOutgoingPacket + { + public byte Channel; + public uint NativePeerId; + public Packet Payload; + } + + public struct IgnoranceConnectionEvent + { + public byte EventType; + public ushort Port; + public uint NativePeerId; + public string IP; + } + + public struct IgnoranceCommandPacket + { + public IgnoranceCommandType Type; + public uint PeerId; + } + + // Stats only - may not always be used! + public struct IgnoranceClientStats + { + + public uint RTT; + public ulong BytesReceived; + public ulong BytesSent; + public ulong PacketsReceived; + public ulong PacketsSent; + public ulong PacketsLost; + } + + public enum IgnoranceCommandType + { + // Client + ClientWantsToStop, + ClientStatusRequest, + // Server + ServerKickPeer, + ServerStatusRequest + } + + // Stats only - may not always be used! + public struct IgnoranceServerStats + { + + public ulong BytesReceived; + public ulong BytesSent; + public ulong PacketsReceived; + public ulong PacketsSent; + public ulong PeersCount; + + public Dictionary PeerStats; + } + + public struct PeerConnectionData + { + public ushort Port; + public uint NativePeerId; + public string IP; + } +} diff --git a/BeatTogether.DedicatedServer.Ignorance/IgnoranceCore/IgnoranceServer.cs b/BeatTogether.DedicatedServer.Ignorance/IgnoranceCore/IgnoranceServer.cs new file mode 100644 index 00000000..91fad905 --- /dev/null +++ b/BeatTogether.DedicatedServer.Ignorance/IgnoranceCore/IgnoranceServer.cs @@ -0,0 +1,435 @@ +// Ignorance 1.4.x LTS (Long Term Support) +// https://github.com/SoftwareGuy/Ignorance +// ----------------- +// Copyright (c) 2019 - 2021 Matt Coburn (SoftwareGuy/Coburn64) +// Ignorance is licensed under the MIT license. Refer +// to the LICENSE file for more information. + +using BeatTogether.DedicatedServer.Ignorance.ENet; +using BeatTogether.DedicatedServer.Ignorance.IgnoranceThirdparty; +using Event = BeatTogether.DedicatedServer.Ignorance.ENet.Event; +using EventType = BeatTogether.DedicatedServer.Ignorance.ENet.EventType; +using Object = System.Object; +using Debug = BeatTogether.DedicatedServer.Ignorance.Util.IgnoranceDebug; + +namespace BeatTogether.DedicatedServer.Ignorance.IgnoranceCore +{ + public class IgnoranceServer + { + // Server Properties + // - Bind Settings + public string BindAddress = "127.0.0.1"; + public int BindPort = 7777; + // - Maximum allowed channels, peers, etc. + public int MaximumChannels = 2; + public int MaximumPeers = 100; + public int MaximumPacketSize = 33554432; // ENet.cs: uint maxPacketSize = 32 * 1024 * 1024 = 33554432 + // - Native poll waiting time + public int PollTime = 1; + // - Verbosity. + public int Verbosity = 1; + // - Queue Sizing + public int IncomingOutgoingBufferSize = 5000; + public int ConnectionEventBufferSize = 100; + // - Fruity devices + public bool IsFruityDevice; + public bool BindAllInterfaces; + + public bool IsAlive => WorkerThread != null && WorkerThread.IsAlive; + + private volatile bool CeaseOperation = false; + + // Queues + // v1.4.0b9: Replace the queues with RingBuffers. + public RingBuffer Incoming; + public RingBuffer Outgoing; + public RingBuffer Commands; + public RingBuffer ConnectionEvents; + public RingBuffer DisconnectionEvents; + public RingBuffer StatusUpdates; + + public RingBuffer RecycledServerStatBlocks = new RingBuffer(100); + + // Thread + private Thread WorkerThread; + + public void Start() + { + if (WorkerThread != null && WorkerThread.IsAlive) + { + // Cannot do that. + Debug.LogError("Ignorance Server: A worker thread is already running. Cannot start another."); + return; + } + + // Setup the ring buffers. + SetupRingBuffersIfNull(); + + CeaseOperation = false; + ThreadParamInfo threadParams = new ThreadParamInfo() + { + IsFruityDevice = IsFruityDevice, + BindAllInterfaces = BindAllInterfaces, + Address = BindAddress, + Port = BindPort, + Peers = MaximumPeers, + Channels = MaximumChannels, + PollTime = PollTime, + PacketSizeLimit = MaximumPacketSize, + Verbosity = Verbosity + }; + + // Drain queues. + if (Incoming != null) while (Incoming.TryDequeue(out _)) ; + if (Outgoing != null) while (Outgoing.TryDequeue(out _)) ; + if (Commands != null) while (Commands.TryDequeue(out _)) ; + if (ConnectionEvents != null) while (ConnectionEvents.TryDequeue(out _)) ; + if (DisconnectionEvents != null) while (DisconnectionEvents.TryDequeue(out _)) ; + if (StatusUpdates != null) while (StatusUpdates.TryDequeue(out _)) ; + + WorkerThread = new Thread(ThreadWorker); + WorkerThread.Start(threadParams); + + // Announce + if (Verbosity > 0) + Debug.Log("Ignorance Server: Dispatched worker thread."); + } + + public void Stop() + { + // v1.4.0b7: Mirror may call this; if the worker thread isn't alive then don't announce it. + if (WorkerThread != null && WorkerThread.IsAlive) + { + if (Verbosity > 0) + Debug.Log("Ignorance Server: Server stop acknowledged. Depending on network load, this may take a moment or two..."); + + CeaseOperation = true; + } + } + + #region The meat and potatoes. + private void ThreadWorker(Object parameters) + { + if (Verbosity > 0) + Debug.Log("Ignorance: Server instance worker thread is initializing. Please stand by..."); + + // Thread cache items + ThreadParamInfo setupInfo; + Address serverAddress = new Address(); + Host serverENetHost; + Event serverENetEvent; + + Peer[] serverPeerArray; + IgnoranceClientStats peerStats = default; + + // Grab the setup information. + if (parameters.GetType() == typeof(ThreadParamInfo)) + { + setupInfo = (ThreadParamInfo)parameters; + } + else + { + Debug.LogError("Ignorance: Server instance worker thread reports startup failure; Invalid thread parameters. Aborting."); + return; + } + + // Attempt to initialize ENet inside the thread. + if (Library.Initialize()) + { + Debug.Log("Ignorance: Server instance worker thread successfully initialized ENet."); + } + else + { + Debug.LogError("Ignorance Server: Failed to initialize ENet Native. Aborting."); + return; + } + + // Configure the server address. + // So... if we're running on a Mac, we have an issue where if we attempt to bind to all devices which usually is either + // 0.0.0.0 or ::0. In older versions of Mac OS, this worked since those addresses are "broadcast" or "listen all"... + // but on newer versions this broke the server functionality. So the fix was rather easy but the debugging was not. + // The fix was to NOT set any IP/Host if we're binding to all addresses on MacOS. Apparently that works. Go figure. + // Afterthought: Maybe it's something funky in ENet...? + + if (setupInfo.IsFruityDevice) + { + if (!setupInfo.BindAllInterfaces) + serverAddress.SetHost(setupInfo.Address); + } + else + { + if (setupInfo.BindAllInterfaces) + serverAddress.SetIP(IgnoranceInternals.BindAnyAddress); + else + serverAddress.SetHost(setupInfo.Address); + } + + // Set the port too. + serverAddress.Port = (ushort)setupInfo.Port; + serverPeerArray = new Peer[setupInfo.Peers]; + + using (serverENetHost = new Host()) + { + // Create the server object. + try + { + serverENetHost.Create(serverAddress, setupInfo.Peers, setupInfo.Channels); + } + catch (Exception ex) + { + Debug.LogError($"Ignorance: Server instance worker thread reports that something went wrong. While attempting to create server host object, we caught an exception:\n{ex.Message}"); + Debug.LogError($"If you are getting a \"Host creation call failed\" exception, please ensure you don't have a server already running on the same IP and Port.\n" + + $"Multiple server instances running on the same port are not supported. Also check to see if ports are not in-use by another application. In the worse case scenario, " + + $"restart your device to ensure no random background ENet threads are active that haven't been cleaned up correctly. If problems persist, please file a support ticket."); + + Library.Deinitialize(); + return; + } + + // Loop until we're told to cease operations. + while (!CeaseOperation) + { + // Intermission: Command Handling + while (Commands.TryDequeue(out IgnoranceCommandPacket commandPacket)) + { + switch (commandPacket.Type) + { + default: + break; + + // Boot a Peer off the Server. + case IgnoranceCommandType.ServerKickPeer: + uint targetPeer = commandPacket.PeerId; + + if (!serverPeerArray[targetPeer].IsSet) continue; + + if (setupInfo.Verbosity > 0) + Debug.Log($"Ignorance: Server instance is disconnecting peer {targetPeer}."); + + IgnoranceConnectionEvent iced = new IgnoranceConnectionEvent + { + EventType = 0x01, + NativePeerId = targetPeer + }; + + DisconnectionEvents.Enqueue(iced); + + // Disconnect and reset the peer array's entry for that peer. + serverPeerArray[targetPeer].DisconnectNow(0); + serverPeerArray[targetPeer] = default; + break; + + case IgnoranceCommandType.ServerStatusRequest: + IgnoranceServerStats serverStats; + if (!RecycledServerStatBlocks.TryDequeue(out serverStats)) + serverStats.PeerStats = new Dictionary(setupInfo.Peers); + + serverStats.PeerStats.Clear(); + + serverStats.BytesReceived = serverENetHost.BytesReceived; + serverStats.BytesSent = serverENetHost.BytesSent; + + serverStats.PacketsReceived = serverENetHost.PacketsReceived; + serverStats.PacketsSent = serverENetHost.PacketsSent; + + serverStats.PeersCount = serverENetHost.PeersCount; + + for (int i = 0; i < serverPeerArray.Length; i++) + { + if (!serverPeerArray[i].IsSet) continue; + + peerStats.RTT = serverPeerArray[i].RoundTripTime; + + peerStats.BytesReceived = serverPeerArray[i].BytesReceived; + peerStats.BytesSent = serverPeerArray[i].BytesSent; + + peerStats.PacketsSent = serverPeerArray[i].PacketsSent; + peerStats.PacketsLost = serverPeerArray[i].PacketsLost; + + serverStats.PeerStats.Add(i, peerStats); + } + + StatusUpdates.Enqueue(serverStats); + break; + } + } + + // Step One: + // ---> Sending to peers + while (Outgoing.TryDequeue(out IgnoranceOutgoingPacket outgoingPacket)) + { + // Only create a packet if the server knows the peer. + if (serverPeerArray[outgoingPacket.NativePeerId].IsSet) + { + int ret = serverPeerArray[outgoingPacket.NativePeerId].Send(outgoingPacket.Channel, ref outgoingPacket.Payload); + + if (ret < 0 && setupInfo.Verbosity > 0) + Debug.LogWarning($"Ignorance: Server instance ENet error {ret} while sending packet to Peer {outgoingPacket.NativePeerId}."); + } + else + { + // A peer might have disconnected, this is OK - just log the packet if set to paranoid. + if (setupInfo.Verbosity > 1) + Debug.LogWarning("Ignorance: Server instance can't send packet, a native peer object is not set. This may be normal if the Peer has disconnected before this send cycle."); + } + + } + + // Step 2 + // <--- Receiving from peers + bool pollComplete = false; + + while (!pollComplete) + { + Packet incomingPacket; + Peer incomingPeer; + int incomingPacketLength; + + // Any events happening? + if (serverENetHost.CheckEvents(out serverENetEvent) <= 0) + { + // If service time is met, break out of it. + if (serverENetHost.Service(setupInfo.PollTime, out serverENetEvent) <= 0) break; + + pollComplete = true; + } + + // Setup the packet references. + incomingPeer = serverENetEvent.Peer; + + // What type are you? + switch (serverENetEvent.Type) + { + // Idle. + case EventType.None: + default: + break; + + // Connection Event. + case EventType.Connect: + if (setupInfo.Verbosity > 1) + Debug.Log($"Ignorance: Server instance says that Peer {incomingPeer.ID} says Hi."); + + IgnoranceConnectionEvent ice = new IgnoranceConnectionEvent() + { + NativePeerId = incomingPeer.ID, + IP = incomingPeer.IP, + Port = incomingPeer.Port + }; + + ConnectionEvents.Enqueue(ice); + + // Assign a reference to the Peer. + serverPeerArray[incomingPeer.ID] = incomingPeer; + break; + + // Disconnect/Timeout. Mirror doesn't care if it's either, so we lump them together. + case EventType.Disconnect: + case EventType.Timeout: + if (!serverPeerArray[incomingPeer.ID].IsSet) break; + + if (setupInfo.Verbosity > 1) + Debug.Log($"Ignorance: Server instance says that Peer {incomingPeer.ID} has disconnected."); + + IgnoranceConnectionEvent iced = new IgnoranceConnectionEvent + { + EventType = 0x01, + NativePeerId = incomingPeer.ID + }; + + DisconnectionEvents.Enqueue(iced); + + // Reset the peer array's entry for that peer. + serverPeerArray[incomingPeer.ID] = default; + break; + + case EventType.Receive: + // Receive event type usually includes a packet; so cache its reference. + incomingPacket = serverENetEvent.Packet; + if (!incomingPacket.IsSet) + { + if (setupInfo.Verbosity > 0) + Debug.LogWarning($"Ignorance: Server instance receive event did not supply us with a packet to work with. This should never happen."); + break; + } + + incomingPacketLength = incomingPacket.Length; + + // Firstly check if the packet is too big. If it is, do not process it - drop it. + if (incomingPacketLength > setupInfo.PacketSizeLimit) + { + if (setupInfo.Verbosity > 0) + Debug.LogWarning($"Ignorance: Server incoming packet is too big. My limit is {setupInfo.PacketSizeLimit} byte(s) whilest this packet is {incomingPacketLength} bytes."); + + incomingPacket.Dispose(); + break; + } + + IgnoranceIncomingPacket incomingQueuePacket = new IgnoranceIncomingPacket + { + Channel = serverENetEvent.ChannelID, + NativePeerId = incomingPeer.ID, + Payload = incomingPacket, + }; + + // Enqueue. + Incoming.Enqueue(incomingQueuePacket); + break; + } + } + } + + if (Verbosity > 0) + Debug.Log("Ignorance: Server instance thread shutdown commencing. Flushing connections."); + + // Cleanup and flush everything. + serverENetHost.Flush(); + + // Kick everyone. + for (int i = 0; i < serverPeerArray.Length; i++) + { + if (!serverPeerArray[i].IsSet) continue; + serverPeerArray[i].DisconnectNow(0); + } + } + + if (setupInfo.Verbosity > 0) + Debug.Log("Ignorance: Server instance thread shutdown complete."); + + Library.Deinitialize(); + } + #endregion + + private void SetupRingBuffersIfNull() + { + Debug.Log($"Ignorance: Setting up ring buffers if they're not already created. " + + $"If they are already, this step will be skipped."); + + if (Incoming == null) + Incoming = new RingBuffer(IncomingOutgoingBufferSize); + if (Outgoing == null) + Outgoing = new RingBuffer(IncomingOutgoingBufferSize); + if (Commands == null) + Commands = new RingBuffer(100); + if (ConnectionEvents == null) + ConnectionEvents = new RingBuffer(ConnectionEventBufferSize); + if (DisconnectionEvents == null) + DisconnectionEvents = new RingBuffer(ConnectionEventBufferSize); + if (StatusUpdates == null) + StatusUpdates = new RingBuffer(10); + } + + private struct ThreadParamInfo + { + public bool IsFruityDevice; + public bool BindAllInterfaces; + public int Channels; + public int Peers; + public int PollTime; + public int Port; + public int PacketSizeLimit; + public int Verbosity; + public string Address; + } + } +} diff --git a/BeatTogether.DedicatedServer.Ignorance/IgnoranceThirdparty/RingBuffer.cs b/BeatTogether.DedicatedServer.Ignorance/IgnoranceThirdparty/RingBuffer.cs new file mode 100644 index 00000000..fd39f7f0 --- /dev/null +++ b/BeatTogether.DedicatedServer.Ignorance/IgnoranceThirdparty/RingBuffer.cs @@ -0,0 +1,278 @@ +// The following dependency was taken from https://github.com/dave-hillier/disruptor-unity3d +// The Apache License 2.0 this dependency follows is located at https://github.com/dave-hillier/disruptor-unity3d/blob/master/LICENSE. +// Modifications were made by SoftwareGuy (Coburn). + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace BeatTogether.DedicatedServer.Ignorance.IgnoranceThirdparty +{ + /// + /// Implementation of the Disruptor pattern + /// + /// the type of item to be stored + public class RingBuffer + { + private readonly T[] _entries; + private readonly int _modMask; + private Volatile.PaddedLong _consumerCursor = new Volatile.PaddedLong(); + private Volatile.PaddedLong _producerCursor = new Volatile.PaddedLong(); + + /// + /// Creates a new RingBuffer with the given capacity + /// + /// The capacity of the buffer + /// Only a single thread may attempt to consume at any one time + public RingBuffer(int capacity) + { + capacity = NextPowerOfTwo(capacity); + _modMask = capacity - 1; + _entries = new T[capacity]; + } + + /// + /// The maximum number of items that can be stored + /// + public int Capacity + { + get { return _entries.Length; } + } + + public T this[long index] + { + get { unchecked { return _entries[index & _modMask]; } } + set { unchecked { _entries[index & _modMask] = value; } } + } + + /// + /// Removes an item from the buffer. + /// + /// The next available item + public T Dequeue() + { + var next = _consumerCursor.ReadAcquireFence() + 1; + while (_producerCursor.ReadAcquireFence() < next) // makes sure we read the data from _entries after we have read the producer cursor + { + Thread.SpinWait(1); + } + var result = this[next]; + _consumerCursor.WriteReleaseFence(next); // makes sure we read the data from _entries before we update the consumer cursor + return result; + } + + /// + /// Attempts to remove an items from the queue + /// + /// the items + /// True if successful + public bool TryDequeue(out T obj) + { + var next = _consumerCursor.ReadAcquireFence() + 1; + + if (_producerCursor.ReadAcquireFence() < next) + { + obj = default(T); + return false; + } + obj = Dequeue(); + return true; + } + + /// + /// Add an item to the buffer + /// + /// + public void Enqueue(T item) + { + var next = _producerCursor.ReadAcquireFence() + 1; + + long wrapPoint = next - _entries.Length; + long min = _consumerCursor.ReadAcquireFence(); + + while (wrapPoint > min) + { + min = _consumerCursor.ReadAcquireFence(); + Thread.SpinWait(1); + } + + this[next] = item; + _producerCursor.WriteReleaseFence(next); // makes sure we write the data in _entries before we update the producer cursor + } + + /// + /// The number of items in the buffer + /// + /// for indicative purposes only, may contain stale data + public int Count { get { return (int)(_producerCursor.ReadFullFence() - _consumerCursor.ReadFullFence()); } } + + private static int NextPowerOfTwo(int x) + { + var result = 2; + while (result < x) + { + result <<= 1; + } + return result; + } + + + } + public static class Volatile + { + private const int CacheLineSize = 64; + + [StructLayout(LayoutKind.Explicit, Size = CacheLineSize * 2)] + public struct PaddedLong + { + [FieldOffset(CacheLineSize)] + private long _value; + + /// + /// Create a new with the given initial value. + /// + /// Initial value + public PaddedLong(long value) + { + _value = value; + } + + /// + /// Read the value without applying any fence + /// + /// The current value + public long ReadUnfenced() + { + return _value; + } + + /// + /// Read the value applying acquire fence semantic + /// + /// The current value + public long ReadAcquireFence() + { + var value = _value; + Thread.MemoryBarrier(); + return value; + } + + /// + /// Read the value applying full fence semantic + /// + /// The current value + public long ReadFullFence() + { + Thread.MemoryBarrier(); + return _value; + } + + /// + /// Read the value applying a compiler only fence, no CPU fence is applied + /// + /// The current value + [MethodImpl(MethodImplOptions.NoOptimization)] + public long ReadCompilerOnlyFence() + { + return _value; + } + + /// + /// Write the value applying release fence semantic + /// + /// The new value + public void WriteReleaseFence(long newValue) + { + Thread.MemoryBarrier(); + _value = newValue; + } + + /// + /// Write the value applying full fence semantic + /// + /// The new value + public void WriteFullFence(long newValue) + { + Thread.MemoryBarrier(); + _value = newValue; + } + + /// + /// Write the value applying a compiler fence only, no CPU fence is applied + /// + /// The new value + [MethodImpl(MethodImplOptions.NoOptimization)] + public void WriteCompilerOnlyFence(long newValue) + { + _value = newValue; + } + + /// + /// Write without applying any fence + /// + /// The new value + public void WriteUnfenced(long newValue) + { + _value = newValue; + } + + /// + /// Atomically set the value to the given updated value if the current value equals the comparand + /// + /// The new value + /// The comparand (expected value) + /// + public bool AtomicCompareExchange(long newValue, long comparand) + { + return Interlocked.CompareExchange(ref _value, newValue, comparand) == comparand; + } + + /// + /// Atomically set the value to the given updated value + /// + /// The new value + /// The original value + public long AtomicExchange(long newValue) + { + return Interlocked.Exchange(ref _value, newValue); + } + + /// + /// Atomically add the given value to the current value and return the sum + /// + /// The value to be added + /// The sum of the current value and the given value + public long AtomicAddAndGet(long delta) + { + return Interlocked.Add(ref _value, delta); + } + + /// + /// Atomically increment the current value and return the new value + /// + /// The incremented value. + public long AtomicIncrementAndGet() + { + return Interlocked.Increment(ref _value); + } + + /// + /// Atomically increment the current value and return the new value + /// + /// The decremented value. + public long AtomicDecrementAndGet() + { + return Interlocked.Decrement(ref _value); + } + + /// + /// Returns the string representation of the current value. + /// + /// the string representation of the current value. + public override string ToString() + { + var value = ReadFullFence(); + return value.ToString(); + } + } + } +} diff --git a/BeatTogether.DedicatedServer.Ignorance/LICENSES.md b/BeatTogether.DedicatedServer.Ignorance/LICENSES.md new file mode 100644 index 00000000..7456c4fc --- /dev/null +++ b/BeatTogether.DedicatedServer.Ignorance/LICENSES.md @@ -0,0 +1,56 @@ +# Ignorance +MIT License + +Copyright (c) 2019 - 2021 Matt Coburn (Coburn, SoftwareGuy) +This software uses dependencies (Mirror, ENet-C#) that are also licensed under +the MIT License. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +# ENet-CSharp +MIT License + +Copyright (c) 2018 Stanislav Denisov (nxrighthere@gmail.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +# ENet +Copyright (c) 2002-2020 Lee Salzman + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/BeatTogether.DedicatedServer.Ignorance/README.md b/BeatTogether.DedicatedServer.Ignorance/README.md new file mode 100644 index 00000000..b9b233b8 --- /dev/null +++ b/BeatTogether.DedicatedServer.Ignorance/README.md @@ -0,0 +1,5 @@ +# BeatTogether.DedicatedServer.Ignorance +This library wraps ENet for use with the BeatTogether dedicated server. + +**Based on Ignorance [1.4.0r2 (LTS)](https://github.com/SoftwareGuy/Ignorance/releases/tag/v1.4.0r2).** + diff --git a/BeatTogether.DedicatedServer.Ignorance/Util/IgnoranceDebug.cs b/BeatTogether.DedicatedServer.Ignorance/Util/IgnoranceDebug.cs new file mode 100644 index 00000000..1406fe2c --- /dev/null +++ b/BeatTogether.DedicatedServer.Ignorance/Util/IgnoranceDebug.cs @@ -0,0 +1,20 @@ + +// ReSharper disable TemplateIsNotCompileTimeConstantProblem + +using Serilog; + +namespace BeatTogether.DedicatedServer.Ignorance.Util; + +public static class IgnoranceDebug +{ + public static ILogger? Logger = null; + + public static void Log(string message) => + Logger?.Verbose(message); + + public static void LogWarning(string message) => + Logger?.Warning(message); + + public static void LogError(string message) => + Logger?.Error(message); +} \ No newline at end of file diff --git a/BeatTogether.DedicatedServer.Ignorance/enet.dll b/BeatTogether.DedicatedServer.Ignorance/enet.dll new file mode 100644 index 00000000..f75351bb Binary files /dev/null and b/BeatTogether.DedicatedServer.Ignorance/enet.dll differ diff --git a/BeatTogether.DedicatedServer.Ignorance/libenet.so b/BeatTogether.DedicatedServer.Ignorance/libenet.so new file mode 100644 index 00000000..69e104cd Binary files /dev/null and b/BeatTogether.DedicatedServer.Ignorance/libenet.so differ diff --git a/BeatTogether.DedicatedServer.Instancing/Abstractions/IInstanceFactory.cs b/BeatTogether.DedicatedServer.Instancing/Abstractions/IInstanceFactory.cs new file mode 100644 index 00000000..cf67c6f3 --- /dev/null +++ b/BeatTogether.DedicatedServer.Instancing/Abstractions/IInstanceFactory.cs @@ -0,0 +1,11 @@ +using BeatTogether.DedicatedServer.Kernel.Abstractions; +using BeatTogether.Core.Abstractions; + +namespace BeatTogether.DedicatedServer.Instancing.Abstractions +{ + public interface IInstanceFactory + { + public IDedicatedInstance? CreateInstance( + IServerInstance serverInstance); + } +} diff --git a/BeatTogether.DedicatedServer.Instancing/Abstractions/IInstanceRegistry.cs b/BeatTogether.DedicatedServer.Instancing/Abstractions/IInstanceRegistry.cs new file mode 100644 index 00000000..05114182 --- /dev/null +++ b/BeatTogether.DedicatedServer.Instancing/Abstractions/IInstanceRegistry.cs @@ -0,0 +1,15 @@ +using BeatTogether.Core.Enums; +using BeatTogether.DedicatedServer.Kernel.Abstractions; +using System.Diagnostics.CodeAnalysis; + +namespace BeatTogether.DedicatedServer.Instancing.Abstractions +{ + public interface IInstanceRegistry + { + public bool AddInstance(IDedicatedInstance instance); + public bool RemoveInstance(IDedicatedInstance instance); + public bool TryGetInstance(string secret, [MaybeNullWhen(false)] out IDedicatedInstance instance); + public bool TryGetInstanceByCode(string code, [MaybeNullWhen(false)] out IDedicatedInstance instance); + public bool TryGetAvailablePublicServer(InvitePolicy invitePolicy, GameplayServerMode serverMode, SongSelectionMode songMode, GameplayServerControlSettings serverControlSettings, BeatmapDifficultyMask difficultyMask, GameplayModifiersMask modifiersMask, string songPackMasks, [MaybeNullWhen(false)] out IDedicatedInstance instance); + } +} diff --git a/BeatTogether.DedicatedServer.Node/Abstractions/IPortAllocator.cs b/BeatTogether.DedicatedServer.Instancing/Abstractions/IPortAllocator.cs similarity index 63% rename from BeatTogether.DedicatedServer.Node/Abstractions/IPortAllocator.cs rename to BeatTogether.DedicatedServer.Instancing/Abstractions/IPortAllocator.cs index ef13698e..39ac81e3 100644 --- a/BeatTogether.DedicatedServer.Node/Abstractions/IPortAllocator.cs +++ b/BeatTogether.DedicatedServer.Instancing/Abstractions/IPortAllocator.cs @@ -1,4 +1,4 @@ -namespace BeatTogether.DedicatedServer.Node.Abstractions +namespace BeatTogether.DedicatedServer.Instancing.Abstractions { public interface IPortAllocator { diff --git a/BeatTogether.DedicatedServer.Instancing/BeatTogether.DedicatedServer.Instancing.csproj b/BeatTogether.DedicatedServer.Instancing/BeatTogether.DedicatedServer.Instancing.csproj new file mode 100644 index 00000000..ebcdd38d --- /dev/null +++ b/BeatTogether.DedicatedServer.Instancing/BeatTogether.DedicatedServer.Instancing.csproj @@ -0,0 +1,22 @@ + + + + net6.0 + 9 + icon.png + BeatTogether Team + BeatTogether + https://github.com/beattogether/BeatTogether.DedicatedServer + 1.0.0 + enable + + + + + + + + + + + diff --git a/BeatTogether.DedicatedServer.Instancing/Configuration/InstancingConfiguration.cs b/BeatTogether.DedicatedServer.Instancing/Configuration/InstancingConfiguration.cs new file mode 100644 index 00000000..3bcd5368 --- /dev/null +++ b/BeatTogether.DedicatedServer.Instancing/Configuration/InstancingConfiguration.cs @@ -0,0 +1,11 @@ +using System; + +namespace BeatTogether.DedicatedServer.Instancing.Configuration +{ + public sealed class InstancingConfiguration + { + public string HostEndpoint { get; set; } = "127.0.0.1"; + public int BasePort { get; set; } = 30000; + public int MaximumSlots { get; set; } = 10000; + } +} diff --git a/BeatTogether.DedicatedServer.Instancing/Extensions/HostBuilderExtensions.cs b/BeatTogether.DedicatedServer.Instancing/Extensions/HostBuilderExtensions.cs new file mode 100644 index 00000000..c53498dc --- /dev/null +++ b/BeatTogether.DedicatedServer.Instancing/Extensions/HostBuilderExtensions.cs @@ -0,0 +1,27 @@ +using BeatTogether.DedicatedServer.Instancing.Configuration; +using BeatTogether.DedicatedServer.Instancing.Abstractions; +using BeatTogether.Extensions; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using BeatTogether.DedicatedServer.Kernel.Extensions; +using BeatTogether.Core.Abstractions; + +namespace BeatTogether.DedicatedServer.Instancing.Extensions +{ + public static class HostBuilderExtensions + { + public static IHostBuilder UseDedicatedServerInstancing(this IHostBuilder hostBuilder) => + hostBuilder + .ConfigureAppConfiguration() + .UseSerilog() + .UseDedicatedInstances() + .ConfigureServices((hostBuilderContext, services) => + services + .AddConfiguration("ServerConfiguration") + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + ); + } +} \ No newline at end of file diff --git a/BeatTogether.DedicatedServer.Instancing/Implimentations/ServerInstance.cs b/BeatTogether.DedicatedServer.Instancing/Implimentations/ServerInstance.cs new file mode 100644 index 00000000..77e1e24b --- /dev/null +++ b/BeatTogether.DedicatedServer.Instancing/Implimentations/ServerInstance.cs @@ -0,0 +1,46 @@ +using BeatTogether.Core.Abstractions; +using BeatTogether.Core.Enums; +using BeatTogether.Core.Models; +using BeatTogether.DedicatedServer.Kernel.Abstractions; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; + +namespace BeatTogether.DedicatedServer.Instancing.Implimentations +{ + public class ServerInstance : IServerInstance + { + private readonly IDedicatedInstance _ServerInstance; + + public ServerInstance(IDedicatedInstance serverInstance, IPEndPoint instanceEndPoint) + { + _ServerInstance = serverInstance; + InstanceEndPoint = instanceEndPoint; + } + + public string ServerName { get => _ServerInstance._configuration.ServerName; set => throw new NotImplementedException(); } + public IPEndPoint InstanceEndPoint { get; set; } + public string Secret { get => _ServerInstance._configuration.Secret; set => throw new NotImplementedException(); } + public string Code { get => _ServerInstance._configuration.Code; set => throw new NotImplementedException(); } + public MultiplayerGameState GameState { get => (MultiplayerGameState)_ServerInstance.State; set => throw new NotImplementedException(); } + public BeatmapDifficultyMask BeatmapDifficultyMask { get => _ServerInstance._configuration.BeatmapDifficultyMask; set => throw new NotImplementedException(); } + public GameplayModifiersMask GameplayModifiersMask { get => _ServerInstance._configuration.GameplayModifiersMask; set => throw new NotImplementedException(); } + public string SongPackMasks { get => _ServerInstance._configuration.SongPacksMask; set => throw new NotImplementedException(); } + public GameplayServerConfiguration GameplayServerConfiguration { get => _ServerInstance._configuration.GameplayServerConfiguration; set => throw new NotImplementedException(); } + public HashSet PlayerHashes { get => _ServerInstance.GetPlayerRegistry().Players.Select(p => p.HashedUserId).ToHashSet(); set => throw new NotImplementedException(); } + public string InstanceId { get => _ServerInstance._configuration.ServerId; set => throw new NotImplementedException(); } + public string ManagerId { get => _ServerInstance._configuration.ServerOwnerId; set => throw new NotImplementedException(); } + public bool PermanentManager { get => !string.IsNullOrEmpty(_ServerInstance._configuration.SetConstantManagerFromUserId); set => throw new NotImplementedException(); } + public long ServerStartJoinTimeout { get => _ServerInstance._configuration.DestroyInstanceTimeout; set => throw new NotImplementedException(); } + public bool NeverCloseServer { get => _ServerInstance._configuration.DestroyInstanceTimeout == -1; set => throw new NotImplementedException(); } + public long ResultScreenTime { get => _ServerInstance._configuration.CountdownConfig.ResultsScreenTime; set => throw new NotImplementedException(); } + public long BeatmapStartTime { get => _ServerInstance._configuration.CountdownConfig.BeatMapStartCountdownTime; set => throw new NotImplementedException(); } + public long PlayersReadyCountdownTime { get => _ServerInstance._configuration.CountdownConfig.CountdownTimePlayersReady; set => throw new NotImplementedException(); } + public bool AllowPerPlayerModifiers { get => _ServerInstance._configuration.AllowPerPlayerModifiers; set => throw new NotImplementedException(); } + public bool AllowPerPlayerDifficulties { get => _ServerInstance._configuration.AllowPerPlayerDifficulties; set => throw new NotImplementedException(); } + public bool AllowChroma { get => _ServerInstance._configuration.AllowChroma; set => throw new NotImplementedException(); } + public bool AllowME { get => _ServerInstance._configuration.AllowMappingExtensions; set => throw new NotImplementedException(); } + public bool AllowNE { get => _ServerInstance._configuration.AllowNoodleExtensions; set => throw new NotImplementedException(); } + } +} diff --git a/BeatTogether.DedicatedServer.Instancing/InstanceFactory.cs b/BeatTogether.DedicatedServer.Instancing/InstanceFactory.cs new file mode 100644 index 00000000..44c473a6 --- /dev/null +++ b/BeatTogether.DedicatedServer.Instancing/InstanceFactory.cs @@ -0,0 +1,104 @@ +using System; +using BeatTogether.DedicatedServer.Kernel.Abstractions; +using BeatTogether.DedicatedServer.Kernel.Configuration; +using BeatTogether.Core.Enums; +using BeatTogether.DedicatedServer.Instancing.Abstractions; +using Microsoft.Extensions.DependencyInjection; +using BeatTogether.Core.Abstractions; +using System.Net; +using BeatTogether.DedicatedServer.Instancing.Configuration; +using BeatTogether.Core.ServerMessaging; +using BeatTogether.DedicatedServer.Instancing.Implimentations; +using Serilog; + +namespace BeatTogether.DedicatedServer.Instancing +{ + public sealed class InstanceFactory : IInstanceFactory + { + private readonly IInstanceRegistry _instanceRegistry; + private readonly IServiceProvider _serviceProvider; + private readonly IPortAllocator _portAllocator; + private readonly InstancingConfiguration _config; + private readonly ILayer1? _SendEventsLayer; + private readonly ILogger _logger = Log.ForContext(); + + public InstanceFactory( + IInstanceRegistry instanceRegistry, + IServiceProvider serviceProvider, + IPortAllocator portAllocator, + InstancingConfiguration instancingConfiguration) + { + _instanceRegistry = instanceRegistry; + _serviceProvider = serviceProvider; + _portAllocator = portAllocator; + _config = instancingConfiguration; + + _SendEventsLayer = _serviceProvider.GetService(); + } + + public IDedicatedInstance? CreateInstance(IServerInstance serverInstance) + { + var Port = _portAllocator.AcquirePort(); + + if (!Port.HasValue) + return null; + + var scope = _serviceProvider.CreateScope(); + + var instanceConfig = scope.ServiceProvider.GetRequiredService(); + instanceConfig.Port = (int)Port!; + instanceConfig.Secret = serverInstance.Secret; + instanceConfig.Code = serverInstance.Code; + instanceConfig.ServerId = serverInstance.InstanceId; + _logger.Information("Server ID: " + instanceConfig.ServerId); + instanceConfig.ServerOwnerId = serverInstance.ManagerId; + _logger.Information("Server owner ID: " + instanceConfig.ServerOwnerId); + instanceConfig.GameplayServerConfiguration = serverInstance.GameplayServerConfiguration; + instanceConfig.GameplayModifiersMask = serverInstance.GameplayModifiersMask; + instanceConfig.BeatmapDifficultyMask = serverInstance.BeatmapDifficultyMask; + instanceConfig.SongPacksMask = serverInstance.SongPackMasks; + instanceConfig.DestroyInstanceTimeout = serverInstance.ServerStartJoinTimeout; + instanceConfig.ServerName = serverInstance.ServerName; + instanceConfig.CountdownConfig.BeatMapStartCountdownTime = Math.Max(serverInstance.BeatmapStartTime, 0L); + instanceConfig.CountdownConfig.ResultsScreenTime = Math.Max(serverInstance.ResultScreenTime, 0L); //TODO convert the dedi logic to use long instead of float + instanceConfig.AllowChroma = serverInstance.AllowChroma; + instanceConfig.AllowMappingExtensions = serverInstance.AllowME; + instanceConfig.AllowNoodleExtensions = serverInstance.AllowNE; + instanceConfig.AllowPerPlayerDifficulties = serverInstance.AllowPerPlayerDifficulties; + instanceConfig.AllowPerPlayerModifiers = serverInstance.AllowPerPlayerModifiers; + if (serverInstance.PermanentManager) + instanceConfig.SetConstantManagerFromUserId = serverInstance.ManagerId; + instanceConfig.CountdownConfig.CountdownTimePlayersReady = Math.Max(serverInstance.PlayersReadyCountdownTime, 0L); + if (instanceConfig.CountdownConfig.CountdownTimePlayersReady == 0L) + instanceConfig.CountdownConfig.CountdownTimePlayersReady = instanceConfig.GameplayServerConfiguration.GameplayServerMode == GameplayServerMode.Managed ? 15000L : 30000L; + var instance = scope.ServiceProvider.GetRequiredService(); + if (!_instanceRegistry.AddInstance(instance)) + { + return null; + + } + instance.StopEvent += HandleStopEvent; + + serverInstance.InstanceEndPoint = IPEndPoint.Parse($"{_config.HostEndpoint}:{instanceConfig.Port}"); + + //Subscribe to server events if the layer above allows this. + if(_SendEventsLayer != null) + { + instance.StopEvent += (dedi) => _SendEventsLayer.InstanceClosed(new ServerInstance(instance, serverInstance.InstanceEndPoint)); + instance.PlayerConnectedEvent += (player) => _SendEventsLayer.PlayerJoined(new ServerInstance(instance, serverInstance.InstanceEndPoint), player); + instance.PlayerDisconnectedEvent += (player) => _SendEventsLayer.PlayerLeft(new ServerInstance(instance, serverInstance.InstanceEndPoint), player); + instance.PlayerDisconnectBeforeJoining += (a, b, c) => _SendEventsLayer.InstancePlayersChanged(new ServerInstance(instance, serverInstance.InstanceEndPoint)); + instance.GameIsInLobby += (a, b) => _SendEventsLayer.InstanceStateChanged(new ServerInstance(instance, serverInstance.InstanceEndPoint)); + instance.UpdateInstanceEvent += (dedi) => _SendEventsLayer.InstanceConfigChanged(new ServerInstance(instance, serverInstance.InstanceEndPoint)); + } + + return instance; + } + + private void HandleStopEvent(IDedicatedInstance Instance) + { + _instanceRegistry.RemoveInstance(Instance); + _portAllocator.ReleasePort(Instance.Port); + } + } +} diff --git a/BeatTogether.DedicatedServer.Instancing/InstanceRegistry.cs b/BeatTogether.DedicatedServer.Instancing/InstanceRegistry.cs new file mode 100644 index 00000000..edf8f689 --- /dev/null +++ b/BeatTogether.DedicatedServer.Instancing/InstanceRegistry.cs @@ -0,0 +1,60 @@ +using BeatTogether.DedicatedServer.Kernel.Abstractions; +using BeatTogether.DedicatedServer.Instancing.Abstractions; +using System.Collections.Concurrent; +using System.Diagnostics.CodeAnalysis; +using BeatTogether.Core.Enums; +using System.Linq; + +namespace BeatTogether.DedicatedServer.Instancing +{ + public sealed class InstanceRegistry : IInstanceRegistry + { + private readonly ConcurrentDictionary _instances = new(); + private readonly ConcurrentDictionary _instancesByCode = new(); + + public bool AddInstance(IDedicatedInstance instance){ + if (_instances.TryAdd(instance._configuration.Secret, instance)) { + if(_instancesByCode.TryAdd(instance._configuration.Code, instance)) + return true; + _instances.TryRemove(instance._configuration.Secret, out _); + } + return false; + } + + public bool RemoveInstance(IDedicatedInstance instance) => _instances.TryRemove(instance._configuration.Secret, out _) && _instancesByCode.TryRemove(instance._configuration.Code, out _); + + public bool TryGetAvailablePublicServer(InvitePolicy invitePolicy, GameplayServerMode serverMode, SongSelectionMode songMode, GameplayServerControlSettings serverControlSettings, BeatmapDifficultyMask difficultyMask, GameplayModifiersMask modifiersMask, string songPackMasks, [MaybeNullWhen(false)] out IDedicatedInstance instance) + { + instance = null; + var AvaliableServers = _instances.Values.Where(s => + s._configuration.GameplayServerConfiguration.InvitePolicy == invitePolicy && + s._configuration.GameplayServerConfiguration.GameplayServerMode == serverMode && + s._configuration.GameplayServerConfiguration.SongSelectionMode == songMode && + s._configuration.GameplayServerConfiguration.GameplayServerControlSettings == serverControlSettings && + s._configuration.BeatmapDifficultyMask == difficultyMask && + s._configuration.GameplayModifiersMask == modifiersMask && + s._configuration.SongPacksMask == songPackMasks + ); + if (!AvaliableServers.Any()) + return false; + var server = AvaliableServers.First(); + foreach (var publicServer in AvaliableServers) + { + if ((publicServer.GetPlayerRegistry().GetPlayerCount() < publicServer._configuration.GameplayServerConfiguration.MaxPlayerCount && publicServer.GetPlayerRegistry().GetPlayerCount() > server.GetPlayerRegistry().GetPlayerCount())) + { + server = publicServer; + } + } + if (server.GetPlayerRegistry().GetPlayerCount() >= server._configuration.GameplayServerConfiguration.MaxPlayerCount) + return false; + instance = server; + return true; + } + + public bool TryGetInstance(string secret, [MaybeNullWhen(false)] out IDedicatedInstance instance) => + _instances.TryGetValue(secret, out instance); + + public bool TryGetInstanceByCode(string code, [MaybeNullWhen(false)] out IDedicatedInstance instance) => + _instancesByCode.TryGetValue(code, out instance); + } +} diff --git a/BeatTogether.DedicatedServer.Instancing/LayerService.cs b/BeatTogether.DedicatedServer.Instancing/LayerService.cs new file mode 100644 index 00000000..fd496a74 --- /dev/null +++ b/BeatTogether.DedicatedServer.Instancing/LayerService.cs @@ -0,0 +1,93 @@ +using BeatTogether.Core.Abstractions; +using BeatTogether.Core.Enums; +using BeatTogether.DedicatedServer.Instancing.Abstractions; +using BeatTogether.DedicatedServer.Instancing.Configuration; +using BeatTogether.DedicatedServer.Instancing.Implimentations; +using Serilog; +using System.Net; +using System.Threading.Tasks; + +namespace BeatTogether.DedicatedServer.Instancing +{ + public class LayerService : ILayer2 + { + private readonly IInstanceRegistry _instanceRegistry; + private readonly IInstanceFactory _instanceFactory; + private readonly InstancingConfiguration _instancingConfiguration; + private readonly ILogger _logger = Log.ForContext(); + + + public LayerService(IInstanceRegistry instanceRegistry, IInstanceFactory instanceFactory, InstancingConfiguration instancingConfiguration) + { + _instanceRegistry = instanceRegistry; + _instancingConfiguration = instancingConfiguration; + _instanceFactory = instanceFactory; + } + + public Task CloseInstance(string InstanceSecret) + { + if(_instanceRegistry.TryGetInstance(InstanceSecret, out var Instance)){ + Instance.Stop(); + } + return Task.CompletedTask; + } + + public async Task CreateInstance(IServerInstance serverInstance) + { + var inst = _instanceFactory.CreateInstance(serverInstance); + if(inst != null) + await inst.Start(); + return inst != null; + } + + public Task DisconnectPlayer(string InstanceSecret, string PlayerUserId) + { + if (!_instanceRegistry.TryGetInstance(InstanceSecret, out var instance)) + return Task.CompletedTask; + if (instance.GetPlayerRegistry().TryGetPlayer(PlayerUserId, out var player)) + instance.DisconnectPlayer(player); + + return Task.CompletedTask; + } + + public Task GetAvailablePublicServer(InvitePolicy invitePolicy, GameplayServerMode serverMode, SongSelectionMode songMode, GameplayServerControlSettings serverControlSettings, BeatmapDifficultyMask difficultyMask, GameplayModifiersMask modifiersMask, string songPackMasks) + { + IServerInstance? serverInstance = null; + if (_instanceRegistry.TryGetAvailablePublicServer(invitePolicy, serverMode, songMode, serverControlSettings, difficultyMask, modifiersMask, songPackMasks, out var instance)) + { + serverInstance = new ServerInstance(instance, IPEndPoint.Parse($"{_instancingConfiguration.HostEndpoint}:{instance._configuration.Port}")); + } + return Task.FromResult(serverInstance); + } + + public Task GetServer(string secret) + { + IServerInstance? serverInstance = null; + if (_instanceRegistry.TryGetInstance(secret, out var instance)) + { + serverInstance = new ServerInstance(instance, IPEndPoint.Parse($"{_instancingConfiguration.HostEndpoint}:{instance._configuration.Port}")); + } + return Task.FromResult(serverInstance); + } + + public Task GetServerByCode(string code) + { + IServerInstance? serverInstance = null; + if (_instanceRegistry.TryGetInstanceByCode(code, out var instance)) + { + serverInstance = new ServerInstance(instance, IPEndPoint.Parse($"{_instancingConfiguration.HostEndpoint}:{instance._configuration.Port}")); + } + return Task.FromResult(serverInstance); + } + + public Task SetPlayerSessionData(string serverSecret, IPlayer playerSessionData) + { + _logger.Information("Setting playerSessionData: " + playerSessionData.PlayerSessionId + " In instance: " + serverSecret); + if (!_instanceRegistry.TryGetInstance(serverSecret, out var instance)) + return Task.FromResult(false); + _logger.Information("Found instance, setting session data"); + instance.GetPlayerRegistry().AddExtraPlayerSessionData(playerSessionData); + return Task.FromResult(true); + } + } +} diff --git a/BeatTogether.DedicatedServer.Node/PortAllocator.cs b/BeatTogether.DedicatedServer.Instancing/PortAllocator.cs similarity index 81% rename from BeatTogether.DedicatedServer.Node/PortAllocator.cs rename to BeatTogether.DedicatedServer.Instancing/PortAllocator.cs index 5fcd20be..06a88a3d 100644 --- a/BeatTogether.DedicatedServer.Node/PortAllocator.cs +++ b/BeatTogether.DedicatedServer.Instancing/PortAllocator.cs @@ -1,13 +1,13 @@ -using BeatTogether.DedicatedServer.Node.Abstractions; -using BeatTogether.DedicatedServer.Node.Configuration; +using BeatTogether.DedicatedServer.Instancing.Abstractions; +using BeatTogether.DedicatedServer.Instancing.Configuration; using System.Collections.Generic; using System.Linq; -namespace BeatTogether.DedicatedServer.Node +namespace BeatTogether.DedicatedServer.Instancing { public sealed class PortAllocator : IPortAllocator { - private readonly NodeConfiguration _configuration; + private readonly InstancingConfiguration _configuration; private readonly object _lock = new(); @@ -17,7 +17,7 @@ public sealed class PortAllocator : IPortAllocator private int _lastPort; public PortAllocator( - NodeConfiguration configuration) + InstancingConfiguration configuration) { _configuration = configuration; @@ -41,6 +41,7 @@ public PortAllocator( _acquiredPorts.Add(port); return port; } + } public bool ReleasePort(int port) @@ -52,6 +53,7 @@ public bool ReleasePort(int port) _releasedPorts.Add(port); return true; } + } } } diff --git a/BeatTogether.DedicatedServer.Interface/BeatTogether.DedicatedServer.Interface.csproj b/BeatTogether.DedicatedServer.Interface/BeatTogether.DedicatedServer.Interface.csproj index ec8bfa1c..991752d4 100644 --- a/BeatTogether.DedicatedServer.Interface/BeatTogether.DedicatedServer.Interface.csproj +++ b/BeatTogether.DedicatedServer.Interface/BeatTogether.DedicatedServer.Interface.csproj @@ -7,7 +7,7 @@ BeatTogether Team BeatTogether https://github.com/beattogether/BeatTogether.DedicatedServer - 1.6.0 + 2.0.2 enable @@ -17,6 +17,7 @@ + diff --git a/BeatTogether.DedicatedServer.Interface/Enums/CountdownState.cs b/BeatTogether.DedicatedServer.Interface/Enums/CountdownState.cs deleted file mode 100644 index 652daf71..00000000 --- a/BeatTogether.DedicatedServer.Interface/Enums/CountdownState.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace BeatTogether.DedicatedServer.Interface.Enums -{ - public enum CountdownState : byte - { - NotCountingDown = 0, - CountingDown = 1, - StartBeatmapCountdown = 2, - WaitingForEntitlement = 3 - } -} diff --git a/BeatTogether.DedicatedServer.Interface/Enums/DiscoveryPolicy.cs b/BeatTogether.DedicatedServer.Interface/Enums/DiscoveryPolicy.cs deleted file mode 100644 index 06a31e51..00000000 --- a/BeatTogether.DedicatedServer.Interface/Enums/DiscoveryPolicy.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace BeatTogether.DedicatedServer.Interface.Enums -{ - public enum DiscoveryPolicy : byte - { - Hidden = 0, - WithCode = 1, - Public = 2 - } -} diff --git a/BeatTogether.DedicatedServer.Interface/Enums/GameplayServerControlSettings.cs b/BeatTogether.DedicatedServer.Interface/Enums/GameplayServerControlSettings.cs deleted file mode 100644 index c4eb0165..00000000 --- a/BeatTogether.DedicatedServer.Interface/Enums/GameplayServerControlSettings.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; - -namespace BeatTogether.DedicatedServer.Interface.Enums -{ - [Flags] - public enum GameplayServerControlSettings - { - None = 0, - AllowModifierSelection = 1, - AllowSpectate = 2, - All = 3 - } -} diff --git a/BeatTogether.DedicatedServer.Interface/Enums/GameplayServerMode.cs b/BeatTogether.DedicatedServer.Interface/Enums/GameplayServerMode.cs deleted file mode 100644 index 1ad500fc..00000000 --- a/BeatTogether.DedicatedServer.Interface/Enums/GameplayServerMode.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace BeatTogether.DedicatedServer.Interface.Enums -{ - public enum GameplayServerMode - { - Countdown = 0, - Managed = 1, - QuickStartOneSong = 2, - Tournament = 3 - } -} diff --git a/BeatTogether.DedicatedServer.Interface/Enums/GameplayState.cs b/BeatTogether.DedicatedServer.Interface/Enums/GameplayState.cs deleted file mode 100644 index 9b22858e..00000000 --- a/BeatTogether.DedicatedServer.Interface/Enums/GameplayState.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace BeatTogether.DedicatedServer.Interface.Enums -{ - public enum GameplayState : byte - { - None = 0, - SceneLoad = 1, - SongLoad = 1, - Gameplay = 2, - Results = 3 - } -} diff --git a/BeatTogether.DedicatedServer.Interface/Enums/InvitePolicy.cs b/BeatTogether.DedicatedServer.Interface/Enums/InvitePolicy.cs deleted file mode 100644 index 429dc2c3..00000000 --- a/BeatTogether.DedicatedServer.Interface/Enums/InvitePolicy.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace BeatTogether.DedicatedServer.Interface.Enums -{ - public enum InvitePolicy : byte - { - OnlyConnectionOwnerCanInvite = 0, - AnyoneCanInvite = 1 - } -} diff --git a/BeatTogether.DedicatedServer.Interface/Enums/MultiplayerGameState.cs b/BeatTogether.DedicatedServer.Interface/Enums/MultiplayerGameState.cs deleted file mode 100644 index 792b010b..00000000 --- a/BeatTogether.DedicatedServer.Interface/Enums/MultiplayerGameState.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace BeatTogether.DedicatedServer.Interface.Enums -{ - public enum MultiplayerGameState : byte - { - None = 0, - Lobby = 1, - Game = 2 - } -} diff --git a/BeatTogether.DedicatedServer.Interface/Enums/SongSelectionMode.cs b/BeatTogether.DedicatedServer.Interface/Enums/SongSelectionMode.cs deleted file mode 100644 index 532dc61b..00000000 --- a/BeatTogether.DedicatedServer.Interface/Enums/SongSelectionMode.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace BeatTogether.DedicatedServer.Interface.Enums -{ - public enum SongSelectionMode - { - Vote = 0, - Random = 1, - ManagerPicks = 2, - RandomPlayerPicks = 3, - ServerPicks = 4 - } -} diff --git a/BeatTogether.DedicatedServer.Interface/Events/LevelCompletionResultsEvent.cs b/BeatTogether.DedicatedServer.Interface/Events/LevelCompletionResultsEvent.cs deleted file mode 100644 index 8c9b7e64..00000000 --- a/BeatTogether.DedicatedServer.Interface/Events/LevelCompletionResultsEvent.cs +++ /dev/null @@ -1,10 +0,0 @@ -using BeatTogether.DedicatedServer.Interface.Models; -using System.Collections.Generic; - -namespace BeatTogether.DedicatedServer.Interface.Events -{ - public sealed record LevelCompletionResultsEvent( - string Secret, - BeatmapIdentifier Beatmap, - List<(string, BeatmapDifficulty, LevelCompletionResults)> Results); -} diff --git a/BeatTogether.DedicatedServer.Interface/Events/MatchmakingServerStoppedEvent.cs b/BeatTogether.DedicatedServer.Interface/Events/MatchmakingServerStoppedEvent.cs index a0b984f2..f0889094 100644 --- a/BeatTogether.DedicatedServer.Interface/Events/MatchmakingServerStoppedEvent.cs +++ b/BeatTogether.DedicatedServer.Interface/Events/MatchmakingServerStoppedEvent.cs @@ -1,4 +1,5 @@ namespace BeatTogether.DedicatedServer.Interface.Events { - public sealed record MatchmakingServerStoppedEvent(string Secret); + public sealed record MatchmakingServerStoppedEvent( + string Secret); } diff --git a/BeatTogether.DedicatedServer.Interface/Events/NodeOnlineEvent.cs b/BeatTogether.DedicatedServer.Interface/Events/NodeOnlineEvent.cs index d90479f7..6c8a4b10 100644 --- a/BeatTogether.DedicatedServer.Interface/Events/NodeOnlineEvent.cs +++ b/BeatTogether.DedicatedServer.Interface/Events/NodeOnlineEvent.cs @@ -1,6 +1,7 @@ -using System.Net; - + namespace BeatTogether.DedicatedServer.Interface.Events { - public sealed record NodeOnlineEvent(string endPoint, string NodeVersion); + public sealed record NodeOnlineEvent( + string EndPoint, + string NodeVersion); } \ No newline at end of file diff --git a/BeatTogether.DedicatedServer.Interface/Events/NodeReceivedPlayerEncryptionEvent.cs b/BeatTogether.DedicatedServer.Interface/Events/NodeReceivedPlayerEncryptionEvent.cs deleted file mode 100644 index afdffbf7..00000000 --- a/BeatTogether.DedicatedServer.Interface/Events/NodeReceivedPlayerEncryptionEvent.cs +++ /dev/null @@ -1,4 +0,0 @@ -namespace BeatTogether.DedicatedServer.Interface.Events -{ - public sealed record NodeReceivedPlayerEncryptionEvent(string endPoint, string PlayerEndPoint); -} \ No newline at end of file diff --git a/BeatTogether.DedicatedServer.Interface/Events/NodeReceivedPlayerSessionDataEvent.cs b/BeatTogether.DedicatedServer.Interface/Events/NodeReceivedPlayerSessionDataEvent.cs new file mode 100644 index 00000000..92696207 --- /dev/null +++ b/BeatTogether.DedicatedServer.Interface/Events/NodeReceivedPlayerSessionDataEvent.cs @@ -0,0 +1,6 @@ +namespace BeatTogether.DedicatedServer.Interface.Events +{ + public sealed record NodeReceivedPlayerSessionDataEvent( + string EndPoint, + string PlayerSessionId); +} \ No newline at end of file diff --git a/BeatTogether.DedicatedServer.Interface/Events/NodeStartedEvent.cs b/BeatTogether.DedicatedServer.Interface/Events/NodeStartedEvent.cs index fefe835d..06f5501f 100644 --- a/BeatTogether.DedicatedServer.Interface/Events/NodeStartedEvent.cs +++ b/BeatTogether.DedicatedServer.Interface/Events/NodeStartedEvent.cs @@ -1,6 +1,7 @@ -using System.Net; - + namespace BeatTogether.DedicatedServer.Interface.Events { - public sealed record NodeStartedEvent(string endPoint, string NodeVersion); + public sealed record NodeStartedEvent( + string EndPoint, + string NodeVersion); } \ No newline at end of file diff --git a/BeatTogether.DedicatedServer.Interface/Events/PlayerJoinEvent.cs b/BeatTogether.DedicatedServer.Interface/Events/PlayerJoinEvent.cs index c27619c0..4b406be6 100644 --- a/BeatTogether.DedicatedServer.Interface/Events/PlayerJoinEvent.cs +++ b/BeatTogether.DedicatedServer.Interface/Events/PlayerJoinEvent.cs @@ -1,7 +1,7 @@ -using BeatTogether.DedicatedServer.Interface.Models; -using System.Net; - + namespace BeatTogether.DedicatedServer.Interface.Events { - public sealed record PlayerJoinEvent(string Secret, string EndPoint, string UserId, string UserName, byte ConnectionId, int SortId, AvatarData AvatarData); + public sealed record PlayerJoinEvent( + string Secret, + string HashedUserId); } diff --git a/BeatTogether.DedicatedServer.Interface/Events/PlayerLeaveServerEvent.cs b/BeatTogether.DedicatedServer.Interface/Events/PlayerLeaveServerEvent.cs index 44dbe155..3baf7829 100644 --- a/BeatTogether.DedicatedServer.Interface/Events/PlayerLeaveServerEvent.cs +++ b/BeatTogether.DedicatedServer.Interface/Events/PlayerLeaveServerEvent.cs @@ -1,6 +1,7 @@ -using System.Net; - + namespace BeatTogether.DedicatedServer.Interface.Events { - public sealed record PlayerLeaveServerEvent(string Secret, string UserId, string endPoint, int NewPlayerCount); + public sealed record PlayerLeaveServerEvent( + string Secret, + string HashedUserId); } diff --git a/BeatTogether.DedicatedServer.Interface/Events/SelectedbeatmapEvent.cs b/BeatTogether.DedicatedServer.Interface/Events/SelectedbeatmapEvent.cs deleted file mode 100644 index d4320dd6..00000000 --- a/BeatTogether.DedicatedServer.Interface/Events/SelectedbeatmapEvent.cs +++ /dev/null @@ -1,15 +0,0 @@ -using BeatTogether.DedicatedServer.Interface.Models; -using System; - -namespace BeatTogether.DedicatedServer.Interface.Events -{ - public sealed record SelectedBeatmapEvent( - string Secret, - string LevelId, - string Characteristic, - uint Difficulty, - bool Gameplay, - GameplayModifiers GameplayModifiers, - DateTime CountdownEnd //If in gameplay then this is when the beatmap started - ); -} diff --git a/BeatTogether.DedicatedServer.Interface/Events/ServerInGameplayEvent.cs b/BeatTogether.DedicatedServer.Interface/Events/ServerInGameplayEvent.cs index c618bf7c..a644b50c 100644 --- a/BeatTogether.DedicatedServer.Interface/Events/ServerInGameplayEvent.cs +++ b/BeatTogether.DedicatedServer.Interface/Events/ServerInGameplayEvent.cs @@ -1,8 +1,9 @@ - +using BeatTogether.Core.Enums; + namespace BeatTogether.DedicatedServer.Interface.Events { public sealed record ServerInGameplayEvent( string Secret, - bool InGame + MultiplayerGameState MultiplayerGameState ); } diff --git a/BeatTogether.DedicatedServer.Interface/Events/UpdateInstanceConfigEvent.cs b/BeatTogether.DedicatedServer.Interface/Events/UpdateInstanceConfigEvent.cs index 74a92be0..e470e960 100644 --- a/BeatTogether.DedicatedServer.Interface/Events/UpdateInstanceConfigEvent.cs +++ b/BeatTogether.DedicatedServer.Interface/Events/UpdateInstanceConfigEvent.cs @@ -1,10 +1,7 @@ -using BeatTogether.DedicatedServer.Interface.Models; +using BeatTogether.Core.ServerMessaging.Models; namespace BeatTogether.DedicatedServer.Interface.Events { public sealed record UpdateInstanceConfigEvent( - string Secret, //Cannot change the secret - string Code, //Use special mod to change this (patreon only or something) - string ServerName, - GameplayServerConfiguration Configuration); + Server ServerInsance); } diff --git a/BeatTogether.DedicatedServer.Interface/Events/UpdatePlayersEvent.cs b/BeatTogether.DedicatedServer.Interface/Events/UpdatePlayersEvent.cs new file mode 100644 index 00000000..08d45ded --- /dev/null +++ b/BeatTogether.DedicatedServer.Interface/Events/UpdatePlayersEvent.cs @@ -0,0 +1,6 @@ +namespace BeatTogether.DedicatedServer.Interface.Events +{ + public sealed record UpdatePlayersEvent( + string Secret, + string[] HashedUserIds); +} diff --git a/BeatTogether.DedicatedServer.Interface/Events/UpdateServerEvent.cs b/BeatTogether.DedicatedServer.Interface/Events/UpdateServerEvent.cs deleted file mode 100644 index 4e786a0d..00000000 --- a/BeatTogether.DedicatedServer.Interface/Events/UpdateServerEvent.cs +++ /dev/null @@ -1,6 +0,0 @@ -using BeatTogether.DedicatedServer.Interface.Models; - -namespace BeatTogether.DedicatedServer.Interface.Events -{ - public sealed record UpdateServerEvent(string Secret, GameplayServerConfiguration Configuration, int Port, string ManagerId, string ServerId, string ServerName, float DestroyInstanceTimeout, string ConstantManager, bool PerPlayerDifficulties, bool PerPlayerModifiers, bool Chroma, bool MappingExtensions, bool NoodleExtensions, float KickBeforeEntitlementTimeout, float ReadyCountdownTime, float MapStartCountdownTime, float ResultsTime); -} diff --git a/BeatTogether.DedicatedServer.Interface/Events/UpdateStatusEvent.cs b/BeatTogether.DedicatedServer.Interface/Events/UpdateStatusEvent.cs deleted file mode 100644 index c70b0264..00000000 --- a/BeatTogether.DedicatedServer.Interface/Events/UpdateStatusEvent.cs +++ /dev/null @@ -1,11 +0,0 @@ -using BeatTogether.DedicatedServer.Interface.Enums; - -namespace BeatTogether.DedicatedServer.Interface.Events -{ - public sealed record UpdateStatusEvent( - string Secret, - CountdownState CountdownState, - MultiplayerGameState GameState, - GameplayState GameplayState - ); -} diff --git a/BeatTogether.DedicatedServer.Interface/IMatchmakingService.cs b/BeatTogether.DedicatedServer.Interface/IMatchmakingService.cs index 3af5f399..fa061a80 100644 --- a/BeatTogether.DedicatedServer.Interface/IMatchmakingService.cs +++ b/BeatTogether.DedicatedServer.Interface/IMatchmakingService.cs @@ -17,17 +17,14 @@ public override void Build(IServiceContractBuilder builder) => .UseName("DedicatedServer") .AddInterface() .AddEvent() + .AddEvent() + .AddEvent() .AddEvent() .AddEvent() - .AddEvent() + .AddEvent() .AddEvent() - //.AddEvent() - .AddEvent() - //.AddEvent() - //.AddEvent() - //.AddEvent() - .AddEvent() - .AddEvent(); + .AddEvent() + .AddEvent(); } } } diff --git a/BeatTogether.DedicatedServer.Interface/Models/AvatarData.cs b/BeatTogether.DedicatedServer.Interface/Models/AvatarData.cs deleted file mode 100644 index 867a3138..00000000 --- a/BeatTogether.DedicatedServer.Interface/Models/AvatarData.cs +++ /dev/null @@ -1,25 +0,0 @@ -using BeatTogether.DedicatedServer.Interface.Enums; -using System; -using System.Drawing; - -namespace BeatTogether.DedicatedServer.Interface.Models -{ - public record AvatarData( - string HeadTopId, - Color HeadTopPrimaryColor, - Color HeadTopSecondaryColor, - string GlassesId, - Color GlassesColor, - string FacialHairId, - Color FacialHairColor, - string HandsId, - Color HandsColor, - string ClothesId, - Color ClothesPrimaryColor, - Color ClothesSecondaryColor, - Color ClothesDetailColor, - string SkinColorId, - string EyesId, - string MouthId - ); -} diff --git a/BeatTogether.DedicatedServer.Interface/Models/BeatmapIdentifier.cs b/BeatTogether.DedicatedServer.Interface/Models/BeatmapIdentifier.cs deleted file mode 100644 index 8cadeb37..00000000 --- a/BeatTogether.DedicatedServer.Interface/Models/BeatmapIdentifier.cs +++ /dev/null @@ -1,14 +0,0 @@ - -namespace BeatTogether.DedicatedServer.Interface.Models -{ - public record BeatmapIdentifier(string LevelId, string Characteristic, BeatmapDifficulty Difficulty); - - public enum BeatmapDifficulty : uint - { - Easy = 0, - Normal = 1, - Hard = 2, - Expert = 3, - ExpertPlus = 4 - } -} diff --git a/BeatTogether.DedicatedServer.Interface/Models/GameplayModifiers.cs b/BeatTogether.DedicatedServer.Interface/Models/GameplayModifiers.cs deleted file mode 100644 index a652f8af..00000000 --- a/BeatTogether.DedicatedServer.Interface/Models/GameplayModifiers.cs +++ /dev/null @@ -1,44 +0,0 @@ - -namespace BeatTogether.DedicatedServer.Interface.Models -{ - public record GameplayModifiers( - EnergyType Energy, - bool NoFailOn0Energy, - bool DemoNoFail, - bool InstaFail, - bool FailOnSaberClash, - EnabledObstacleType EnabledObstacle, - bool DemoNoObstacles, - bool FastNotes, - bool StrictAngles, - bool DisappearingArrows, - bool GhostNotes, - bool NoBombs, - SongSpeed Speed, - bool NoArrows, - bool ProMode, - bool ZenMode, - bool SmallCubes - ); - - public enum EnabledObstacleType - { - All, - FullHeightOnly, - NoObstacles - } - - public enum EnergyType - { - Bar, - Battery - } - - public enum SongSpeed - { - Normal, - Faster, - Slower, - SuperFast - } -} diff --git a/BeatTogether.DedicatedServer.Interface/Models/GameplayServerConfiguration.cs b/BeatTogether.DedicatedServer.Interface/Models/GameplayServerConfiguration.cs deleted file mode 100644 index 6250cbc0..00000000 --- a/BeatTogether.DedicatedServer.Interface/Models/GameplayServerConfiguration.cs +++ /dev/null @@ -1,12 +0,0 @@ -using BeatTogether.DedicatedServer.Interface.Enums; - -namespace BeatTogether.DedicatedServer.Interface.Models -{ - public record GameplayServerConfiguration( - int MaxPlayerCount, - DiscoveryPolicy DiscoveryPolicy, - InvitePolicy InvitePolicy, - GameplayServerMode GameplayServerMode, - SongSelectionMode SongSelectionMode, - GameplayServerControlSettings GameplayServerControlSettings); -} diff --git a/BeatTogether.DedicatedServer.Interface/Models/LevelCompletionResults.cs b/BeatTogether.DedicatedServer.Interface/Models/LevelCompletionResults.cs deleted file mode 100644 index fa917eee..00000000 --- a/BeatTogether.DedicatedServer.Interface/Models/LevelCompletionResults.cs +++ /dev/null @@ -1,52 +0,0 @@ - -namespace BeatTogether.DedicatedServer.Interface.Models -{ - public record LevelCompletionResults(GameplayModifiers GameplayModifiers, - int ModifiedScore, - int MultipliedScore, - Rank Rank, - bool FullCombo, - float LeftSaberMovementDistance, - float RightSaberMovementDistance, - float LeftHandMovementDistance, - float RightHandMovementDistance, - LevelEndStateType LevelEndStateType, - LevelEndAction LevelEndAction, - float Energy, - int GoodCutsCount, - int BadCutsCount, - int MissedCount, - int NotGoodCount, - int OkCount, - int MaxCutScore, - int TotalCutScore, - int GoodCutsCountForNotesWithFullScoreScoringType, - float AverageCenterDistanceCutScoreForNotesWithFullScoreScoringType, - float AverageCutScoreForNotesWithFullScoreScoringType, - int MaxCombo, - float EndSongTime - ); - public enum Rank - { - E, - D, - C, - B, - A, - S, - SS, - SSS - } - public enum LevelEndAction - { - None, - Quit, - Restart - } - public enum LevelEndStateType - { - Incomplete, - Cleared, - Failed - } -} diff --git a/BeatTogether.DedicatedServer.Interface/Requests/CreateMatchmakingServerRequest.cs b/BeatTogether.DedicatedServer.Interface/Requests/CreateMatchmakingServerRequest.cs index ab7fd119..57dd35b9 100644 --- a/BeatTogether.DedicatedServer.Interface/Requests/CreateMatchmakingServerRequest.cs +++ b/BeatTogether.DedicatedServer.Interface/Requests/CreateMatchmakingServerRequest.cs @@ -1,21 +1,7 @@ -using BeatTogether.DedicatedServer.Interface.Models; +using BeatTogether.Core.ServerMessaging.Models; namespace BeatTogether.DedicatedServer.Interface.Requests { public record CreateMatchmakingServerRequest( - string Secret, - string ManagerId, - GameplayServerConfiguration Configuration, - bool PermanentManager = true, - float Timeout = 10f, - string ServerName = "", - float resultScreenTime = 20.0f, - float BeatmapStartTime = 5.0f, - float PlayersReadyCountdownTime = 0f, - bool AllowPerPlayerModifiers = false, - bool AllowPerPlayerDifficulties = false, - bool AllowChroma = true, - bool AllowME = true, - bool AllowNE = true - ); + Server Server); } diff --git a/BeatTogether.DedicatedServer.Interface/Responses/CreateMatchmakingServerResponse.cs b/BeatTogether.DedicatedServer.Interface/Responses/CreateMatchmakingServerResponse.cs index 3a122e1f..20d2bc16 100644 --- a/BeatTogether.DedicatedServer.Interface/Responses/CreateMatchmakingServerResponse.cs +++ b/BeatTogether.DedicatedServer.Interface/Responses/CreateMatchmakingServerResponse.cs @@ -9,9 +9,7 @@ public enum CreateMatchmakingServerError public record CreateMatchmakingServerResponse( CreateMatchmakingServerError Error, - string? RemoteEndPoint = null, - byte[]? Random = null, - byte[]? PublicKey = null) + string RemoteEndPoint) { public bool Success => Error == default; } diff --git a/BeatTogether.DedicatedServer.Kernel/Abstractions/BaseHandshakeMessageHandler.cs b/BeatTogether.DedicatedServer.Kernel/Abstractions/BaseHandshakeMessageHandler.cs deleted file mode 100644 index 04d67198..00000000 --- a/BeatTogether.DedicatedServer.Kernel/Abstractions/BaseHandshakeMessageHandler.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Threading.Tasks; -using BeatTogether.Core.Messaging.Abstractions; -using BeatTogether.DedicatedServer.Kernel.Handshake; - -namespace BeatTogether.DedicatedServer.Kernel.Abstractions -{ - public abstract class BaseHandshakeMessageHandler : IHandshakeMessageHandler - where TMessage : class, IMessage - { - public abstract Task Handle(HandshakeSession session, TMessage message); - - public Task Handle(HandshakeSession session, IMessage message) => - Handle(session, (TMessage) message); - } -} \ No newline at end of file diff --git a/BeatTogether.DedicatedServer.Kernel/Abstractions/BasePacketHandler.cs b/BeatTogether.DedicatedServer.Kernel/Abstractions/BasePacketHandler.cs index 0b9dd5ba..46935353 100644 --- a/BeatTogether.DedicatedServer.Kernel/Abstractions/BasePacketHandler.cs +++ b/BeatTogether.DedicatedServer.Kernel/Abstractions/BasePacketHandler.cs @@ -1,14 +1,13 @@ -using BeatTogether.LiteNetLib.Abstractions; -using System.Threading.Tasks; +using BeatTogether.DedicatedServer.Messaging.Abstractions; namespace BeatTogether.DedicatedServer.Kernel.Abstractions { public abstract class BasePacketHandler : IPacketHandler where TPacket : class, INetSerializable { - public abstract Task Handle(IPlayer sender, TPacket packet); + public abstract void Handle(IPlayer sender, TPacket packet); - public Task Handle(IPlayer sender, INetSerializable packet) => + public void Handle(IPlayer sender, INetSerializable packet) => Handle(sender, (TPacket)packet); } } diff --git a/BeatTogether.DedicatedServer.Kernel/Abstractions/IDedicatedInstance.cs b/BeatTogether.DedicatedServer.Kernel/Abstractions/IDedicatedInstance.cs index 0c7c2386..957529a7 100644 --- a/BeatTogether.DedicatedServer.Kernel/Abstractions/IDedicatedInstance.cs +++ b/BeatTogether.DedicatedServer.Kernel/Abstractions/IDedicatedInstance.cs @@ -2,8 +2,8 @@ using System.Net; using System.Threading; using System.Threading.Tasks; +using BeatTogether.Core.Enums; using BeatTogether.DedicatedServer.Kernel.Configuration; -using BeatTogether.DedicatedServer.Messaging.Enums; namespace BeatTogether.DedicatedServer.Kernel.Abstractions { @@ -11,29 +11,21 @@ public interface IDedicatedInstance { //event Action StartEvent; event Action StopEvent; - event Action PlayerConnectedEvent; - event Action PlayerDisconnectedEvent; - event Action PlayerDisconnectBeforeJoining; + event Action PlayerConnectedEvent; + event Action PlayerDisconnectedEvent; + event Action PlayerDisconnectBeforeJoining; event Action GameIsInLobby; - //event Action StateChangedEvent; - //event Action UpdateInstanceEvent; - //event Action UpdateBeatmapEvent; - //event Action> LevelFinishedEvent; + event Action UpdateInstanceEvent; - //void PlayerUpdated(IPlayer player); - //void InstanceStateChanged(CountdownState countdown, GameplayManagerState gameplay); - //void BeatmapChanged(BeatmapIdentifier? map, GameplayModifiers modifiers, bool IsGameplay, DateTime CountdownEnd); - //void LevelFinished(BeatmapIdentifier beatmap, List<(string, BeatmapDifficulty, LevelCompletionResults)> Results); - //void InstanceChanged(); + void InstanceConfigUpdated(); InstanceConfiguration _configuration { get; } bool IsRunning { get; } - float RunTime { get; } - int Port { get; } + long RunTime { get; } + public int Port { get; } MultiplayerGameState State { get; } - float NoPlayersTime { get; } + long NoPlayersTime { get; } - IHandshakeSessionRegistry GetHandshakeSessionRegistry(); IPlayerRegistry GetPlayerRegistry(); IServiceProvider GetServiceProvider(); @@ -41,7 +33,7 @@ public interface IDedicatedInstance Task Stop(CancellationToken cancellationToken = default); - void DisconnectPlayer(string UserId); + void DisconnectPlayer(IPlayer player); int GetNextSortIndex(); void ReleaseSortIndex(int sortIndex); byte GetNextConnectionId(); diff --git a/BeatTogether.DedicatedServer.Kernel/Abstractions/IHandshakeMessageHandler.cs b/BeatTogether.DedicatedServer.Kernel/Abstractions/IHandshakeMessageHandler.cs deleted file mode 100644 index 3ac42934..00000000 --- a/BeatTogether.DedicatedServer.Kernel/Abstractions/IHandshakeMessageHandler.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Threading.Tasks; -using BeatTogether.Core.Messaging.Abstractions; -using BeatTogether.DedicatedServer.Kernel.Handshake; - -namespace BeatTogether.DedicatedServer.Kernel.Abstractions -{ - public interface IHandshakeMessageHandler - { - Task Handle(HandshakeSession session, IMessage message); - } - - public interface IHandshakeMessageHandler : IHandshakeMessageHandler - where TMessage : class, IMessage - { - Task Handle(HandshakeSession session, TMessage message); - } -} diff --git a/BeatTogether.DedicatedServer.Kernel/Abstractions/IHandshakeService.cs b/BeatTogether.DedicatedServer.Kernel/Abstractions/IHandshakeService.cs deleted file mode 100644 index 7b1fe24f..00000000 --- a/BeatTogether.DedicatedServer.Kernel/Abstractions/IHandshakeService.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Threading.Tasks; -using BeatTogether.DedicatedServer.Kernel.Handshake; -using BeatTogether.DedicatedServer.Messaging.Messages.GameLift; -using BeatTogether.DedicatedServer.Messaging.Messages.Handshake; - -namespace BeatTogether.DedicatedServer.Kernel.Abstractions -{ - public interface IHandshakeService - { - Task ClientHello(HandshakeSession session, ClientHelloRequest request); - Task ClientHelloWithCookie(HandshakeSession session, ClientHelloWithCookieRequest request); - Task ClientKeyExchange(HandshakeSession session, ClientKeyExchangeRequest request); - Task AuthenticateGameLiftUser(HandshakeSession session, AuthenticateGameLiftUserRequest request); - } -} \ No newline at end of file diff --git a/BeatTogether.DedicatedServer.Kernel/Abstractions/IHandshakeSessionRegistry.cs b/BeatTogether.DedicatedServer.Kernel/Abstractions/IHandshakeSessionRegistry.cs deleted file mode 100644 index 2152632a..00000000 --- a/BeatTogether.DedicatedServer.Kernel/Abstractions/IHandshakeSessionRegistry.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Net; -using BeatTogether.DedicatedServer.Kernel.Handshake; - -namespace BeatTogether.DedicatedServer.Kernel.Abstractions -{ - public interface IHandshakeSessionRegistry - { - HandshakeSession GetOrAdd(EndPoint endPoint); - HandshakeSession? TryGetByPlayerSessionId(string playerSessionId); - void AddPendingPlayerSessionId(string playerSessionId); - bool TryRemovePendingPlayerSessionId(string playerSessionId); - } -} \ No newline at end of file diff --git a/BeatTogether.DedicatedServer.Kernel/Abstractions/IPacketDispatcher.cs b/BeatTogether.DedicatedServer.Kernel/Abstractions/IPacketDispatcher.cs index 8853fc59..aea0eddf 100644 --- a/BeatTogether.DedicatedServer.Kernel/Abstractions/IPacketDispatcher.cs +++ b/BeatTogether.DedicatedServer.Kernel/Abstractions/IPacketDispatcher.cs @@ -1,19 +1,21 @@ -using BeatTogether.LiteNetLib.Abstractions; -using BeatTogether.LiteNetLib.Enums; +using BeatTogether.DedicatedServer.Ignorance.IgnoranceCore; +using BeatTogether.DedicatedServer.Messaging.Abstractions; namespace BeatTogether.DedicatedServer.Kernel.Abstractions { public interface IPacketDispatcher { - void SendToPlayer(IPlayer player, INetSerializable packet, DeliveryMethod deliveryMethod); - void SendToPlayer(IPlayer player, INetSerializable[] packets, DeliveryMethod deliveryMethod); - void SendFromPlayerToPlayer(IPlayer fromPlayer, IPlayer toPlayer, INetSerializable packet, DeliveryMethod deliveryMethod); - void SendFromPlayerToPlayer(IPlayer fromPlayer, IPlayer toPlayer, INetSerializable[] packets, DeliveryMethod deliveryMethod); - void SendToNearbyPlayers(INetSerializable packet, DeliveryMethod deliveryMethod); - void SendToNearbyPlayers(INetSerializable[] packets, DeliveryMethod deliveryMethod); - void SendExcludingPlayer(IPlayer excludedPlayer, INetSerializable packet, DeliveryMethod deliveryMethod); - void SendExcludingPlayer(IPlayer excludedPlayer, INetSerializable[] packets, DeliveryMethod deliveryMethod); - void SendFromPlayer(IPlayer fromPlayer, INetSerializable packet, DeliveryMethod deliveryMethod); - void SendFromPlayer(IPlayer fromPlayer, INetSerializable[] packets, DeliveryMethod deliveryMethod); - } + void SendToPlayer(IPlayer player, INetSerializable packet, IgnoranceChannelTypes deliveryMethod); + void SendToPlayer(IPlayer player, INetSerializable[] packets, IgnoranceChannelTypes deliveryMethod); + void SendFromPlayerToPlayer(IPlayer fromPlayer, IPlayer toPlayer, INetSerializable packet, IgnoranceChannelTypes deliveryMethod); + void SendFromPlayerToPlayer(IPlayer fromPlayer, IPlayer toPlayer, INetSerializable[] packets, IgnoranceChannelTypes deliveryMethod); + void SendToNearbyPlayers(INetSerializable packet, IgnoranceChannelTypes deliveryMethod); + void SendToNearbyPlayers(INetSerializable[] packets, IgnoranceChannelTypes deliveryMethod); + void SendToPlayers(IPlayer[] Players, INetSerializable packet, IgnoranceChannelTypes deliveryMethod); + void SendToPlayers(IPlayer[] Players, INetSerializable[] packets, IgnoranceChannelTypes deliveryMethod); + void SendExcludingPlayer(IPlayer excludedPlayer, INetSerializable packet, IgnoranceChannelTypes deliveryMethod); + void SendExcludingPlayer(IPlayer excludedPlayer, INetSerializable[] packets, IgnoranceChannelTypes deliveryMethod); + void SendFromPlayer(IPlayer fromPlayer, INetSerializable packet, IgnoranceChannelTypes deliveryMethod); + void SendFromPlayer(IPlayer fromPlayer, INetSerializable[] packets, IgnoranceChannelTypes deliveryMethod); + } } diff --git a/BeatTogether.DedicatedServer.Kernel/Abstractions/IPacketHandler.cs b/BeatTogether.DedicatedServer.Kernel/Abstractions/IPacketHandler.cs index df5bdf0c..f52a69cf 100644 --- a/BeatTogether.DedicatedServer.Kernel/Abstractions/IPacketHandler.cs +++ b/BeatTogether.DedicatedServer.Kernel/Abstractions/IPacketHandler.cs @@ -1,16 +1,15 @@ -using BeatTogether.LiteNetLib.Abstractions; -using System.Threading.Tasks; +using BeatTogether.DedicatedServer.Messaging.Abstractions; namespace BeatTogether.DedicatedServer.Kernel.Abstractions { public interface IPacketHandler { - Task Handle(IPlayer sender, INetSerializable packet); + void Handle(IPlayer sender, INetSerializable packet); } public interface IPacketHandler : IPacketHandler where TPacket : class, INetSerializable { - Task Handle(IPlayer sender, TPacket packet); + void Handle(IPlayer sender, TPacket packet); } } diff --git a/BeatTogether.DedicatedServer.Kernel/Abstractions/IPlayer.cs b/BeatTogether.DedicatedServer.Kernel/Abstractions/IPlayer.cs index a89c49f1..6a88398e 100644 --- a/BeatTogether.DedicatedServer.Kernel/Abstractions/IPlayer.cs +++ b/BeatTogether.DedicatedServer.Kernel/Abstractions/IPlayer.cs @@ -1,39 +1,40 @@ -using BeatTogether.DedicatedServer.Kernel.Types; +using BeatTogether.DedicatedServer.Kernel.Enums; +using System.Net; +using BeatTogether.DedicatedServer.Kernel.Types; using BeatTogether.DedicatedServer.Messaging.Enums; using BeatTogether.DedicatedServer.Messaging.Models; -using System.Net; +using System.Collections.Generic; namespace BeatTogether.DedicatedServer.Kernel.Abstractions { - public interface IPlayer + public interface IPlayer : Core.Abstractions.IPlayer { EndPoint Endpoint { get; } IDedicatedInstance Instance { get; } byte ConnectionId { get; } byte RemoteConnectionId { get; } - string Secret { get; } - string UserId { get; } + //string UserId { get; } string UserName { get; } + //string PlayerSessionId { get; } + byte[]? Random { get; set; } byte[]? PublicEncryptionKey { get; set; } + //string ClientVersion { get; set; } + //Platform Platform { get; set; } + //string PlatformUserId { get; set; } + + uint ENetPeerId { get; set; } - object LatencyLock { get; set; } RollingAverage Latency { get; } - float SyncTime { get; } - object SortLock { get; set; } + long SyncTime { get; } int SortIndex { get; set; } - object PlayerIdentityLock { get; set; } - AvatarData Avatar { get; set; } - object ReadyLock { get; set; } + MultiplayerAvatarsData Avatar { get; set; } bool IsReady { get; set; } - object BeatmapLock { get; set; } BeatmapIdentifier? BeatmapIdentifier { get; set; } - object ModifiersLock { get; set; } GameplayModifiers Modifiers { get; set; } - object StateLock { get; set; } PlayerStateHash State { get; set; } - + public bool ForceLateJoin { get; set; } public bool IsServerOwner { get; } public bool CanRecommendBeatmaps { get; } public bool CanRecommendModifiers { get; } @@ -49,18 +50,20 @@ public interface IPlayer bool IsActive { get; } bool FinishedLevel { get; } bool InMenu { get; } - bool IsModded { get; } - object InLobbyLock { get; set; } bool InLobby { get; set; } - object EntitlementLock { get; set; } + bool IsPatreon { get; set; } + bool CanTextChat { get; set; } + public bool CanReceiveVoiceChat { get; set; } + public bool CanTransmitVoiceChat { get; set; } + public AccessLevel GetAccessLevel(); + public void SetAccessLevel(AccessLevel newAccessLevel); EntitlementStatus GetEntitlement(string levelId); void SetEntitlement(string levelId, EntitlementStatus entitlement); bool UpdateEntitlement { get; set; } + public string MapHash { get; set; } - public bool Chroma { get; set; } - public bool NoodleExtensions { get; set; } - public bool MappingExtensions { get; set; } - public BeatmapDifficulty[] BeatmapDifficulties { get; set; } - void ResetRecommendedMapRequirements(); + public Dictionary BeatmapDifficultiesRequirements { get; set; } + long TicksAtLastSyncStateDelta { get; set; } + long TicksAtLastSyncState { get; set; } } } diff --git a/BeatTogether.DedicatedServer.Kernel/Abstractions/IPlayerRegistry.cs b/BeatTogether.DedicatedServer.Kernel/Abstractions/IPlayerRegistry.cs index b630e42e..f837e818 100644 --- a/BeatTogether.DedicatedServer.Kernel/Abstractions/IPlayerRegistry.cs +++ b/BeatTogether.DedicatedServer.Kernel/Abstractions/IPlayerRegistry.cs @@ -6,15 +6,17 @@ namespace BeatTogether.DedicatedServer.Kernel.Abstractions public interface IPlayerRegistry { IPlayer[] Players { get; } - bool AddPlayer(IPlayer player); void RemovePlayer(IPlayer player); int GetPlayerCount(); - IPlayer GetPlayer(EndPoint remoteEndPoint); - IPlayer GetPlayer(byte connectionId); - IPlayer GetPlayer(string userId); bool TryGetPlayer(EndPoint remoteEndPoint, [MaybeNullWhen(false)] out IPlayer player); bool TryGetPlayer(byte connectionId, [MaybeNullWhen(false)] out IPlayer player); bool TryGetPlayer(string userId, [MaybeNullWhen(false)] out IPlayer player); + long GetMillisBetweenPoseSyncStateDeltaPackets(); + long GetMillisBetweenScoreSyncStateDeltaPackets(); + + public void AddExtraPlayerSessionData(Core.Abstractions.IPlayer playerSessionData); + public bool RemoveExtraPlayerSessionDataAndApply(Core.Abstractions.IPlayer playerSessionData/*out string ClientVersion, out byte Platform, out string PlayerPlatformUserId*/); + public bool RemoveExtraPlayerSessionData(string playerSessionId/*out string ClientVersion, out byte Platform, out string PlayerPlatformUserId*/); } } diff --git a/BeatTogether.DedicatedServer.Kernel/Abstractions/ITextCommand.cs b/BeatTogether.DedicatedServer.Kernel/Abstractions/ITextCommand.cs new file mode 100644 index 00000000..dc8bd381 --- /dev/null +++ b/BeatTogether.DedicatedServer.Kernel/Abstractions/ITextCommand.cs @@ -0,0 +1,10 @@ +namespace BeatTogether.DedicatedServer.Kernel.Abstractions +{ + public interface ITextCommand + { + string CommandName { get; } + string ShortHandName { get; } + string Description { get; } + public void ReadValues(string[] Values); + } +} diff --git a/BeatTogether.DedicatedServer.Kernel/Abstractions/ITextCommandRepository.cs b/BeatTogether.DedicatedServer.Kernel/Abstractions/ITextCommandRepository.cs new file mode 100644 index 00000000..0ce12844 --- /dev/null +++ b/BeatTogether.DedicatedServer.Kernel/Abstractions/ITextCommandRepository.cs @@ -0,0 +1,11 @@ +using BeatTogether.DedicatedServer.Kernel.Enums; + +namespace BeatTogether.DedicatedServer.Kernel.Abstractions +{ + public interface ITextCommandRepository + { + bool GetCommand(string[] commandValues, AccessLevel accessLevel, out ITextCommand Command); + void RegisterCommand(AccessLevel accessLevel) where T : class, ITextCommand, new(); + string[] GetTextCommandNames(AccessLevel accessLevel); + } +} diff --git a/BeatTogether.DedicatedServer.Kernel/Abstractions/IUnconnectedDispatcher.cs b/BeatTogether.DedicatedServer.Kernel/Abstractions/IUnconnectedDispatcher.cs deleted file mode 100644 index e72f8020..00000000 --- a/BeatTogether.DedicatedServer.Kernel/Abstractions/IUnconnectedDispatcher.cs +++ /dev/null @@ -1,11 +0,0 @@ -using BeatTogether.Core.Messaging.Abstractions; -using BeatTogether.DedicatedServer.Kernel.Handshake; - -namespace BeatTogether.DedicatedServer.Kernel.Abstractions -{ - public interface IUnconnectedDispatcher - { - void Send(HandshakeSession session, IMessage message, bool retry = false); - bool Acknowledge(HandshakeSession session, uint responseId, bool handled = true); - } -} \ No newline at end of file diff --git a/BeatTogether.DedicatedServer.Kernel/BeatTogether.DedicatedServer.Kernel.csproj b/BeatTogether.DedicatedServer.Kernel/BeatTogether.DedicatedServer.Kernel.csproj index a7f9ea63..8f78936c 100644 --- a/BeatTogether.DedicatedServer.Kernel/BeatTogether.DedicatedServer.Kernel.csproj +++ b/BeatTogether.DedicatedServer.Kernel/BeatTogether.DedicatedServer.Kernel.csproj @@ -7,6 +7,7 @@ + diff --git a/BeatTogether.DedicatedServer.Kernel/CommandHandlers/BaseCommandHandler.cs b/BeatTogether.DedicatedServer.Kernel/CommandHandlers/BaseCommandHandler.cs new file mode 100644 index 00000000..65d57ac5 --- /dev/null +++ b/BeatTogether.DedicatedServer.Kernel/CommandHandlers/BaseCommandHandler.cs @@ -0,0 +1,23 @@ +using BeatTogether.DedicatedServer.Kernel.Abstractions; + +namespace BeatTogether.DedicatedServer.Kernel.CommandHandlers +{ + public interface ICommandHandler + { + void Handle(IPlayer sender, ITextCommand command); + } + + public interface ICommandHandler : ICommandHandler + where TCommand : class, ITextCommand + { + void Handle(IPlayer sender, TCommand command); + } + + public abstract class BaseCommandHandler : ICommandHandler where Tcommand : class, ITextCommand + { + public abstract void Handle(IPlayer sender, Tcommand command); + + public void Handle(IPlayer sender, ITextCommand command) + => Handle(sender, (Tcommand)command); + } +} diff --git a/BeatTogether.DedicatedServer.Kernel/CommandHandlers/ForceStartCommandHandler.cs b/BeatTogether.DedicatedServer.Kernel/CommandHandlers/ForceStartCommandHandler.cs new file mode 100644 index 00000000..1110c76b --- /dev/null +++ b/BeatTogether.DedicatedServer.Kernel/CommandHandlers/ForceStartCommandHandler.cs @@ -0,0 +1,24 @@ +using BeatTogether.DedicatedServer.Kernel.Abstractions; +using BeatTogether.DedicatedServer.Kernel.CommandHandlers; +using BeatTogether.DedicatedServer.Kernel.Managers.Abstractions; +using Serilog; + +namespace BeatTogether.DedicatedServer.Kernel.Commands.CommandHandlers +{ + class ForceStartCommandHandler : BaseCommandHandler + { + private readonly ILogger _logger = Log.ForContext(); + private readonly ILobbyManager _lobbyManager; + + public ForceStartCommandHandler(ILobbyManager lobbyManager) + { + _lobbyManager = lobbyManager; + } + + public override void Handle(IPlayer player, ForceStartCommand command) + { + _logger.Information(player.UserName + "Has force started a beatmap"); + _lobbyManager.ForceStartSelectedBeatmap = true; + } + } +} diff --git a/BeatTogether.DedicatedServer.Kernel/CommandHandlers/HelpCommandHandler.cs b/BeatTogether.DedicatedServer.Kernel/CommandHandlers/HelpCommandHandler.cs new file mode 100644 index 00000000..0c85c67a --- /dev/null +++ b/BeatTogether.DedicatedServer.Kernel/CommandHandlers/HelpCommandHandler.cs @@ -0,0 +1,50 @@ +using BeatTogether.DedicatedServer.Ignorance.IgnoranceCore; +using BeatTogether.DedicatedServer.Kernel.Abstractions; +using BeatTogether.DedicatedServer.Kernel.CommandHandlers; +using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MPChatPackets; +using System.Text; + +namespace BeatTogether.DedicatedServer.Kernel.Commands.CommandHandlers +{ + class HelpCommandHandler : BaseCommandHandler + { + private readonly ITextCommandRepository _commandRepository; + private readonly IPacketDispatcher _packetDisapatcher; + + public HelpCommandHandler(ITextCommandRepository commandRepository, IPacketDispatcher packetDisapatcher) + { + _commandRepository = commandRepository; + _packetDisapatcher = packetDisapatcher; + } + + public override void Handle(IPlayer player, HelpCommand command) + { + if (command.SpecificCommandName != null) + { + if (!_commandRepository.GetCommand(command.SpecificCommandName, player.GetAccessLevel(), out var textCommand)) + { + _packetDisapatcher.SendToPlayer(player, new MpcTextChatPacket + { + Text = "Command you searched help for does not exist or is above your access level" + }, IgnoranceChannelTypes.Reliable); + } + _packetDisapatcher.SendToPlayer(player, new MpcTextChatPacket + { + Text = textCommand.CommandName + " : " + textCommand.Description + " : ShortHand: " + textCommand.ShortHandName + }, IgnoranceChannelTypes.Reliable); + return; + } + string[] CommandList = _commandRepository.GetTextCommandNames(player.GetAccessLevel()); + StringBuilder Response = new(); + Response.Append("Command List: "); + for (int i = 0; i < CommandList.Length; i++) + { + Response.Append("/" + CommandList[i] + " : "); + } + _packetDisapatcher.SendToPlayer(player, new MpcTextChatPacket + { + Text = Response.ToString() + }, IgnoranceChannelTypes.Reliable); + } + } +} diff --git a/BeatTogether.DedicatedServer.Kernel/CommandHandlers/SetBeatmapRoutingHandler.cs b/BeatTogether.DedicatedServer.Kernel/CommandHandlers/SetBeatmapRoutingHandler.cs new file mode 100644 index 00000000..3dc42bdd --- /dev/null +++ b/BeatTogether.DedicatedServer.Kernel/CommandHandlers/SetBeatmapRoutingHandler.cs @@ -0,0 +1,29 @@ +using BeatTogether.DedicatedServer.Ignorance.IgnoranceCore; +using BeatTogether.DedicatedServer.Kernel.Abstractions; +using BeatTogether.DedicatedServer.Kernel.CommandHandlers; +using BeatTogether.DedicatedServer.Kernel.Configuration; +using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MPChatPackets; + +namespace BeatTogether.DedicatedServer.Kernel.Commands.CommandHandlers +{ + class SetNoteRoutingHandler : BaseCommandHandler + { + private readonly IPacketDispatcher _packetDisapatcher; + private readonly InstanceConfiguration _Configuration; + + public SetNoteRoutingHandler(IPacketDispatcher packetDisapatcher, InstanceConfiguration instanceConfiguration) + { + _packetDisapatcher = packetDisapatcher; + _Configuration = instanceConfiguration; + } + + public override void Handle(IPlayer player, SetNoteRouting command) + { + _Configuration.DisableNotes = !command.Enabled; + _packetDisapatcher.SendToNearbyPlayers(new MpcTextChatPacket + { + Text = "Beatmap notes: " + command.Enabled + }, IgnoranceChannelTypes.Reliable); + } + } +} diff --git a/BeatTogether.DedicatedServer.Kernel/CommandHandlers/SetChromaCommandHandler.cs b/BeatTogether.DedicatedServer.Kernel/CommandHandlers/SetChromaCommandHandler.cs new file mode 100644 index 00000000..4ba6d6cd --- /dev/null +++ b/BeatTogether.DedicatedServer.Kernel/CommandHandlers/SetChromaCommandHandler.cs @@ -0,0 +1,29 @@ +using BeatTogether.DedicatedServer.Ignorance.IgnoranceCore; +using BeatTogether.DedicatedServer.Kernel.Abstractions; +using BeatTogether.DedicatedServer.Kernel.CommandHandlers; +using BeatTogether.DedicatedServer.Kernel.Configuration; +using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MPChatPackets; + +namespace BeatTogether.DedicatedServer.Kernel.Commands.CommandHandlers +{ + class SetChromaHandler : BaseCommandHandler + { + private readonly IPacketDispatcher _packetDisapatcher; + private readonly InstanceConfiguration _Configuration; + + public SetChromaHandler(IPacketDispatcher packetDisapatcher, InstanceConfiguration instanceConfiguration) + { + _packetDisapatcher = packetDisapatcher; + _Configuration = instanceConfiguration; + } + + public override void Handle(IPlayer player, SetChroma command) + { + _Configuration.AllowChroma = command.Enabled; + _packetDisapatcher.SendToNearbyPlayers(new MpcTextChatPacket + { + Text = "Chroma: " + command.Enabled + }, IgnoranceChannelTypes.Reliable); + } + } +} diff --git a/BeatTogether.DedicatedServer.Kernel/CommandHandlers/SetCountdownTimeHandler.cs b/BeatTogether.DedicatedServer.Kernel/CommandHandlers/SetCountdownTimeHandler.cs new file mode 100644 index 00000000..521f4d42 --- /dev/null +++ b/BeatTogether.DedicatedServer.Kernel/CommandHandlers/SetCountdownTimeHandler.cs @@ -0,0 +1,29 @@ +using BeatTogether.DedicatedServer.Ignorance.IgnoranceCore; +using BeatTogether.DedicatedServer.Kernel.Abstractions; +using BeatTogether.DedicatedServer.Kernel.CommandHandlers; +using BeatTogether.DedicatedServer.Kernel.Configuration; +using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MPChatPackets; + +namespace BeatTogether.DedicatedServer.Kernel.Commands.CommandHandlers +{ + class SetCountdownTimeHandler : BaseCommandHandler + { + private readonly IPacketDispatcher _packetDisapatcher; + private readonly InstanceConfiguration _Configuration; + + public SetCountdownTimeHandler(IPacketDispatcher packetDisapatcher, InstanceConfiguration instanceConfiguration) + { + _packetDisapatcher = packetDisapatcher; + _Configuration = instanceConfiguration; + } + + public override void Handle(IPlayer player, SetCountdown command) + { + _Configuration.CountdownConfig.CountdownTimePlayersReady = command.Countdown * 1000; + _packetDisapatcher.SendToNearbyPlayers(new MpcTextChatPacket + { + Text = "Countdown time is now: " + command.Countdown + }, IgnoranceChannelTypes.Reliable); + } + } +} diff --git a/BeatTogether.DedicatedServer.Kernel/CommandHandlers/SetMappingExtensionsHandler.cs b/BeatTogether.DedicatedServer.Kernel/CommandHandlers/SetMappingExtensionsHandler.cs new file mode 100644 index 00000000..98d3ac8d --- /dev/null +++ b/BeatTogether.DedicatedServer.Kernel/CommandHandlers/SetMappingExtensionsHandler.cs @@ -0,0 +1,29 @@ +using BeatTogether.DedicatedServer.Ignorance.IgnoranceCore; +using BeatTogether.DedicatedServer.Kernel.Abstractions; +using BeatTogether.DedicatedServer.Kernel.CommandHandlers; +using BeatTogether.DedicatedServer.Kernel.Configuration; +using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MPChatPackets; + +namespace BeatTogether.DedicatedServer.Kernel.Commands.CommandHandlers +{ + class SetMappingExtensionsHandler : BaseCommandHandler + { + private readonly IPacketDispatcher _packetDisapatcher; + private readonly InstanceConfiguration _Configuration; + + public SetMappingExtensionsHandler(IPacketDispatcher packetDisapatcher, InstanceConfiguration instanceConfiguration) + { + _packetDisapatcher = packetDisapatcher; + _Configuration = instanceConfiguration; + } + + public override void Handle(IPlayer player, SetMappingExtensions command) + { + _Configuration.AllowMappingExtensions = command.Enabled; + _packetDisapatcher.SendToNearbyPlayers(new MpcTextChatPacket + { + Text = "Mapping Extensions: " + command.Enabled + }, IgnoranceChannelTypes.Reliable); + } + } +} diff --git a/BeatTogether.DedicatedServer.Kernel/CommandHandlers/SetNoodleExtensionsHandler.cs b/BeatTogether.DedicatedServer.Kernel/CommandHandlers/SetNoodleExtensionsHandler.cs new file mode 100644 index 00000000..9c61dc41 --- /dev/null +++ b/BeatTogether.DedicatedServer.Kernel/CommandHandlers/SetNoodleExtensionsHandler.cs @@ -0,0 +1,29 @@ +using BeatTogether.DedicatedServer.Ignorance.IgnoranceCore; +using BeatTogether.DedicatedServer.Kernel.Abstractions; +using BeatTogether.DedicatedServer.Kernel.CommandHandlers; +using BeatTogether.DedicatedServer.Kernel.Configuration; +using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MPChatPackets; + +namespace BeatTogether.DedicatedServer.Kernel.Commands.CommandHandlers +{ + class SetNoodleExtensionsHandler : BaseCommandHandler + { + private readonly IPacketDispatcher _packetDisapatcher; + private readonly InstanceConfiguration _Configuration; + + public SetNoodleExtensionsHandler(IPacketDispatcher packetDisapatcher, InstanceConfiguration instanceConfiguration) + { + _packetDisapatcher = packetDisapatcher; + _Configuration = instanceConfiguration; + } + + public override void Handle(IPlayer player, SetNoodleExtensions command) + { + _Configuration.AllowNoodleExtensions = command.Enabled; + _packetDisapatcher.SendToNearbyPlayers(new MpcTextChatPacket + { + Text = "Noodle Extensions: " + command.Enabled + }, IgnoranceChannelTypes.Reliable); + } + } +} diff --git a/BeatTogether.DedicatedServer.Kernel/CommandHandlers/SetPerPlayerDiffHandler.cs b/BeatTogether.DedicatedServer.Kernel/CommandHandlers/SetPerPlayerDiffHandler.cs new file mode 100644 index 00000000..0224708d --- /dev/null +++ b/BeatTogether.DedicatedServer.Kernel/CommandHandlers/SetPerPlayerDiffHandler.cs @@ -0,0 +1,35 @@ +using BeatTogether.DedicatedServer.Ignorance.IgnoranceCore; +using BeatTogether.DedicatedServer.Kernel.Abstractions; +using BeatTogether.DedicatedServer.Kernel.CommandHandlers; +using BeatTogether.DedicatedServer.Kernel.Configuration; +using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MPChatPackets; +using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MpCorePackets; + +namespace BeatTogether.DedicatedServer.Kernel.Commands.CommandHandlers +{ + class SetPerPlayerDiffHandler : BaseCommandHandler + { + private readonly IPacketDispatcher _packetDisapatcher; + private readonly InstanceConfiguration _Configuration; + + public SetPerPlayerDiffHandler(IPacketDispatcher packetDisapatcher, InstanceConfiguration instanceConfiguration) + { + _packetDisapatcher = packetDisapatcher; + _Configuration = instanceConfiguration; + } + + public override void Handle(IPlayer player, SetPerPlayerDifficulties command) + { + _Configuration.AllowPerPlayerDifficulties = command.Enabled; + _packetDisapatcher.SendToNearbyPlayers(new MpcTextChatPacket + { + Text = "Per player difficulties: " + command.Enabled + }, IgnoranceChannelTypes.Reliable); + _packetDisapatcher.SendToNearbyPlayers(new MpPerPlayerPacket() + { + PPDEnabled = _Configuration.AllowPerPlayerDifficulties, + PPMEnabled = _Configuration.AllowPerPlayerModifiers, + }, IgnoranceChannelTypes.Reliable); + } + } +} diff --git a/BeatTogether.DedicatedServer.Kernel/CommandHandlers/SetPerPlayerModHandler.cs b/BeatTogether.DedicatedServer.Kernel/CommandHandlers/SetPerPlayerModHandler.cs new file mode 100644 index 00000000..a7baf5f9 --- /dev/null +++ b/BeatTogether.DedicatedServer.Kernel/CommandHandlers/SetPerPlayerModHandler.cs @@ -0,0 +1,36 @@ +using BeatTogether.DedicatedServer.Ignorance.IgnoranceCore; +using BeatTogether.DedicatedServer.Kernel.Abstractions; +using BeatTogether.DedicatedServer.Kernel.CommandHandlers; +using BeatTogether.DedicatedServer.Kernel.Configuration; +using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MPChatPackets; +using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MpCorePackets; +using Microsoft.Extensions.Configuration; + +namespace BeatTogether.DedicatedServer.Kernel.Commands.CommandHandlers +{ + class SetPerPlayerModifiersHandler : BaseCommandHandler + { + private readonly IPacketDispatcher _packetDisapatcher; + private readonly InstanceConfiguration _Configuration; + + public SetPerPlayerModifiersHandler(IPacketDispatcher packetDisapatcher, InstanceConfiguration instanceConfiguration) + { + _packetDisapatcher = packetDisapatcher; + _Configuration = instanceConfiguration; + } + + public override void Handle(IPlayer player, SetPerPlayerModifiers command) + { + _Configuration.AllowPerPlayerModifiers = command.Enabled; + _packetDisapatcher.SendToNearbyPlayers(new MpcTextChatPacket + { + Text = "Per player modifiers: " + command.Enabled + }, IgnoranceChannelTypes.Reliable); + _packetDisapatcher.SendToNearbyPlayers(new MpPerPlayerPacket() + { + PPDEnabled = _Configuration.AllowPerPlayerDifficulties, + PPMEnabled = _Configuration.AllowPerPlayerModifiers, + }, IgnoranceChannelTypes.Reliable); + } + } +} diff --git a/BeatTogether.DedicatedServer.Kernel/CommandHandlers/SetResultsTimeHandler.cs b/BeatTogether.DedicatedServer.Kernel/CommandHandlers/SetResultsTimeHandler.cs new file mode 100644 index 00000000..0063f325 --- /dev/null +++ b/BeatTogether.DedicatedServer.Kernel/CommandHandlers/SetResultsTimeHandler.cs @@ -0,0 +1,29 @@ +using BeatTogether.DedicatedServer.Ignorance.IgnoranceCore; +using BeatTogether.DedicatedServer.Kernel.Abstractions; +using BeatTogether.DedicatedServer.Kernel.CommandHandlers; +using BeatTogether.DedicatedServer.Kernel.Configuration; +using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MPChatPackets; + +namespace BeatTogether.DedicatedServer.Kernel.Commands.CommandHandlers +{ + class SetResultsTimeHandler : BaseCommandHandler + { + private readonly IPacketDispatcher _packetDisapatcher; + private readonly InstanceConfiguration _Configuration; + + public SetResultsTimeHandler(IPacketDispatcher packetDisapatcher, InstanceConfiguration instanceConfiguration) + { + _packetDisapatcher = packetDisapatcher; + _Configuration = instanceConfiguration; + } + + public override void Handle(IPlayer player, SetResultsTime command) + { + _Configuration.CountdownConfig.ResultsScreenTime = command.Countdown*1000; + _packetDisapatcher.SendToNearbyPlayers(new MpcTextChatPacket + { + Text = "Results screen time is now: " + command.Countdown + }, IgnoranceChannelTypes.Reliable); + } + } +} diff --git a/BeatTogether.DedicatedServer.Kernel/CommandHandlers/SetServerNameHandler.cs b/BeatTogether.DedicatedServer.Kernel/CommandHandlers/SetServerNameHandler.cs new file mode 100644 index 00000000..115d3b83 --- /dev/null +++ b/BeatTogether.DedicatedServer.Kernel/CommandHandlers/SetServerNameHandler.cs @@ -0,0 +1,32 @@ +using BeatTogether.DedicatedServer.Ignorance.IgnoranceCore; +using BeatTogether.DedicatedServer.Kernel.Abstractions; +using BeatTogether.DedicatedServer.Kernel.CommandHandlers; +using BeatTogether.DedicatedServer.Kernel.Configuration; +using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MPChatPackets; + +namespace BeatTogether.DedicatedServer.Kernel.Commands.CommandHandlers +{ + class SetServerNameHandler : BaseCommandHandler + { + private readonly IPacketDispatcher _packetDisapatcher; + private readonly InstanceConfiguration _Configuration; + private readonly IDedicatedInstance _instance; + + public SetServerNameHandler(IPacketDispatcher packetDisapatcher, InstanceConfiguration instanceConfiguration, IDedicatedInstance instance) + { + _packetDisapatcher = packetDisapatcher; + _Configuration = instanceConfiguration; + _instance = instance; + } + + public override void Handle(IPlayer player, SetServerName command) + { + _Configuration.ServerName = command.Name; + _instance.InstanceConfigUpdated(); + _packetDisapatcher.SendToNearbyPlayers(new MpcTextChatPacket + { + Text = "Server name is now: " + command.Name + }, IgnoranceChannelTypes.Reliable); + } + } +} diff --git a/BeatTogether.DedicatedServer.Kernel/CommandHandlers/SetWelcomeMessageHandler.cs b/BeatTogether.DedicatedServer.Kernel/CommandHandlers/SetWelcomeMessageHandler.cs new file mode 100644 index 00000000..b690df65 --- /dev/null +++ b/BeatTogether.DedicatedServer.Kernel/CommandHandlers/SetWelcomeMessageHandler.cs @@ -0,0 +1,29 @@ +using BeatTogether.DedicatedServer.Ignorance.IgnoranceCore; +using BeatTogether.DedicatedServer.Kernel.Abstractions; +using BeatTogether.DedicatedServer.Kernel.CommandHandlers; +using BeatTogether.DedicatedServer.Kernel.Configuration; +using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MPChatPackets; + +namespace BeatTogether.DedicatedServer.Kernel.Commands.CommandHandlers +{ + class SetWelcomeMessageHandler : BaseCommandHandler + { + private readonly IPacketDispatcher _packetDisapatcher; + private readonly InstanceConfiguration _Configuration; + + public SetWelcomeMessageHandler(IPacketDispatcher packetDisapatcher, InstanceConfiguration instanceConfiguration) + { + _packetDisapatcher = packetDisapatcher; + _Configuration = instanceConfiguration; + } + + public override void Handle(IPlayer player, SetWelcomeMessage command) + { + _Configuration.WelcomeMessage = command.Text; + _packetDisapatcher.SendToPlayer(player, new MpcTextChatPacket + { + Text = "Server name is now: " + command.Text + }, IgnoranceChannelTypes.Reliable); + } + } +} diff --git a/BeatTogether.DedicatedServer.Kernel/CommandHandlers/StartBeatmapTimeHandler.cs b/BeatTogether.DedicatedServer.Kernel/CommandHandlers/StartBeatmapTimeHandler.cs new file mode 100644 index 00000000..d857b6dc --- /dev/null +++ b/BeatTogether.DedicatedServer.Kernel/CommandHandlers/StartBeatmapTimeHandler.cs @@ -0,0 +1,29 @@ +using BeatTogether.DedicatedServer.Ignorance.IgnoranceCore; +using BeatTogether.DedicatedServer.Kernel.Abstractions; +using BeatTogether.DedicatedServer.Kernel.CommandHandlers; +using BeatTogether.DedicatedServer.Kernel.Configuration; +using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MPChatPackets; + +namespace BeatTogether.DedicatedServer.Kernel.Commands.CommandHandlers +{ + class StartBeatmapTimeHandler : BaseCommandHandler + { + private readonly IPacketDispatcher _packetDisapatcher; + private readonly InstanceConfiguration _Configuration; + + public StartBeatmapTimeHandler(IPacketDispatcher packetDisapatcher, InstanceConfiguration instanceConfiguration) + { + _packetDisapatcher = packetDisapatcher; + _Configuration = instanceConfiguration; + } + + public override void Handle(IPlayer player, SetBeatmapStartTime command) + { + _Configuration.CountdownConfig.BeatMapStartCountdownTime = command.Countdown*1000; + _packetDisapatcher.SendToNearbyPlayers(new MpcTextChatPacket + { + Text = "Beatmap start time is now: " + command.Countdown + }, IgnoranceChannelTypes.Reliable); + } + } +} diff --git a/BeatTogether.DedicatedServer.Kernel/Commands/ForceStartCommand.cs b/BeatTogether.DedicatedServer.Kernel/Commands/ForceStartCommand.cs new file mode 100644 index 00000000..d106ad44 --- /dev/null +++ b/BeatTogether.DedicatedServer.Kernel/Commands/ForceStartCommand.cs @@ -0,0 +1,15 @@ +using BeatTogether.DedicatedServer.Kernel.Abstractions; + +namespace BeatTogether.DedicatedServer.Kernel.Commands +{ + public class ForceStartCommand : ITextCommand + { + public string CommandName => "forcestart"; + public string ShortHandName => "fs"; + public string Description => "force starts the beatmap ignoring all players entitlements. Could cause players to have issues"; + + public void ReadValues(string[] Values) + { + } + } +} diff --git a/BeatTogether.DedicatedServer.Kernel/Commands/HelpCommand.cs b/BeatTogether.DedicatedServer.Kernel/Commands/HelpCommand.cs new file mode 100644 index 00000000..4ea5afeb --- /dev/null +++ b/BeatTogether.DedicatedServer.Kernel/Commands/HelpCommand.cs @@ -0,0 +1,19 @@ +using BeatTogether.DedicatedServer.Kernel.Abstractions; + +namespace BeatTogether.DedicatedServer.Kernel.Commands +{ + public class HelpCommand : ITextCommand + { + public string CommandName => "help"; + public string ShortHandName => "h"; + public string Description => "Displays a list of useable commands, Type the name of the command after to get its description"; + + public string[]? SpecificCommandName = null; + + public void ReadValues(string[] Values) + { + if (Values != null) + SpecificCommandName = Values; + } + } +} diff --git a/BeatTogether.DedicatedServer.Kernel/Commands/SetBeatmapRouting.cs b/BeatTogether.DedicatedServer.Kernel/Commands/SetBeatmapRouting.cs new file mode 100644 index 00000000..ae2ca993 --- /dev/null +++ b/BeatTogether.DedicatedServer.Kernel/Commands/SetBeatmapRouting.cs @@ -0,0 +1,19 @@ +using BeatTogether.DedicatedServer.Kernel.Abstractions; + +namespace BeatTogether.DedicatedServer.Kernel.Commands +{ + public class SetNoteRouting : ITextCommand + { + public string CommandName => "snotesvisible"; + public string ShortHandName => "n"; + public string Description => "disables beatmap notes if set to true. Notes will be disabled automaticly if there are over 14 players, default false"; + + public bool Enabled = true; + + public void ReadValues(string[] Values) + { + if (Values != null) + Enabled = Values[0] == "true" || Values[0] == "t"; + } + } +} diff --git a/BeatTogether.DedicatedServer.Kernel/Commands/SetBeatmapStartTime.cs b/BeatTogether.DedicatedServer.Kernel/Commands/SetBeatmapStartTime.cs new file mode 100644 index 00000000..e9d00e31 --- /dev/null +++ b/BeatTogether.DedicatedServer.Kernel/Commands/SetBeatmapStartTime.cs @@ -0,0 +1,19 @@ +using BeatTogether.DedicatedServer.Kernel.Abstractions; + +namespace BeatTogether.DedicatedServer.Kernel.Commands +{ + public class SetBeatmapStartTime : ITextCommand + { + public string CommandName => "setbeatmapstart"; + public string ShortHandName => "sbs"; + public string Description => "this is the countdown that triggers once everyone is ready, and when the beatmap starts downloading, default is 5 seconds"; + + public int Countdown = 5; + + public void ReadValues(string[] Values) + { + if (Values != null) + Countdown = int.Parse(Values[0]); + } + } +} diff --git a/BeatTogether.DedicatedServer.Kernel/Commands/SetChroma.cs b/BeatTogether.DedicatedServer.Kernel/Commands/SetChroma.cs new file mode 100644 index 00000000..c53ad358 --- /dev/null +++ b/BeatTogether.DedicatedServer.Kernel/Commands/SetChroma.cs @@ -0,0 +1,19 @@ +using BeatTogether.DedicatedServer.Kernel.Abstractions; + +namespace BeatTogether.DedicatedServer.Kernel.Commands +{ + public class SetChroma : ITextCommand + { + public string CommandName => "setchroma"; + public string ShortHandName => "ch"; + public string Description => "if set to false, then chroma maps will be unplayable, default true"; + + public bool Enabled = true; + + public void ReadValues(string[] Values) + { + if (Values != null) + Enabled = Values[0] == "true" || Values[0] == "t"; + } + } +} diff --git a/BeatTogether.DedicatedServer.Kernel/Commands/SetCountdown.cs b/BeatTogether.DedicatedServer.Kernel/Commands/SetCountdown.cs new file mode 100644 index 00000000..a27c29b1 --- /dev/null +++ b/BeatTogether.DedicatedServer.Kernel/Commands/SetCountdown.cs @@ -0,0 +1,19 @@ +using BeatTogether.DedicatedServer.Kernel.Abstractions; + +namespace BeatTogether.DedicatedServer.Kernel.Commands +{ + public class SetCountdown : ITextCommand + { + public string CommandName => "setcountdown"; + public string ShortHandName => "sc"; + public string Description => "enter a number to set the countdown, default is 30 seconds"; + + public int Countdown = 30; + + public void ReadValues(string[] Values) + { + if (Values != null) + Countdown = int.Parse(Values[0]); + } + } +} diff --git a/BeatTogether.DedicatedServer.Kernel/Commands/SetMappingExtensions.cs b/BeatTogether.DedicatedServer.Kernel/Commands/SetMappingExtensions.cs new file mode 100644 index 00000000..71a1901a --- /dev/null +++ b/BeatTogether.DedicatedServer.Kernel/Commands/SetMappingExtensions.cs @@ -0,0 +1,19 @@ +using BeatTogether.DedicatedServer.Kernel.Abstractions; + +namespace BeatTogether.DedicatedServer.Kernel.Commands +{ + public class SetMappingExtensions : ITextCommand + { + public string CommandName => "setmappingextensions"; + public string ShortHandName => "me"; + public string Description => "if set to false, then mapping extensions maps will be unplayable, default true"; + + public bool Enabled = true; + + public void ReadValues(string[] Values) + { + if (Values != null) + Enabled = Values[0] == "true" || Values[0] == "t"; + } + } +} diff --git a/BeatTogether.DedicatedServer.Kernel/Commands/SetNoodle.cs b/BeatTogether.DedicatedServer.Kernel/Commands/SetNoodle.cs new file mode 100644 index 00000000..33279b31 --- /dev/null +++ b/BeatTogether.DedicatedServer.Kernel/Commands/SetNoodle.cs @@ -0,0 +1,19 @@ +using BeatTogether.DedicatedServer.Kernel.Abstractions; + +namespace BeatTogether.DedicatedServer.Kernel.Commands +{ + public class SetNoodleExtensions : ITextCommand + { + public string CommandName => "setnoodleextensions"; + public string ShortHandName => "ne"; + public string Description => "if set to false, then noodle maps will be unplayable, default true"; + + public bool Enabled = true; + + public void ReadValues(string[] Values) + { + if (Values != null) + Enabled = Values[0] == "true" || Values[0] == "t"; + } + } +} diff --git a/BeatTogether.DedicatedServer.Kernel/Commands/SetPerPlayerDifficulties.cs b/BeatTogether.DedicatedServer.Kernel/Commands/SetPerPlayerDifficulties.cs new file mode 100644 index 00000000..3dc1ce9d --- /dev/null +++ b/BeatTogether.DedicatedServer.Kernel/Commands/SetPerPlayerDifficulties.cs @@ -0,0 +1,19 @@ +using BeatTogether.DedicatedServer.Kernel.Abstractions; + +namespace BeatTogether.DedicatedServer.Kernel.Commands +{ + public class SetPerPlayerDifficulties : ITextCommand + { + public string CommandName => "setperplayerdifficulties"; + public string ShortHandName => "ppd"; + public string Description => "if set to true, then players will use what ever difficulty they have selected, default false"; + + public bool Enabled = false; + + public void ReadValues(string[] Values) + { + if (Values != null) + Enabled = Values[0] == "true" || Values[0] == "t"; + } + } +} diff --git a/BeatTogether.DedicatedServer.Kernel/Commands/SetPerPlayerModifiers.cs b/BeatTogether.DedicatedServer.Kernel/Commands/SetPerPlayerModifiers.cs new file mode 100644 index 00000000..26248bc5 --- /dev/null +++ b/BeatTogether.DedicatedServer.Kernel/Commands/SetPerPlayerModifiers.cs @@ -0,0 +1,19 @@ +using BeatTogether.DedicatedServer.Kernel.Abstractions; + +namespace BeatTogether.DedicatedServer.Kernel.Commands +{ + public class SetPerPlayerModifiers : ITextCommand + { + public string CommandName => "setperplayermodifiers"; + public string ShortHandName => "ppm"; + public string Description => "if set to true, then players will use what ever modifiers they have selected, default false"; + + public bool Enabled = false; + + public void ReadValues(string[] Values) + { + if (Values != null) + Enabled = Values[0] == "true" || Values[0] == "t"; + } + } +} diff --git a/BeatTogether.DedicatedServer.Kernel/Commands/SetResultsTime.cs b/BeatTogether.DedicatedServer.Kernel/Commands/SetResultsTime.cs new file mode 100644 index 00000000..a44b11e5 --- /dev/null +++ b/BeatTogether.DedicatedServer.Kernel/Commands/SetResultsTime.cs @@ -0,0 +1,19 @@ +using BeatTogether.DedicatedServer.Kernel.Abstractions; + +namespace BeatTogether.DedicatedServer.Kernel.Commands +{ + public class SetResultsTime : ITextCommand + { + public string CommandName => "setresultstime"; + public string ShortHandName => "srt"; + public string Description => "the length of the results screen, default is 20 seconds"; + + public int Countdown = 20; + + public void ReadValues(string[] Values) + { + if (Values != null) + Countdown = int.Parse(Values[0]); + } + } +} diff --git a/BeatTogether.DedicatedServer.Kernel/Commands/SetServerName.cs b/BeatTogether.DedicatedServer.Kernel/Commands/SetServerName.cs new file mode 100644 index 00000000..42de9ac2 --- /dev/null +++ b/BeatTogether.DedicatedServer.Kernel/Commands/SetServerName.cs @@ -0,0 +1,19 @@ +using BeatTogether.DedicatedServer.Kernel.Abstractions; + +namespace BeatTogether.DedicatedServer.Kernel.Commands +{ + public class SetServerName : ITextCommand + { + public string CommandName => "setservername"; + public string ShortHandName => "ssn"; + public string Description => "Enter some text to set the name of the server"; + + public string Name = string.Empty; + + public void ReadValues(string[] Values) + { + if (Values != null) + Name = Values[0]; + } + } +} diff --git a/BeatTogether.DedicatedServer.Kernel/Commands/SetWelcomeMessage.cs b/BeatTogether.DedicatedServer.Kernel/Commands/SetWelcomeMessage.cs new file mode 100644 index 00000000..c476b1aa --- /dev/null +++ b/BeatTogether.DedicatedServer.Kernel/Commands/SetWelcomeMessage.cs @@ -0,0 +1,19 @@ +using BeatTogether.DedicatedServer.Kernel.Abstractions; + +namespace BeatTogether.DedicatedServer.Kernel.Commands +{ + public class SetWelcomeMessage : ITextCommand + { + public string CommandName => "setwelcomemessage"; + public string ShortHandName => "swm"; + public string Description => "Enter some text to set the welcome message for this server"; + + public string Text = string.Empty; + + public void ReadValues(string[] Values) + { + if (Values != null) + Text = Values[0]; + } + } +} diff --git a/BeatTogether.DedicatedServer.Kernel/Configuration/InstanceConfiguration.cs b/BeatTogether.DedicatedServer.Kernel/Configuration/InstanceConfiguration.cs index dd9c69ff..60aa612b 100644 --- a/BeatTogether.DedicatedServer.Kernel/Configuration/InstanceConfiguration.cs +++ b/BeatTogether.DedicatedServer.Kernel/Configuration/InstanceConfiguration.cs @@ -1,4 +1,5 @@ -using BeatTogether.DedicatedServer.Kernel.Enums; +using BeatTogether.Core.Enums; +using BeatTogether.Core.Models; namespace BeatTogether.DedicatedServer.Kernel.Configuration { @@ -6,32 +7,39 @@ public sealed class InstanceConfiguration { public int Port { get; set; } public string Secret { get; set; } = string.Empty; + public string Code { get; set; } = string.Empty; public string ServerOwnerId { get; set; } = string.Empty; public string ServerId { get; set; } = "ziuMSceapEuNN7wRGQXrZg"; + public string WelcomeMessage { get; set; } = string.Empty; public string ServerName { get; set; } = string.Empty; - public float DestroyInstanceTimeout { get; set; } = 10f; //set to -1 for no timeout(must close using api), 0 would be for instant close, 10 seconds is default. Less than 6 seconds can cause cfr-3 issues + public long DestroyInstanceTimeout { get; set; } = 10000L; //set to -1 for no timeout(must close using api), 0 would be for instant close, 10 seconds is default. Less than 6 seconds can cause cfr-3 issues public string SetConstantManagerFromUserId { get; set; } = string.Empty; //If a user creates a server using the api and enteres there userId (eg uses discord bot with linked account)) public bool AllowPerPlayerDifficulties { get; set; } = false; public bool AllowPerPlayerModifiers { get; set; } = false; public CountdownConfig CountdownConfig { get; set; } = new(); - public int MaxPlayerCount { get; set; } - public DiscoveryPolicy DiscoveryPolicy { get; set; } - public InvitePolicy InvitePolicy { get; set; } - public GameplayServerMode GameplayServerMode { get; set; } - public SongSelectionMode SongSelectionMode { get; set; } - public GameplayServerControlSettings GameplayServerControlSettings { get; set; } + + public GameplayServerConfiguration GameplayServerConfiguration { get; set; } = new(); + + public BeatmapDifficultyMask BeatmapDifficultyMask { get; set; } + public GameplayModifiersMask GameplayModifiersMask { get; set; } + public string SongPacksMask { get; set; } = string.Empty; + public bool AllowChroma { get; set; } public bool AllowMappingExtensions { get; set; } public bool AllowNoodleExtensions { get; set; } - public float KickPlayersWithoutEntitlementTimeout { get; set; } = 30f; - public bool DisablePlayerMovement { get; set; } = false; - public bool DisableNotes { get; set; } = false; + public bool DisableNotes { get; set; } + public bool ForceEnableNotes { get; set; } = false; + public bool ForceStartMode { get; set; } = false; + public long SendPlayersWithoutEntitlementToSpectateTimeout { get; set; } = 30000L; + public int MaxLengthCommand { get; set; } = 200; + public bool ApplyNoFailModifier { get; set; } = true; + public int DisableNotesPlayerCount { get; set; } = 16; } public sealed class CountdownConfig { - public float CountdownTimePlayersReady { get; set; } = 30.0f; - public float BeatMapStartCountdownTime { get; set; } = 5.0f; - public float ResultsScreenTime { get; set; } = 20.0f; + public long CountdownTimePlayersReady { get; set; } = 30000L; + public long BeatMapStartCountdownTime { get; set; } = 5000L; + public long ResultsScreenTime { get; set; } = 20000L; } } diff --git a/BeatTogether.DedicatedServer.Kernel/DedicatedInstance.cs b/BeatTogether.DedicatedServer.Kernel/DedicatedInstance.cs index e835f9c6..1a8ab84a 100644 --- a/BeatTogether.DedicatedServer.Kernel/DedicatedInstance.cs +++ b/BeatTogether.DedicatedServer.Kernel/DedicatedInstance.cs @@ -1,29 +1,29 @@ using System; -using System.Collections.Concurrent; +using System.Collections.Generic; using System.Linq; using System.Net; -using System.Runtime.Serialization; using System.Threading; using System.Threading.Tasks; using BeatTogether.DedicatedServer.Kernel.Abstractions; using BeatTogether.DedicatedServer.Kernel.Configuration; -using BeatTogether.DedicatedServer.Kernel.Encryption; -using BeatTogether.DedicatedServer.Kernel.Enums; +using BeatTogether.DedicatedServer.Kernel.ENet; +using BeatTogether.DedicatedServer.Kernel.Extensions; using BeatTogether.DedicatedServer.Messaging.Enums; using BeatTogether.DedicatedServer.Messaging.Models; using BeatTogether.DedicatedServer.Messaging.Packets; using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MenuRpc; -using BeatTogether.LiteNetLib; -using BeatTogether.LiteNetLib.Abstractions; -using BeatTogether.LiteNetLib.Configuration; -using BeatTogether.LiteNetLib.Enums; -using Krypton.Buffers; -using Microsoft.Extensions.DependencyInjection; +using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MPChatPackets; +using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MpCorePackets; +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.DedicatedServer.Messaging.Util; using Serilog; +using BeatTogether.DedicatedServer.Ignorance.IgnoranceCore; +using Microsoft.Extensions.DependencyInjection; +using BeatTogether.Core.Enums; namespace BeatTogether.DedicatedServer.Kernel { - public sealed class DedicatedInstance : LiteNetServer, IDedicatedInstance + public sealed class DedicatedInstance : ENetServer, IDedicatedInstance { // Milliseconds instance will wait for a player to connect. public const int WaitForPlayerTimeLimit = 10000; @@ -32,62 +32,52 @@ public sealed class DedicatedInstance : LiteNetServer, IDedicatedInstance public const int SyncTimeDelay = 5000; public InstanceConfiguration _configuration { get; private set; } - public bool IsRunning => IsStarted; - public float RunTime => (DateTime.UtcNow.Ticks - _startTime) / 10000000.0f; - public float NoPlayersTime { get; private set; } = -1; //tracks the instance time once there are 0 players in the lobby + public int Port => _configuration.Port; + public bool IsRunning => IsAlive; + public long RunTime => (DateTime.UtcNow.Ticks - _startTime) / 10000L; + public long NoPlayersTime { get; private set; } = -1; //tracks the instance time once there are 0 players in the lobby public MultiplayerGameState State { get; private set; } = MultiplayerGameState.Lobby; public event Action StopEvent = null!; - public event Action PlayerConnectedEvent = null!; - public event Action PlayerDisconnectedEvent = null!; - public event Action PlayerDisconnectBeforeJoining = null!; + public event Action PlayerConnectedEvent = null!; + public event Action PlayerDisconnectedEvent = null!; + public event Action PlayerDisconnectBeforeJoining = null!; public event Action GameIsInLobby = null!; + public event Action UpdateInstanceEvent = null!; - private readonly IHandshakeSessionRegistry _handshakeSessionRegistry; private readonly IPlayerRegistry _playerRegistry; private readonly IServiceProvider _serviceProvider; - private readonly PacketEncryptionLayer _packetEncryptionLayer; - - private readonly ConcurrentQueue _releasedConnectionIds = new(); - private readonly ConcurrentQueue _releasedSortIndices = new(); - private readonly ILogger _logger = Log.ForContext(); + private IPacketDispatcher PacketDispatcher; + private PacketSource ConnectedMessageSource; + + private readonly PlayerStateHash ServerStateHash = new(); - private long _startTime; private byte _connectionIdCount = 0; private int _lastSortIndex = -1; + private readonly Queue _releasedConnectionIds = new(); + private readonly Queue _releasedSortIndices = new(); + private readonly ILogger _logger = Log.ForContext(); + + private long _startTime; private CancellationTokenSource? _waitForPlayerCts = null; private CancellationTokenSource? _stopServerCts; - private IPacketDispatcher _packetDispatcher = null!; public DedicatedInstance( InstanceConfiguration configuration, - IHandshakeSessionRegistry handshakeSessionRegistry, IPlayerRegistry playerRegistry, - LiteNetConfiguration liteNetConfiguration, - LiteNetPacketRegistry registry, - IServiceProvider serviceProvider, - IPacketLayer packetLayer, - PacketEncryptionLayer packetEncryptionLayer) - : base ( - new IPEndPoint(IPAddress.Any, configuration.Port), - liteNetConfiguration, - registry, - serviceProvider, - packetLayer) + IServiceProvider serviceProvider) + : base (configuration.Port) { _configuration = configuration; - - _handshakeSessionRegistry = handshakeSessionRegistry; _playerRegistry = playerRegistry; _serviceProvider = serviceProvider; - _packetEncryptionLayer = packetEncryptionLayer; } #region Public Methods - - public IHandshakeSessionRegistry GetHandshakeSessionRegistry() + + public void InstanceConfigUpdated() { - return _handshakeSessionRegistry; + UpdateInstanceEvent?.Invoke(this); } public IPlayerRegistry GetPlayerRegistry() @@ -99,39 +89,45 @@ public IServiceProvider GetServiceProvider() return _serviceProvider; } - public Task Start(CancellationToken cancellationToken = default) + public async Task Start(CancellationToken cancellationToken = default) { if (IsRunning) - return Task.CompletedTask; + return; - _packetDispatcher = _serviceProvider.GetRequiredService(); + PacketDispatcher = _serviceProvider.GetRequiredService(); + ConnectedMessageSource = _serviceProvider.GetRequiredService(); + _startTime = DateTime.UtcNow.Ticks; _logger.Information( "Starting dedicated server " + - $"(Port={Port}," + + $"Endpoint={EndPoint}," + $"ServerName='{_configuration.ServerName}', " + $"Secret='{_configuration.Secret}', " + + $"Code='{_configuration.Code}', " + $"ManagerId='{_configuration.ServerOwnerId}', " + - $"MaxPlayerCount={_configuration.MaxPlayerCount}, " + - $"DiscoveryPolicy={_configuration.DiscoveryPolicy}, " + - $"InvitePolicy={_configuration.InvitePolicy}, " + - $"GameplayServerMode={_configuration.GameplayServerMode}, " + - $"SongSelectionMode={_configuration.SongSelectionMode}, " + - $"GameplayServerControlSettings={_configuration.GameplayServerControlSettings})." + $"MaxPlayerCount={_configuration.GameplayServerConfiguration.MaxPlayerCount}, " + + $"DiscoveryPolicy={_configuration.GameplayServerConfiguration.DiscoveryPolicy}, " + + $"InvitePolicy={_configuration.GameplayServerConfiguration.InvitePolicy}, " + + $"GameplayServerMode={_configuration.GameplayServerConfiguration.GameplayServerMode}, " + + $"SongSelectionMode={_configuration.GameplayServerConfiguration.SongSelectionMode}, " + + $"GameplayServerControlSettings={_configuration.GameplayServerConfiguration.GameplayServerControlSettings})" ); _stopServerCts = new CancellationTokenSource(); - - - - if (_configuration.DestroyInstanceTimeout != -1) + + if (_configuration.DestroyInstanceTimeout >= 0) { _waitForPlayerCts = new CancellationTokenSource(); - Task.Delay((WaitForPlayerTimeLimit + (int)(_configuration.DestroyInstanceTimeout * 1000)), _waitForPlayerCts.Token).ContinueWith(t => + + var waitTimeLimit = (int)(WaitForPlayerTimeLimit + _configuration.DestroyInstanceTimeout); + + _ = Task.Delay(waitTimeLimit, _waitForPlayerCts.Token) + .ContinueWith(t => { if (!t.IsCanceled) { - _logger.Warning("Stopping instance: " + _configuration.ServerName); + _logger.Warning("Stopping instance (no players joined timeout): {Instance}", + _configuration.ServerName); _ = Stop(CancellationToken.None); } else @@ -140,459 +136,578 @@ public Task Start(CancellationToken cancellationToken = default) } }, cancellationToken); } - - //StartEvent?.Invoke(this); - - base.Start(); - Task.Run(() => SendSyncTime(_stopServerCts.Token), cancellationToken); - return Task.CompletedTask; + ServerStateHash.WriteToBitMask("dedicated_server"); + await base.Start(); + + _ = Task.Run(() => SendSyncTime(_stopServerCts.Token), cancellationToken); } - public Task Stop(CancellationToken cancellationToken = default) + public async Task Stop(CancellationToken cancellationToken = default) { if (!IsRunning) - return Task.CompletedTask; + return; _logger.Information( "Stopping dedicated server " + - $"(Port={Port}," + + $"(Port={Port}, " + $"ServerName='{_configuration.ServerName}', " + $"Secret='{_configuration.Secret}', " + + $"Code='{_configuration.Code}', " + $"ManagerId='{_configuration.ServerOwnerId}', " + - $"MaxPlayerCount={_configuration.MaxPlayerCount}, " + - $"DiscoveryPolicy={_configuration.DiscoveryPolicy}, " + - $"InvitePolicy={_configuration.InvitePolicy}, " + - $"GameplayServerMode={_configuration.GameplayServerMode}, " + - $"SongSelectionMode={_configuration.SongSelectionMode}, " + - $"GameplayServerControlSettings={_configuration.GameplayServerControlSettings})." + $"MaxPlayerCount={_configuration.GameplayServerConfiguration.MaxPlayerCount}, " + + $"DiscoveryPolicy={_configuration.GameplayServerConfiguration.DiscoveryPolicy}, " + + $"InvitePolicy={_configuration.GameplayServerConfiguration.InvitePolicy}, " + + $"GameplayServerMode={_configuration.GameplayServerConfiguration.GameplayServerMode}, " + + $"SongSelectionMode={_configuration.GameplayServerConfiguration.SongSelectionMode}, " + + $"GameplayServerControlSettings={_configuration.GameplayServerConfiguration.GameplayServerControlSettings})." ); - _packetDispatcher.SendToNearbyPlayers(new KickPlayerPacket - { - DisconnectedReason = DisconnectedReason.ServerTerminated - }, DeliveryMethod.ReliableOrdered); + ServerStateHash.WriteToBitMask("terminating"); + PacketDispatcher.SendToNearbyPlayers(new INetSerializable[]{ + new PlayerStatePacket + { + PlayerState = ServerStateHash + }, + new KickPlayerPacket + { + DisconnectedReason = DisconnectedReason.ServerTerminated + } + }, IgnoranceChannelTypes.Reliable); + + KickAllPeers(); _stopServerCts!.Cancel(); + _waitForPlayerCts!.Cancel(); + StopEvent?.Invoke(this); - base.Stop(); - return Task.CompletedTask; + await base.Stop(); } - object SortIndexLock = new(); public int GetNextSortIndex() { if (_releasedSortIndices.TryDequeue(out var sortIndex)) return sortIndex; - lock (SortIndexLock) + + _lastSortIndex++; + if (_lastSortIndex > _configuration.GameplayServerConfiguration.MaxPlayerCount) { - _lastSortIndex++; - if (_lastSortIndex > _configuration.MaxPlayerCount) - { - return 0; - } - return _lastSortIndex; + return 0; } + return _lastSortIndex; } - public void ReleaseSortIndex(int sortIndex) => + public void ReleaseSortIndex(int sortIndex) + { _releasedSortIndices.Enqueue(sortIndex); - - object ConnectionIDLock = new(); - public byte GetNextConnectionId() //ID 0 is server, ID 127 also means send to all players, 255 will mean perm spectator when/if implimented. Starts at 1 because funny server logic + } + + public byte GetNextConnectionId() //ID 0 is server, ID 127 means send to all players { if (_releasedConnectionIds.TryDequeue(out var connectionId)) return connectionId; - lock (ConnectionIDLock) - { + + _connectionIdCount++; + if (_connectionIdCount == 127) _connectionIdCount++; - if (_connectionIdCount == 127) - _connectionIdCount++; - if (_connectionIdCount > (byte.MaxValue - 5)) - return 255; //Give them an unusedID so they dont conflict with anyone - return _connectionIdCount; - } + if (_connectionIdCount > (byte.MaxValue - 5)) //Currently not implimented to use ID's over 126 client side + return 255; //Give them an unusedID so they dont conflict with anyone + return _connectionIdCount; } - public void ReleaseConnectionId(byte connectionId) => + public void ReleaseConnectionId(byte connectionId) + { + _releasedConnectionIds.Enqueue(connectionId); + } public void SetState(MultiplayerGameState state) { State = state; - _packetDispatcher.SendToNearbyPlayers(new SetMultiplayerGameStatePacket() + PacketDispatcher.SendToNearbyPlayers(new SetMultiplayerGameStatePacket() { State = state - }, DeliveryMethod.ReliableOrdered); + }, IgnoranceChannelTypes.Reliable); GameIsInLobby?.Invoke(_configuration.Secret, state == MultiplayerGameState.Lobby); } - #endregion - - #region LiteNetServer - - object AcceptConnectionLock = new(); - - public override bool ShouldAcceptConnection(EndPoint endPoint, ref SpanBufferReader additionalData) + public override IPlayer? TryAcceptConnection(IPEndPoint endPoint, ref SpanBuffer Data) { + bool PlayerNoJoin = false; - if (ShouldDenyConnection(endPoint, ref additionalData)) - { - PlayerDisconnectBeforeJoining?.Invoke(_configuration.Secret, endPoint, _playerRegistry.GetPlayerCount()); - return false; - } - return true; - } - public bool ShouldDenyConnection(EndPoint endPoint, ref SpanBufferReader additionalData) - { - var connectionRequestData = new ConnectionRequestData(); + ConnectionRequestData connectionRequestData = new(); try { - connectionRequestData.ReadFrom(ref additionalData); + connectionRequestData.ReadFrom(ref Data); } - catch (Exception e) + catch (Exception ex) { - _logger.Warning(e, - "Failed to deserialize connection request data " + + _logger.Warning(ex + + "Failed to deserialize connection request data" + $"(RemoteEndPoint='{endPoint}')." ); - return true; + PlayerNoJoin = true; + goto EndOfTryAccept; } - _logger.Debug( - "Handling connection request " + + _logger.Information( + "Handling connection request though Enet" + $"(RemoteEndPoint='{endPoint}', " + - $"Secret='{connectionRequestData.Secret}', " + + $"PlayerSessionId='{connectionRequestData.PlayerSessionId}', " + $"UserId='{connectionRequestData.UserId}', " + - $"UserName='{connectionRequestData.UserName}', " + - $"IsConnectionOwner={connectionRequestData.IsConnectionOwner})." + $"UserName='{connectionRequestData.UserName}', " ); if (string.IsNullOrEmpty(connectionRequestData.UserId) || string.IsNullOrEmpty(connectionRequestData.UserName)) - //string.IsNullOrEmpty(connectionRequestData.Secret)) { _logger.Warning( "Received a connection request with invalid data " + $"(RemoteEndPoint='{endPoint}', " + - //$"Secret='{connectionRequestData.Secret}', " + $"UserId='{connectionRequestData.UserId}', " + $"UserName='{connectionRequestData.UserName}', " + $"IsConnectionOwner={connectionRequestData.IsConnectionOwner})." ); - return true; + PlayerNoJoin = true; + goto EndOfTryAccept; } - - lock (AcceptConnectionLock) + if (_playerRegistry.GetPlayerCount() >= _configuration.GameplayServerConfiguration.MaxPlayerCount) { - if (_playerRegistry.GetPlayerCount() >= _configuration.MaxPlayerCount) - { - return true; - } - int sortIndex = GetNextSortIndex(); - byte connectionId = GetNextConnectionId(); - - var player = new Player( - endPoint, - this, - connectionId, - _configuration.Secret, - connectionRequestData.UserId, - connectionRequestData.UserName, - connectionRequestData.PlayerSessionId - ) - { - SortIndex = sortIndex - }; - - if (!_playerRegistry.AddPlayer(player)) - { - ReleaseSortIndex(player.SortIndex); - ReleaseConnectionId(player.ConnectionId); - return true; - } - _logger.Information( - "Player joined dedicated server " + - $"(RemoteEndPoint='{player.Endpoint}', " + - $"ConnectionId={player.ConnectionId}, " + - $"Secret='{player.Secret}', " + - $"UserId='{player.UserId}', " + - $"UserName='{player.UserName}', " + - $"SortIndex={player.SortIndex})." - ); + _logger.Warning("Master server sent a player to a full server"); + PlayerNoJoin = true; + goto EndOfTryAccept; + } + if (connectionRequestData.UserName == "IGGAMES" || connectionRequestData.UserName == "IGGGAMES") + { + _logger.Information("an IGG player just tried joining after passing master auth"); + PlayerNoJoin = true; + goto EndOfTryAccept; + } + int sortIndex = GetNextSortIndex(); + byte connectionId = GetNextConnectionId(); + + var player = new Player( + endPoint, + this, + connectionId, + connectionRequestData.UserId, + connectionRequestData.UserName, + connectionRequestData.PlayerSessionId + ) + { + SortIndex = sortIndex + }; + if (!_playerRegistry.AddPlayer(player)) + { + ReleaseSortIndex(player.SortIndex); + ReleaseConnectionId(player.ConnectionId); + PlayerNoJoin = true; + goto EndOfTryAccept; + } - if (_waitForPlayerCts != null) - _waitForPlayerCts.Cancel(); + if (_configuration.ServerName == string.Empty) + { + //_logger.Information("About to update servers name" + _configuration.ServerName); + _configuration.ServerName = player.UserName + "'s server"; + InstanceConfigUpdated(); + _logger.Information("Updated servers name to: " + _configuration.ServerName); } - - // Retrieve encryption params from handshake process by player session token, if provided - if (!string.IsNullOrEmpty(connectionRequestData.PlayerSessionId)) + _logger.Information( + "Player joined dedicated server " + + $"(RemoteEndPoint='{player.Endpoint}', " + + $"ConnectionId={player.ConnectionId}, " + + $"PlayerSessionId='{player.PlayerSessionId}', " + + $"UserId='{player.HashedUserId}', " + + $"UserName='{player.UserName}', " + + $"SortIndex={player.SortIndex})." + ); + + if (_waitForPlayerCts != null) + _waitForPlayerCts.Cancel(); + + //UserID, UserName, and session + if (!GetPlayerRegistry().RemoveExtraPlayerSessionDataAndApply(player)) { - var handshakeSession = - _handshakeSessionRegistry.TryGetByPlayerSessionId(connectionRequestData.PlayerSessionId); + goto EndOfTryAccept; + } - if (handshakeSession != null && handshakeSession.EncryptionParameters != null) - { - _packetEncryptionLayer.AddEncryptedEndPoint((IPEndPoint)endPoint, - handshakeSession.EncryptionParameters, true); - } + + return player; + + EndOfTryAccept: + if (PlayerNoJoin) + { + GetPlayerRegistry().RemoveExtraPlayerSessionData(connectionRequestData.PlayerSessionId); + string[] Players = _playerRegistry.Players.Select(p => p.HashedUserId).ToArray(); + PlayerDisconnectBeforeJoining?.Invoke(_configuration.Secret, endPoint, Players); + return null; } - - return false; + return null; } - public override void OnLatencyUpdate(EndPoint endPoint, int latency) - => _logger.Verbose($"Latency updated (RemoteEndPoint='{endPoint}', Latency={0.001f * latency})."); + public override void OnReceive(EndPoint remoteEndPoint, ref SpanBuffer reader, IgnoranceChannelTypes method) + { + ConnectedMessageSource.OnReceive(remoteEndPoint, ref reader, method); + } - object ConnectionLock = new(); public override void OnConnect(EndPoint endPoint) { - lock (ConnectionLock) + //_logger.Information($"Endpoint connected (RemoteEndPoint='{endPoint}')"); + + if (!_playerRegistry.TryGetPlayer(endPoint, out var player)) { - _logger.Information($"Endpoint connected (RemoteEndPoint='{endPoint}')"); + _logger.Warning( + "Failed to retrieve player " + + $"(RemoteEndPoint='{endPoint}')." + ); + return; + } + PlayerConnectedEvent?.Invoke(player); - if (!_playerRegistry.TryGetPlayer(endPoint, out var player)) + //Send to other players that there is a new player + PacketDispatcher.SendExcludingPlayer(player, new INetSerializable[]{ + new PlayerConnectedPacket { - _logger.Warning( - "Failed to retrieve player " + - $"(RemoteEndPoint='{endPoint}')." - ); - Disconnect(endPoint); - return; + RemoteConnectionId = player.ConnectionId, + UserId = player.HashedUserId, + UserName = player.UserName, + IsConnectionOwner = false + }, + new PlayerSortOrderPacket + { + UserId = player.HashedUserId, + SortIndex = player.SortIndex + }, + new MpNodePoseSyncStatePacket + { + fullStateUpdateFrequency = Math.Max(_playerRegistry.GetMillisBetweenPoseSyncStateDeltaPackets(), 100L), + deltaUpdateFrequency = _playerRegistry.GetMillisBetweenPoseSyncStateDeltaPackets() + }, + new MpScoreSyncStatePacket + { + fullStateUpdateFrequency = Math.Max(_playerRegistry.GetMillisBetweenScoreSyncStateDeltaPackets(), 500L), + deltaUpdateFrequency = _playerRegistry.GetMillisBetweenScoreSyncStateDeltaPackets() } + }, IgnoranceChannelTypes.Reliable); - //Send to existing players that a new player has joined - _packetDispatcher.SendExcludingPlayer(player, new INetSerializable[] + //Send server infomation to player + var Player_ConnectPacket = new INetSerializable[] + { + new PingPacket { - new SyncTimePacket - { - SyncTime = RunTime - }, - new PlayerConnectedPacket - { - RemoteConnectionId = player.ConnectionId, - UserId = player.UserId, - UserName = player.UserName, - IsConnectionOwner = false - }, - new PlayerSortOrderPacket - { - UserId = player.UserId, - SortIndex = player.SortIndex - } - } - ,DeliveryMethod.ReliableOrdered); - - //Send new player their sort order and other data - _packetDispatcher.SendToPlayer(player, new INetSerializable[] + PingTime = RunTime + }, + new SyncTimePacket { - new SyncTimePacket - { - SyncTime = RunTime - }, - new PlayerSortOrderPacket - { - UserId = player.UserId, - SortIndex = player.SortIndex - }, - new PlayerConnectedPacket - { - RemoteConnectionId = 0, - UserId = _configuration.ServerId, - UserName = _configuration.ServerName, - IsConnectionOwner = true - }, - new SetIsStartButtonEnabledPacket// Disables start button if they are server owner without selected song - { - Reason = player.UserId == _configuration.ServerOwnerId ? CannotStartGameReason.NoSongSelected : CannotStartGameReason.None - } - } - ,DeliveryMethod.ReliableOrdered); + SyncTime = RunTime + }, + new PlayerSortOrderPacket + { + UserId = player.HashedUserId, + SortIndex = player.SortIndex + }, + new PlayerConnectedPacket + { + RemoteConnectionId = 0, + UserId = _configuration.ServerId, + UserName = _configuration.ServerName, + IsConnectionOwner = true + }, + new PlayerStatePacket + { + PlayerState = ServerStateHash + }, + new SetIsStartButtonEnabledPacket// Disables start button if they are server owner without selected song + { + Reason = player.HashedUserId == _configuration.ServerOwnerId ? CannotStartGameReason.NoSongSelected : CannotStartGameReason.None + }, + new MpNodePoseSyncStatePacket + { + fullStateUpdateFrequency = Math.Max(_playerRegistry.GetMillisBetweenPoseSyncStateDeltaPackets(), 100L), + deltaUpdateFrequency = _playerRegistry.GetMillisBetweenPoseSyncStateDeltaPackets() + }, + new MpScoreSyncStatePacket + { + fullStateUpdateFrequency = Math.Max(_playerRegistry.GetMillisBetweenScoreSyncStateDeltaPackets(), 500L), + deltaUpdateFrequency = _playerRegistry.GetMillisBetweenScoreSyncStateDeltaPackets() + } + }; + PacketDispatcher.SendToPlayer(player, Player_ConnectPacket, IgnoranceChannelTypes.Reliable); - foreach (IPlayer p in _playerRegistry.Players) + //Send other connected players to new player + List MakeBigPacketToSendToPlayer = new(); + foreach (IPlayer p in _playerRegistry.Players) + { + if(p.ConnectionId != player.ConnectionId) { - if (p.ConnectionId != player.ConnectionId) + MakeBigPacketToSendToPlayer.Add(new PlayerConnectedPacket { - // Send all player connection data packets to new player - _packetDispatcher.SendToPlayer(player,new INetSerializable[]{ - new PlayerConnectedPacket - { - RemoteConnectionId = p.ConnectionId, - UserId = p.UserId, - UserName = p.UserName, - IsConnectionOwner = false - }, - new PlayerSortOrderPacket - { - UserId = p.UserId, - SortIndex = p.SortIndex - } - }, DeliveryMethod.ReliableOrdered); - - // Send all player identity packets to new player - _packetDispatcher.SendFromPlayerToPlayer(p, player, new PlayerIdentityPacket - { - PlayerState = p.State, - PlayerAvatar = p.Avatar, - Random = new ByteArray { Data = p.Random }, - PublicEncryptionKey = new ByteArray { Data = p.PublicEncryptionKey } - }, DeliveryMethod.ReliableOrdered); - } + RemoteConnectionId = p.ConnectionId, + UserId = p.HashedUserId, + UserName = p.UserName, + IsConnectionOwner = false + }); + MakeBigPacketToSendToPlayer.Add(new PlayerSortOrderPacket + { + UserId = p.HashedUserId, + SortIndex = p.SortIndex + }); } + } + foreach (var SubPacket in MakeBigPacketToSendToPlayer.Chunk(20)) + { + PacketDispatcher.SendToPlayer(player, SubPacket.ToArray(), IgnoranceChannelTypes.Reliable); + } - // Update permissions - constant manager possibly does not work - if ((_configuration.SetConstantManagerFromUserId == player.UserId || _playerRegistry.GetPlayerCount() == 1) && _configuration.GameplayServerMode == Enums.GameplayServerMode.Managed) + //send player avatars and states of other players in server to new player + INetSerializable[] SendToPlayerFromPlayers = new INetSerializable[2]; + SendToPlayerFromPlayers[0] = new PlayerIdentityPacket(); + SendToPlayerFromPlayers[1] = new MpPlayerData(); + //TODO send selected modifiers if they have any, selected beatmap and custom bm packet. + + foreach (IPlayer p in _playerRegistry.Players) + { + if (p.ConnectionId != player.ConnectionId) { - _configuration.ServerOwnerId = player.UserId; + // Send player to player data to new player + ((PlayerIdentityPacket)SendToPlayerFromPlayers[0]).PlayerState = p.State; + ((PlayerIdentityPacket)SendToPlayerFromPlayers[0]).PlayerAvatar = p.Avatar; + ((PlayerIdentityPacket)SendToPlayerFromPlayers[0]).Random = new ByteArray { Data = p.Random }; + ((PlayerIdentityPacket)SendToPlayerFromPlayers[0]).PublicEncryptionKey = new ByteArray { Data = p.PublicEncryptionKey }; + ((MpPlayerData)SendToPlayerFromPlayers[1]).PlatformID = p.PlatformUserId; + ((MpPlayerData)SendToPlayerFromPlayers[1]).Platform = p.PlayerPlatform.Convert(); + ((MpPlayerData)SendToPlayerFromPlayers[1]).ClientVersion = p.PlayerClientVersion.ToString(); + + // Send all player avatars and states to just joined player + PacketDispatcher.SendFromPlayerToPlayer(p, player, SendToPlayerFromPlayers, IgnoranceChannelTypes.Reliable); } + } - _packetDispatcher.SendToNearbyPlayers(new SetPlayersPermissionConfigurationPacket + // Update permissions + if ((_configuration.SetConstantManagerFromUserId == player.HashedUserId || _playerRegistry.GetPlayerCount() == 1) && _configuration.GameplayServerConfiguration.GameplayServerMode == Core.Enums.GameplayServerMode.Managed) + SetNewServerOwner(player); + + if (player.PlayerPlatform == Core.Enums.Platform.Test) //If the player is a bot, send permissions. Normal players request this in a packet when they join + { + bool HasManager = (_playerRegistry.TryGetPlayer(_configuration.ServerOwnerId, out var ServerOwner) && !player.IsServerOwner); + PlayerPermissionConfiguration[] playerPermissionConfigurations = new PlayerPermissionConfiguration[HasManager ? 2 : 1]; + playerPermissionConfigurations[0] = new PlayerPermissionConfiguration + { + UserId = player.HashedUserId, + IsServerOwner = player.IsServerOwner, + HasRecommendBeatmapsPermission = player.CanRecommendBeatmaps, + HasRecommendGameplayModifiersPermission = player.CanRecommendModifiers, + HasKickVotePermission = player.CanKickVote, + HasInvitePermission = player.CanInvite + }; + if (HasManager) + playerPermissionConfigurations[1] = new PlayerPermissionConfiguration + { + UserId = ServerOwner!.HashedUserId, + IsServerOwner = ServerOwner!.IsServerOwner, + HasRecommendBeatmapsPermission = ServerOwner!.CanRecommendBeatmaps, + HasRecommendGameplayModifiersPermission = ServerOwner!.CanRecommendModifiers, + HasKickVotePermission = ServerOwner!.CanKickVote, + HasInvitePermission = ServerOwner!.CanInvite + }; + PacketDispatcher.SendToPlayer(player, new SetPlayersPermissionConfigurationPacket { PermissionConfiguration = new PlayersPermissionConfiguration { - PlayersPermission = _playerRegistry.Players.Select(x => new PlayerPermissionConfiguration - { - UserId = x.UserId, - IsServerOwner = x.IsServerOwner, - HasRecommendBeatmapsPermission = x.CanRecommendBeatmaps, - HasRecommendGameplayModifiersPermission = x.CanRecommendModifiers, - HasKickVotePermission = x.CanKickVote, - HasInvitePermission = x.CanInvite - }).ToArray() + PlayersPermission = playerPermissionConfigurations } - }, DeliveryMethod.ReliableOrdered); - PlayerConnectedEvent?.Invoke(player, _playerRegistry.GetPlayerCount()); + }, IgnoranceChannelTypes.Reliable); } - - } - object DisconnectplayerLock = new(); - public void DisconnectPlayer(string UserId) - { - lock (DisconnectplayerLock) + //Send joining players mpcore data to everyone else + foreach (IPlayer p in _playerRegistry.Players) { - if(_playerRegistry.TryGetPlayer(UserId, out var player)) - _packetDispatcher.SendToPlayer(player, new KickPlayerPacket + if (p.ConnectionId != player.ConnectionId) + { + PacketDispatcher.SendFromPlayerToPlayer(player, p, new MpPlayerData { - DisconnectedReason = DisconnectedReason.Kicked - }, DeliveryMethod.ReliableOrdered); + PlatformID = player.PlatformUserId!, + Platform = player.PlayerPlatform.Convert(), + ClientVersion = player.PlayerClientVersion.ToString()! + }, IgnoranceChannelTypes.Reliable); + } } + + PacketDispatcher.SendToNearbyPlayers(new MpcTextChatPacket + { + Text = player.UserName + " Joined, Platform: " + player.PlayerPlatform.ToString() + " Version: " + player.PlayerClientVersion.ToString() + }, IgnoranceChannelTypes.Reliable); + + //_logger.Information($"Sent connection data though for (RemoteEndPoint='{endPoint}')"); } - object DisconnectLock = new(); - public override void OnDisconnect(EndPoint endPoint, DisconnectReason reason) + public void DisconnectPlayer(IPlayer player) + { + PacketDispatcher.SendToPlayer(player, new KickPlayerPacket + { + DisconnectedReason = DisconnectedReason.Kicked + }, IgnoranceChannelTypes.Reliable); + KickPeer(player.ENetPeerId); + } + + public override void OnDisconnect(EndPoint endPoint) { _logger.Information( "Endpoint disconnected " + - $"(RemoteEndPoint='{endPoint}', DisconnectReason={reason})." + $"(RemoteEndPoint='{endPoint}')." ); - if (reason == DisconnectReason.Reconnect || reason == DisconnectReason.PeerToPeerConnection) + + if (!_playerRegistry.TryGetPlayer(endPoint, out var player)) { - _logger.Information( - "Endpoint reconnecting or is peer to peer." - ); return; } + //Sends to all players that they have disconnected - // Disconnect player - lock (DisconnectLock) + PacketDispatcher.SendFromPlayer(player, new PlayerDisconnectedPacket { - if (_playerRegistry.TryGetPlayer(endPoint, out var player)) - { - _packetDispatcher.SendFromPlayer(player, new PlayerDisconnectedPacket - { - DisconnectedReason = DisconnectedReason.ClientConnectionClosed - }, DeliveryMethod.ReliableOrdered); + DisconnectedReason = DisconnectedReason.ClientConnectionClosed + }, IgnoranceChannelTypes.Reliable); - if (_configuration.ServerOwnerId == player.UserId) - _configuration.ServerOwnerId = ""; + _playerRegistry.RemovePlayer(player); + ReleaseSortIndex(player.SortIndex); + ReleaseConnectionId(player.ConnectionId); - _playerRegistry.RemovePlayer(player); - ReleaseSortIndex(player.SortIndex); - ReleaseConnectionId(player.ConnectionId); + PlayerDisconnectedEvent?.Invoke(player); - PlayerDisconnectedEvent?.Invoke(player, _playerRegistry.GetPlayerCount()); + if(_playerRegistry.GetPlayerCount() != 0) + { + if (player.IsServerOwner && _configuration.GameplayServerConfiguration.GameplayServerMode == Core.Enums.GameplayServerMode.Managed) + { + // Update permissions + SetNewServerOwner(_playerRegistry.Players[0]); } + PacketDispatcher.SendToNearbyPlayers(new INetSerializable[] { + new MpNodePoseSyncStatePacket + { + fullStateUpdateFrequency = Math.Max(_playerRegistry.GetMillisBetweenPoseSyncStateDeltaPackets(), 100L), + deltaUpdateFrequency = _playerRegistry.GetMillisBetweenPoseSyncStateDeltaPackets() + }, + new MpScoreSyncStatePacket + { + fullStateUpdateFrequency = Math.Max(_playerRegistry.GetMillisBetweenScoreSyncStateDeltaPackets(), 500L), + deltaUpdateFrequency = _playerRegistry.GetMillisBetweenScoreSyncStateDeltaPackets() + } + }, IgnoranceChannelTypes.Reliable); + } + else + { + NoPlayersTime = RunTime; + if (_configuration.DestroyInstanceTimeout != -1) + { + _waitForPlayerCts = new CancellationTokenSource(); + _ = Task.Delay((int)(_configuration.DestroyInstanceTimeout), _waitForPlayerCts.Token).ContinueWith(t => + { + if (!t.IsCanceled && _playerRegistry.GetPlayerCount() == 0) + { + _logger.Information("No players joined within the closing timeout, stopping lobby now"); + _ = Stop(CancellationToken.None); + } + else + { + _waitForPlayerCts = null; + } + }); + } + } + } + #endregion + + #region Private Methods - if (_playerRegistry.GetPlayerCount() == 0) + private void SetNewServerOwner(IPlayer NewOwner) + { + if (_playerRegistry.TryGetPlayer(_configuration.ServerOwnerId, out var OldOwner)) + { + _configuration.ServerOwnerId = NewOwner.HashedUserId; + PacketDispatcher.SendToNearbyPlayers(new SetPlayersPermissionConfigurationPacket { - NoPlayersTime = RunTime; - if (_configuration.DestroyInstanceTimeout != -1) + PermissionConfiguration = new PlayersPermissionConfiguration { - _waitForPlayerCts = new CancellationTokenSource(); - _ = Task.Delay((int)(_configuration.DestroyInstanceTimeout * 1000), _waitForPlayerCts.Token).ContinueWith(t => + PlayersPermission = new PlayerPermissionConfiguration[] { - if (!t.IsCanceled && _playerRegistry.GetPlayerCount() == 0) + new PlayerPermissionConfiguration() { - _logger.Information("No players joined within the closing timeout, stopping lobby now"); - _ = Stop(CancellationToken.None); - } - else + UserId = OldOwner.HashedUserId, + IsServerOwner = OldOwner.IsServerOwner, + HasRecommendBeatmapsPermission = OldOwner.CanRecommendBeatmaps, + HasRecommendGameplayModifiersPermission = OldOwner.CanRecommendModifiers, + HasKickVotePermission = OldOwner.CanKickVote, + HasInvitePermission = OldOwner.CanInvite + }, + new PlayerPermissionConfiguration() { - _waitForPlayerCts = null; + UserId = NewOwner.HashedUserId, + IsServerOwner = NewOwner.IsServerOwner, + HasRecommendBeatmapsPermission = NewOwner.CanRecommendBeatmaps, + HasRecommendGameplayModifiersPermission = NewOwner.CanRecommendModifiers, + HasKickVotePermission = NewOwner.CanKickVote, + HasInvitePermission = NewOwner.CanInvite } - }); + } } - } - else + }, IgnoranceChannelTypes.Reliable); + PacketDispatcher.SendToPlayer(OldOwner, new SetIsStartButtonEnabledPacket { - // Set new server owner if server owner left - if (_configuration.ServerOwnerId == "" && _configuration.GameplayServerMode == GameplayServerMode.Managed) + Reason = CannotStartGameReason.None + }, IgnoranceChannelTypes.Reliable); + } + else + { + _configuration.ServerOwnerId = NewOwner.HashedUserId; + PacketDispatcher.SendToNearbyPlayers(new SetPlayersPermissionConfigurationPacket + { + PermissionConfiguration = new PlayersPermissionConfiguration { - _configuration.ServerOwnerId = _playerRegistry.Players[0].UserId; - var serverOwner = _playerRegistry.GetPlayer(_configuration.ServerOwnerId); - - // Disable start button if they are server owner without selected song - if (serverOwner.BeatmapIdentifier == null) - _packetDispatcher.SendToPlayer(serverOwner, new SetIsStartButtonEnabledPacket - { - Reason = CannotStartGameReason.NoSongSelected - }, DeliveryMethod.ReliableOrdered); - - // Update permissions - _packetDispatcher.SendToNearbyPlayers(new SetPlayersPermissionConfigurationPacket + PlayersPermission = new PlayerPermissionConfiguration[] { - PermissionConfiguration = new PlayersPermissionConfiguration + new PlayerPermissionConfiguration() { - PlayersPermission = _playerRegistry.Players.Select(x => new PlayerPermissionConfiguration - { - UserId = x.UserId, - IsServerOwner = x.IsServerOwner, - HasRecommendBeatmapsPermission = x.CanRecommendBeatmaps, - HasRecommendGameplayModifiersPermission = x.CanRecommendModifiers, - HasKickVotePermission = x.CanKickVote, - HasInvitePermission = x.CanInvite - }).ToArray() + UserId = NewOwner.HashedUserId, + IsServerOwner = NewOwner.IsServerOwner, + HasRecommendBeatmapsPermission = NewOwner.CanRecommendBeatmaps, + HasRecommendGameplayModifiersPermission = NewOwner.CanRecommendModifiers, + HasKickVotePermission = NewOwner.CanKickVote, + HasInvitePermission = NewOwner.CanInvite } - }, DeliveryMethod.ReliableOrdered); + } } - } + }, IgnoranceChannelTypes.Reliable); } + //Disable start button if no map is selected + if (NewOwner.BeatmapIdentifier == null) + PacketDispatcher.SendToPlayer(NewOwner, new SetIsStartButtonEnabledPacket + { + Reason = CannotStartGameReason.NoSongSelected + }, IgnoranceChannelTypes.Reliable); } - #endregion - - #region Private Methods private async void SendSyncTime(CancellationToken cancellationToken) { - foreach (IPlayer player in _playerRegistry.Players) + while (!cancellationToken.IsCancellationRequested) { - _packetDispatcher.SendToPlayer(player, new SyncTimePacket() + foreach (IPlayer player in _playerRegistry.Players) { - SyncTime = player.SyncTime - }, DeliveryMethod.ReliableOrdered); - } - try - { - await Task.Delay(SyncTimeDelay, cancellationToken); - } - catch (TaskCanceledException) - { - return; + PacketDispatcher.SendToPlayer(player, new SyncTimePacket() + { + SyncTime = player.SyncTime + }, IgnoranceChannelTypes.Reliable); + } + try + { + await Task.Delay(SyncTimeDelay, cancellationToken); + } + catch (TaskCanceledException) + { + return; + } } - SendSyncTime(cancellationToken); } #endregion diff --git a/BeatTogether.DedicatedServer.Kernel/ENet/ENetConnection.cs b/BeatTogether.DedicatedServer.Kernel/ENet/ENetConnection.cs new file mode 100644 index 00000000..d3922732 --- /dev/null +++ b/BeatTogether.DedicatedServer.Kernel/ENet/ENetConnection.cs @@ -0,0 +1,30 @@ +using System.Net; + +namespace BeatTogether.DedicatedServer.Kernel.ENet +{ + public class ENetConnection + { + public ENetServer ENetServer { get; private set; } + public uint NativePeerId { get; private set; } + public IPEndPoint EndPoint { get; private set; } + public ENetConnectionState State { get; set; } + + public ENetConnection(ENetServer eNetServer, uint nativePeerId, string ip, int port) + { + ENetServer = eNetServer; + NativePeerId = nativePeerId; + EndPoint = new IPEndPoint(IPAddress.Parse(ip), port); + State = ENetConnectionState.Pending; + } + + public void Disconnect(bool clientInitiated = false) + => ENetServer.KickPeer(NativePeerId, sendKick: !clientInitiated); + } + + public enum ENetConnectionState + { + Pending = 0, + Accepted = 1, + Disconnected = 2 + } +} \ No newline at end of file diff --git a/BeatTogether.DedicatedServer.Kernel/ENet/ENetServer.cs b/BeatTogether.DedicatedServer.Kernel/ENet/ENetServer.cs new file mode 100644 index 00000000..784e4d29 --- /dev/null +++ b/BeatTogether.DedicatedServer.Kernel/ENet/ENetServer.cs @@ -0,0 +1,331 @@ +using System; +using System.Buffers; +using System.Collections.Generic; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using BeatTogether.DedicatedServer.Ignorance.ENet; +using BeatTogether.DedicatedServer.Ignorance.IgnoranceCore; +using BeatTogether.DedicatedServer.Ignorance.Util; +using BeatTogether.DedicatedServer.Kernel.Abstractions; +using BeatTogether.Extensions; +using BeatTogether.DedicatedServer.Messaging.Util; +using Krypton.Buffers; +using Serilog; + +namespace BeatTogether.DedicatedServer.Kernel.ENet +{ + /// + /// Server/socket for ENet (1.31+) connectivity. + /// + public class ENetServer : IDisposable + { + private readonly int Port; + private readonly IgnoranceServer _ignorance; + private readonly ILogger _logger; + private CancellationTokenSource _runtimeCts; + private readonly Thread _workerThread; + private readonly Dictionary _connections; + + private byte[]? _receiveBuffer; + + public IPEndPoint EndPoint => new(IPAddress.Any, Port); + public bool IsAlive => _ignorance.IsAlive; + + public ENetServer(int port) + { + Port = port; + + _ignorance = new IgnoranceServer(); + _logger = Log.ForContext(); + _runtimeCts = new(); + _workerThread = new(ThreadWorker); + _connections = new(); + + EnsureReceiveBufferSize(_ignorance.IncomingOutgoingBufferSize); + + IgnoranceDebug.Logger = _logger; + } + + #region Start / Stop + + public async Task Start() + { + await Stop(); + + _runtimeCts = new(); + + _logger.Information("Starting companion ENet socket (Endpoint={Endpoint})", EndPoint); + + _ignorance.BindPort = Port; + _ignorance.BindAddress = IPAddress.Any.ToString(); + _ignorance.BindAllInterfaces = true; + _ignorance.Start(); + + _workerThread.Start(); + } + + public async Task Stop() + { + _runtimeCts.Cancel(); + _ignorance.Stop(); + + while (_ignorance.IsAlive) + await Task.Delay(1); + } + + public void Dispose() + { + _runtimeCts.Cancel(); + _ignorance.Stop(); + + ReturnReceiveBuffer(); + } + + #endregion + + private void ThreadWorker() + { + _connections.Clear(); + + while (!_runtimeCts!.IsCancellationRequested) + { + HandleConnectionEvents(); + HandleDisconnectionEvents(); + HandleIncomingEvents(); + + Thread.Sleep(1); + } + } + + #region Ignorance event queues + + private void HandleConnectionEvents() + { + var ctsToken = _runtimeCts!.Token; + + while (!ctsToken.IsCancellationRequested + && (_ignorance.ConnectionEvents?.TryDequeue(out var e) ?? false)) + { + CreateConnection(e); + } + } + + private void HandleDisconnectionEvents() + { + var ctsToken = _runtimeCts!.Token; + + while (!ctsToken.IsCancellationRequested + && (_ignorance.DisconnectionEvents?.TryDequeue(out var e) ?? false)) + { + if (_connections.TryGetValue(e.NativePeerId, out var connection)) + connection.Disconnect(true); + } + } + + private void HandleIncomingEvents() + { + var ctsToken = _runtimeCts!.Token; + + while (!ctsToken.IsCancellationRequested + && (_ignorance.Incoming?.TryDequeue(out var e) ?? false)) + { + if (!_connections.TryGetValue(e.NativePeerId, out var connection) || connection.State == ENetConnectionState.Disconnected) + { + // Connection not established, ignore incoming + e.Payload.Dispose(); + continue; + } + + // Read incoming into buffer + var receiveLength = e.Payload.Length; + EnsureReceiveBufferSize(receiveLength); + e.Payload.CopyTo(_receiveBuffer!, 0); + e.Payload.Dispose(); + + var bufferReader = new SpanBuffer(_receiveBuffer.AsSpan(..receiveLength)); + + // If pending: first packet should be connection request only (IgnCon) + if (connection.State == ENetConnectionState.Pending) + { + if (!TryAcceptConnection(connection, ref bufferReader)) + connection.Disconnect(); + + return; + } + // Process actual payload + var deliveryMethod = e.Channel == 0 ? IgnoranceChannelTypes.Reliable : IgnoranceChannelTypes.Unreliable; + OnReceive(connection.EndPoint, ref bufferReader, deliveryMethod); + } + } + + #endregion + + #region Connections + + private ENetConnection CreateConnection(IgnoranceConnectionEvent e) + { + KickPeer(e.NativePeerId); // clean up any previous use of peer ID + + var connection = new ENetConnection(this, e.NativePeerId, e.IP, e.Port); + _connections[e.NativePeerId] = connection; + + _logger.Verbose("Incoming ENet connection (PeerId={PeerId}, EndPoint={EndPoint})", + connection.NativePeerId, connection.EndPoint); + + return connection; + } + + private bool TryAcceptConnection(ENetConnection connection, ref SpanBuffer reader) + { + try + { + // First packet from pending connection: should be IgnCon request + var requestPrefix = reader.ReadString(); + + if (requestPrefix != "IgnCon") + { + _logger.Warning("Invalid first ENet msg: bad prefix string"); + return false; + } + + // Continue with regular accept flow (remainder is regular GameLift connection request) + var player = TryAcceptConnection(connection.EndPoint, ref reader); + + + if (player != null) + { + // Accept success + player.ENetPeerId = connection.NativePeerId; + connection.State = ENetConnectionState.Accepted; + OnConnect(connection.EndPoint); + return true; + } + } + catch (EndOfBufferException) + { + // Invalid connection request - read/length error + _logger.Warning("Invalid first ENet msg: length read error"); + } + + return false; + } + + public void KickPeer(uint peerId, bool sendKick = true) + { + if (!_connections.TryGetValue(peerId, out var connection) || !_connections.Remove(peerId)) + // Connection not established + return; + + _logger.Verbose("Closing ENet connection (PeerId={PeerId}, EndPoint={Port})", + connection.NativePeerId, connection.EndPoint); + + connection.State = ENetConnectionState.Disconnected; + OnDisconnect(connection.EndPoint); + + if (!IsAlive || !sendKick) + // Can't or won't send kick + return; + + _ignorance.Commands.Enqueue(new IgnoranceCommandPacket() + { + Type = IgnoranceCommandType.ServerKickPeer, + PeerId = peerId + }); + } + + public void KickAllPeers() + { + foreach (var connection in _connections.Values) + KickPeer(connection.NativePeerId, true); + } + + #endregion + + #region Send + + public void Send(IPlayer player, ReadOnlySpan message, IgnoranceChannelTypes deliveryMethod) + { + Send(player.ENetPeerId, message, deliveryMethod); + } + + public void Send(uint peerId, ReadOnlySpan message, IgnoranceChannelTypes deliveryMethod) + { + if (!_connections.TryGetValue(peerId, out var connection)) + // Invalid peer + return; + + Send(connection, message, deliveryMethod); + } + + public void Send(ENetConnection connection, ReadOnlySpan message, IgnoranceChannelTypes deliveryMethod) + { + if (!IsAlive) + return; + + if (connection.State != ENetConnectionState.Accepted) + // Do not send if pending/disconnected + return; + + var eNetPacket = default(Packet); + eNetPacket.Create(message.ToArray(), message.Length/*, (PacketFlags)deliveryMethod*/); + + _ignorance.Outgoing.Enqueue(new IgnoranceOutgoingPacket() + { + Channel = (byte)(deliveryMethod == IgnoranceChannelTypes.Reliable ? 0 : 1), // 1 = Unreliable, 0 = reliable + NativePeerId = connection.NativePeerId, + Payload = eNetPacket + }); + } + + #endregion + + #region Buffer + + private void EnsureReceiveBufferSize(int length) + { + if (length >= _ignorance.MaximumPacketSize) + throw new ArgumentException("Receive buffer size should never exceed MaximumPacketSize"); + + if (_receiveBuffer != null && _receiveBuffer.Length >= length) + // Buffer does not need adjusting + return; + + ReturnReceiveBuffer(); + _receiveBuffer = ArrayPool.Shared.Rent(length); + } + + private void ReturnReceiveBuffer() + { + if (_receiveBuffer != null) + { + ArrayPool.Shared.Return(_receiveBuffer); + _receiveBuffer = null; + } + } + + #endregion + + + + #region Overrideables + public virtual IPlayer? TryAcceptConnection(IPEndPoint endPoint, ref SpanBuffer reader) + { + return null; + } + + public virtual void OnConnect(EndPoint endPoint) + { + } + + public virtual void OnDisconnect(EndPoint endPoint) + { + } + + public virtual void OnReceive(EndPoint remoteEndPoint, ref SpanBuffer reader, IgnoranceChannelTypes method) + { + } + + #endregion + } +} \ No newline at end of file diff --git a/BeatTogether.DedicatedServer.Kernel/Encryption/Abstractions/IEncryptedPacketReader.cs b/BeatTogether.DedicatedServer.Kernel/Encryption/Abstractions/IEncryptedPacketReader.cs deleted file mode 100644 index b245f797..00000000 --- a/BeatTogether.DedicatedServer.Kernel/Encryption/Abstractions/IEncryptedPacketReader.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Krypton.Buffers; -using System; -using System.Security.Cryptography; - -namespace BeatTogether.DedicatedServer.Kernel.Encryption.Abstractions -{ - public interface IEncryptedPacketReader - { - ReadOnlyMemory ReadFrom(ref SpanBufferReader bufferReader, byte[] key, HMAC hmac); - } -} diff --git a/BeatTogether.DedicatedServer.Kernel/Encryption/Abstractions/IEncryptedPacketWriter.cs b/BeatTogether.DedicatedServer.Kernel/Encryption/Abstractions/IEncryptedPacketWriter.cs deleted file mode 100644 index d441431d..00000000 --- a/BeatTogether.DedicatedServer.Kernel/Encryption/Abstractions/IEncryptedPacketWriter.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Security.Cryptography; -using BinaryRecords; -using Krypton.Buffers; - -namespace BeatTogether.DedicatedServer.Kernel.Encryption.Abstractions -{ - public interface IEncryptedPacketWriter - { - void WriteTo(ref SpanBufferWriter bufferWriter, ReadOnlySpan data, uint sequenceId, byte[] key, HMAC hmac); - } -} diff --git a/BeatTogether.DedicatedServer.Kernel/Encryption/EncryptedPacketReader.cs b/BeatTogether.DedicatedServer.Kernel/Encryption/EncryptedPacketReader.cs deleted file mode 100644 index 5c1a9978..00000000 --- a/BeatTogether.DedicatedServer.Kernel/Encryption/EncryptedPacketReader.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System; -using System.Linq; -using System.Security.Cryptography; -using BeatTogether.DedicatedServer.Kernel.Encryption.Abstractions; -using Krypton.Buffers; -using Serilog; - -namespace BeatTogether.DedicatedServer.Kernel.Encryption -{ - public sealed class EncryptedPacketReader : IEncryptedPacketReader - { - private readonly ILogger _logger = Log.ForContext(); - - /// - public ReadOnlyMemory ReadFrom(ref SpanBufferReader bufferReader, byte[] key, HMAC hmac) - { - var sequenceId = bufferReader.ReadUInt32(); - var iv = bufferReader.ReadBytes(16).ToArray(); - var decryptedBuffer = bufferReader.RemainingData.ToArray(); - using (var aes = Aes.Create()) - { - aes.Padding = PaddingMode.None; - using (var cryptoTransform = aes.CreateDecryptor(key, iv)) - { - var bytesWritten = 0; - var offset = 0; - for (var i = decryptedBuffer.Length; i >= cryptoTransform.InputBlockSize; i -= bytesWritten) - { - var inputCount = cryptoTransform.CanTransformMultipleBlocks - ? (i / cryptoTransform.InputBlockSize * cryptoTransform.InputBlockSize) - : cryptoTransform.InputBlockSize; - bytesWritten = cryptoTransform.TransformBlock( - decryptedBuffer, offset, inputCount, - decryptedBuffer, offset - ); - offset += bytesWritten; - } - } - } - - var paddingByteCount = decryptedBuffer[decryptedBuffer.Length - 1] + 1; - var hmacStart = decryptedBuffer.Length - paddingByteCount - 10; - var decryptedBufferSpan = decryptedBuffer.AsSpan(); - var hash = decryptedBufferSpan.Slice(hmacStart, 10); - var hashBufferWriter = new SpanBufferWriter(stackalloc byte[decryptedBuffer.Length + 4]); - hashBufferWriter.WriteBytes(decryptedBufferSpan.Slice(0, hmacStart)); - hashBufferWriter.WriteUInt32(sequenceId); - Span computedHash = stackalloc byte[32]; - if (!hmac.TryComputeHash(hashBufferWriter.Data, computedHash, out _)) - throw new Exception("Failed to compute message hash."); - if (!hash.SequenceEqual(computedHash.Slice(0, 10))) - throw new Exception("Message hash does not match the computed hash."); - - return decryptedBufferSpan.Slice(0, hmacStart).ToArray(); - } - } -} diff --git a/BeatTogether.DedicatedServer.Kernel/Encryption/EncryptedPacketWriter.cs b/BeatTogether.DedicatedServer.Kernel/Encryption/EncryptedPacketWriter.cs deleted file mode 100644 index 8963e66c..00000000 --- a/BeatTogether.DedicatedServer.Kernel/Encryption/EncryptedPacketWriter.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System; -using System.Security.Cryptography; -using BeatTogether.DedicatedServer.Kernel.Encryption.Abstractions; -using Krypton.Buffers; - -namespace BeatTogether.DedicatedServer.Kernel.Encryption -{ - public sealed class EncryptedPacketWriter : IEncryptedPacketWriter - { - private readonly RNGCryptoServiceProvider _rngCryptoServiceProvider; - - public EncryptedPacketWriter( - RNGCryptoServiceProvider rngCryptoServiceProvider) - { - _rngCryptoServiceProvider = rngCryptoServiceProvider; - } - - public void WriteTo(ref SpanBufferWriter bufferWriter, ReadOnlySpan data, uint sequenceId, byte[] key, HMAC hmac) - { - var unencryptedBufferWriter = new SpanBufferWriter(stackalloc byte[data.Length]); - unencryptedBufferWriter.WriteBytes(data); - - var hashBufferWriter = new SpanBufferWriter(stackalloc byte[data.Length + 4]); - hashBufferWriter.WriteBytes(data); - hashBufferWriter.WriteUInt32(sequenceId); - Span hash = stackalloc byte[32]; - if (!hmac.TryComputeHash(hashBufferWriter.Data, hash, out _)) - throw new Exception("Failed to compute message hash."); - unencryptedBufferWriter.WriteBytes(hash.Slice(0, 10)); - - var iv = new byte[16]; - _rngCryptoServiceProvider.GetBytes(iv); - - var paddingByteCount = (byte)((16 - ((unencryptedBufferWriter.Size + 1) & 15)) & 15); - for (var i = 0; i < paddingByteCount + 1; i++) - unencryptedBufferWriter.WriteUInt8(paddingByteCount); - - var encryptedBuffer = unencryptedBufferWriter.Data.ToArray(); - using (var aes = Aes.Create()) - { - aes.Padding = PaddingMode.None; - using (var cryptoTransform = aes.CreateEncryptor(key, iv)) - { - var bytesWritten = 0; - for (var i = encryptedBuffer.Length; i >= cryptoTransform.InputBlockSize; i -= bytesWritten) - { - var inputCount = cryptoTransform.CanTransformMultipleBlocks - ? (i / cryptoTransform.InputBlockSize * cryptoTransform.InputBlockSize) - : cryptoTransform.InputBlockSize; - bytesWritten = cryptoTransform.TransformBlock( - encryptedBuffer, bytesWritten, inputCount, - encryptedBuffer, bytesWritten - ); - } - } - } - - bufferWriter.WriteUInt32(sequenceId); - bufferWriter.WriteBytes(iv); - bufferWriter.WriteBytes(encryptedBuffer); - } - } -} diff --git a/BeatTogether.DedicatedServer.Kernel/Encryption/EncryptionParameters.cs b/BeatTogether.DedicatedServer.Kernel/Encryption/EncryptionParameters.cs deleted file mode 100644 index 8e725f73..00000000 --- a/BeatTogether.DedicatedServer.Kernel/Encryption/EncryptionParameters.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Threading; - -namespace BeatTogether.DedicatedServer.Kernel.Encryption -{ - public sealed class EncryptionParameters - { - public byte[] ReceiveKey { get; } - public byte[] SendKey { get; } - public byte[] ReceiveMac { get; } - public byte[] SendMac { get; } - - private uint _lastSequenceId = 0U; - - public EncryptionParameters(byte[] receiveKey, byte[] sendKey, byte[] receiveMac, byte[] sendMac) - { - ReceiveKey = receiveKey; - SendKey = sendKey; - ReceiveMac = receiveMac; - SendMac = sendMac; - } - - public uint GetNextSequenceId() => - unchecked(Interlocked.Increment(ref _lastSequenceId)); - } -} diff --git a/BeatTogether.DedicatedServer.Kernel/Encryption/PacketEncryptionLayer.cs b/BeatTogether.DedicatedServer.Kernel/Encryption/PacketEncryptionLayer.cs deleted file mode 100644 index d97753df..00000000 --- a/BeatTogether.DedicatedServer.Kernel/Encryption/PacketEncryptionLayer.cs +++ /dev/null @@ -1,264 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Diagnostics.CodeAnalysis; -using System.Net; -using System.Security.Cryptography; -using System.Text; -using BeatTogether.Core.Security.Abstractions; -using BeatTogether.Core.Security.Models; -using BeatTogether.DedicatedServer.Kernel.Encryption.Abstractions; -using BeatTogether.LiteNetLib.Abstractions; -using Krypton.Buffers; -using Serilog; - -namespace BeatTogether.DedicatedServer.Kernel.Encryption -{ - public sealed class PacketEncryptionLayer : IPacketLayer - { - public byte[] Random { get; } = new byte[32]; - public ECKeyPair KeyPair { get; } - - private readonly IEncryptedPacketReader _encryptedPacketReader; - private readonly IEncryptedPacketWriter _encryptedPacketWriter; - private readonly IDiffieHellmanService _diffieHellmanService; - private readonly ILogger _logger = Log.ForContext(); - - private readonly ConcurrentDictionary _potentialEncryptionParameters = new(); - private readonly ConcurrentDictionary _encryptionParameters = new(); - - private static readonly byte[] _masterSecretSeed = Encoding.UTF8.GetBytes("master secret"); - private static readonly byte[] _keyExpansionSeed = Encoding.UTF8.GetBytes("key expansion"); - - public PacketEncryptionLayer( - IEncryptedPacketReader encryptedPacketReader, - IEncryptedPacketWriter encryptedPacketWriter, - IDiffieHellmanService diffieHellmanService, - RNGCryptoServiceProvider rngCryptoServiceProvider) - { - _encryptedPacketReader = encryptedPacketReader; - _encryptedPacketWriter = encryptedPacketWriter; - _diffieHellmanService = diffieHellmanService; - - rngCryptoServiceProvider.GetBytes(Random); - KeyPair = _diffieHellmanService.GetECKeyPair(); - } - - #region Public Methods - - public void AddEncryptedEndPoint(IPEndPoint endPoint, EncryptionParameters encryptionParameters, - bool definitive = false) - { - if (definitive) - { - _encryptionParameters[endPoint] = encryptionParameters; - } - else - { - _potentialEncryptionParameters[endPoint.Address] = encryptionParameters; - _encryptionParameters.TryRemove(endPoint, out _); - } - } - - public void AddEncryptedEndPoint(IPEndPoint endPoint, - BeatTogether.Core.Messaging.Models.EncryptionParameters encryptionParameters, - bool definitive = false) - { - AddEncryptedEndPoint(endPoint, new EncryptionParameters(encryptionParameters.ReceiveKey, - encryptionParameters.SendKey, encryptionParameters.ReceiveMac.Key, encryptionParameters.SendMac.Key), - definitive); - } - - public void AddEncryptedEndPoint( - IPEndPoint endPoint, - byte[] clientRandom, - byte[] clientPublicKey, - bool definitive = false) - { - var clientPublicKeyParameters = _diffieHellmanService.DeserializeECPublicKey(clientPublicKey); - var preMasterSecret = - _diffieHellmanService.GetPreMasterSecret(clientPublicKeyParameters, KeyPair.PrivateKeyParameters); - var sendKey = new byte[32]; - var receiveKey = new byte[32]; - var sendMacSourceArray = new byte[64]; - var receiveMacSourceArray = new byte[64]; - var masterSecretSeed = MakeSeed(_masterSecretSeed, Random, clientRandom); - var keyExpansionSeed = MakeSeed(_keyExpansionSeed, Random, clientRandom); - var sourceArray = PRF( - PRF(preMasterSecret, masterSecretSeed, 48), - keyExpansionSeed, - 192 - ); - Array.Copy(sourceArray, 0, sendKey, 0, 32); - Array.Copy(sourceArray, 32, receiveKey, 0, 32); - Array.Copy(sourceArray, 64, sendMacSourceArray, 0, 64); - Array.Copy(sourceArray, 128, receiveMacSourceArray, 0, 64); - var encryptionParameters = new EncryptionParameters( - receiveKey, - sendKey, - receiveMacSourceArray, - sendMacSourceArray - ); - - AddEncryptedEndPoint(endPoint, encryptionParameters, definitive); - } - - public void RemoveEncryptedEndPoint(IPEndPoint endPoint) - { - _potentialEncryptionParameters.TryRemove(endPoint.Address, out _); - _encryptionParameters.TryRemove(endPoint, out _); - } - - public void ProcessInboundPacket(EndPoint endPoint, ref Span data) - { - var address = ((IPEndPoint) endPoint).Address; - - if (data.Length == 0) - return; - - var bufferReader = new SpanBufferReader(data); - - if (!bufferReader.ReadBool()) // NotEncrypted - { - // Received an unencrypted packet - this is valid if the client is still negotiating - // Slice out the encryption flag and continue - data = data[1..]; - // TODO Reject unencrypted inbound packets for regular clients past the negotiation stage? - return; - } - - byte[]? decryptedData; - - if (_encryptionParameters.TryGetValue(endPoint, out var encryptionParameters)) - { - if (TryDecrypt(ref bufferReader, encryptionParameters, out decryptedData)) - data = decryptedData; - else - data = Array.Empty(); - return; - } - - if (_potentialEncryptionParameters.TryGetValue(address, out encryptionParameters)) - { - if (TryDecrypt(ref bufferReader, encryptionParameters, out decryptedData)) - { - _encryptionParameters[endPoint] = encryptionParameters; - _potentialEncryptionParameters.TryRemove(address, out _); - data = decryptedData; - } - else - data = Array.Empty(); - - return; - } - - // Cannot decrypt incoming packet - // This can happen briefly when the handshake process switches to encrypted mode - _logger.Verbose( - "Failed to retrieve decryption parameters " + - $"(RemoteEndPoint='{endPoint}')." - ); - data = Array.Empty(); - } - - public void ProcessOutBoundPacket(EndPoint endPoint, ref Span data) - { - if (!_encryptionParameters.TryGetValue(endPoint, out var encryptionParameters)) - { - if (_potentialEncryptionParameters.TryGetValue(((IPEndPoint) endPoint).Address, - out var encryptionParametersOld)) - { - _logger.Warning( - $"Re-assigning encryption parameters as old parameters (RemoteEndPoint='{endPoint}')."); - encryptionParameters = _encryptionParameters.GetOrAdd(endPoint, encryptionParametersOld); - } - } - - var bufferWriter = new SpanBufferWriter(stackalloc byte[412]); - - if (encryptionParameters != null) - { - bufferWriter.WriteBool(true); // isEncrypted - - using (var hmac = new HMACSHA256(encryptionParameters.SendMac)) - { - _encryptedPacketWriter.WriteTo( - ref bufferWriter, data, - encryptionParameters.GetNextSequenceId(), - encryptionParameters.SendKey, hmac); - } - } - else - { - // Failed to retrieve encryption parameters for send - // During early handshake, this is legitimate - - bufferWriter.WriteBool(false); // NotEncrypted - bufferWriter.WriteBytes(data); - } - data = bufferWriter.Data.ToArray(); - } - - #endregion - - #region Private Methods - - private static byte[] MakeSeed(byte[] baseSeed, byte[] serverSeed, byte[] clientSeed) - { - var seed = new byte[baseSeed.Length + serverSeed.Length + clientSeed.Length]; - Array.Copy(baseSeed, 0, seed, 0, baseSeed.Length); - Array.Copy(serverSeed, 0, seed, baseSeed.Length, serverSeed.Length); - Array.Copy(clientSeed, 0, seed, baseSeed.Length + serverSeed.Length, clientSeed.Length); - return seed; - } - - private static byte[] PRF(byte[] key, byte[] seed, int length) - { - var i = 0; - var array = new byte[length + seed.Length]; - while (i < length) - { - Array.Copy(seed, 0, array, i, seed.Length); - PRFHash(key, array, ref i); - } - - var array2 = new byte[length]; - Array.Copy(array, 0, array2, 0, length); - return array2; - } - - private static void PRFHash(byte[] key, byte[] seed, ref int length) - { - using var hmacsha256 = new HMACSHA256(key); - var array = hmacsha256.ComputeHash(seed, 0, length); - var num = Math.Min(length + array.Length, seed.Length); - Array.Copy(array, 0, seed, length, num - length); - length = num; - } - - private bool TryDecrypt( - ref SpanBufferReader bufferReader, - EncryptionParameters encryptionParameters, - [MaybeNullWhen(false)] out byte[] data) - { - try - { - using (var hmac = new HMACSHA256(encryptionParameters.ReceiveMac)) - { - data = _encryptedPacketReader - .ReadFrom(ref bufferReader, encryptionParameters.ReceiveKey, hmac) - .ToArray(); - } - - return true; - } - catch (Exception e) - { - _logger.Warning($"Failed to decrypt packet: {e.Message}"); - data = null; - return false; - } - } - - #endregion - } -} \ No newline at end of file diff --git a/BeatTogether.DedicatedServer.Kernel/Enums/AccessLevel.cs b/BeatTogether.DedicatedServer.Kernel/Enums/AccessLevel.cs new file mode 100644 index 00000000..36ae1b5f --- /dev/null +++ b/BeatTogether.DedicatedServer.Kernel/Enums/AccessLevel.cs @@ -0,0 +1,13 @@ +namespace BeatTogether.DedicatedServer.Kernel.Enums +{ + public enum AccessLevel + { + Player = 0, + PatreonPlayer = 1, + SubManager = 2, + PatreonSubManager = 3, + Manager = 4, + PatreonManager = 5, + Dev = 6 + } +} diff --git a/BeatTogether.DedicatedServer.Kernel/Enums/CountdownState.cs b/BeatTogether.DedicatedServer.Kernel/Enums/CountdownState.cs deleted file mode 100644 index 5ffa7571..00000000 --- a/BeatTogether.DedicatedServer.Kernel/Enums/CountdownState.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace BeatTogether.DedicatedServer.Kernel.Enums -{ - public enum CountdownState : byte - { - NotCountingDown = 0, - CountingDown = 1, - StartBeatmapCountdown = 2, - WaitingForEntitlement = 3 - } -} diff --git a/BeatTogether.DedicatedServer.Kernel/Enums/DiscoveryPolicy.cs b/BeatTogether.DedicatedServer.Kernel/Enums/DiscoveryPolicy.cs deleted file mode 100644 index 59339aff..00000000 --- a/BeatTogether.DedicatedServer.Kernel/Enums/DiscoveryPolicy.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace BeatTogether.DedicatedServer.Kernel.Enums -{ - public enum DiscoveryPolicy : byte - { - Hidden = 0, - WithCode = 1, - Public = 2 - } -} diff --git a/BeatTogether.DedicatedServer.Kernel/Enums/GameplayServerControlSettings.cs b/BeatTogether.DedicatedServer.Kernel/Enums/GameplayServerControlSettings.cs deleted file mode 100644 index 797c68bf..00000000 --- a/BeatTogether.DedicatedServer.Kernel/Enums/GameplayServerControlSettings.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; - -namespace BeatTogether.DedicatedServer.Kernel.Enums -{ - [Flags] - public enum GameplayServerControlSettings - { - None = 0, - AllowModifierSelection = 1, - AllowSpectate = 2, - All = 3 - } -} diff --git a/BeatTogether.DedicatedServer.Kernel/Enums/GameplayServerMode.cs b/BeatTogether.DedicatedServer.Kernel/Enums/GameplayServerMode.cs deleted file mode 100644 index 74eaed61..00000000 --- a/BeatTogether.DedicatedServer.Kernel/Enums/GameplayServerMode.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace BeatTogether.DedicatedServer.Kernel.Enums -{ - public enum GameplayServerMode - { - Countdown = 0, - Managed = 1, - QuickStartOneSong = 2, - Tournament = 3 - } -} diff --git a/BeatTogether.DedicatedServer.Kernel/Enums/InvitePolicy.cs b/BeatTogether.DedicatedServer.Kernel/Enums/InvitePolicy.cs deleted file mode 100644 index 2439c019..00000000 --- a/BeatTogether.DedicatedServer.Kernel/Enums/InvitePolicy.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace BeatTogether.DedicatedServer.Kernel.Enums -{ - public enum InvitePolicy : byte - { - OnlyConnectionOwnerCanInvite = 0, - AnyoneCanInvite = 1 - } -} diff --git a/BeatTogether.DedicatedServer.Kernel/Enums/SongSelectionMode.cs b/BeatTogether.DedicatedServer.Kernel/Enums/SongSelectionMode.cs deleted file mode 100644 index 7781431e..00000000 --- a/BeatTogether.DedicatedServer.Kernel/Enums/SongSelectionMode.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace BeatTogether.DedicatedServer.Kernel.Enums -{ - public enum SongSelectionMode - { - Vote = 0, - Random = 1, - ServerOwnerPicks = 2, - RandomPlayerPicks = 3, - ServerPicks = 4 - } -} diff --git a/BeatTogether.DedicatedServer.Kernel/Extensions/GamePlatformToMpCorePlatform.cs b/BeatTogether.DedicatedServer.Kernel/Extensions/GamePlatformToMpCorePlatform.cs new file mode 100644 index 00000000..4c9b870d --- /dev/null +++ b/BeatTogether.DedicatedServer.Kernel/Extensions/GamePlatformToMpCorePlatform.cs @@ -0,0 +1,20 @@ +using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MpCorePackets; + +namespace BeatTogether.DedicatedServer.Kernel.Extensions +{ + public static class GamePlatformToMpCorePlatform + { + public static Platform Convert(this Core.Enums.Platform gamePlatform) + { + return gamePlatform switch + { + Core.Enums.Platform.Test => Platform.Unknown, + Core.Enums.Platform.OculusRift => Platform.OculusPC, + Core.Enums.Platform.OculusQuest => Platform.OculusQuest, + Core.Enums.Platform.Steam => Platform.Steam, + Core.Enums.Platform.PS4 or Core.Enums.Platform.PS4Dev or Core.Enums.Platform.PS4Cert => Platform.PS4, + _ => 0, + }; + } + } +} diff --git a/BeatTogether.DedicatedServer.Kernel/Extensions/HostBuilderExtensions.cs b/BeatTogether.DedicatedServer.Kernel/Extensions/HostBuilderExtensions.cs index e37e01b2..b81bee35 100644 --- a/BeatTogether.DedicatedServer.Kernel/Extensions/HostBuilderExtensions.cs +++ b/BeatTogether.DedicatedServer.Kernel/Extensions/HostBuilderExtensions.cs @@ -1,16 +1,21 @@ -using BeatTogether.Core.Messaging.Abstractions; +//using BeatTogether.Core.Messaging.Abstractions; using BeatTogether.DedicatedServer.Kernel.Abstractions; +//using BeatTogether.DedicatedServer.Kernel.Commands.CommandHandlers; using BeatTogether.DedicatedServer.Kernel.Configuration; -using BeatTogether.DedicatedServer.Kernel.Handshake; +using BeatTogether.DedicatedServer.Kernel.ENet; +//using BeatTogether.DedicatedServer.Kernel.Handshake; using BeatTogether.DedicatedServer.Kernel.Managers; using BeatTogether.DedicatedServer.Kernel.Managers.Abstractions; -using BeatTogether.DedicatedServer.Messaging.Registries.Unconnected; +using BeatTogether.DedicatedServer.Kernel.PacketHandlers.MultiplayerSession.MPChat; +//using BeatTogether.DedicatedServer.Messaging.Registries; +//using BeatTogether.DedicatedServer.Messaging.Registries.Unconnected; using BeatTogether.Extensions; -using BeatTogether.LiteNetLib; +/*using BeatTogether.LiteNetLib; +using BeatTogether.LiteNetLib.Abstractions; using BeatTogether.LiteNetLib.Configuration; using BeatTogether.LiteNetLib.Dispatchers; using BeatTogether.LiteNetLib.Extensions; -using BeatTogether.LiteNetLib.Sources; +using BeatTogether.LiteNetLib.Sources;*/ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -24,31 +29,33 @@ public static IHostBuilder UseDedicatedInstances(this IHostBuilder hostBuilder) .UseSerilog() .ConfigureServices((hostBuilderContext, services) => services - .AddLiteNetMessaging() - .AddConfiguration("LiteNetLib") + //.AddLiteNetMessaging() + //.AddConfiguration("LiteNetLib") .AddScoped() .AddDedicatedServerMessaging() .AddScoped() .AddExisting() - .AddExisting() - .AddScoped() + .AddExisting() //Used to be like this for lnl + //.AddScoped() .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped() - .AddExisting() - .AddExisting() + //.AddScoped() + .AddScoped() + //.AddScoped() + //.AddExisting() + //.AddExisting() .AddScoped() .AddExisting() - .AddExisting() - .AddScoped() + //.AddExisting() + //.AddScoped() .AddScoped() .AddScoped() - .AddCoreMessaging() - .AddSingleton() - .AddSingleton() - .AddAllHandshakeMessageHandlersFromAssembly(typeof(UnconnectedSource).Assembly) + //.AddCoreMessaging() + .AddSingleton() + //.AddSingleton() + //.AddSingleton() + //.AddAllHandshakeMessageHandlersFromAssembly(typeof(UnconnectedSource).Assembly) .AddAllPacketHandlersFromAssembly(typeof(PacketSource).Assembly) + .AddAllCommandHandlersFromAssembly(typeof(MpcTextChatPacketHandler).Assembly) ); } } diff --git a/BeatTogether.DedicatedServer.Kernel/Extensions/ServiceCollectionExtensions.cs b/BeatTogether.DedicatedServer.Kernel/Extensions/ServiceCollectionExtensions.cs index 4acf68de..2e8f38be 100644 --- a/BeatTogether.DedicatedServer.Kernel/Extensions/ServiceCollectionExtensions.cs +++ b/BeatTogether.DedicatedServer.Kernel/Extensions/ServiceCollectionExtensions.cs @@ -1,6 +1,7 @@ using System.Linq; using System.Reflection; using BeatTogether.DedicatedServer.Kernel.Abstractions; +using BeatTogether.DedicatedServer.Kernel.CommandHandlers; using Microsoft.Extensions.DependencyInjection; namespace BeatTogether.Extensions @@ -25,10 +26,10 @@ public static IServiceCollection AddAllPacketHandlersFromAssembly(this IServiceC eventHandlerType); return services; } - - public static IServiceCollection AddAllHandshakeMessageHandlersFromAssembly(this IServiceCollection services, Assembly assembly) + + public static IServiceCollection AddAllCommandHandlersFromAssembly(this IServiceCollection services, Assembly assembly) { - var genericInterface = typeof(IHandshakeMessageHandler<>); + var genericInterface = typeof(ICommandHandler<>); var eventHandlerTypes = assembly .GetTypes() .Where(type => type.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == genericInterface)); @@ -39,5 +40,19 @@ public static IServiceCollection AddAllHandshakeMessageHandlersFromAssembly(this eventHandlerType); return services; } + +/* public static IServiceCollection AddAllHandshakeMessageHandlersFromAssembly(this IServiceCollection services, Assembly assembly) + { + var genericInterface = typeof(IHandshakeMessageHandler<>); + var eventHandlerTypes = assembly + .GetTypes() + .Where(type => type.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == genericInterface)); + foreach (var eventHandlerType in eventHandlerTypes) + if (!eventHandlerType.IsAbstract) + services.AddTransient( + genericInterface.MakeGenericType(eventHandlerType.BaseType!.GetGenericArguments()), + eventHandlerType); + return services; + }*/ } } diff --git a/BeatTogether.DedicatedServer.Kernel/Handshake/HandshakePendingMultipart.cs b/BeatTogether.DedicatedServer.Kernel/Handshake/HandshakePendingMultipart.cs deleted file mode 100644 index e2ad249b..00000000 --- a/BeatTogether.DedicatedServer.Kernel/Handshake/HandshakePendingMultipart.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using BeatTogether.Core.Messaging.Messages; -using Krypton.Buffers; - -namespace BeatTogether.DedicatedServer.Kernel.Handshake -{ - public class HandshakePendingMultipart - { - public UnconnectedSource UnconnectedSource { get; private set; } - public HandshakeSession Session { get; private set; } - public uint MultipartMessageId { get; private set; } - public uint TotalLength { get; private set; } - public DateTime LastReceive { get; private set; } - - private readonly ConcurrentDictionary _messages; - private uint _receivedLength; - - public bool IsComplete { get; private set; } - - public HandshakePendingMultipart(UnconnectedSource unconnectedSource, HandshakeSession session, - uint multipartMessageId, uint totalLength) - { - UnconnectedSource = unconnectedSource; - Session = session; - MultipartMessageId = multipartMessageId; - TotalLength = totalLength; - LastReceive = DateTime.Now; - - _messages = new(); - _receivedLength = 0; - } - - public void AddMessage(MultipartMessage message) - { - if (message.MultipartMessageId != MultipartMessageId) - // Invalid id - return; - - if (_receivedLength >= TotalLength) - // Already received all messages - return; - - if (!_messages.TryAdd(message.Offset, message)) - // Already received this message - return; - - // Receive success - LastReceive = DateTime.Now; - - if (Interlocked.Add(ref _receivedLength, message.Length) < TotalLength) - // Not yet complete, wait for all messages to come in - return; - - FinalizeMessage(); - } - - private void FinalizeMessage() - { - if (IsComplete) - return; - - IsComplete = true; - - var bufferWriter = new SpanBufferWriter(stackalloc byte[(int)TotalLength]); - - foreach (var kvp in _messages.OrderBy(kvp => kvp.Key)) - bufferWriter.WriteBytes(kvp.Value.Data); - - var bufferReader = new SpanBufferReader(bufferWriter.Data); - var fullMessage = UnconnectedSource.GetMessageReader().ReadFrom(ref bufferReader); - - Task.Run(() => UnconnectedSource.HandleMessage(Session, fullMessage)); - } - - public double MsSinceLastReceive => DateTime.Now.Subtract(LastReceive).TotalMilliseconds; - - public bool HasExpired => MsSinceLastReceive > 10000; - } -} \ No newline at end of file diff --git a/BeatTogether.DedicatedServer.Kernel/Handshake/HandshakePendingRequest.cs b/BeatTogether.DedicatedServer.Kernel/Handshake/HandshakePendingRequest.cs deleted file mode 100644 index d5b15e4f..00000000 --- a/BeatTogether.DedicatedServer.Kernel/Handshake/HandshakePendingRequest.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System; -using BeatTogether.Core.Messaging.Abstractions; -using BeatTogether.DedicatedServer.Kernel.Abstractions; - -namespace BeatTogether.DedicatedServer.Kernel.Handshake -{ - public class HandshakePendingRequest - { - public IUnconnectedDispatcher UnconnectedDispatcher { get; private set; } - public HandshakeSession Session { get; private set; } - public IReliableRequest Request { get; private set; } - public DateTime LastSend { get; private set; } - public int RetryCount { get; private set; } - - public uint RequestId => Request.RequestId; - - public HandshakePendingRequest(IUnconnectedDispatcher unconnectedDispatcher, HandshakeSession session, - IReliableRequest request) - { - UnconnectedDispatcher = unconnectedDispatcher; - Session = session; - Request = request; - LastSend = DateTime.Now; - RetryCount = 0; - } - - - public void Retry() - { - UnconnectedDispatcher.Send(Session, Request, true); - LastSend = DateTime.Now; - RetryCount++; - } - - public int? GetRetryInterval() - { - switch (RetryCount) - { - case 0: - return 200; - case 1: - return 300; - case 2: - return 450; - case 3: - return 600; - case 4: - return 1000; - default: - return null; - } - } - - public double MsSinceLastSend => DateTime.Now.Subtract(LastSend).TotalMilliseconds; - - public bool HasExpired => GetRetryInterval() == null; - - public bool ShouldRetry - { - get - { - var interval = GetRetryInterval(); - return interval.HasValue && MsSinceLastSend >= interval; - } - } - } -} \ No newline at end of file diff --git a/BeatTogether.DedicatedServer.Kernel/Handshake/HandshakeService.cs b/BeatTogether.DedicatedServer.Kernel/Handshake/HandshakeService.cs deleted file mode 100644 index 0f1891c0..00000000 --- a/BeatTogether.DedicatedServer.Kernel/Handshake/HandshakeService.cs +++ /dev/null @@ -1,249 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Cryptography; -using System.Security.Cryptography.X509Certificates; -using System.Text; -using System.Threading.Tasks; -using BeatTogether.Core.Security.Abstractions; -using BeatTogether.DedicatedServer.Kernel.Abstractions; -using BeatTogether.DedicatedServer.Messaging.Messages.GameLift; -using BeatTogether.DedicatedServer.Messaging.Messages.Handshake; -using Krypton.Buffers; -using Serilog; - -namespace BeatTogether.DedicatedServer.Kernel.Handshake -{ - public class HandshakeService : IHandshakeService - { - private readonly IUnconnectedDispatcher _messageDispatcher; - private readonly ICookieProvider _cookieProvider; - private readonly IRandomProvider _randomProvider; - private readonly X509Certificate2 _certificate; - private readonly ICertificateSigningService _certificateSigningService; - private readonly IDiffieHellmanService _diffieHellmanService; - private readonly IHandshakeSessionRegistry _handshakeSessionRegistry; - - private readonly ILogger _logger; - - private static readonly byte[] MasterSecretSeed = Encoding.UTF8.GetBytes("master secret"); - private static readonly byte[] KeyExpansionSeed = Encoding.UTF8.GetBytes("key expansion"); - private const uint EpochMask = 0xff000000; - - public HandshakeService( - IUnconnectedDispatcher messageDispatcher, - ICookieProvider cookieProvider, - IRandomProvider randomProvider, - X509Certificate2 certificate, - ICertificateSigningService certificateSigningService, - IDiffieHellmanService diffieHellmanService, - IHandshakeSessionRegistry handshakeSessionRegistry) - { - _messageDispatcher = messageDispatcher; - _cookieProvider = cookieProvider; - _randomProvider = randomProvider; - _certificate = certificate; - _certificateSigningService = certificateSigningService; - _diffieHellmanService = diffieHellmanService; - _handshakeSessionRegistry = handshakeSessionRegistry; - - _logger = Log.ForContext(); - } - - #region Public Methods - - public Task ClientHello(HandshakeSession session, ClientHelloRequest request) - { - _logger.Verbose( - $"Handling {nameof(ClientHelloRequest)} " + - $"(Random='{BitConverter.ToString(request.Random)}')." - ); - - session.Epoch = request.RequestId & EpochMask; - session.EncryptionParameters = null; - session.Cookie = _cookieProvider.GetCookie(); - session.ClientRandom = request.Random; - - return Task.FromResult(new HelloVerifyRequest - { - Cookie = session.Cookie - }); - } - - public async Task ClientHelloWithCookie(HandshakeSession session, - ClientHelloWithCookieRequest request) - { - _logger.Verbose( - $"Handling {nameof(ClientHelloWithCookieRequest)} " + - $"(CertificateResponseId={request.CertificateResponseId}, " + - $"Random='{BitConverter.ToString(request.Random)}', " + - $"Cookie='{BitConverter.ToString(request.Cookie)}')." - ); - - if (!request.Cookie.SequenceEqual(session.Cookie)) - { - _logger.Warning( - $"Session sent {nameof(ClientHelloWithCookieRequest)} with a mismatching cookie " + - $"(EndPoint='{session.EndPoint}', " + - $"Cookie='{BitConverter.ToString(request.Cookie)}', " + - $"Expected='{BitConverter.ToString(session.Cookie ?? new byte[0])}')." - ); - return null; - } - - if (!request.Random.SequenceEqual(session.ClientRandom)) - { - _logger.Warning( - $"Session sent {nameof(ClientHelloWithCookieRequest)} with a mismatching client random " + - $"(EndPoint='{session.EndPoint}', " + - $"Random='{BitConverter.ToString(request.Random)}', " + - $"Expected='{BitConverter.ToString(session.ClientRandom ?? new byte[0])}')." - ); - return null; - } - - // Generate a server random - session.ServerRandom = _randomProvider.GetRandom(); - - // Generate a key pair - var keyPair = _diffieHellmanService.GetECKeyPair(); - session.ServerPrivateKeyParameters = keyPair.PrivateKeyParameters; - - // Generate a signature - var signature = MakeSignature(session.ClientRandom, session.ServerRandom, keyPair.PublicKey); - - _messageDispatcher.Send(session, new ServerCertificateRequest() - { - ResponseId = request.CertificateResponseId, - Certificates = new List() {_certificate.RawData} - }); - - return new ServerHelloRequest - { - Random = session.ServerRandom, - PublicKey = keyPair.PublicKey, - Signature = signature - }; - } - - public Task ClientKeyExchange(HandshakeSession session, - ClientKeyExchangeRequest request) - { - _logger.Verbose( - $"Handling {nameof(ClientKeyExchange)} " + - $"(ClientPublicKey='{BitConverter.ToString(request.ClientPublicKey)}')." - ); - - session.ClientPublicKey = request.ClientPublicKey; - session.ClientPublicKeyParameters = _diffieHellmanService.DeserializeECPublicKey(request.ClientPublicKey); - session.PreMasterSecret = _diffieHellmanService.GetPreMasterSecret( - session.ClientPublicKeyParameters, - session.ServerPrivateKeyParameters! - ); - - - var sendKey = new byte[32]; - var receiveKey = new byte[32]; - var sendMacSourceArray = new byte[64]; - var receiveMacSourceArray = new byte[64]; - var masterSecretSeed = MakeSeed(MasterSecretSeed, session.ServerRandom!, session.ClientRandom!); - var keyExpansionSeed = MakeSeed(KeyExpansionSeed, session.ServerRandom!, session.ClientRandom!); - var sourceArray = PRF( - PRF(session.PreMasterSecret, masterSecretSeed, 48), - keyExpansionSeed, - 192 - ); - Array.Copy(sourceArray, 0, sendKey, 0, 32); - Array.Copy(sourceArray, 32, receiveKey, 0, 32); - Array.Copy(sourceArray, 64, sendMacSourceArray, 0, 64); - Array.Copy(sourceArray, 128, receiveMacSourceArray, 0, 64); - session.EncryptionParameters = new BeatTogether.Core.Messaging.Models.EncryptionParameters( - receiveKey, - sendKey, - new HMACSHA256(receiveMacSourceArray), - new HMACSHA256(sendMacSourceArray) - ); - - return Task.FromResult(new ChangeCipherSpecRequest()); - } - - public Task AuthenticateGameLiftUser(HandshakeSession session, - AuthenticateGameLiftUserRequest request) - { - if (!_handshakeSessionRegistry.TryRemovePendingPlayerSessionId(request.PlayerSessionId)) - { - // Not a pending player session ID, auth failure - _logger.Warning("Bad session token, GameLift auth failed " + - "(EndPoint={EndPoint}, PlayerSessionId={PlayerSessionId})", - session.EndPoint.ToString(), request.PlayerSessionId); - - return Task.FromResult(new AuthenticateGameLiftUserResponse() - { - Result = AuthenticateUserResult.Failed - }); - } - - // Auth success - session.PlayerSessionId = request.PlayerSessionId; - session.UserId = request.UserId; - session.UserName = request.UserName; - - _logger.Information("GameLift user authenticated (EndPoint={EndPoint}, UserId={UserId}, " + - "UserName={UserName}, PlayerSessionId={PlayerSessionId})", - session.EndPoint.ToString(), request.UserId, request.UserName, request.PlayerSessionId); - - return Task.FromResult(new AuthenticateGameLiftUserResponse() - { - Result = AuthenticateUserResult.Success - }); - } - - #endregion - - #region Hash utils - - private byte[] MakeSignature(byte[] clientRandom, byte[] serverRandom, byte[] publicKey) - { - var bufferWriter = new SpanBufferWriter(stackalloc byte[512]); - bufferWriter.WriteBytes(clientRandom); - bufferWriter.WriteBytes(serverRandom); - bufferWriter.WriteBytes(publicKey); - return _certificateSigningService.Sign(bufferWriter.Data.ToArray()); - } - - private byte[] MakeSeed(byte[] baseSeed, byte[] serverSeed, byte[] clientSeed) - { - var seed = new byte[baseSeed.Length + serverSeed.Length + clientSeed.Length]; - Array.Copy(baseSeed, 0, seed, 0, baseSeed.Length); - Array.Copy(serverSeed, 0, seed, baseSeed.Length, serverSeed.Length); - Array.Copy(clientSeed, 0, seed, baseSeed.Length + serverSeed.Length, clientSeed.Length); - return seed; - } - - private byte[] PRF(byte[] key, byte[] seed, int length) - { - var i = 0; - var array = new byte[length + seed.Length]; - while (i < length) - { - Array.Copy(seed, 0, array, i, seed.Length); - PRFHash(key, array, ref i); - } - - var array2 = new byte[length]; - Array.Copy(array, 0, array2, 0, length); - return array2; - } - - private void PRFHash(byte[] key, byte[] seed, ref int length) - { - using var hmacsha256 = new HMACSHA256(key); - var array = hmacsha256.ComputeHash(seed, 0, length); - var num = Math.Min(length + array.Length, seed.Length); - Array.Copy(array, 0, seed, length, num - length); - length = num; - } - - #endregion - } -} \ No newline at end of file diff --git a/BeatTogether.DedicatedServer.Kernel/Handshake/HandshakeSession.cs b/BeatTogether.DedicatedServer.Kernel/Handshake/HandshakeSession.cs deleted file mode 100644 index b812b4c3..00000000 --- a/BeatTogether.DedicatedServer.Kernel/Handshake/HandshakeSession.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Net; -using BeatTogether.Core.Messaging.Implementations; -using Org.BouncyCastle.Crypto.Parameters; - -namespace BeatTogether.DedicatedServer.Kernel.Handshake -{ - public enum HandshakeSessionState - { - None = 0, - New = 1, - Established = 2, - Authenticated = 3 - } - - public class HandshakeSession : BaseSession - { - - public byte[] Cookie { get; set; } - public byte[] ClientRandom { get; set; } - public byte[] ServerRandom { get; set; } - public byte[] ClientPublicKey { get; set; } - public ECPublicKeyParameters ClientPublicKeyParameters { get; set; } - public ECPrivateKeyParameters ServerPrivateKeyParameters { get; set; } - public byte[] PreMasterSecret { get; set; } - public DateTimeOffset LastKeepAlive { get; set; } - - public string? UserId { get; set; } = null; - public string? UserName { get; set; } = null; - public string? PlayerSessionId { get; set; } = null; - - public ConcurrentDictionary PendingRequests; - public ConcurrentDictionary PendingMultiparts; - - public HandshakeSession(EndPoint endPoint) : base(endPoint) - { - PendingRequests = new(); - PendingMultiparts = new(); - } - } -} \ No newline at end of file diff --git a/BeatTogether.DedicatedServer.Kernel/Handshake/HandshakeSessionRegistry.cs b/BeatTogether.DedicatedServer.Kernel/Handshake/HandshakeSessionRegistry.cs deleted file mode 100644 index 86bc01ae..00000000 --- a/BeatTogether.DedicatedServer.Kernel/Handshake/HandshakeSessionRegistry.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System.Collections.Concurrent; -using System.Linq; -using System.Net; -using BeatTogether.DedicatedServer.Kernel.Abstractions; -using Serilog; - -namespace BeatTogether.DedicatedServer.Kernel.Handshake -{ - public class HandshakeSessionRegistry : IHandshakeSessionRegistry - { - private readonly ConcurrentDictionary _sessions; - private readonly ConcurrentDictionary _pendingPlayerSessionIds; - - private readonly ILogger _logger; - - public HandshakeSessionRegistry() - { - _sessions = new(); - _pendingPlayerSessionIds = new(); - - _logger = Log.ForContext(); - } - - public HandshakeSession GetOrAdd(EndPoint endPoint) - { - return _sessions.GetOrAdd(endPoint, (ep) => new HandshakeSession(ep)); - } - - public HandshakeSession? TryGetByPlayerSessionId(string playerSessionId) - { - return (from s in _sessions - where s.Value.PlayerSessionId == playerSessionId - select s.Value).FirstOrDefault(); - } - - public void AddPendingPlayerSessionId(string playerSessionId) - { - _pendingPlayerSessionIds[playerSessionId] = true; - } - - public bool TryRemovePendingPlayerSessionId(string playerSessionId) - { - return _pendingPlayerSessionIds.TryRemove(playerSessionId, out _); - } - } -} \ No newline at end of file diff --git a/BeatTogether.DedicatedServer.Kernel/Handshake/UnconnectedDispatcher.cs b/BeatTogether.DedicatedServer.Kernel/Handshake/UnconnectedDispatcher.cs deleted file mode 100644 index f81d2c27..00000000 --- a/BeatTogether.DedicatedServer.Kernel/Handshake/UnconnectedDispatcher.cs +++ /dev/null @@ -1,141 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using BeatTogether.Core.Messaging.Abstractions; -using BeatTogether.DedicatedServer.Kernel.Abstractions; -using BeatTogether.DedicatedServer.Kernel.Encryption; -using BeatTogether.DedicatedServer.Messaging.Messages.Handshake; -using BeatTogether.LiteNetLib; -using BeatTogether.LiteNetLib.Dispatchers; -using BeatTogether.LiteNetLib.Enums; -using Krypton.Buffers; -using Serilog; - -namespace BeatTogether.DedicatedServer.Kernel.Handshake -{ - public class UnconnectedDispatcher : UnconnectedMessageDispatcher, IUnconnectedDispatcher, IDisposable - { - private readonly IDedicatedInstance _instance; - private readonly IMessageWriter _messageWriter; - private readonly PacketEncryptionLayer _packetEncryptionLayer; - - private readonly ConcurrentDictionary _activeSessions; - private readonly CancellationTokenSource _stopCts; - private readonly ILogger _logger; - - public UnconnectedDispatcher(LiteNetServer server, IDedicatedInstance instance, IMessageWriter messageWriter, - PacketEncryptionLayer packetEncryptionLayer) : base(server) - { - _instance = instance; - _messageWriter = messageWriter; - _packetEncryptionLayer = packetEncryptionLayer; - - _activeSessions = new(); - _stopCts = new(); - _logger = Log.ForContext(); - - Task.Run(() => UpdateLoop(_stopCts.Token)); - _instance.StopEvent += HandleInstanceStop; - } - - public void Dispose() - { - _instance.StopEvent -= HandleInstanceStop; - } - - private void HandleInstanceStop(IDedicatedInstance inst) => _stopCts.Cancel(); - - #region API - - public void Send(HandshakeSession session, IMessage message, bool retry = false) - { - _logger.Verbose("Sending handshake message of type {MessageType} (EndPoint={EndPoint})", - message.GetType().Name, session.EndPoint.ToString()); - - // Assign request ID to outgoing requests - if (message is IRequest requestMessage && !retry) - { - requestMessage.RequestId = session.GetNextRequestId(); - } - - // Track reliable requests for retry - if (message is IReliableRequest reliableRequest) - { - if (!retry) - { - session.PendingRequests[reliableRequest.RequestId] = - new HandshakePendingRequest(this, session, reliableRequest); - _activeSessions.TryAdd(session.EndPoint, session); - } - } - - var bufferWriter = new SpanBufferWriter(stackalloc byte[412]); - _messageWriter.WriteTo(ref bufferWriter, message); - Send(session.EndPoint, bufferWriter, UnconnectedMessageType.BasicMessage); - } - - public bool Acknowledge(HandshakeSession session, uint responseId, bool handled = true) - { - var ackOk = session.PendingRequests.TryRemove(responseId, out var ackedRequest); - - if (ackOk && handled) - { - if (ackedRequest!.Request is ChangeCipherSpecRequest) - { - // The client acknowledged & handled the change cipher request - // We can now turn on the encryption layer - _packetEncryptionLayer.AddEncryptedEndPoint((IPEndPoint)session.EndPoint, - session.EncryptionParameters!); - } - } - - return ackOk; - } - - #endregion - - #region Update / Retry - - private async Task UpdateLoop(CancellationToken cancellationToken) - { - while (!cancellationToken.IsCancellationRequested) - { - foreach (var session in _activeSessions.Values) - { - var pendingInSession = session.PendingRequests.Values; - - if (pendingInSession.Count == 0) - { - // Nothing left to retry in session, remove from tracked list - _activeSessions.TryRemove(session.EndPoint, out _); - continue; - } - - foreach (var pendingRequest in pendingInSession) - { - if (pendingRequest.HasExpired) - { - // Max retries exceeded - session.PendingRequests.TryRemove(pendingRequest.RequestId, out _); - break; - } - - if (!pendingRequest.ShouldRetry) - { - // Waiting for retry interval - continue; - } - - pendingRequest.Retry(); - } - } - - await Task.Delay(100, cancellationToken); - } - } - - #endregion - } -} \ No newline at end of file diff --git a/BeatTogether.DedicatedServer.Kernel/Handshake/UnconnectedSource.cs b/BeatTogether.DedicatedServer.Kernel/Handshake/UnconnectedSource.cs deleted file mode 100644 index c70797c8..00000000 --- a/BeatTogether.DedicatedServer.Kernel/Handshake/UnconnectedSource.cs +++ /dev/null @@ -1,191 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using BeatTogether.Core.Messaging.Abstractions; -using BeatTogether.Core.Messaging.Messages; -using BeatTogether.DedicatedServer.Kernel.Abstractions; -using BeatTogether.DedicatedServer.Kernel.Encryption; -using BeatTogether.DedicatedServer.Messaging.Messages.Handshake; -using BeatTogether.LiteNetLib.Enums; -using BeatTogether.LiteNetLib.Sources; -using Krypton.Buffers; -using Serilog; - -namespace BeatTogether.DedicatedServer.Kernel.Handshake -{ - public class UnconnectedSource : UnconnectedMessageSource, IDisposable - { - private readonly IDedicatedInstance _instance; - private readonly IServiceProvider _serviceProvider; - private readonly IUnconnectedDispatcher _unconnectedDispatcher; - private readonly IMessageReader _messageReader; - private readonly IHandshakeSessionRegistry _handshakeSessionRegistry; - private readonly PacketEncryptionLayer _packetEncryptionLayer; - - private readonly ConcurrentDictionary _activeSessions; - private readonly CancellationTokenSource _stopCts; - private readonly ILogger _logger; - - public UnconnectedSource( - IDedicatedInstance instance, - IServiceProvider serviceProvider, - IUnconnectedDispatcher unconnectedDispatcher, - IMessageReader messageReader, - IHandshakeSessionRegistry handshakeSessionRegistry, - PacketEncryptionLayer packetEncryptionLayer) - { - _instance = instance; - _serviceProvider = serviceProvider; - _unconnectedDispatcher = unconnectedDispatcher; - _messageReader = messageReader; - _handshakeSessionRegistry = handshakeSessionRegistry; - _packetEncryptionLayer = packetEncryptionLayer; - - _activeSessions = new(); - _stopCts = new(); - _logger = Log.ForContext(); - - Task.Run(() => UpdateLoop(_stopCts.Token)); - _instance.StopEvent += HandleInstanceStop; - } - - public void Dispose() - { - _instance.StopEvent -= HandleInstanceStop; - } - - private void HandleInstanceStop(IDedicatedInstance inst) => _stopCts.Cancel(); - - public IMessageReader GetMessageReader() => _messageReader; - - #region Receive - - public override void OnReceive(EndPoint remoteEndPoint, ref SpanBufferReader reader, - UnconnectedMessageType type) - { - var session = _handshakeSessionRegistry.GetOrAdd(remoteEndPoint); - var message = _messageReader.ReadFrom(ref reader); - - Task.Run(() => HandleMessage(session, message)); - } - - public async Task HandleMessage(HandshakeSession session, IMessage message) - { - var messageType = message.GetType(); - - if (message is ClientHelloRequest) - { - // Received client hello, first handshake message - ensure encryption is OFF for this endpoint - // This prevents outbound encryption with stale parameters from previous/incomplete handshake - _packetEncryptionLayer.RemoveEncryptedEndPoint((IPEndPoint)session.EndPoint); - } - - // Skip previously handled messages - if (message is IRequest request && !session.ShouldHandleRequest(request.RequestId)) - { - _logger.Warning("Skipping duplicate request (MessageType={Type}, RequestId={RequestId})", - messageType.Name, request.RequestId - ); - return; - } - - // Acknowledge reliable messages - uint requestId = 0; - - if (message is IReliableRequest reliableRequest) - { - requestId = reliableRequest.RequestId; - - _unconnectedDispatcher.Send(session, new AcknowledgeMessage() - { - ResponseId = requestId, - MessageHandled = true - }); - } - - // Dispatch to handler - _logger.Verbose("Handling handshake message of type {MessageType} (EndPoint={EndPoint})", - messageType.Name, session.EndPoint.ToString()); - - if (message is MultipartMessage multipartMessage) - { - if (!session.PendingMultiparts.ContainsKey(multipartMessage.MultipartMessageId)) - { - session.PendingMultiparts.TryAdd(multipartMessage.MultipartMessageId, - new HandshakePendingMultipart(this, session, multipartMessage.MultipartMessageId, - multipartMessage.TotalLength)); - } - - session.PendingMultiparts[multipartMessage.MultipartMessageId].AddMessage(multipartMessage); - return; - } - - var targetHandlerType = typeof(IHandshakeMessageHandler<>).MakeGenericType(messageType); - var messageHandler = _serviceProvider.GetService(targetHandlerType); - - if (messageHandler is null) - { - _logger.Warning("No handler exists for handshake message {MessageType}", - messageType.Name); - return; - } - - try - { - var replyMessage = await ((IHandshakeMessageHandler) messageHandler).Handle(session, message); - - // Send response, if any - if (replyMessage == null) - return; - - if (replyMessage is IResponse responseMessage) - responseMessage.ResponseId = requestId; - - _unconnectedDispatcher.Send(session, replyMessage); - } - catch (Exception ex) - { - _logger.Error(ex, "Exception handling message {MessageType} (EndPoint={EndPoint})", - messageType.Name, session.EndPoint.ToString()); - } - } - - #endregion - - #region Update / Retry - - private async Task UpdateLoop(CancellationToken cancellationToken) - { - while (!cancellationToken.IsCancellationRequested) - { - foreach (var session in _activeSessions.Values) - { - var pendingInSession = session.PendingMultiparts.Values; - - if (pendingInSession.Count == 0) - { - // Nothing left pending in session, remove from tracked list - _activeSessions.TryRemove(session.EndPoint, out _); - continue; - } - - foreach (var pendingRequest in pendingInSession) - { - if (pendingRequest.HasExpired || pendingRequest.IsComplete) - { - // Clean up completed / expired - session.PendingMultiparts.TryRemove(pendingRequest.MultipartMessageId, out _); - break; - } - } - } - - await Task.Delay(1000, cancellationToken); - } - } - - #endregion - } -} \ No newline at end of file diff --git a/BeatTogether.DedicatedServer.Kernel/Managers/Abstractions/IGameplayManager.cs b/BeatTogether.DedicatedServer.Kernel/Managers/Abstractions/IGameplayManager.cs index e3d7828a..5987a8e3 100644 --- a/BeatTogether.DedicatedServer.Kernel/Managers/Abstractions/IGameplayManager.cs +++ b/BeatTogether.DedicatedServer.Kernel/Managers/Abstractions/IGameplayManager.cs @@ -12,9 +12,8 @@ public interface IGameplayManager GameplayManagerState State { get; } BeatmapIdentifier? CurrentBeatmap { get; } GameplayModifiers CurrentModifiers { get; } - public float _songStartTime { get; } - void HandlePlayerLeaveGameplay(IPlayer player, int Unused = 0); + void HandlePlayerLeaveGameplay(IPlayer player); void HandleGameSceneLoaded(IPlayer player, SetGameplaySceneReadyPacket packet); void HandleGameSongLoaded(IPlayer player); void HandleLevelFinished(IPlayer player, LevelFinishedPacket packet); diff --git a/BeatTogether.DedicatedServer.Kernel/Managers/Abstractions/ILobbyManager.cs b/BeatTogether.DedicatedServer.Kernel/Managers/Abstractions/ILobbyManager.cs index 232c2420..4d60a0c2 100644 --- a/BeatTogether.DedicatedServer.Kernel/Managers/Abstractions/ILobbyManager.cs +++ b/BeatTogether.DedicatedServer.Kernel/Managers/Abstractions/ILobbyManager.cs @@ -1,4 +1,6 @@ -using BeatTogether.DedicatedServer.Kernel.Enums; +using BeatTogether.Core.Enums; +using BeatTogether.DedicatedServer.Kernel.Abstractions; +using BeatTogether.DedicatedServer.Messaging.Enums; using BeatTogether.DedicatedServer.Messaging.Models; using System.Collections.Generic; @@ -7,17 +9,20 @@ namespace BeatTogether.DedicatedServer.Kernel.Managers.Abstractions public interface ILobbyManager { bool AllPlayersReady { get; } - bool SomePlayersReady { get; } + bool AnyPlayersReady { get; } bool NoPlayersReady { get; } - bool AllPlayersSpectating { get; } - + bool AllPlayersNotWantToPlayNextLevel { get; } + bool CanEveryonePlayBeatmap { get; } BeatmapIdentifier? SelectedBeatmap { get; } GameplayModifiers SelectedModifiers { get; } CountdownState CountDownState { get; } - float CountdownEndTime { get; } + long CountdownEndTime { get; } GameplayModifiers EmptyModifiers {get; } + public bool UpdateSpectatingPlayers { get; set; } + public bool ForceStartSelectedBeatmap { get; set; } void Update(); - BeatmapDifficulty[] GetSelectedBeatmapDifficulties(); + Dictionary? GetSelectedBeatmapDifficultiesRequirements(); + CannotStartGameReason GetCannotStartGameReason(IPlayer player, bool DoesEveryoneOwnBeatmap); } } diff --git a/BeatTogether.DedicatedServer.Kernel/Managers/GameplayManager.cs b/BeatTogether.DedicatedServer.Kernel/Managers/GameplayManager.cs index 408f4ec3..cbfdcea6 100644 --- a/BeatTogether.DedicatedServer.Kernel/Managers/GameplayManager.cs +++ b/BeatTogether.DedicatedServer.Kernel/Managers/GameplayManager.cs @@ -1,10 +1,12 @@ -using BeatTogether.DedicatedServer.Kernel.Abstractions; +using BeatTogether.Core.Enums; +using BeatTogether.DedicatedServer.Ignorance.IgnoranceCore; +using BeatTogether.DedicatedServer.Kernel.Abstractions; using BeatTogether.DedicatedServer.Kernel.Enums; using BeatTogether.DedicatedServer.Kernel.Managers.Abstractions; using BeatTogether.DedicatedServer.Messaging.Enums; using BeatTogether.DedicatedServer.Messaging.Models; using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.GameplayRpc; -using BeatTogether.LiteNetLib.Enums; +using Serilog; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -28,12 +30,13 @@ public sealed class GameplayManager : IGameplayManager, IDisposable public BeatmapIdentifier? CurrentBeatmap { get; private set; } = null; public GameplayModifiers CurrentModifiers { get; private set; } = new(); - private const float SongStartDelay = 0.5f; + private const long SongStartDelay = 500L; private const float SceneLoadTimeLimit = 15.0f; private const float SongLoadTimeLimit = 15.0f; - public float _songStartTime { get; private set; } - List PlayersAtStart = new(); + private long SongStartTime { get; set; } + + private readonly List PlayersAtStart = new(); private CancellationTokenSource? _requestReturnToMenuCts; @@ -56,6 +59,8 @@ public sealed class GameplayManager : IGameplayManager, IDisposable private CancellationTokenSource? songReadyCts = null; private CancellationTokenSource? linkedSongReadyCts = null; + private readonly ILogger _logger = Log.ForContext(); + public GameplayManager( IDedicatedInstance instance, IPlayerRegistry playerRegistry, @@ -97,8 +102,12 @@ public async void StartSong(CancellationToken cancellationToken) State = GameplayManagerState.SceneLoad; foreach (var player in _playerRegistry.Players)//Array of players that are playing at the start { - if (!player.IsSpectating) - PlayersAtStart.Add(player.UserId); + if (player.WantsToPlayNextLevel && !player.IsSpectating && !player.IsBackgrounded && !player.ForceLateJoin) + { + PlayersAtStart.Add(player.HashedUserId); + } + //if (!player.IsSpectating && !player.ForceLateJoin && !player.IsBackgrounded) + // PlayersAtStart.Add(player.UserId); } // Create level finished tasks (players may send these at any time during gameplay) @@ -129,7 +138,7 @@ public async void StartSong(CancellationToken cancellationToken) }); // Wait for scene ready - _packetDispatcher.SendToNearbyPlayers(new GetGameplaySceneReadyPacket(), DeliveryMethod.ReliableOrdered); + _packetDispatcher.SendToNearbyPlayers(new GetGameplaySceneReadyPacket(), IgnoranceChannelTypes.Reliable); sceneReadyCts.CancelAfter((int)((SceneLoadTimeLimit + (PlayersAtStart.Count * 0.3f)) * 1000)); await Task.WhenAll(_sceneReadyTcs.Values.Select(p => p.Task)); @@ -140,50 +149,64 @@ public async void StartSong(CancellationToken cancellationToken) { ActivePlayerSpecificSettingsAtStart = _playerSpecificSettings.Values.ToArray() } - }, DeliveryMethod.ReliableOrdered); + }, IgnoranceChannelTypes.Reliable); // Set scene sync finished State = GameplayManagerState.SongLoad; //Wait for players to have the song ready - _packetDispatcher.SendToNearbyPlayers(new GetGameplaySongReadyPacket(), DeliveryMethod.ReliableOrdered); + _packetDispatcher.SendToNearbyPlayers(new GetGameplaySongReadyPacket(), IgnoranceChannelTypes.Reliable); songReadyCts.CancelAfter((int)((SongLoadTimeLimit + (PlayersAtStart.Count*0.3f)) * 1000)); await Task.WhenAll(_songReadyTcs.Values.Select(p => p.Task)); - float StartDelay = 0; + long StartDelay = 0; foreach (var UserId in PlayersAtStart) { if (_playerRegistry.TryGetPlayer(UserId, out var p)) { - if (!p.InGameplay || p.InLobby) + if (!p.InGameplay || !p.IsActive) HandlePlayerLeaveGameplay(p); StartDelay = Math.Max(StartDelay, p.Latency.CurrentAverage); } } // Start song and wait for finish - _songStartTime = _instance.RunTime + SongStartDelay + (StartDelay * 2f); + SongStartTime = (_instance.RunTime + SongStartDelay + (StartDelay * 2)); + _logger.Verbose($"SongStartTime: {SongStartTime} RunTime: {_instance.RunTime}"); State = GameplayManagerState.Gameplay; _packetDispatcher.SendToNearbyPlayers(new SetSongStartTimePacket { - StartTime = _songStartTime - }, DeliveryMethod.ReliableOrdered); + StartTime = SongStartTime + }, IgnoranceChannelTypes.Reliable); + //Initiates the song start process for forced late joiners - try force them to spectate if they were causing issues in countdown + foreach(IPlayer p in _playerRegistry.Players) + { + if (p.ForceLateJoin) + { + _packetDispatcher.SendToPlayer(p, new Messaging.Packets.MultiplayerSession.MenuRpc.StartLevelPacket + { + Beatmap = CurrentBeatmap, + Modifiers = CurrentModifiers, + StartTime = SongStartTime + }, IgnoranceChannelTypes.Reliable); + } + } await Task.WhenAll(_levelFinishedTcs.Values.Select(p => p.Task)); State = GameplayManagerState.Results; - if (_levelCompletionResults.Values.Any(result => result.LevelEndStateType == LevelEndStateType.Cleared) && _instance._configuration.CountdownConfig.ResultsScreenTime > 0) - await Task.Delay((int)(_instance._configuration.CountdownConfig.ResultsScreenTime * 1000), cancellationToken); + if (_levelCompletionResults.Values.Any(result => result.LevelEndStateType == LevelEndStateType.Cleared) && _instance._configuration.CountdownConfig.ResultsScreenTime != 0) + await Task.Delay((int)_instance._configuration.CountdownConfig.ResultsScreenTime, cancellationToken); // End gameplay and reset SetBeatmap(null, new()); ResetValues(); _instance.SetState(MultiplayerGameState.Lobby); - _packetDispatcher.SendToNearbyPlayers( new ReturnToMenuPacket(), DeliveryMethod.ReliableOrdered); + _packetDispatcher.SendToNearbyPlayers( new ReturnToMenuPacket(), IgnoranceChannelTypes.Reliable); } private void ResetValues() @@ -192,7 +215,7 @@ private void ResetValues() _levelFinishedTcs.Clear(); _sceneReadyTcs.Clear(); _songReadyTcs.Clear(); - _songStartTime = 0; + SongStartTime = 0; _playerSpecificSettings.Clear(); _levelCompletionResults.Clear(); PlayersAtStart.Clear(); @@ -201,17 +224,17 @@ private void ResetValues() public void HandleGameSceneLoaded(IPlayer player, SetGameplaySceneReadyPacket packet) { - if (State == GameplayManagerState.SceneLoad && _sceneReadyTcs.TryGetValue(player.UserId, out var tcs) && !tcs.Task.IsCompleted) + if (State == GameplayManagerState.SceneLoad && _sceneReadyTcs.TryGetValue(player.HashedUserId, out var tcs) && !tcs.Task.IsCompleted) { - _playerSpecificSettings[player.UserId] = packet.PlayerSpecificSettings; - PlayerSceneReady(player.UserId); + _playerSpecificSettings[player.HashedUserId] = packet.PlayerSpecificSettings; + PlayerSceneReady(player.HashedUserId); return; } if (_instance.State != MultiplayerGameState.Game || State == GameplayManagerState.Results || State == GameplayManagerState.None) //Returns player to lobby { - _packetDispatcher.SendToPlayer(player, new ReturnToMenuPacket(), DeliveryMethod.ReliableOrdered); - LeaveGameplay(player.UserId); + _packetDispatcher.SendToPlayer(player, new ReturnToMenuPacket(), IgnoranceChannelTypes.Reliable); + LeaveGameplay(player.HashedUserId); return; } @@ -219,60 +242,56 @@ public void HandleGameSceneLoaded(IPlayer player, SetGameplaySceneReadyPacket pa { _packetDispatcher.SendToNearbyPlayers(new SetPlayerDidConnectLatePacket { - UserId = player.UserId, + UserId = player.HashedUserId, PlayersAtStart = new PlayerSpecificSettingsAtStart { ActivePlayerSpecificSettingsAtStart = _playerSpecificSettings.Values.ToArray() }, SessionGameId = SessionGameId - }, DeliveryMethod.ReliableOrdered); - _packetDispatcher.SendToPlayer(player, new GetGameplaySongReadyPacket(), DeliveryMethod.ReliableOrdered); + }, IgnoranceChannelTypes.Reliable); + _packetDispatcher.SendToPlayer(player, new GetGameplaySongReadyPacket(), IgnoranceChannelTypes.Reliable); } } public void HandleGameSongLoaded(IPlayer player) { - if (State == GameplayManagerState.SongLoad && _songReadyTcs.TryGetValue(player.UserId, out var tcs) && !tcs.Task.IsCompleted) + if (State == GameplayManagerState.SongLoad && _songReadyTcs.TryGetValue(player.HashedUserId, out var tcs) && !tcs.Task.IsCompleted) { - PlayerSongReady(player.UserId); + PlayerSongReady(player.HashedUserId); return; } if (_instance.State != MultiplayerGameState.Game || State == GameplayManagerState.Results || State == GameplayManagerState.None) //Player is sent back to lobby { - _packetDispatcher.SendToPlayer(player, new ReturnToMenuPacket(), DeliveryMethod.ReliableOrdered); + _packetDispatcher.SendToPlayer(player, new ReturnToMenuPacket(), IgnoranceChannelTypes.Reliable); HandlePlayerLeaveGameplay(player); return; } if (State != GameplayManagerState.SceneLoad) //Late joiners get sent start time - if(_songStartTime != 0) + if(SongStartTime != 0) _packetDispatcher.SendToPlayer(player, new SetSongStartTimePacket { - StartTime = _songStartTime - }, DeliveryMethod.ReliableOrdered); + StartTime = SongStartTime + }, IgnoranceChannelTypes.Reliable); } public void HandleLevelFinished(IPlayer player, LevelFinishedPacket packet) { - if (_levelFinishedTcs.TryGetValue(player.UserId, out var tcs) && tcs.Task.IsCompleted) + if (_levelFinishedTcs.TryGetValue(player.HashedUserId, out var tcs) && tcs.Task.IsCompleted) return; - _levelCompletionResults[player.UserId] = packet.Results.LevelCompletionResults; - PlayerFinishLevel(player.UserId); + _levelCompletionResults[player.HashedUserId] = packet.Results.LevelCompletionResults; + PlayerFinishLevel(player.HashedUserId); } - object RequestReturnLock = new(); public void SignalRequestReturnToMenu() { - lock (RequestReturnLock) - { - if (_requestReturnToMenuCts != null && !_requestReturnToMenuCts.IsCancellationRequested) - _requestReturnToMenuCts.Cancel(); - } + if (_requestReturnToMenuCts != null && !_requestReturnToMenuCts.IsCancellationRequested) + _requestReturnToMenuCts.Cancel(); } //will set players tasks as done if they leave gameplay due to disconnect or returning to the menu - public void HandlePlayerLeaveGameplay(IPlayer player, int Unused = 0) + public void HandlePlayerLeaveGameplay(IPlayer player) { - LeaveGameplay(player.UserId); + LeaveGameplay(player.HashedUserId); } private void LeaveGameplay(string UserId) diff --git a/BeatTogether.DedicatedServer.Kernel/Managers/LobbyManager.cs b/BeatTogether.DedicatedServer.Kernel/Managers/LobbyManager.cs index 074d6cca..872580f7 100644 --- a/BeatTogether.DedicatedServer.Kernel/Managers/LobbyManager.cs +++ b/BeatTogether.DedicatedServer.Kernel/Managers/LobbyManager.cs @@ -3,46 +3,47 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using BeatTogether.Core.Enums; +using BeatTogether.DedicatedServer.Ignorance.IgnoranceCore; using BeatTogether.DedicatedServer.Kernel.Abstractions; using BeatTogether.DedicatedServer.Kernel.Configuration; -using BeatTogether.DedicatedServer.Kernel.Enums; using BeatTogether.DedicatedServer.Kernel.Managers.Abstractions; using BeatTogether.DedicatedServer.Messaging.Enums; using BeatTogether.DedicatedServer.Messaging.Models; -using BeatTogether.DedicatedServer.Messaging.Packets; using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MenuRpc; -using BeatTogether.LiteNetLib.Abstractions; -using BeatTogether.LiteNetLib.Enums; using Serilog; /*Lobby manager code * Contains the logic code for * - different game modes - * - setting the beatmap - * - setting the modifiers + * - managing the beatmap + * - managing the modifiers * - managing the countdown - * - checking player entitlements - * - when to start gameplay + * - managing player entitlements + * - managing when to start gameplay */ namespace BeatTogether.DedicatedServer.Kernel.Managers { - public sealed class LobbyManager : ILobbyManager, IDisposable + public sealed class LobbyManager : ILobbyManager, IDisposable { - - public bool AllPlayersReady => _playerRegistry.Players.All(p => p.IsReady || !p.WantsToPlayNextLevel); //if all players are ready OR spectating - public bool SomePlayersReady => _playerRegistry.Players.Any(p => p.IsReady); //if *any* are ready - public bool NoPlayersReady => _playerRegistry.Players.All(p => !p.IsReady || !p.WantsToPlayNextLevel); //players not ready or spectating - public bool AllPlayersSpectating => _playerRegistry.Players.All(p => !p.WantsToPlayNextLevel); //if all spectating + public bool AllPlayersReady => _playerRegistry.Players.All(p => p.IsReady || !p.WantsToPlayNextLevel || p.IsBackgrounded || p.IsSpectating); //If all are ready or spectating or backgrounded or a spectator type + public bool AnyPlayersReady => _playerRegistry.Players.Any(p => p.IsReady && p.WantsToPlayNextLevel && !p.IsBackgrounded && !p.IsSpectating); //If anyone who is active wants to play + public bool NoPlayersReady => !AnyPlayersReady;//no players want to play right now + public bool AllPlayersNotWantToPlayNextLevel => _playerRegistry.Players.All(p => !p.WantsToPlayNextLevel);//if all are going to be spectating + public bool AllPlayersAreInLobby => _playerRegistry.Players.All(p => p.InMenu); + public bool CanEveryonePlayBeatmap => SelectedBeatmap != null && !_playerRegistry.Players.Any(p => (p.GetEntitlement(SelectedBeatmap.LevelId) is EntitlementStatus.NotOwned) && !p.IsSpectating && !p.IsBackgrounded && p.WantsToPlayNextLevel); + public bool UpdateSpectatingPlayers { get; set; } = false; + public bool ForceStartSelectedBeatmap { get; set; } = false; //For future server-side things public BeatmapIdentifier? SelectedBeatmap { get; private set; } = null; public GameplayModifiers SelectedModifiers { get; private set; } = new(); public CountdownState CountDownState { get; private set; } = CountdownState.NotCountingDown; - public float CountdownEndTime { get; private set; } = 0; + public long CountdownEndTime { get; private set; } = 0; private BeatmapIdentifier? _lastBeatmap = null; private bool _lastSpectatorState; - private bool _AllOwnMap; - private string _lastManagerId = null!; + private bool _LastCanEveryonePlayBeatmap; + //private string _lastManagerId = null!; private readonly CancellationTokenSource _stopCts = new(); private const int LoopTime = 100; public GameplayModifiers EmptyModifiers { get; } = new(); @@ -82,15 +83,66 @@ private void Stop(IDedicatedInstance inst) private async void UpdateLoop(CancellationToken cancellationToken) { - try + while (!cancellationToken.IsCancellationRequested) { - await Task.Delay(LoopTime, cancellationToken); Update(); - UpdateLoop(cancellationToken); + try + { + await Task.Delay(LoopTime, cancellationToken); + } + catch (TaskCanceledException) { continue; } + catch (OperationCanceledException) { continue; } } - catch + } + + /// + /// Force starts the beatmap without waiting for players to download the map. If they dont download the map in time then they should end up spectating + /// Currently not used, probably needs fixing + /// + public void ForceStartBeatmapUpdate() + { + if(SelectedBeatmap != null) { + SetCountdown(CountdownState.StartBeatmapCountdown, _configuration.CountdownConfig.BeatMapStartCountdownTime); + if (CountdownEndTime <= _instance.RunTime) + { + if (CountDownState != CountdownState.WaitingForEntitlement) + { + SetCountdown(CountdownState.WaitingForEntitlement); + } + if (_playerRegistry.Players.All(p => (p.GetEntitlement(SelectedBeatmap!.LevelId) is EntitlementStatus.Ok) || p.IsSpectating || !p.WantsToPlayNextLevel || p.IsBackgrounded || p.ForceLateJoin)) + { + //starts beatmap + _gameplayManager.SetBeatmap(SelectedBeatmap!, SelectedModifiers); + Task.Run(() => _gameplayManager.StartSong(CancellationToken.None)); + //stops countdown + SetCountdown(CountdownState.NotCountingDown); + ForceStartSelectedBeatmap = false; + return; + } + else + { + foreach(IPlayer p in _playerRegistry.Players) + { + if(p.GetEntitlement(SelectedBeatmap.LevelId) is EntitlementStatus.NotOwned or EntitlementStatus.Unknown || p.IsSpectating || !p.WantsToPlayNextLevel || p.ForceLateJoin) + { + p.ForceLateJoin = true; + } + } + } + if(CountdownEndTime + _configuration.SendPlayersWithoutEntitlementToSpectateTimeout <= _instance.RunTime) + { + IPlayer[] MissingEntitlement = _playerRegistry.Players.Where(p => p.GetEntitlement(SelectedBeatmap!.LevelId) is not EntitlementStatus.Ok).ToArray(); + foreach (IPlayer p in MissingEntitlement) + { + //Force the player to join late + p.ForceLateJoin = true; + _packetDispatcher.SendToPlayer(p, new CancelLevelStartPacket(), IgnoranceChannelTypes.Reliable); + _packetDispatcher.SendToPlayer(p, new SetIsReadyPacket() { IsReady = false }, IgnoranceChannelTypes.Reliable); + } + } + } } } @@ -99,141 +151,113 @@ public void Update() if (_instance.State != MultiplayerGameState.Lobby) return; - if (!_playerRegistry.TryGetPlayer(_configuration.ServerOwnerId, out var serverOwner) && _configuration.SongSelectionMode == SongSelectionMode.ServerOwnerPicks) + if (!_playerRegistry.TryGetPlayer(_configuration.ServerOwnerId, out var serverOwner) && _configuration.GameplayServerConfiguration.SongSelectionMode == SongSelectionMode.ManagerPicks) return; UpdateBeatmap(GetSelectedBeatmap(), GetSelectedModifiers()); - if (_lastManagerId != null && _lastManagerId != _configuration.ServerOwnerId && _playerRegistry.TryGetPlayer(_lastManagerId, out var OldManager)) - _packetDispatcher.SendToPlayer(OldManager, new SetIsStartButtonEnabledPacket - { - Reason = CannotStartGameReason.None - }, DeliveryMethod.ReliableOrdered); + UpdatePlayersMissingEntitlementsMessages(); - foreach (IPlayer player in _playerRegistry.Players) + if (_configuration.GameplayServerConfiguration.SongSelectionMode == SongSelectionMode.ManagerPicks) { - lock (player.EntitlementLock) + if (_lastBeatmap != SelectedBeatmap || _LastCanEveryonePlayBeatmap != CanEveryonePlayBeatmap || _lastSpectatorState != AllPlayersNotWantToPlayNextLevel) { - if (player.UpdateEntitlement) + _packetDispatcher.SendToPlayer(serverOwner!, new SetIsStartButtonEnabledPacket { - if (player.BeatmapIdentifier != null) - _packetDispatcher.SendToPlayer(player, new SetPlayersMissingEntitlementsToLevelPacket - { - PlayersWithoutEntitlements = _playerRegistry.Players - .Where(p => p.GetEntitlement(player.BeatmapIdentifier.LevelId) is EntitlementStatus.NotOwned/* or EntitlementStatus.Unknown*/) - .Select(p => p.UserId).ToArray() - }, DeliveryMethod.ReliableOrdered); - player.UpdateEntitlement = false; - } + Reason = GetCannotStartGameReason(serverOwner!, CanEveryonePlayBeatmap) + }, IgnoranceChannelTypes.Reliable); } } - if (SelectedBeatmap != null) - { - bool allPlayersOwnBeatmap = _playerRegistry.Players - .All(p => p.GetEntitlement(SelectedBeatmap.LevelId) is EntitlementStatus.Ok or EntitlementStatus.NotDownloaded); - - if(_configuration.SongSelectionMode == SongSelectionMode.ServerOwnerPicks) - { - if (_lastBeatmap != SelectedBeatmap || _AllOwnMap != allPlayersOwnBeatmap || _lastSpectatorState != AllPlayersSpectating) - { - if (AllPlayersSpectating) - _packetDispatcher.SendToPlayer(serverOwner!, new SetIsStartButtonEnabledPacket - { - Reason = CannotStartGameReason.AllPlayersSpectating - }, DeliveryMethod.ReliableOrdered); - else - { - _packetDispatcher.SendToPlayer(serverOwner!, new SetIsStartButtonEnabledPacket - { - Reason = allPlayersOwnBeatmap ? CannotStartGameReason.None : CannotStartGameReason.DoNotOwnSong - }, DeliveryMethod.ReliableOrdered); - } - } - } - _AllOwnMap = allPlayersOwnBeatmap; - - switch (_configuration.SongSelectionMode) //server modes - { - case SongSelectionMode.ServerOwnerPicks: - CountingDown(serverOwner!.IsReady, !serverOwner.IsReady); - break; - case SongSelectionMode.Vote: - CountingDown(SomePlayersReady, NoPlayersReady); - break; - case SongSelectionMode.RandomPlayerPicks: - CountingDown(SomePlayersReady, NoPlayersReady); - break; - case SongSelectionMode.ServerPicks: - CountingDown(true, false); - break; - } - } - else + switch (_configuration.GameplayServerConfiguration.SongSelectionMode) //server modes { - if(_configuration.SongSelectionMode == SongSelectionMode.ServerOwnerPicks && _lastBeatmap != SelectedBeatmap) - _packetDispatcher.SendToPlayer(serverOwner!, new SetIsStartButtonEnabledPacket - { - Reason = CannotStartGameReason.NoSongSelected - }, DeliveryMethod.ReliableOrdered); - //Send stop countdown packet if the beatmap is somehow set to null (serrver owner may disconnect, or if tournament server setting the beatmap to null should stop the countdown) - if (CountDownState != CountdownState.NotCountingDown) - CancelCountdown(); + case SongSelectionMode.ManagerPicks: + CountingDown(serverOwner!.IsReady, !serverOwner.IsReady || AllPlayersNotWantToPlayNextLevel || !CanEveryonePlayBeatmap); + break; + case SongSelectionMode.Vote: + CountingDown(AnyPlayersReady, NoPlayersReady || AllPlayersNotWantToPlayNextLevel || !CanEveryonePlayBeatmap); + break; + case SongSelectionMode.RandomPlayerPicks: + CountingDown(AnyPlayersReady, NoPlayersReady || AllPlayersNotWantToPlayNextLevel || !CanEveryonePlayBeatmap); + break; + case SongSelectionMode.ServerPicks: + CountingDown(true, false); + break; } - _lastManagerId = _configuration.ServerOwnerId; - _lastSpectatorState = AllPlayersSpectating; + _LastCanEveryonePlayBeatmap = CanEveryonePlayBeatmap; + //_lastManagerId = _configuration.ServerOwnerId; + _lastSpectatorState = AllPlayersNotWantToPlayNextLevel; _lastBeatmap = SelectedBeatmap; } private void CountingDown(bool isReady, bool NotStartable) { + //_logger.Debug($"CountdownEndTime '{CountdownEndTime}' RunTime '{_instance.RunTime}' BeatMapStartCountdownTime '{_configuration.CountdownConfig.BeatMapStartCountdownTime}' CountdownTimePlayersReady '{_configuration.CountdownConfig.CountdownTimePlayersReady}'"); // If not already counting down if (CountDownState == CountdownState.NotCountingDown) { if (CountdownEndTime != 0 && CountdownEndTime <= _instance.RunTime) CancelCountdown(); - if ((AllPlayersReady && !AllPlayersSpectating && _AllOwnMap)) - SetCountdown(CountdownState.StartBeatmapCountdown, _configuration.CountdownConfig.BeatMapStartCountdownTime); - else if (isReady && _AllOwnMap) - SetCountdown(CountdownState.CountingDown, _configuration.CountdownConfig.CountdownTimePlayersReady); + if (isReady && !NotStartable) + SetCountdown(CountdownState.CountingDown, _configuration.CountdownConfig.CountdownTimePlayersReady); //Begin normal countdown + else if (AllPlayersReady && !NotStartable) + SetCountdown(CountdownState.StartBeatmapCountdown, _configuration.CountdownConfig.BeatMapStartCountdownTime); //Lock in beatmap and being starting countdown } // If counting down if (CountDownState != CountdownState.NotCountingDown) { - if(CountdownEndTime <= _instance.RunTime) + //_logger.Debug($"CountdownEndTime '{CountdownEndTime}' RunTime '{_instance.RunTime}'"); + //If the beatmap is not playable or the game is not startable + if ( NotStartable ) + { + _logger.Debug($"Canceling countdown check SelectedBeatmapNull={SelectedBeatmap == null}"); + foreach (var p in _playerRegistry.Players.Where(p => (SelectedBeatmap != null && p.GetEntitlement(SelectedBeatmap.LevelId) is EntitlementStatus.NotOwned) && !p.IsSpectating && !p.IsBackgrounded && p.WantsToPlayNextLevel)) + { + _logger.Debug($"Player causing cancel UserId={p.HashedUserId} Username={p.UserName} Entitlement={(SelectedBeatmap != null ? p.GetEntitlement(SelectedBeatmap.LevelId) : "SelectedBeatmap is null")} IsSpectating={p.IsSpectating} IsBackgrounded={p.IsBackgrounded} WantsToPlayNextLevel={p.WantsToPlayNextLevel}"); + } + CancelCountdown(); + return; + } + if (CountdownEndTime <= _instance.RunTime) { + _logger.Debug($"Countdown finished, sending map again and waiting for entitlement check"); // If countdown just finished, send map then pause lobby untill all players have map downloaded if (CountDownState != CountdownState.WaitingForEntitlement) { SetCountdown(CountdownState.WaitingForEntitlement); + _packetDispatcher.SendToPlayers(_playerRegistry.Players.Where(p => p.GetEntitlement(SelectedBeatmap!.LevelId) == Messaging.Enums.EntitlementStatus.Unknown).ToArray(), new GetIsEntitledToLevelPacket + { + LevelId = SelectedBeatmap!.LevelId + }, IgnoranceChannelTypes.Reliable); } - if (_playerRegistry.Players.All(p => p.GetEntitlement(SelectedBeatmap!.LevelId) is EntitlementStatus.Ok)) + if (_playerRegistry.Players.All(p => (p.GetEntitlement(SelectedBeatmap!.LevelId) is EntitlementStatus.Ok) || p.IsSpectating || !p.WantsToPlayNextLevel || p.IsBackgrounded || p.ForceLateJoin)) { + _logger.Debug($"All players have entitlement or are not playing, starting map"); //starts beatmap _gameplayManager.SetBeatmap(SelectedBeatmap!, SelectedModifiers); - Task.Run(() => _gameplayManager.StartSong(CancellationToken.None)); - //stops countdown SetCountdown(CountdownState.NotCountingDown); + Task.Run(() => _gameplayManager.StartSong(CancellationToken.None)); return; } - if (CountdownEndTime + _configuration.KickPlayersWithoutEntitlementTimeout <= _instance.RunTime) + if (CountdownEndTime + _configuration.SendPlayersWithoutEntitlementToSpectateTimeout <= _instance.RunTime) //If takes too long to start then players are sent to spectate by telling them the beatmap already started { - IPlayer[] MissingEntitlement = _playerRegistry.Players.Where(p => p.GetEntitlement(SelectedBeatmap!.LevelId) is not EntitlementStatus.Ok).ToArray(); + _logger.Debug($"Took too long to start, kicking problem players"); + IPlayer[] MissingEntitlement = _playerRegistry.Players.Where(p => p.GetEntitlement(SelectedBeatmap!.LevelId) is not EntitlementStatus.Ok && !p.IsSpectating && p.WantsToPlayNextLevel && !p.IsBackgrounded).ToArray(); foreach (IPlayer p in MissingEntitlement) { - _packetDispatcher.SendToPlayer(p, new KickPlayerPacket() - { - DisconnectedReason = DisconnectedReason.Kicked, - }, DeliveryMethod.ReliableOrdered); + /* //Force the player to join late + p.ForceLateJoin = true; + _packetDispatcher.SendToPlayer(p, new CancelLevelStartPacket(), IgnoranceChannelTypes.Reliable); + _packetDispatcher.SendToPlayer(p, new SetIsReadyPacket() { IsReady = false }, IgnoranceChannelTypes.Reliable);*/ + _instance.DisconnectPlayer(p); } } } - // If server owner/all players are no longer ready or not all players own beatmap - if (NotStartable || !_AllOwnMap || AllPlayersSpectating) - CancelCountdown(); - else if (CountDownState == CountdownState.CountingDown && (AllPlayersReady || (CountdownEndTime - _instance.RunTime) < _configuration.CountdownConfig.BeatMapStartCountdownTime)) + if (CountDownState == CountdownState.CountingDown && (AllPlayersReady || (CountdownEndTime - _instance.RunTime) < _configuration.CountdownConfig.BeatMapStartCountdownTime)) + { SetCountdown(CountdownState.StartBeatmapCountdown, _configuration.CountdownConfig.BeatMapStartCountdownTime); + } } } @@ -246,9 +270,9 @@ private void UpdateBeatmap(BeatmapIdentifier? beatmap, GameplayModifiers modifie _packetDispatcher.SendToNearbyPlayers(new SetSelectedBeatmap() { Beatmap = SelectedBeatmap - }, DeliveryMethod.ReliableOrdered); + }, IgnoranceChannelTypes.Reliable); //TODO send custom mp packet details else - _packetDispatcher.SendToNearbyPlayers(new ClearSelectedBeatmap(), DeliveryMethod.ReliableOrdered); + _packetDispatcher.SendToNearbyPlayers(new ClearSelectedBeatmap(), IgnoranceChannelTypes.Reliable); } if (SelectedModifiers != modifiers) { @@ -257,32 +281,67 @@ private void UpdateBeatmap(BeatmapIdentifier? beatmap, GameplayModifiers modifie _packetDispatcher.SendToNearbyPlayers(new SetSelectedGameplayModifiers() { Modifiers = SelectedModifiers - }, DeliveryMethod.ReliableOrdered); + }, IgnoranceChannelTypes.Reliable); else - _packetDispatcher.SendToNearbyPlayers(new ClearSelectedGameplayModifiers(), DeliveryMethod.ReliableOrdered); + _packetDispatcher.SendToNearbyPlayers(new ClearSelectedGameplayModifiers(), IgnoranceChannelTypes.Reliable); } } - public BeatmapDifficulty[] GetSelectedBeatmapDifficulties() + private void UpdatePlayersMissingEntitlementsMessages() { - if (!SelectedBeatmap!.LevelId.StartsWith("custom_level_")) + foreach (IPlayer player in _playerRegistry.Players) { - return Array.Empty(); + if (player.UpdateEntitlement || UpdateSpectatingPlayers) + { + player.UpdateEntitlement = false; + if (player.BeatmapIdentifier != null) + { + _packetDispatcher.SendToPlayer(player, new SetPlayersMissingEntitlementsToLevelPacket + { + PlayersWithoutEntitlements = _playerRegistry.Players + .Where(p => (p.GetEntitlement(player.BeatmapIdentifier.LevelId) is EntitlementStatus.NotOwned) && !p.IsSpectating && p.WantsToPlayNextLevel && !p.IsBackgrounded) + .Select(p => p.HashedUserId).ToArray() + }, IgnoranceChannelTypes.Reliable); + //_logger.Debug("Sent missing entitlement packet"); + } + else + { //Send empty if no map is selected + _packetDispatcher.SendToPlayer(player, new SetPlayersMissingEntitlementsToLevelPacket + { + PlayersWithoutEntitlements = { } + }, IgnoranceChannelTypes.Reliable); + } + } } + UpdateSpectatingPlayers = false; + } + + //TODO do something better than iterating, probs gonna be storing this server side anyway at some point soon + public Dictionary? GetSelectedBeatmapDifficultiesRequirements() + { + if (!SelectedBeatmap!.LevelId.StartsWith("custom_level_")) + { + return null; + } + + var selectedLevelHash = SelectedBeatmap!.LevelId.Substring(13); + foreach (var player in _playerRegistry.Players) { - if(SelectedBeatmap!.LevelId == player.MapHash) + _logger.Verbose($"GetDiffRequirements checking: SelectedHash {selectedLevelHash} Player MapHash {player.MapHash}"); + if(selectedLevelHash == player.MapHash) { - return player.BeatmapDifficulties; + return player.BeatmapDifficultiesRequirements; } } - return Array.Empty(); + _logger.Error($"Failed to find matching requirements searched SelectedHash {selectedLevelHash}"); + return null; } // Sets countdown and beatmap how the client would expect it to - // If you want to cancel the countdown use CancelCountdown(), Not SetCountdown as CancelCountdown() ALSO informs the clients it has been canceled, whereas SetCountdown will now - private void SetCountdown(CountdownState countdownState, float countdown = 0) + // If you want to cancel the countdown use CancelCountdown(), Not SetCountdown as CancelCountdown() ALSO informs the clients it has been canceled, whereas SetCountdown will not + private void SetCountdown(CountdownState countdownState, long countdown = 0) { CountDownState = countdownState; switch (CountDownState) @@ -294,16 +353,16 @@ private void SetCountdown(CountdownState countdownState, float countdown = 0) break; case CountdownState.CountingDown: if (countdown == 0) - countdown = 30f; + countdown = 30000L; CountdownEndTime = _instance.RunTime + countdown; _packetDispatcher.SendToNearbyPlayers(new SetCountdownEndTimePacket { CountdownTime = CountdownEndTime - }, DeliveryMethod.ReliableOrdered); + }, IgnoranceChannelTypes.Reliable); break; case CountdownState.StartBeatmapCountdown: if (countdown == 0) - countdown = 5f; + countdown = 5000L; CountdownEndTime = _instance.RunTime + countdown; StartBeatmapPacket(); break; @@ -311,11 +370,13 @@ private void SetCountdown(CountdownState countdownState, float countdown = 0) StartBeatmapPacket(); break; } + _logger.Debug($"CountdownEndTime final set to '{CountdownEndTime}' CountdownState '{CountDownState}' countdown is '{countdown}' RunTime is '{_instance.RunTime}'"); } //Checks the lobby settings and sends the player the correct beatmap private void StartBeatmapPacket() { + _logger.Debug("Sending start level packet"); if (!_configuration.AllowPerPlayerModifiers && !_configuration.AllowPerPlayerDifficulties) { _packetDispatcher.SendToNearbyPlayers(new StartLevelPacket @@ -323,21 +384,28 @@ private void StartBeatmapPacket() Beatmap = SelectedBeatmap!, Modifiers = SelectedModifiers, StartTime = CountdownEndTime - }, DeliveryMethod.ReliableOrdered); + }, IgnoranceChannelTypes.Reliable); return; } - BeatmapDifficulty[] diff = GetSelectedBeatmapDifficulties(); + var diff = GetSelectedBeatmapDifficultiesRequirements(); BeatmapIdentifier bm = SelectedBeatmap!; foreach (var player in _playerRegistry.Players) { - if (_configuration.AllowPerPlayerDifficulties && player.BeatmapIdentifier != null && diff.Contains(player.BeatmapIdentifier.Difficulty)) + // Check that PPD is enabled and that the difficulty the player has selected + // exists on the level or if the player has the same map selected + if (_configuration.AllowPerPlayerDifficulties && player.BeatmapIdentifier != null && (diff != null && diff.ContainsKey((uint)player.BeatmapIdentifier.Difficulty) || SelectedBeatmap.LevelId == player.BeatmapIdentifier.LevelId)) bm.Difficulty = player.BeatmapIdentifier.Difficulty; + _logger.Debug($"Start level settings for player '{player.UserName}|{player.HashedUserId}'" + + $"(LevelId={bm.LevelId}, Difficulty={bm.Difficulty} Modifiers={(_configuration.AllowPerPlayerModifiers ? player.Modifiers : SelectedModifiers)}) " + + $"Checks: (AllowPerPlayerDifficulties={_configuration.AllowPerPlayerDifficulties}, " + + $"diff == null? {diff == null}, " + + $"diff.ContainsKey={(player.BeatmapIdentifier?.Difficulty != null && diff != null ? diff.ContainsKey((uint)player.BeatmapIdentifier.Difficulty) : "Player beatmap null or diff null")})"); _packetDispatcher.SendToPlayer(player, new StartLevelPacket { - Beatmap = bm!, + Beatmap = bm!, Modifiers = _configuration.AllowPerPlayerModifiers ? player.Modifiers : SelectedModifiers, StartTime = CountdownEndTime - }, DeliveryMethod.ReliableOrdered); + }, IgnoranceChannelTypes.Reliable); } } @@ -346,15 +414,15 @@ private void CancelCountdown() switch (CountDownState) { case CountdownState.CountingDown or CountdownState.NotCountingDown: - _packetDispatcher.SendToNearbyPlayers(new CancelCountdownPacket(), DeliveryMethod.ReliableOrdered); + _packetDispatcher.SendToNearbyPlayers(new CancelCountdownPacket(), IgnoranceChannelTypes.Reliable); break; case CountdownState.StartBeatmapCountdown or CountdownState.WaitingForEntitlement: foreach (IPlayer player in _playerRegistry.Players) //This stays because players dont send they are un-ready after the level is canceled causing bad client behaviour { player.IsReady = false; } - _packetDispatcher.SendToNearbyPlayers(new CancelLevelStartPacket(), DeliveryMethod.ReliableOrdered); - _packetDispatcher.SendToNearbyPlayers(new SetIsReadyPacket() { IsReady = false }, DeliveryMethod.ReliableOrdered); + _packetDispatcher.SendToNearbyPlayers(new CancelLevelStartPacket(), IgnoranceChannelTypes.Reliable); + _packetDispatcher.SendToNearbyPlayers(new SetIsReadyPacket() { IsReady = false }, IgnoranceChannelTypes.Reliable); break; default: _logger.Warning("Canceling countdown when there is no countdown to cancel"); @@ -363,24 +431,33 @@ private void CancelCountdown() SetCountdown(CountdownState.NotCountingDown); } + private bool PlayerMapCheck(IPlayer p) + { + if(p.BeatmapIdentifier == null) return false; + //If no map hash then treat as base game map for compat reasons and while waiting for a packet + var Passed = string.IsNullOrEmpty(p.MapHash); + //If not passed, then we have difficulties, and if we have the diff we are looking for, then we can check it for requirements. + if (!Passed && p.BeatmapDifficultiesRequirements.TryGetValue((uint)p.BeatmapIdentifier!.Difficulty, out string[]? Requirements)) + Passed = !(!_configuration.AllowChroma && Requirements.Contains("Chroma")) || !(!_configuration.AllowMappingExtensions && Requirements.Contains("Mapping Extensions")) || !(!_configuration.AllowNoodleExtensions && Requirements.Contains("Noodle Extensions")); + return Passed; + } + private BeatmapIdentifier? GetSelectedBeatmap() { - switch(_configuration.SongSelectionMode) + switch(_configuration.GameplayServerConfiguration.SongSelectionMode) { - case SongSelectionMode.ServerOwnerPicks: + case SongSelectionMode.ManagerPicks: { - if(_playerRegistry.TryGetPlayer(_configuration.ServerOwnerId, out var p)) - if(p.BeatmapIdentifier != null) - { - bool passed = ((!(p.Chroma && !_configuration.AllowChroma) || !(p.MappingExtensions && !_configuration.AllowMappingExtensions) || !(p.NoodleExtensions && !_configuration.AllowNoodleExtensions)) && p.MapHash == p.BeatmapIdentifier!.LevelId) || p.MapHash != p.BeatmapIdentifier!.LevelId; - if (passed) - return p.BeatmapIdentifier; - } + if (_playerRegistry.TryGetPlayer(_configuration.ServerOwnerId, out var p) && p.BeatmapIdentifier != null) + { + if (PlayerMapCheck(p)) + return p.BeatmapIdentifier; + } return null; } case SongSelectionMode.Vote: Dictionary voteDictionary = new(); - foreach (IPlayer player in _playerRegistry.Players.Where(p => p.BeatmapIdentifier != null&& (((!(p.Chroma && !_configuration.AllowChroma) || !(p.MappingExtensions && !_configuration.AllowMappingExtensions) || !(p.NoodleExtensions && !_configuration.AllowNoodleExtensions)) && p.MapHash == p.BeatmapIdentifier!.LevelId) || p.MapHash != p.BeatmapIdentifier!.LevelId))) + foreach (IPlayer player in _playerRegistry.Players.Where(p => PlayerMapCheck(p))) { if (voteDictionary.ContainsKey(player.BeatmapIdentifier!)) voteDictionary[player.BeatmapIdentifier!]++; @@ -391,26 +468,44 @@ private void CancelCountdown() { return null; } - return voteDictionary.OrderByDescending(n => n.Value).First().Key; - case SongSelectionMode.RandomPlayerPicks: //TODO, Make this pick a random beatsaver map or something like that - return new BeatmapIdentifier() + BeatmapIdentifier? Selected = null; + int Votes = 0; + foreach (var item in voteDictionary) { - Characteristic = "Standard", - Difficulty = BeatmapDifficulty.ExpertPlus, - LevelId = "custom_level_103d39b43966277c5e4167ab086f404e0943891f" - }; + if (item.Value > Votes) + { + Selected = item.Key; + Votes = item.Value; + } + } + return Selected; + case SongSelectionMode.RandomPlayerPicks: + if (CountDownState == CountdownState.CountingDown || CountDownState == CountdownState.NotCountingDown) + { + Random rand = new(); + int selectedPlayer = rand.Next(_playerRegistry.GetPlayerCount() - 1); + RandomlyPickedPlayer = _playerRegistry.Players[selectedPlayer].HashedUserId; + return PlayerMapCheck(_playerRegistry.Players[selectedPlayer]) ? _playerRegistry.Players[selectedPlayer].BeatmapIdentifier : null; + } + return SelectedBeatmap; + case SongSelectionMode.ServerPicks: return SelectedBeatmap!; }; return null; } + string RandomlyPickedPlayer = string.Empty; + private GameplayModifiers GetSelectedModifiers() { - switch(_configuration.SongSelectionMode) + switch(_configuration.GameplayServerConfiguration.SongSelectionMode) { - case SongSelectionMode.ServerOwnerPicks: return _playerRegistry.GetPlayer(_configuration.ServerOwnerId).Modifiers; - case SongSelectionMode.Vote or SongSelectionMode.RandomPlayerPicks: + case SongSelectionMode.ManagerPicks: + if(_playerRegistry.TryGetPlayer(_configuration.ServerOwnerId, out var ServerOwner)) + return ServerOwner.Modifiers; + return EmptyModifiers; + case SongSelectionMode.Vote: GameplayModifiers gameplayModifiers = new(); Dictionary voteDictionary = new(); foreach (IPlayer player in _playerRegistry.Players.Where(p => p.Modifiers != null)) @@ -420,14 +515,53 @@ private GameplayModifiers GetSelectedModifiers() else voteDictionary.Add(player.Modifiers!, 1); } - if (voteDictionary.Any()) - gameplayModifiers = voteDictionary.OrderByDescending(n => n.Value).First().Key; - gameplayModifiers.NoFailOn0Energy = true; + if (!voteDictionary.Any()) + { + int Votes = 0; + foreach (var item in voteDictionary) + { + if (item.Value > Votes) + { + gameplayModifiers = item.Key; + Votes = item.Value; + } + } + } + if(_configuration.ApplyNoFailModifier) + gameplayModifiers.NoFailOn0Energy = true; + return gameplayModifiers; + case SongSelectionMode.RandomPlayerPicks: + if (RandomlyPickedPlayer == string.Empty) + { + GameplayModifiers Modifiers = new() + { + NoFailOn0Energy = _configuration.ApplyNoFailModifier + }; + return Modifiers; + } + gameplayModifiers = new(); + if (_playerRegistry.TryGetPlayer(RandomlyPickedPlayer, out var Randomplayer)) + gameplayModifiers = Randomplayer.Modifiers; + if (_configuration.ApplyNoFailModifier) + gameplayModifiers.NoFailOn0Energy = true; return gameplayModifiers; case SongSelectionMode.ServerPicks: return SelectedModifiers; }; return new GameplayModifiers(); } + + public CannotStartGameReason GetCannotStartGameReason(IPlayer player, bool DoesEveryoneOwnBeatmap) + { + if (player.IsServerOwner && player.BeatmapIdentifier == null) + return CannotStartGameReason.NoSongSelected; + if (!AllPlayersAreInLobby) + return CannotStartGameReason.AllPlayersNotInLobby; + if (AllPlayersNotWantToPlayNextLevel) + return CannotStartGameReason.AllPlayersSpectating; + if (SelectedBeatmap != null && ((_configuration.ForceStartMode && player.GetEntitlement(SelectedBeatmap.LevelId) == EntitlementStatus.NotOwned) || (!_configuration.ForceStartMode && !DoesEveryoneOwnBeatmap))) + return CannotStartGameReason.DoNotOwnSong; + return CannotStartGameReason.None; + } } } diff --git a/BeatTogether.DedicatedServer.Kernel/PacketDispatcher.cs b/BeatTogether.DedicatedServer.Kernel/PacketDispatcher.cs index d7d36723..5f2d3c30 100644 --- a/BeatTogether.DedicatedServer.Kernel/PacketDispatcher.cs +++ b/BeatTogether.DedicatedServer.Kernel/PacketDispatcher.cs @@ -1,18 +1,14 @@ using BeatTogether.DedicatedServer.Kernel.Abstractions; using BeatTogether.Extensions; -using BeatTogether.LiteNetLib; -using BeatTogether.LiteNetLib.Abstractions; -using BeatTogether.LiteNetLib.Configuration; -using BeatTogether.LiteNetLib.Dispatchers; -using BeatTogether.LiteNetLib.Enums; -using Krypton.Buffers; +using BeatTogether.DedicatedServer.Ignorance.IgnoranceCore; +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.DedicatedServer.Messaging.Util; using Serilog; -using System; -using System.Net; +using BeatTogether.DedicatedServer.Kernel.ENet; namespace BeatTogether.DedicatedServer.Kernel { - public sealed class PacketDispatcher : ConnectedMessageDispatcher, IPacketDispatcher + public sealed class PacketDispatcher : IPacketDispatcher { public const byte LocalConnectionId = 0; public const byte ServerId = 0; @@ -20,206 +16,256 @@ public sealed class PacketDispatcher : ConnectedMessageDispatcher, IPacketDispat private readonly IPacketRegistry _packetRegistry; private readonly IPlayerRegistry _playerRegistry; + private readonly ENetServer _serverInstance; private readonly ILogger _logger = Log.ForContext(); public PacketDispatcher( IPacketRegistry packetRegistry, IPlayerRegistry playerRegistry, - LiteNetConfiguration configuration, - LiteNetServer server) - : base ( - configuration, - server) + ENetServer serverInstance) { _packetRegistry = packetRegistry; _playerRegistry = playerRegistry; + _serverInstance = serverInstance; } - public void SendToNearbyPlayers(INetSerializable packet, DeliveryMethod deliveryMethod) + private void SendInternal(IPlayer player, ref SpanBuffer writer, IgnoranceChannelTypes deliveryMethod) + { + _logger.Verbose($"Sending packet (SenderId={ServerId}) to player {player.ConnectionId} with UserId {player.HashedUserId}"); + _serverInstance.Send(player, writer.Data, deliveryMethod); + } + + #region Sends + public void SendToNearbyPlayers(INetSerializable packet, IgnoranceChannelTypes deliveryMethod) { _logger.Debug( $"Sending packet of type '{packet.GetType().Name}' " + $"(SenderId={ServerId})" ); - var writer = new SpanBufferWriter(stackalloc byte[412]); + var writer = new SpanBuffer(stackalloc byte[412]); writer.WriteRoutingHeader(ServerId, LocalConnectionId); WriteOne(ref writer, packet); - _logger.Verbose("Packet: " + packet.GetType().Name + " Was entered into the spanbuffer correctly, now sending once to each player"); foreach (var player in _playerRegistry.Players) - Send(player.Endpoint, writer.Data, deliveryMethod); + SendInternal(player, ref writer, deliveryMethod); + } - public void SendToNearbyPlayers(INetSerializable[] packets, DeliveryMethod deliveryMethod) + public void SendToNearbyPlayers(INetSerializable[] packets, IgnoranceChannelTypes deliveryMethod) { _logger.Debug( $"Sending MultiPacket " + $"(SenderId={ServerId})" ); - var writer = new SpanBufferWriter(stackalloc byte[412]); + var writer = new SpanBuffer(stackalloc byte[1024]); writer.WriteRoutingHeader(ServerId, LocalConnectionId); WriteMany(ref writer, packets); - _logger.Verbose("Packets were entered into the spanbuffer correctly, now sending once to each player"); foreach (var player in _playerRegistry.Players) - Send(player.Endpoint, writer.Data, deliveryMethod); + SendInternal(player, ref writer, deliveryMethod); } - - public void SendExcludingPlayer(IPlayer excludedPlayer, INetSerializable packet, DeliveryMethod deliveryMethod) + public void SendToPlayers(IPlayer[] players, INetSerializable packet, IgnoranceChannelTypes deliveryMethod) { _logger.Debug( - $"Sending packet of type '{packet.GetType().Name}' " + - $"(ExcludedId={excludedPlayer.ConnectionId})" + $"Sending packet of type '{packet.GetType().Name}' to specific players" + + $"(SenderId={ServerId})" ); - var writer = new SpanBufferWriter(stackalloc byte[412]); + var writer = new SpanBuffer(stackalloc byte[412]); writer.WriteRoutingHeader(ServerId, LocalConnectionId); WriteOne(ref writer, packet); - - foreach (IPlayer player in _playerRegistry.Players) - if (player.ConnectionId != excludedPlayer.ConnectionId) - Send(player.Endpoint, writer.Data, deliveryMethod); + foreach (var player in players) + SendInternal(player, ref writer, deliveryMethod); + } - public void SendExcludingPlayer(IPlayer excludedPlayer, INetSerializable[] packets, DeliveryMethod deliveryMethod) + public void SendToPlayers(IPlayer[] players, INetSerializable[] packets, IgnoranceChannelTypes deliveryMethod) { _logger.Debug( - $"Sending MultiPacket " + - $"(ExcludedId={excludedPlayer.ConnectionId})" + $"Sending MultiPacket to specific players" + + $"(SenderId={ServerId})" ); - var writer = new SpanBufferWriter(stackalloc byte[412]); + var writer = new SpanBuffer(stackalloc byte[1024]); writer.WriteRoutingHeader(ServerId, LocalConnectionId); WriteMany(ref writer, packets); - - foreach (IPlayer player in _playerRegistry.Players) - if (player.ConnectionId != excludedPlayer.ConnectionId) - Send(player.Endpoint, writer.Data, deliveryMethod); + foreach (var player in players) + SendInternal(player, ref writer, deliveryMethod); } - public void SendToEndpoint(EndPoint endpoint, INetSerializable packet, DeliveryMethod deliveryMethod) + public void SendExcludingPlayer(IPlayer excludedPlayer, INetSerializable packet, IgnoranceChannelTypes deliveryMethod) { _logger.Debug( $"Sending packet of type '{packet.GetType().Name}' " + - $"(To endpoint ={endpoint})" + $"(ExcludedId={excludedPlayer.ConnectionId})" ); - var writer = new SpanBufferWriter(stackalloc byte[412]); + var writer = new SpanBuffer(stackalloc byte[412]); writer.WriteRoutingHeader(ServerId, LocalConnectionId); WriteOne(ref writer, packet); - Send(endpoint, writer.Data, deliveryMethod); + foreach (IPlayer player in _playerRegistry.Players) + if (player.ConnectionId != excludedPlayer.ConnectionId) + SendInternal(player, ref writer, deliveryMethod); } - public void SendToEndpoint(EndPoint endpoint, INetSerializable[] packets, DeliveryMethod deliveryMethod) + + public void SendExcludingPlayer(IPlayer excludedPlayer, INetSerializable[] packets, IgnoranceChannelTypes deliveryMethod) { _logger.Debug( $"Sending MultiPacket " + - $"(To endpoint ={endpoint})" + $"(ExcludedId={excludedPlayer.ConnectionId})" ); + if (_logger.IsEnabled(Serilog.Events.LogEventLevel.Verbose)) + for (int i = 0; i < packets.Length; i++) + { + _logger.Verbose( + $"Packet {i} is of type '{packets[i].GetType().Name}' " + ); + } - var writer = new SpanBufferWriter(stackalloc byte[412]); + var writer = new SpanBuffer(stackalloc byte[1024]); writer.WriteRoutingHeader(ServerId, LocalConnectionId); WriteMany(ref writer, packets); - Send(endpoint, writer.Data, deliveryMethod); + foreach (IPlayer player in _playerRegistry.Players) + if (player.ConnectionId != excludedPlayer.ConnectionId) + SendInternal(player, ref writer, deliveryMethod); + } + + public void RouteExcludingPlayer(IPlayer excludedPlayer, ref SpanBuffer writer, IgnoranceChannelTypes deliveryMethod) + { + _logger.Debug( + $"Sending routed packet " + + $"(ExcludedId={excludedPlayer.ConnectionId})" + ); + + foreach (IPlayer player in _playerRegistry.Players) + if (player.ConnectionId != excludedPlayer.ConnectionId) + SendInternal(player, ref writer, deliveryMethod); } - public void SendFromPlayer(IPlayer fromPlayer, INetSerializable packet, DeliveryMethod deliveryMethod) + + public void SendFromPlayer(IPlayer fromPlayer, INetSerializable packet, IgnoranceChannelTypes deliveryMethod) { _logger.Debug( $"Sending packet of type '{packet.GetType().Name}' " + $"(SenderId={fromPlayer.ConnectionId})" ); - var writer = new SpanBufferWriter(stackalloc byte[412]); + var writer = new SpanBuffer(stackalloc byte[412]); writer.WriteRoutingHeader(fromPlayer.ConnectionId, LocalConnectionId); WriteOne(ref writer, packet); foreach (var player in _playerRegistry.Players) - Send(player.Endpoint, writer.Data, deliveryMethod); + SendInternal(player, ref writer, deliveryMethod); } - public void SendFromPlayer(IPlayer fromPlayer, INetSerializable[] packets, DeliveryMethod deliveryMethod) + public void SendFromPlayer(IPlayer fromPlayer, INetSerializable[] packets, IgnoranceChannelTypes deliveryMethod) { _logger.Debug( $"Sending MultiPacket " + $"(SenderId={fromPlayer.ConnectionId})" ); - var writer = new SpanBufferWriter(stackalloc byte[412]); + var writer = new SpanBuffer(stackalloc byte[1024]); writer.WriteRoutingHeader(fromPlayer.ConnectionId, LocalConnectionId); WriteMany(ref writer, packets); foreach (var player in _playerRegistry.Players) - Send(player.Endpoint, writer.Data, deliveryMethod); + SendInternal(player, ref writer, deliveryMethod); } - public void SendFromPlayerToPlayer(IPlayer fromPlayer, IPlayer toPlayer, INetSerializable packet, DeliveryMethod deliveryMethod) + public void SendFromPlayerToPlayer(IPlayer fromPlayer, IPlayer toPlayer, INetSerializable packet, IgnoranceChannelTypes deliveryMethod) { _logger.Debug( $"Sending packet of type '{packet.GetType().Name}' " + $"(SenderId={fromPlayer.ConnectionId}, ReceiverId={LocalConnectionId})." ); - var writer = new SpanBufferWriter(stackalloc byte[412]); + var writer = new SpanBuffer(stackalloc byte[412]); writer.WriteRoutingHeader(fromPlayer.ConnectionId, LocalConnectionId); WriteOne(ref writer, packet); - Send(toPlayer.Endpoint, writer.Data, deliveryMethod); + SendInternal(toPlayer, ref writer, deliveryMethod); } - public void SendFromPlayerToPlayer(IPlayer fromPlayer, IPlayer toPlayer, INetSerializable[] packets, DeliveryMethod deliveryMethod) + public void SendFromPlayerToPlayer(IPlayer fromPlayer, IPlayer toPlayer, INetSerializable[] packets, IgnoranceChannelTypes deliveryMethod) { _logger.Debug( $"Sending MultiPacket" + $"(SenderId={fromPlayer.ConnectionId}, ReceiverId={LocalConnectionId})." ); - var writer = new SpanBufferWriter(stackalloc byte[412]); + var writer = new SpanBuffer(stackalloc byte[1024]); writer.WriteRoutingHeader(fromPlayer.ConnectionId, LocalConnectionId); WriteMany(ref writer, packets); - Send(toPlayer.Endpoint, writer.Data, deliveryMethod); + SendInternal(toPlayer, ref writer, deliveryMethod); } - public void SendToPlayer(IPlayer player, INetSerializable packet, DeliveryMethod deliveryMethod) + public void RouteFromPlayerToPlayer(IPlayer fromPlayer, IPlayer toPlayer, ref SpanBuffer writer, IgnoranceChannelTypes deliveryMethod) + { + _logger.Debug( + $"Sending routed packet " + + $"(SenderId={fromPlayer.ConnectionId}, ReceiverId={LocalConnectionId})." + ); + + SendInternal(toPlayer, ref writer, deliveryMethod); + } + + public void SendToPlayer(IPlayer player, INetSerializable packet, IgnoranceChannelTypes deliveryMethod) { _logger.Debug( $"Sending packet of type '{packet.GetType().Name}' " + $"(SenderId={ServerId}, ReceiverId={LocalConnectionId})." ); - var writer = new SpanBufferWriter(stackalloc byte[412]); + var writer = new SpanBuffer(stackalloc byte[412]); writer.WriteRoutingHeader(ServerId, LocalConnectionId); WriteOne(ref writer, packet); - Send(player.Endpoint, writer, deliveryMethod); + SendInternal(player, ref writer, deliveryMethod); } - - public void SendToPlayer(IPlayer player, INetSerializable[] packets, DeliveryMethod deliveryMethod) + public void SendToPlayer(IPlayer player, INetSerializable[] packets, IgnoranceChannelTypes deliveryMethod) { _logger.Debug( $"Sending MultiPacket " + $"(SenderId={ServerId}, ReceiverId={LocalConnectionId})." ); - var writer = new SpanBufferWriter(stackalloc byte[412]); + var writer = new SpanBuffer(stackalloc byte[1024]); writer.WriteRoutingHeader(ServerId, LocalConnectionId); WriteMany(ref writer, packets); - Send(player.Endpoint, writer, deliveryMethod); + SendInternal(player, ref writer, deliveryMethod); } + #endregion - public void WriteOne(ref SpanBufferWriter writer, INetSerializable packet) + #region Writers + public void WriteOne(ref SpanBuffer writer, INetSerializable packet) { var type = packet.GetType(); - if (!_packetRegistry.TryGetPacketIds(type, out var packetIds)) - throw new Exception($"Failed to retrieve identifier for packet of type '{type.Name}'"); - var packetWriter = new SpanBufferWriter(stackalloc byte[412]); - foreach (byte packetId in packetIds) - packetWriter.WriteUInt8(packetId); + var packetWriter = new SpanBuffer(stackalloc byte[412]); + + if (_packetRegistry.TryGetPacketIds(type, out var packetIds)) + { + foreach (byte packetId in packetIds) + packetWriter.WriteUInt8(packetId); + } + else + { + + packetWriter.WriteUInt8(7); + packetWriter.WriteUInt8(100); + packetWriter.WriteString(type.Name); + //Presume it is a mpcore packet and use the mpcore packet ID, would thow an exeption here if not + //throw new Exception($"Failed to retrieve identifier for packet of type '{type.Name}'"); + //this should be fine as its only for packets sent from the server + } packet.WriteTo(ref packetWriter); writer.WriteVarUInt((uint)packetWriter.Size); writer.WriteBytes(packetWriter.Data.ToArray()); } - - public void WriteMany(ref SpanBufferWriter writer, INetSerializable[] packets) + public void WriteMany(ref SpanBuffer writer, INetSerializable[] packets) { for (int i = 0; i < packets.Length; i++) WriteOne(ref writer, packets[i]); } + + #endregion + } } diff --git a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/GameplayRpc/LevelFinishedPacketHandler.cs b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/GameplayRpc/LevelFinishedPacketHandler.cs index ced5e9af..82cb94ec 100644 --- a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/GameplayRpc/LevelFinishedPacketHandler.cs +++ b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/GameplayRpc/LevelFinishedPacketHandler.cs @@ -2,14 +2,13 @@ using BeatTogether.DedicatedServer.Kernel.Managers.Abstractions; using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.GameplayRpc; using Serilog; -using System.Threading.Tasks; namespace BeatTogether.DedicatedServer.Kernel.PacketHandlers.MultiplayerSession.GameplayRpc { public sealed class LevelFinishedPacketHandler : BasePacketHandler { - private IGameplayManager _gameplayManager; - private ILogger _logger = Log.ForContext(); + private readonly IGameplayManager _gameplayManager; + private readonly ILogger _logger = Log.ForContext(); public LevelFinishedPacketHandler( IGameplayManager gameplayManager) @@ -17,15 +16,16 @@ public LevelFinishedPacketHandler( _gameplayManager = gameplayManager; } - public override Task Handle(IPlayer sender, LevelFinishedPacket packet) + public override void Handle(IPlayer sender, LevelFinishedPacket packet) { _logger.Debug( $"Handling packet of type '{nameof(LevelFinishedPacket)}' " + - $"(SenderId={sender.ConnectionId})." + $"(SenderId={sender.ConnectionId}, HasValue0={packet.HasValue0}, HasAnyResult={packet.Results.HasAnyResult()}, " + + $"ModifiedScore={(packet.HasValue0 && packet.Results.HasAnyResult() ? packet.Results.LevelCompletionResults.ModifiedScore : "NoValue/NoResults" )}, " + + $"MultipliedScore={(packet.HasValue0 && packet.Results.HasAnyResult() ? packet.Results.LevelCompletionResults.MultipliedScore : "NoValue/NoResults")})." ); _gameplayManager.HandleLevelFinished(sender, packet); - return Task.CompletedTask; } } } diff --git a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/GameplayRpc/RequestReturnToMenuPacketHandler.cs b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/GameplayRpc/RequestReturnToMenuPacketHandler.cs index fcc52171..abafd1cd 100644 --- a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/GameplayRpc/RequestReturnToMenuPacketHandler.cs +++ b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/GameplayRpc/RequestReturnToMenuPacketHandler.cs @@ -2,7 +2,6 @@ using BeatTogether.DedicatedServer.Kernel.Managers.Abstractions; using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.GameplayRpc; using Serilog; -using System.Threading.Tasks; namespace BeatTogether.DedicatedServer.Kernel.PacketHandlers.MultiplayerSession.GameplayRpc { @@ -17,7 +16,7 @@ public RequestReturnToMenuPacketHandler( _gameplayManager = gameplayManager; } - public override Task Handle(IPlayer sender, RequestReturnToMenuPacket packet) + public override void Handle(IPlayer sender, RequestReturnToMenuPacket packet) { _logger.Debug( $"Handling packet of type '{nameof(RequestReturnToMenuPacket)}' " + @@ -26,8 +25,6 @@ public override Task Handle(IPlayer sender, RequestReturnToMenuPacket packet) if (sender.IsServerOwner) _gameplayManager.SignalRequestReturnToMenu(); - - return Task.CompletedTask; } } } diff --git a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/GameplayRpc/SetGameplaySceneReadyPacketHandler.cs b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/GameplayRpc/SetGameplaySceneReadyPacketHandler.cs index 509ba7df..8e1f23bb 100644 --- a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/GameplayRpc/SetGameplaySceneReadyPacketHandler.cs +++ b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/GameplayRpc/SetGameplaySceneReadyPacketHandler.cs @@ -2,14 +2,13 @@ using BeatTogether.DedicatedServer.Kernel.Managers.Abstractions; using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.GameplayRpc; using Serilog; -using System.Threading.Tasks; namespace BeatTogether.DedicatedServer.Kernel.PacketHandlers.MultiplayerSession.GameplayRpc { public sealed class SetGameplaySceneReadyPacketHandler : BasePacketHandler { - private IGameplayManager _gameplayManager; - private ILogger _logger = Log.ForContext(); + private readonly IGameplayManager _gameplayManager; + private readonly ILogger _logger = Log.ForContext(); public SetGameplaySceneReadyPacketHandler( IGameplayManager gameplayManager) @@ -17,7 +16,7 @@ public SetGameplaySceneReadyPacketHandler( _gameplayManager = gameplayManager; } - public override Task Handle(IPlayer sender, SetGameplaySceneReadyPacket packet) + public override void Handle(IPlayer sender, SetGameplaySceneReadyPacket packet) { _logger.Debug( $"Handling packet of type '{nameof(SetGameplaySceneReadyPacket)}' " + @@ -25,7 +24,6 @@ public override Task Handle(IPlayer sender, SetGameplaySceneReadyPacket packet) ); _gameplayManager.HandleGameSceneLoaded(sender, packet); - return Task.CompletedTask; } } } diff --git a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/GameplayRpc/SetGameplaySongReadyPacketHandler.cs b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/GameplayRpc/SetGameplaySongReadyPacketHandler.cs index 2e4329d1..4f9ff254 100644 --- a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/GameplayRpc/SetGameplaySongReadyPacketHandler.cs +++ b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/GameplayRpc/SetGameplaySongReadyPacketHandler.cs @@ -2,14 +2,13 @@ using BeatTogether.DedicatedServer.Kernel.Managers.Abstractions; using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.GameplayRpc; using Serilog; -using System.Threading.Tasks; namespace BeatTogether.DedicatedServer.Kernel.PacketHandlers.MultiplayerSession.GameplayRpc { public sealed class SetGameplaySongReadyPacketHandler : BasePacketHandler { - private IGameplayManager _gameplayManager; - private ILogger _logger = Log.ForContext(); + private readonly IGameplayManager _gameplayManager; + private readonly ILogger _logger = Log.ForContext(); public SetGameplaySongReadyPacketHandler( IGameplayManager gameplayManager) @@ -17,7 +16,7 @@ public SetGameplaySongReadyPacketHandler( _gameplayManager = gameplayManager; } - public override Task Handle(IPlayer sender, SetGameplaySongReadyPacket packet) + public override void Handle(IPlayer sender, SetGameplaySongReadyPacket packet) { _logger.Debug( $"Handling packet of type '{nameof(SetGameplaySongReadyPacket)}' " + @@ -25,7 +24,6 @@ public override Task Handle(IPlayer sender, SetGameplaySongReadyPacket packet) ); _gameplayManager.HandleGameSongLoaded(sender); - return Task.CompletedTask; } } } diff --git a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MPChat/MpcCapabilitiesPacketHandler.cs b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MPChat/MpcCapabilitiesPacketHandler.cs new file mode 100644 index 00000000..c18d46ad --- /dev/null +++ b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MPChat/MpcCapabilitiesPacketHandler.cs @@ -0,0 +1,42 @@ +using BeatTogether.DedicatedServer.Ignorance.IgnoranceCore; +using BeatTogether.DedicatedServer.Kernel.Abstractions; +using BeatTogether.DedicatedServer.Kernel.Configuration; +using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MPChatPackets; +using Serilog; + +namespace BeatTogether.DedicatedServer.Kernel.PacketHandlers.MultiplayerSession.MPChat +{ + public class MpcCapabilitiesPacketHandler : BasePacketHandler + { + private readonly IPacketDispatcher _packetDispatcher; + private readonly InstanceConfiguration _instanceConfiguration; + //private readonly ILogger _logger = Log.ForContext(); + public MpcCapabilitiesPacketHandler(IPacketDispatcher packetDispatcher, + InstanceConfiguration instanceConfiguration) + { + _packetDispatcher = packetDispatcher; + _instanceConfiguration = instanceConfiguration; + } + + public override void Handle(IPlayer sender, MpcCapabilitiesPacket packet) + { + bool FirstJoin = !sender.CanTextChat && packet.CanTextChat; + sender.CanReceiveVoiceChat = packet.CanReceiveVoiceChat; + sender.CanTransmitVoiceChat = packet.CanTransmitVoiceChat; + sender.CanTextChat = packet.CanTextChat; + if (FirstJoin) + { + _packetDispatcher.SendToPlayer(sender, new MpcTextChatPacket + { + Text = "Welcome to " + _instanceConfiguration.ServerName + " Type /help to get a list of commands!" + }, IgnoranceChannelTypes.Reliable); + if (_instanceConfiguration.WelcomeMessage != string.Empty) + _packetDispatcher.SendToPlayer(sender, new MpcTextChatPacket + { + Text = _instanceConfiguration.WelcomeMessage + }, IgnoranceChannelTypes.Reliable); + + } + } + } +} diff --git a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MPChat/MpcTextChatPacketHandler.cs b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MPChat/MpcTextChatPacketHandler.cs new file mode 100644 index 00000000..e4a19708 --- /dev/null +++ b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MPChat/MpcTextChatPacketHandler.cs @@ -0,0 +1,52 @@ +using BeatTogether.DedicatedServer.Ignorance.IgnoranceCore; +using BeatTogether.DedicatedServer.Kernel.Abstractions; +using BeatTogether.DedicatedServer.Kernel.CommandHandlers; +using BeatTogether.DedicatedServer.Kernel.Configuration; +using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MPChatPackets; +using Serilog; +using System; + +namespace BeatTogether.DedicatedServer.Kernel.PacketHandlers.MultiplayerSession.MPChat +{ + public class MpcTextChatPacketHandler : BasePacketHandler + { + private readonly IPacketDispatcher _packetDispatcher; + private readonly InstanceConfiguration _instanceConfiguration; + private readonly ITextCommandRepository _CommandRepository; + private readonly IServiceProvider _serviceProvider; + //private readonly ILogger _logger = Log.ForContext(); + + + public MpcTextChatPacketHandler(IPacketDispatcher packetDispatcher, + ITextCommandRepository textCommandRepository, + InstanceConfiguration instanceConfiguration, + IServiceProvider serviceProvider) + { + _packetDispatcher = packetDispatcher; + _instanceConfiguration = instanceConfiguration; + _CommandRepository = textCommandRepository; + _serviceProvider = serviceProvider; + } + + public override void Handle(IPlayer sender, MpcTextChatPacket packet) + { + if (packet.Text.Length < _instanceConfiguration.MaxLengthCommand && packet.Text.StartsWith("/")){ + string[] CommandValues = packet.Text[1..].Split(' '); + if (_CommandRepository.GetCommand(CommandValues, sender.GetAccessLevel(), out var TextCommand)) + { + var commandType = TextCommand.GetType(); + var packetHandlerType = typeof(ICommandHandler<>) + .MakeGenericType(commandType); + var Command = _serviceProvider.GetService(packetHandlerType); + if (Command != null) + ((ICommandHandler)Command).Handle(sender, TextCommand); + return; + } + _packetDispatcher.SendToPlayer(sender, new MpcTextChatPacket + { + Text = "Command not found or too long" + }, IgnoranceChannelTypes.Reliable); + } + } + } +} diff --git a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/ClearRecommendedBeatmapPacketHandler.cs b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/ClearRecommendedBeatmapPacketHandler.cs index 53cf03bb..1c65d73c 100644 --- a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/ClearRecommendedBeatmapPacketHandler.cs +++ b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/ClearRecommendedBeatmapPacketHandler.cs @@ -1,10 +1,9 @@ -using BeatTogether.DedicatedServer.Kernel.Abstractions; +using BeatTogether.DedicatedServer.Ignorance.IgnoranceCore; +using BeatTogether.DedicatedServer.Kernel.Abstractions; using BeatTogether.DedicatedServer.Kernel.Managers.Abstractions; using BeatTogether.DedicatedServer.Messaging.Enums; using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MenuRpc; -using BeatTogether.LiteNetLib.Enums; using Serilog; -using System.Threading.Tasks; namespace BeatTogether.DedicatedServer.Kernel.PacketHandlers.MultiplayerSession.MenuRpc { @@ -22,22 +21,19 @@ public ClearRecommendedBeatmapPacketHandler( _lobbyManager = lobbyManager; } - public override Task Handle(IPlayer sender, ClearRecommendedBeatmapPacket packet) + public override void Handle(IPlayer sender, ClearRecommendedBeatmapPacket packet) { _logger.Debug( $"Handling packet of type '{nameof(ClearRecommendedBeatmapPacket)}' " + $"(SenderId={sender.ConnectionId})." ); - lock (sender.BeatmapLock) + sender.BeatmapIdentifier = null; + sender.MapHash = string.Empty; + sender.BeatmapDifficultiesRequirements.Clear(); + _packetDispatcher.SendToPlayer(sender, new SetIsStartButtonEnabledPacket { - sender.BeatmapIdentifier = null; - sender.ResetRecommendedMapRequirements(); - _packetDispatcher.SendToPlayer(sender, new SetIsStartButtonEnabledPacket - { - Reason = CannotStartGameReason.NoSongSelected - }, DeliveryMethod.ReliableOrdered); - } - return Task.CompletedTask; + Reason = sender.IsServerOwner ? CannotStartGameReason.NoSongSelected : CannotStartGameReason.None + }, IgnoranceChannelTypes.Reliable); } } } diff --git a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/ClearRecommendedModifiersPacketHandler.cs b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/ClearRecommendedModifiersPacketHandler.cs index f710a617..66f9058a 100644 --- a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/ClearRecommendedModifiersPacketHandler.cs +++ b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/ClearRecommendedModifiersPacketHandler.cs @@ -1,9 +1,7 @@ using BeatTogether.DedicatedServer.Kernel.Abstractions; using BeatTogether.DedicatedServer.Kernel.Managers.Abstractions; -using BeatTogether.DedicatedServer.Messaging.Models; using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MenuRpc; using Serilog; -using System.Threading.Tasks; namespace BeatTogether.DedicatedServer.Kernel.PacketHandlers.MultiplayerSession.MenuRpc { @@ -18,17 +16,14 @@ public ClearRecommendedModifiersPacketHandler( _lobbyManager = lobbyManager; } - public override Task Handle(IPlayer sender, ClearRecommendedModifiersPacket packet) + public override void Handle(IPlayer sender, ClearRecommendedModifiersPacket packet) { _logger.Debug( $"Handling packet of type '{nameof(ClearRecommendedModifiersPacket)}' " + $"(SenderId={sender.ConnectionId})." ); - lock (sender.ModifiersLock) - { - sender.Modifiers = new GameplayModifiers(); - } - return Task.CompletedTask; + + sender.Modifiers = _lobbyManager.EmptyModifiers; } } } diff --git a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/GetCountdownEndTimePacketHandler.cs b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/GetCountdownEndTimePacketHandler.cs index 7fda03f6..b15f7e66 100644 --- a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/GetCountdownEndTimePacketHandler.cs +++ b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/GetCountdownEndTimePacketHandler.cs @@ -1,9 +1,9 @@ -using BeatTogether.DedicatedServer.Kernel.Abstractions; +using BeatTogether.Core.Enums; +using BeatTogether.DedicatedServer.Ignorance.IgnoranceCore; +using BeatTogether.DedicatedServer.Kernel.Abstractions; using BeatTogether.DedicatedServer.Kernel.Managers.Abstractions; using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MenuRpc; -using BeatTogether.LiteNetLib.Enums; using Serilog; -using System.Threading.Tasks; namespace BeatTogether.DedicatedServer.Kernel.PacketHandlers.MultiplayerSession.MenuRpc { @@ -21,19 +21,18 @@ public GetCountdownEndTimePacketHandler( _packetDispatcher = packetDispatcher; } - public override Task Handle(IPlayer sender, GetCountdownEndTimePacket packet) + public override void Handle(IPlayer sender, GetCountdownEndTimePacket packet) { _logger.Debug( $"Handling packet of type '{nameof(GetCountdownEndTimePacket)}' " + - $"(SenderId={sender.ConnectionId})." + $"(SenderId={sender.ConnectionId} CountdownTime={_lobbyManager.CountdownEndTime})." ); - if(_lobbyManager.CountDownState == Enums.CountdownState.CountingDown) + if(_lobbyManager.CountDownState == CountdownState.CountingDown) _packetDispatcher.SendToPlayer(sender, new SetCountdownEndTimePacket { CountdownTime = _lobbyManager.CountdownEndTime - }, DeliveryMethod.ReliableOrdered); + }, IgnoranceChannelTypes.Reliable); - return Task.CompletedTask; } } } diff --git a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/GetIsInLobbyPacketHandler.cs b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/GetIsInLobbyPacketHandler.cs index e0439fa9..0ce108bf 100644 --- a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/GetIsInLobbyPacketHandler.cs +++ b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/GetIsInLobbyPacketHandler.cs @@ -1,11 +1,9 @@ -using BeatTogether.DedicatedServer.Kernel.Abstractions; -using BeatTogether.DedicatedServer.Kernel.Enums; +using BeatTogether.Core.Enums; +using BeatTogether.DedicatedServer.Ignorance.IgnoranceCore; +using BeatTogether.DedicatedServer.Kernel.Abstractions; using BeatTogether.DedicatedServer.Kernel.Managers.Abstractions; -using BeatTogether.DedicatedServer.Messaging.Enums; using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MenuRpc; -using BeatTogether.LiteNetLib.Enums; using Serilog; -using System.Threading.Tasks; namespace BeatTogether.DedicatedServer.Kernel.PacketHandlers.MultiplayerSession.MenuRpc { @@ -24,7 +22,7 @@ public GetIsInLobbyPacketHandler( _gameplayManager = gameplayManager; } - public override Task Handle(IPlayer sender, GetIsInLobbyPacket packet) + public override void Handle(IPlayer sender, GetIsInLobbyPacket packet) { _logger.Debug( $"Handling packet of type '{nameof(GetIsInLobbyPacket)}' " + @@ -34,9 +32,8 @@ public override Task Handle(IPlayer sender, GetIsInLobbyPacket packet) _packetDispatcher.SendToPlayer(sender, new SetIsInLobbyPacket { IsInLobby = _instance.State != MultiplayerGameState.Game - }, DeliveryMethod.ReliableOrdered); + }, IgnoranceChannelTypes.Reliable); - return Task.CompletedTask; } } } diff --git a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/GetIsReadyPacketHandler.cs b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/GetIsReadyPacketHandler.cs index 85c877eb..7206fe23 100644 --- a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/GetIsReadyPacketHandler.cs +++ b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/GetIsReadyPacketHandler.cs @@ -1,8 +1,7 @@ -using BeatTogether.DedicatedServer.Kernel.Abstractions; +using BeatTogether.DedicatedServer.Ignorance.IgnoranceCore; +using BeatTogether.DedicatedServer.Kernel.Abstractions; using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MenuRpc; -using BeatTogether.LiteNetLib.Enums; using Serilog; -using System.Threading.Tasks; namespace BeatTogether.DedicatedServer.Kernel.PacketHandlers.MultiplayerSession.MenuRpc { @@ -17,7 +16,7 @@ public GetIsReadyPacketHandler( _packetDispatcher = packetDispatcher; } - public override Task Handle(IPlayer sender, GetIsReadyPacket packet) + public override void Handle(IPlayer sender, GetIsReadyPacket packet) { _logger.Debug( $"Handling packet of type '{nameof(GetIsReadyPacket)}' " + @@ -27,9 +26,7 @@ public override Task Handle(IPlayer sender, GetIsReadyPacket packet) _packetDispatcher.SendToPlayer(sender, new SetIsReadyPacket { IsReady = sender.IsReady - }, DeliveryMethod.ReliableOrdered); - - return Task.CompletedTask; + }, IgnoranceChannelTypes.Reliable); } } } diff --git a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/GetMultiplayerGameStatePacketHandler.cs b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/GetMultiplayerGameStatePacketHandler.cs index 27e98cdb..3eca4464 100644 --- a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/GetMultiplayerGameStatePacketHandler.cs +++ b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/GetMultiplayerGameStatePacketHandler.cs @@ -1,8 +1,7 @@ -using BeatTogether.DedicatedServer.Kernel.Abstractions; +using BeatTogether.DedicatedServer.Ignorance.IgnoranceCore; +using BeatTogether.DedicatedServer.Kernel.Abstractions; using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MenuRpc; -using BeatTogether.LiteNetLib.Enums; using Serilog; -using System.Threading.Tasks; namespace BeatTogether.DedicatedServer.Kernel.PacketHandlers.MultiplayerSession.MenuRpc { @@ -20,7 +19,7 @@ public GetMultiplayerGameStatePacketHandler( _packetDispatcher = packetDispatcher; } - public override Task Handle(IPlayer sender, GetMultiplayerGameStatePacket packet) + public override void Handle(IPlayer sender, GetMultiplayerGameStatePacket packet) { _logger.Debug( $"Handling packet of type '{nameof(GetMultiplayerGameStatePacket)}' " + @@ -30,9 +29,7 @@ public override Task Handle(IPlayer sender, GetMultiplayerGameStatePacket packet _packetDispatcher.SendToPlayer(sender, new SetMultiplayerGameStatePacket { State = _instance.State - }, DeliveryMethod.ReliableOrdered); - - return Task.CompletedTask; + }, IgnoranceChannelTypes.Reliable); } } } diff --git a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/GetPlayersPermissionConfigurationPacketHandler.cs b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/GetPlayersPermissionConfigurationPacketHandler.cs index f908cbbd..14b243e1 100644 --- a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/GetPlayersPermissionConfigurationPacketHandler.cs +++ b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/GetPlayersPermissionConfigurationPacketHandler.cs @@ -1,11 +1,9 @@ -using BeatTogether.DedicatedServer.Kernel.Abstractions; +using BeatTogether.DedicatedServer.Ignorance.IgnoranceCore; +using BeatTogether.DedicatedServer.Kernel.Abstractions; using BeatTogether.DedicatedServer.Kernel.Configuration; using BeatTogether.DedicatedServer.Messaging.Models; using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MenuRpc; -using BeatTogether.LiteNetLib.Enums; using Serilog; -using System.Linq; -using System.Threading.Tasks; namespace BeatTogether.DedicatedServer.Kernel.PacketHandlers.MultiplayerSession.MenuRpc { @@ -26,30 +24,41 @@ public GetPlayersPermissionConfigurationPacketHandler( _playerRegistry = playerRegistry; } - public override Task Handle(IPlayer sender, GetPlayersPermissionConfigurationPacket packet) + public override void Handle(IPlayer sender, GetPlayersPermissionConfigurationPacket packet) { _logger.Debug( $"Handling packet of type '{nameof(GetPlayersPermissionConfigurationPacket)}' " + $"(SenderId={sender.ConnectionId})." ); - + //sends player there own permissions, and those of the player who is the current manager. + bool HasManager = (_playerRegistry.TryGetPlayer(_configuration.ServerOwnerId, out var ServerOwner) && !sender.IsServerOwner); + PlayerPermissionConfiguration[] playerPermissionConfigurations = new PlayerPermissionConfiguration[HasManager ? 2 : 1]; + playerPermissionConfigurations[0] = new PlayerPermissionConfiguration + { + UserId = sender.HashedUserId, + IsServerOwner = sender.IsServerOwner, + HasRecommendBeatmapsPermission = sender.CanRecommendBeatmaps, + HasRecommendGameplayModifiersPermission = sender.CanRecommendModifiers, + HasKickVotePermission = sender.CanKickVote, + HasInvitePermission = sender.CanInvite + }; + if (HasManager) + playerPermissionConfigurations[1] = new PlayerPermissionConfiguration + { + UserId = ServerOwner!.HashedUserId, + IsServerOwner = ServerOwner!.IsServerOwner, + HasRecommendBeatmapsPermission = ServerOwner!.CanRecommendBeatmaps, + HasRecommendGameplayModifiersPermission = ServerOwner!.CanRecommendModifiers, + HasKickVotePermission = ServerOwner!.CanKickVote, + HasInvitePermission = ServerOwner!.CanInvite + }; _packetDispatcher.SendToPlayer(sender, new SetPlayersPermissionConfigurationPacket { PermissionConfiguration = new PlayersPermissionConfiguration { - PlayersPermission = _playerRegistry.Players.Select(x => new PlayerPermissionConfiguration - { - UserId = x.UserId, - IsServerOwner = x.IsServerOwner, - HasRecommendBeatmapsPermission = x.CanRecommendBeatmaps, - HasRecommendGameplayModifiersPermission = x.CanRecommendModifiers, - HasKickVotePermission = x.CanKickVote, - HasInvitePermission = x.CanInvite - }).ToArray() + PlayersPermission = playerPermissionConfigurations } - }, DeliveryMethod.ReliableOrdered); - - return Task.CompletedTask; + }, IgnoranceChannelTypes.Reliable); } } } diff --git a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/GetRecommendedBeatmapPacketHandler.cs b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/GetRecommendedBeatmapPacketHandler.cs index 6494f869..e75fdfbf 100644 --- a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/GetRecommendedBeatmapPacketHandler.cs +++ b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/GetRecommendedBeatmapPacketHandler.cs @@ -1,8 +1,7 @@ -using BeatTogether.DedicatedServer.Kernel.Abstractions; +using BeatTogether.DedicatedServer.Ignorance.IgnoranceCore; +using BeatTogether.DedicatedServer.Kernel.Abstractions; using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MenuRpc; -using BeatTogether.LiteNetLib.Enums; using Serilog; -using System.Threading.Tasks; namespace BeatTogether.DedicatedServer.Kernel.PacketHandlers.MultiplayerSession.MenuRpc { @@ -17,20 +16,18 @@ public GetRecommendedBeatmapPacketHandler( _packetDispatcher = packetDispatcher; } - public override Task Handle(IPlayer sender, GetRecommendedBeatmapPacket packet) + public override void Handle(IPlayer sender, GetRecommendedBeatmapPacket packet) { _logger.Debug( $"Handling packet of type '{nameof(GetRecommendedBeatmapPacket)}' " + $"(SenderId={sender.ConnectionId})." ); - - if(sender.BeatmapIdentifier != null) + //TODO send custom packet details + if (sender.BeatmapIdentifier != null) _packetDispatcher.SendToPlayer(sender, new SetRecommendedBeatmapPacket { BeatmapIdentifier = sender.BeatmapIdentifier - }, DeliveryMethod.ReliableOrdered); - - return Task.CompletedTask; + }, IgnoranceChannelTypes.Reliable); } } } diff --git a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/GetRecommendedModifiersPacketHandler.cs b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/GetRecommendedModifiersPacketHandler.cs index ab5b9044..bc7bfd59 100644 --- a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/GetRecommendedModifiersPacketHandler.cs +++ b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/GetRecommendedModifiersPacketHandler.cs @@ -1,8 +1,7 @@ -using BeatTogether.DedicatedServer.Kernel.Abstractions; +using BeatTogether.DedicatedServer.Ignorance.IgnoranceCore; +using BeatTogether.DedicatedServer.Kernel.Abstractions; using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MenuRpc; -using BeatTogether.LiteNetLib.Enums; using Serilog; -using System.Threading.Tasks; namespace BeatTogether.DedicatedServer.Kernel.PacketHandlers.MultiplayerSession.MenuRpc { @@ -17,7 +16,7 @@ public GetRecommendedModifiersPacketHandler( _packetDispatcher = packetDispatcher; } - public override Task Handle(IPlayer sender, GetRecommendedModifiersPacket packet) + public override void Handle(IPlayer sender, GetRecommendedModifiersPacket packet) { _logger.Debug( $"Handling packet of type '{nameof(GetRecommendedModifiersPacket)}' " + @@ -26,9 +25,7 @@ public override Task Handle(IPlayer sender, GetRecommendedModifiersPacket packet _packetDispatcher.SendToPlayer(sender, new SetRecommendedModifiersPacket { Modifiers = sender.Modifiers - }, DeliveryMethod.ReliableOrdered); - - return Task.CompletedTask; + }, IgnoranceChannelTypes.Reliable); } } } diff --git a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/GetSelectedBeatmapHandler.cs b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/GetSelectedBeatmapHandler.cs index c64f29c5..33f6646f 100644 --- a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/GetSelectedBeatmapHandler.cs +++ b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/GetSelectedBeatmapHandler.cs @@ -1,9 +1,9 @@ -using BeatTogether.DedicatedServer.Kernel.Abstractions; +using BeatTogether.Core.Enums; +using BeatTogether.DedicatedServer.Ignorance.IgnoranceCore; +using BeatTogether.DedicatedServer.Kernel.Abstractions; using BeatTogether.DedicatedServer.Kernel.Managers.Abstractions; using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MenuRpc; -using BeatTogether.LiteNetLib.Enums; using Serilog; -using System.Threading.Tasks; namespace BeatTogether.DedicatedServer.Kernel.PacketHandlers.MultiplayerSession.MenuRpc { @@ -27,30 +27,30 @@ public GetSelectedBeatmapHandler( _instance = instance; } - public override Task Handle(IPlayer sender, GetSelectedBeatmap packet) + public override void Handle(IPlayer sender, GetSelectedBeatmap packet) { _logger.Debug( $"Handling packet of type '{nameof(GetSelectedBeatmap)}' " + $"(SenderId={sender.ConnectionId})." ); - if(_instance.State == Messaging.Enums.MultiplayerGameState.Lobby && _lobbyManager.SelectedBeatmap != null) + //TODO send custom packet details + if (_instance.State != MultiplayerGameState.Game && _lobbyManager.SelectedBeatmap != null) { _packetDispatcher.SendToPlayer(sender, new SetSelectedBeatmap { Beatmap = _lobbyManager.SelectedBeatmap - }, DeliveryMethod.ReliableOrdered); - return Task.CompletedTask; + }, IgnoranceChannelTypes.Reliable); + return; } - if (_instance.State == Messaging.Enums.MultiplayerGameState.Game && _gameplayManager.CurrentBeatmap != null) + if (_instance.State == MultiplayerGameState.Game && _gameplayManager.CurrentBeatmap != null) { _packetDispatcher.SendToPlayer(sender, new SetSelectedBeatmap { Beatmap = _gameplayManager.CurrentBeatmap - }, DeliveryMethod.ReliableOrdered); - return Task.CompletedTask; + }, IgnoranceChannelTypes.Reliable); + return; } - _packetDispatcher.SendToPlayer(sender, new ClearSelectedBeatmap(), DeliveryMethod.ReliableOrdered); - return Task.CompletedTask; + _packetDispatcher.SendToPlayer(sender, new ClearSelectedBeatmap(), IgnoranceChannelTypes.Reliable); } } } diff --git a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/GetSelectedGameplayModifiersHandler.cs b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/GetSelectedGameplayModifiersHandler.cs index dfbe796b..1979e4fe 100644 --- a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/GetSelectedGameplayModifiersHandler.cs +++ b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/GetSelectedGameplayModifiersHandler.cs @@ -1,9 +1,9 @@ -using BeatTogether.DedicatedServer.Kernel.Abstractions; +using BeatTogether.Core.Enums; +using BeatTogether.DedicatedServer.Ignorance.IgnoranceCore; +using BeatTogether.DedicatedServer.Kernel.Abstractions; using BeatTogether.DedicatedServer.Kernel.Managers.Abstractions; using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MenuRpc; -using BeatTogether.LiteNetLib.Enums; using Serilog; -using System.Threading.Tasks; namespace BeatTogether.DedicatedServer.Kernel.PacketHandlers.MultiplayerSession.MenuRpc { @@ -27,30 +27,29 @@ public GetSelectedGameplayModifiersHandler( _instance = dedicatedInstance; } - public override Task Handle(IPlayer sender, GetSelectedGameplayModifiers packet) + public override void Handle(IPlayer sender, GetSelectedGameplayModifiers packet) { _logger.Debug( $"Handling packet of type '{nameof(GetSelectedGameplayModifiers)}' " + $"(SenderId={sender.ConnectionId})." ); - if(_instance.State == Messaging.Enums.MultiplayerGameState.Lobby && _lobbyManager.SelectedModifiers != _lobbyManager.EmptyModifiers) + if(_instance.State != MultiplayerGameState.Game && _lobbyManager.SelectedModifiers != _lobbyManager.EmptyModifiers) { _packetDispatcher.SendToPlayer(sender, new SetSelectedGameplayModifiers { Modifiers = _lobbyManager.SelectedModifiers - }, DeliveryMethod.ReliableOrdered); - return Task.CompletedTask; + }, IgnoranceChannelTypes.Reliable); + return; } - if (_instance.State == Messaging.Enums.MultiplayerGameState.Game) + if (_instance.State == MultiplayerGameState.Game) { _packetDispatcher.SendToPlayer(sender, new SetSelectedGameplayModifiers { Modifiers = _gameplayManager.CurrentModifiers - }, DeliveryMethod.ReliableOrdered); - return Task.CompletedTask; + }, IgnoranceChannelTypes.Reliable); + return; } - _packetDispatcher.SendToPlayer(sender, new ClearSelectedGameplayModifiers(), DeliveryMethod.ReliableOrdered); - return Task.CompletedTask; + _packetDispatcher.SendToPlayer(sender, new ClearSelectedGameplayModifiers(), IgnoranceChannelTypes.Reliable); } } } diff --git a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/GetStartedLevelPacketHandler.cs b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/GetStartedLevelPacketHandler.cs index 7d6d46ff..10910223 100644 --- a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/GetStartedLevelPacketHandler.cs +++ b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/GetStartedLevelPacketHandler.cs @@ -1,14 +1,13 @@ -using BeatTogether.DedicatedServer.Kernel.Abstractions; +using BeatTogether.Core.Enums; +using BeatTogether.DedicatedServer.Ignorance.IgnoranceCore; +using BeatTogether.DedicatedServer.Kernel.Abstractions; using BeatTogether.DedicatedServer.Kernel.Configuration; using BeatTogether.DedicatedServer.Kernel.Enums; using BeatTogether.DedicatedServer.Kernel.Managers.Abstractions; using BeatTogether.DedicatedServer.Messaging.Enums; using BeatTogether.DedicatedServer.Messaging.Models; using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MenuRpc; -using BeatTogether.LiteNetLib.Enums; using Serilog; -using System.Linq; -using System.Threading.Tasks; namespace BeatTogether.DedicatedServer.Kernel.PacketHandlers.MultiplayerSession.MenuRpc { @@ -35,7 +34,7 @@ public GetStartedLevelPacketHandler( _packetDispatcher = packetDispatcher; } - public override Task Handle(IPlayer sender, GetStartedLevelPacket packet) + public override void Handle(IPlayer sender, GetStartedLevelPacket packet) { _logger.Debug( $"Handling packet of type '{nameof(GetStartedLevelPacket)}' " + @@ -48,16 +47,17 @@ public override Task Handle(IPlayer sender, GetStartedLevelPacket packet) Beatmap = _gameplayManager.CurrentBeatmap, Modifiers = _gameplayManager.CurrentModifiers, StartTime = _instance.RunTime - }, DeliveryMethod.ReliableOrdered); + }, IgnoranceChannelTypes.Reliable); } else { if (_lobbyManager.SelectedBeatmap != null) { - _packetDispatcher.SendToPlayer(sender, new GetIsEntitledToLevelPacket - { - LevelId = _lobbyManager.SelectedBeatmap.LevelId - }, DeliveryMethod.ReliableOrdered); + if(sender.GetEntitlement(_lobbyManager.SelectedBeatmap.LevelId) != EntitlementStatus.Ok) + _packetDispatcher.SendToPlayer(sender, new GetIsEntitledToLevelPacket + { + LevelId = _lobbyManager.SelectedBeatmap.LevelId + }, IgnoranceChannelTypes.Reliable); if(_lobbyManager.CountDownState == CountdownState.WaitingForEntitlement || _lobbyManager.CountDownState == CountdownState.StartBeatmapCountdown) { @@ -67,8 +67,8 @@ public override Task Handle(IPlayer sender, GetStartedLevelPacket packet) Modifiers = sender.Modifiers; if (_configuration.AllowPerPlayerDifficulties) { - BeatmapDifficulty[] diff = _lobbyManager.GetSelectedBeatmapDifficulties(); - if (sender.BeatmapIdentifier != null && diff.Contains(sender.BeatmapIdentifier.Difficulty)) + var diff = _lobbyManager.GetSelectedBeatmapDifficultiesRequirements(); + if (sender.BeatmapIdentifier != null && diff != null && diff.ContainsKey((uint)sender.BeatmapIdentifier.Difficulty)) Beatmap.Difficulty = sender.BeatmapIdentifier.Difficulty; } _packetDispatcher.SendToPlayer(sender, new StartLevelPacket @@ -76,13 +76,12 @@ public override Task Handle(IPlayer sender, GetStartedLevelPacket packet) Beatmap = Beatmap, Modifiers = Modifiers, StartTime = _lobbyManager.CountdownEndTime - }, DeliveryMethod.ReliableOrdered); + }, IgnoranceChannelTypes.Reliable); } } else - _packetDispatcher.SendToPlayer(sender, new CancelLevelStartPacket(), DeliveryMethod.ReliableOrdered); + _packetDispatcher.SendToPlayer(sender, new CancelLevelStartPacket(), IgnoranceChannelTypes.Reliable); } - return Task.CompletedTask; } } } diff --git a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/RequestKickPlayerPacketHandler.cs b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/RequestKickPlayerPacketHandler.cs index da0754a0..a037bdb6 100644 --- a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/RequestKickPlayerPacketHandler.cs +++ b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/RequestKickPlayerPacketHandler.cs @@ -1,10 +1,6 @@ using BeatTogether.DedicatedServer.Kernel.Abstractions; -using BeatTogether.DedicatedServer.Messaging.Enums; -using BeatTogether.DedicatedServer.Messaging.Packets; using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MenuRpc; -using BeatTogether.LiteNetLib.Enums; using Serilog; -using System.Threading.Tasks; namespace BeatTogether.DedicatedServer.Kernel.PacketHandlers.MultiplayerSession.MenuRpc { @@ -12,31 +8,29 @@ public sealed class RequestKickPlayerPacketHandler : BasePacketHandler(); public RequestKickPlayerPacketHandler( IPlayerRegistry playerRegistry, - IPacketDispatcher packetDispatcher) + IPacketDispatcher packetDispatcher, + IDedicatedInstance dedicatedInstance) { _playerRegistry = playerRegistry; _packetDispatcher = packetDispatcher; + _instance = dedicatedInstance; } - public override Task Handle(IPlayer sender, RequestKickPlayerPacket packet) + public override void Handle(IPlayer sender, RequestKickPlayerPacket packet) { - _logger.Information( + _logger.Debug( $"Handling packet of type '{nameof(RequestKickPlayerPacket)}' " + $"(SenderId={sender.ConnectionId}, KickedPlayerId={packet.KickedPlayerId})." ); - if (sender.CanKickVote) if (_playerRegistry.TryGetPlayer(packet.KickedPlayerId, out var kickedPlayer)) - _packetDispatcher.SendToPlayer(kickedPlayer, new KickPlayerPacket - { - DisconnectedReason = DisconnectedReason.Kicked - }, DeliveryMethod.ReliableOrdered); + _instance.DisconnectPlayer(kickedPlayer); - return Task.CompletedTask; } } } diff --git a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/SetIsEntitledToLevelPacketHandler.cs b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/SetIsEntitledToLevelPacketHandler.cs index b62546c5..6160f3ac 100644 --- a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/SetIsEntitledToLevelPacketHandler.cs +++ b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/SetIsEntitledToLevelPacketHandler.cs @@ -2,7 +2,6 @@ using BeatTogether.DedicatedServer.Kernel.Managers.Abstractions; using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MenuRpc; using Serilog; -using System.Threading.Tasks; namespace BeatTogether.DedicatedServer.Kernel.PacketHandlers.MultiplayerSession.MenuRpc { @@ -23,22 +22,18 @@ public SetIsEntitledToLevelPacketHandler( _playerRegistry = playerRegistry; } - public override Task Handle(IPlayer sender, SetIsEntitledToLevelPacket packet) + public override void Handle(IPlayer sender, SetIsEntitledToLevelPacket packet) { _logger.Debug( $"Handling packet of type '{nameof(SetIsEntitledToLevelPacket)}' " + $"(SenderId={sender.ConnectionId}, LevelId={packet.LevelId}, Entitlement={packet.Entitlement})." ); - lock (sender.EntitlementLock) + sender.SetEntitlement(packet.LevelId, packet.Entitlement); + foreach (IPlayer player in _playerRegistry.Players) { - sender.SetEntitlement(packet.LevelId, packet.Entitlement); - foreach (IPlayer player in _playerRegistry.Players) - { - if(player.BeatmapIdentifier != null && player.BeatmapIdentifier.LevelId == packet.LevelId) - player.UpdateEntitlement = true; - } + if(player.BeatmapIdentifier != null && player.BeatmapIdentifier.LevelId == packet.LevelId) + player.UpdateEntitlement = true; } - return Task.CompletedTask; } } } diff --git a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/SetIsInLobbyPacketHandler.cs b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/SetIsInLobbyPacketHandler.cs index d8443e78..abacfb37 100644 --- a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/SetIsInLobbyPacketHandler.cs +++ b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/SetIsInLobbyPacketHandler.cs @@ -1,11 +1,9 @@ -using BeatTogether.DedicatedServer.Kernel.Abstractions; +using BeatTogether.DedicatedServer.Ignorance.IgnoranceCore; +using BeatTogether.DedicatedServer.Kernel.Abstractions; using BeatTogether.DedicatedServer.Kernel.Managers.Abstractions; using BeatTogether.DedicatedServer.Messaging.Enums; using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MenuRpc; -using BeatTogether.LiteNetLib.Enums; using Serilog; -using System; -using System.Threading.Tasks; namespace BeatTogether.DedicatedServer.Kernel.PacketHandlers.MultiplayerSession.MenuRpc { @@ -32,40 +30,36 @@ public SetIsInLobbyPacketHandler( _lobbyManager = lobbyManager; } - public override Task Handle(IPlayer sender, SetIsInLobbyPacket packet) + public override void Handle(IPlayer sender, SetIsInLobbyPacket packet) { _logger.Debug( $"Handling packet of type '{nameof(SetIsInLobbyPacket)}' " + $"(SenderId={sender.ConnectionId}, InLobby={packet.IsInLobby})." ); - lock (sender.InLobbyLock) + sender.InLobby = packet.IsInLobby; + if (sender.InLobby) { - if (packet.IsInLobby && !sender.InLobby) + _packetDispatcher.SendToPlayer(sender, new SetIsStartButtonEnabledPacket { - _packetDispatcher.SendToPlayer(sender, new SetIsStartButtonEnabledPacket - { - Reason = CannotStartGameReason.NoSongSelected - }, DeliveryMethod.ReliableOrdered); - } - sender.InLobby = packet.IsInLobby; + Reason = sender.IsServerOwner ? _lobbyManager.GetCannotStartGameReason(sender, _lobbyManager.CanEveryonePlayBeatmap) : CannotStartGameReason.None + }, IgnoranceChannelTypes.Reliable); if (_lobbyManager.SelectedBeatmap is null) - _packetDispatcher.SendToPlayer(sender, new ClearSelectedBeatmap(), DeliveryMethod.ReliableOrdered); + _packetDispatcher.SendToPlayer(sender, new ClearSelectedBeatmap(), IgnoranceChannelTypes.Reliable); else _packetDispatcher.SendToPlayer(sender, new SetSelectedBeatmap { Beatmap = _lobbyManager.SelectedBeatmap - }, DeliveryMethod.ReliableOrdered); + }, IgnoranceChannelTypes.Reliable); if (_lobbyManager.SelectedModifiers == _lobbyManager.EmptyModifiers) - _packetDispatcher.SendToPlayer(sender, new ClearSelectedGameplayModifiers(), DeliveryMethod.ReliableOrdered); + _packetDispatcher.SendToPlayer(sender, new ClearSelectedGameplayModifiers(), IgnoranceChannelTypes.Reliable); else _packetDispatcher.SendToPlayer(sender, new SetSelectedGameplayModifiers { Modifiers = _lobbyManager.SelectedModifiers - }, DeliveryMethod.ReliableOrdered); + }, IgnoranceChannelTypes.Reliable); } - return Task.CompletedTask; } } } diff --git a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/SetIsReadyPacketHandler.cs b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/SetIsReadyPacketHandler.cs index 24635f67..db533291 100644 --- a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/SetIsReadyPacketHandler.cs +++ b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/SetIsReadyPacketHandler.cs @@ -1,11 +1,10 @@ -using BeatTogether.DedicatedServer.Kernel.Abstractions; +using BeatTogether.Core.Enums; +using BeatTogether.DedicatedServer.Ignorance.IgnoranceCore; +using BeatTogether.DedicatedServer.Kernel.Abstractions; using BeatTogether.DedicatedServer.Kernel.Enums; using BeatTogether.DedicatedServer.Kernel.Managers.Abstractions; -using BeatTogether.DedicatedServer.Messaging.Enums; using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MenuRpc; -using BeatTogether.LiteNetLib.Enums; using Serilog; -using System.Threading.Tasks; namespace BeatTogether.DedicatedServer.Kernel.PacketHandlers.MultiplayerSession.MenuRpc { @@ -29,27 +28,21 @@ public SetIsReadyPacketHandler( _packetDispatcher = packetDispatcher; } - public override Task Handle(IPlayer sender, SetIsReadyPacket packet) + public override void Handle(IPlayer sender, SetIsReadyPacket packet) { _logger.Debug( $"Handling packet of type '{nameof(SetIsReadyPacket)}' " + $"(SenderId={sender.ConnectionId}, IsReady={packet.IsReady})." ); - lock (sender.ReadyLock) - { - sender.IsReady = packet.IsReady; - //If the player somehow is in the lobby during gameplay then readying should send them to spectate - if (sender.IsReady && _instance.State == MultiplayerGameState.Game && _gameplayManager.CurrentBeatmap != null && _gameplayManager.State == GameplayManagerState.Gameplay) + sender.IsReady = packet.IsReady; + //If the player somehow is in the lobby during gameplay then readying should send them to spectate + if (sender.IsReady && _instance.State == MultiplayerGameState.Game && _gameplayManager.CurrentBeatmap != null && _gameplayManager.State == GameplayManagerState.Gameplay) + _packetDispatcher.SendToPlayer(sender, new StartLevelPacket { - _packetDispatcher.SendToPlayer(sender, new StartLevelPacket - { - Beatmap = _gameplayManager.CurrentBeatmap!, - Modifiers = _gameplayManager.CurrentModifiers, - StartTime = _instance.RunTime - }, DeliveryMethod.ReliableOrdered); - } - } - return Task.CompletedTask; + Beatmap = _gameplayManager.CurrentBeatmap!, + Modifiers = _gameplayManager.CurrentModifiers, + StartTime = _instance.RunTime + }, IgnoranceChannelTypes.Reliable); } } } diff --git a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/SetRecommendedBeatmapPacketHandler.cs b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/SetRecommendedBeatmapPacketHandler.cs index 07a454ab..adf46185 100644 --- a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/SetRecommendedBeatmapPacketHandler.cs +++ b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/SetRecommendedBeatmapPacketHandler.cs @@ -1,9 +1,9 @@ -using BeatTogether.DedicatedServer.Kernel.Abstractions; +using BeatTogether.DedicatedServer.Ignorance.IgnoranceCore; +using BeatTogether.DedicatedServer.Kernel.Abstractions; using BeatTogether.DedicatedServer.Kernel.Managers.Abstractions; using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MenuRpc; -using BeatTogether.LiteNetLib.Enums; using Serilog; -using System.Threading.Tasks; +using System.Linq; namespace BeatTogether.DedicatedServer.Kernel.PacketHandlers.MultiplayerSession.MenuRpc { @@ -24,28 +24,29 @@ public SetRecommendedBeatmapPacketHandler( _playerRegistry = playerRegistry; } - public override Task Handle(IPlayer sender, SetRecommendedBeatmapPacket packet) + public override void Handle(IPlayer sender, SetRecommendedBeatmapPacket packet) { _logger.Debug( $"Handling packet of type '{nameof(SetRecommendedBeatmapPacket)}' " + $"(SenderId={sender.ConnectionId}, LevelId={packet.BeatmapIdentifier.LevelId}, Difficulty={packet.BeatmapIdentifier.Difficulty})." ); - lock (sender.BeatmapLock) - { - if (sender.CanRecommendBeatmaps) - { - sender.BeatmapIdentifier = packet.BeatmapIdentifier; - if (sender.BeatmapIdentifier.LevelId != sender.MapHash) - sender.ResetRecommendedMapRequirements(); - _packetDispatcher.SendToNearbyPlayers(new GetIsEntitledToLevelPacket - { - LevelId = packet.BeatmapIdentifier.LevelId - }, DeliveryMethod.ReliableOrdered); - sender.UpdateEntitlement = true; - } - } - return Task.CompletedTask; - } + if (sender.CanRecommendBeatmaps) + { + if(sender.BeatmapIdentifier != null && sender.BeatmapIdentifier.LevelId != packet.BeatmapIdentifier.LevelId) + { + sender.BeatmapDifficultiesRequirements.Clear(); + sender.MapHash = string.Empty; + } + sender.BeatmapIdentifier = packet.BeatmapIdentifier; + sender.UpdateEntitlement = true; + //Our custom mpbeatmap packet stuff gets sent anyway + //TODO apply this logic to all entitlement checks, and check it works well. Might need to send everyones entitlements to a player when they select a map + _packetDispatcher.SendToPlayers(_playerRegistry.Players.Where(p => p.GetEntitlement(sender.BeatmapIdentifier.LevelId) == Messaging.Enums.EntitlementStatus.Unknown).ToArray(), new GetIsEntitledToLevelPacket + { + LevelId = packet.BeatmapIdentifier.LevelId + }, IgnoranceChannelTypes.Reliable); + } + } } } diff --git a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/SetRecommendedModifiersPacketHandler.cs b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/SetRecommendedModifiersPacketHandler.cs index 1a3d87d7..75c14da9 100644 --- a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/SetRecommendedModifiersPacketHandler.cs +++ b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MenuRpc/SetRecommendedModifiersPacketHandler.cs @@ -2,8 +2,6 @@ using BeatTogether.DedicatedServer.Kernel.Managers.Abstractions; using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MenuRpc; using Serilog; -using System; -using System.Threading.Tasks; namespace BeatTogether.DedicatedServer.Kernel.PacketHandlers.MultiplayerSession.MenuRpc { @@ -21,18 +19,14 @@ public SetRecommendedModifiersPacketHandler( _lobbyManager = lobbyManager; } - public override Task Handle(IPlayer sender, SetRecommendedModifiersPacket packet) + public override void Handle(IPlayer sender, SetRecommendedModifiersPacket packet) { _logger.Debug( $"Handling packet of type '{nameof(SetRecommendedModifiersPacket)}' " + $"(SenderId={sender.ConnectionId})." ); - lock (sender.ModifiersLock) - { - if (sender.CanRecommendModifiers) - sender.Modifiers = packet.Modifiers; - } - return Task.CompletedTask; - } + if (sender.CanRecommendModifiers) + sender.Modifiers = packet.Modifiers; + } } } diff --git a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MultiplayerCore/DediPacketSetNewManagerHandler.cs b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MultiplayerCore/DediPacketSetNewManagerHandler.cs index 5a3ab27f..9367b7f8 100644 --- a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MultiplayerCore/DediPacketSetNewManagerHandler.cs +++ b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MultiplayerCore/DediPacketSetNewManagerHandler.cs @@ -1,20 +1,20 @@ -using BeatTogether.DedicatedServer.Kernel.Abstractions; +using BeatTogether.Core.Enums; +using BeatTogether.DedicatedServer.Ignorance.IgnoranceCore; +using BeatTogether.DedicatedServer.Kernel.Abstractions; using BeatTogether.DedicatedServer.Kernel.Configuration; using BeatTogether.DedicatedServer.Messaging.Models; using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MenuRpc; using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MpCorePackets; -using BeatTogether.LiteNetLib.Enums; using Serilog; using System.Linq; -using System.Threading.Tasks; namespace BeatTogether.DedicatedServer.Kernel.PacketHandlers.MultiplayerSession.MenuRpc { class DediPacketSetNewManagerPacketHandler : BasePacketHandler { - public InstanceConfiguration _configuration; - public readonly IPacketDispatcher _packetDispatcher; - public readonly IPlayerRegistry _playerRegistry; + private readonly InstanceConfiguration _configuration; + private readonly IPacketDispatcher _packetDispatcher; + private readonly IPlayerRegistry _playerRegistry; private readonly ILogger _logger = Log.ForContext(); public DediPacketSetNewManagerPacketHandler( @@ -27,37 +27,33 @@ public DediPacketSetNewManagerPacketHandler( _configuration = configuration; } - object handleLock = new(); - public override Task Handle(IPlayer sender, DediPacketSetNewManagerPacket packet) + public override void Handle(IPlayer sender, DediPacketSetNewManagerPacket packet) { _logger.Debug( $"Handling packet of type '{nameof(DediPacketSetNewManagerPacket)}' " + $"(SenderId={sender.ConnectionId})." ); - lock (handleLock) + + if (sender.IsServerOwner && _configuration.GameplayServerConfiguration.GameplayServerMode == GameplayServerMode.Managed) { - if (sender.IsServerOwner && _configuration.GameplayServerMode == Enums.GameplayServerMode.Managed) - { - _configuration.ServerOwnerId = packet.NewManagerID; + _configuration.ServerOwnerId = packet.NewManagerID; - _packetDispatcher.SendToNearbyPlayers(new SetPlayersPermissionConfigurationPacket + _packetDispatcher.SendToNearbyPlayers(new SetPlayersPermissionConfigurationPacket + { + PermissionConfiguration = new PlayersPermissionConfiguration { - PermissionConfiguration = new PlayersPermissionConfiguration + PlayersPermission = _playerRegistry.Players.Select(x => new PlayerPermissionConfiguration { - PlayersPermission = _playerRegistry.Players.Select(x => new PlayerPermissionConfiguration - { - UserId = x.UserId, - IsServerOwner = x.IsServerOwner, - HasRecommendBeatmapsPermission = x.CanRecommendBeatmaps, - HasRecommendGameplayModifiersPermission = x.CanRecommendModifiers, - HasKickVotePermission = x.CanKickVote, - HasInvitePermission = x.CanInvite - }).ToArray() - } - }, DeliveryMethod.ReliableOrdered); - } + UserId = x.HashedUserId, + IsServerOwner = x.IsServerOwner, + HasRecommendBeatmapsPermission = x.CanRecommendBeatmaps, + HasRecommendGameplayModifiersPermission = x.CanRecommendModifiers, + HasKickVotePermission = x.CanKickVote, + HasInvitePermission = x.CanInvite + }).ToArray() + } + }, IgnoranceChannelTypes.Reliable); } - return Task.CompletedTask; } } } diff --git a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MultiplayerCore/GetExtraPlayerDataPacketHandler.cs b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MultiplayerCore/GetExtraPlayerDataPacketHandler.cs new file mode 100644 index 00000000..cb6d0746 --- /dev/null +++ b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MultiplayerCore/GetExtraPlayerDataPacketHandler.cs @@ -0,0 +1,35 @@ +using BeatTogether.DedicatedServer.Ignorance.IgnoranceCore; +using BeatTogether.DedicatedServer.Kernel.Abstractions; +using BeatTogether.DedicatedServer.Kernel.Extensions; +using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MpCorePackets; + +namespace BeatTogether.DedicatedServer.Kernel.PacketHandlers.MultiplayerSession.MenuRpc +{ + class GetMpPlayerDataPacketHandler : BasePacketHandler + { + private readonly IPacketDispatcher _PacketDispatcher; + private readonly IPlayerRegistry _PlayerRegistry; + + public GetMpPlayerDataPacketHandler( + IPacketDispatcher packetDispatcher, + IPlayerRegistry playerRegistry) + { + _PacketDispatcher = packetDispatcher; + _PlayerRegistry = playerRegistry; + } + + public override void Handle(IPlayer sender, MpPlayerData packet) + { + + foreach (var Player in _PlayerRegistry.Players) + { + _PacketDispatcher.SendFromPlayerToPlayer(Player, sender, new MpPlayerData() + { + PlatformID = Player.PlatformUserId, + Platform = Player.PlayerPlatform.Convert(), + ClientVersion = Player.PlayerClientVersion.ToString(), + }, IgnoranceChannelTypes.Reliable); + } + } + } +} diff --git a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MultiplayerCore/GetMpPerPlayerPacketHandler.cs b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MultiplayerCore/GetMpPerPlayerPacketHandler.cs new file mode 100644 index 00000000..b367bed2 --- /dev/null +++ b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MultiplayerCore/GetMpPerPlayerPacketHandler.cs @@ -0,0 +1,37 @@ +using BeatTogether.DedicatedServer.Ignorance.IgnoranceCore; +using BeatTogether.DedicatedServer.Kernel.Abstractions; +using BeatTogether.DedicatedServer.Kernel.Configuration; +using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MpCorePackets; +using Serilog; + +namespace BeatTogether.DedicatedServer.Kernel.PacketHandlers.MultiplayerSession.MenuRpc +{ + class GetMpPerPlayerPacketHandler : BasePacketHandler + { + private readonly InstanceConfiguration _configuration; + private readonly IPacketDispatcher _PacketDispatcher; + private readonly ILogger _logger = Log.ForContext(); + + public GetMpPerPlayerPacketHandler( + IPacketDispatcher PacketDispatcher, + InstanceConfiguration configuration) + { + _PacketDispatcher = PacketDispatcher; + _configuration = configuration; + } + + public override void Handle(IPlayer sender, GetMpPerPlayerPacket packet) + { + + _logger.Debug( + $"Handling packet of type '{nameof(GetMpPerPlayerPacket)}' " + + $"(SenderId={sender.ConnectionId})." + ); + _PacketDispatcher.SendToPlayer(sender, new MpPerPlayerPacket() + { + PPDEnabled = _configuration.AllowPerPlayerDifficulties, + PPMEnabled = _configuration.AllowPerPlayerModifiers, + }, IgnoranceChannelTypes.Reliable); + } + } +} \ No newline at end of file diff --git a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MultiplayerCore/MPBeatmapPacketHandler.cs b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MultiplayerCore/MPBeatmapPacketHandler.cs index bb76730f..597f9572 100644 --- a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MultiplayerCore/MPBeatmapPacketHandler.cs +++ b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MultiplayerCore/MPBeatmapPacketHandler.cs @@ -5,7 +5,6 @@ using Serilog; using System; using System.Linq; -using System.Threading.Tasks; namespace BeatTogether.DedicatedServer.Kernel.PacketHandlers.MultiplayerSession.MenuRpc { @@ -20,22 +19,23 @@ public MpBeatmapPacketHandler( _lobbyManager = lobbyManager; } - public override Task Handle(IPlayer sender, MpBeatmapPacket packet) + public override void Handle(IPlayer sender, MpBeatmapPacket packet) { _logger.Debug( $"Handling packet of type '{nameof(MpBeatmapPacket)}' " + $"(SenderId={sender.ConnectionId})." ); - sender.MapHash = "custom_level_" + packet.levelHash; - if(packet.requirements.TryGetValue(packet.difficulty, out string[]? Requirements)) - { - sender.Chroma = Requirements.Contains("Chroma"); - sender.NoodleExtensions = Requirements.Contains("Noodle Extensions"); - sender.MappingExtensions = Requirements.Contains("Mapping Extensions"); - } - sender.BeatmapDifficulties = packet.requirements.Keys.Select(b => (BeatmapDifficulty)b).ToArray(); - return Task.CompletedTask; + sender.MapHash = packet.levelHash; + + if(sender.BeatmapIdentifier == null) + sender.BeatmapIdentifier = new BeatmapIdentifier(); + sender.BeatmapIdentifier.LevelId = "custom_level_" + packet.levelHash; + sender.BeatmapIdentifier.Characteristic = packet.characteristic; + sender.BeatmapIdentifier.Difficulty = (BeatmapDifficulty)packet.difficulty; + sender.BeatmapDifficultiesRequirements = packet.requirements; + + sender.UpdateEntitlement = true; } } } diff --git a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MultiplayerCore/MpPerPlayerPacketHandler.cs b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MultiplayerCore/MpPerPlayerPacketHandler.cs new file mode 100644 index 00000000..42d7eb53 --- /dev/null +++ b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/MultiplayerSession/MultiplayerCore/MpPerPlayerPacketHandler.cs @@ -0,0 +1,44 @@ +using BeatTogether.DedicatedServer.Ignorance.IgnoranceCore; +using BeatTogether.DedicatedServer.Kernel.Abstractions; +using BeatTogether.DedicatedServer.Kernel.Configuration; +using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MpCorePackets; +using Serilog; + +namespace BeatTogether.DedicatedServer.Kernel.PacketHandlers.MultiplayerSession.MenuRpc +{ + class MpPerPlayerPacketHandler : BasePacketHandler + { + private readonly InstanceConfiguration _configuration; + private readonly IPacketDispatcher _PacketDispatcher; + private readonly ILogger _logger = Log.ForContext(); + + public MpPerPlayerPacketHandler( + IPacketDispatcher PacketDispatcher, + InstanceConfiguration configuration) + { + _PacketDispatcher = PacketDispatcher; + _configuration = configuration; + } + + public override void Handle(IPlayer sender, MpPerPlayerPacket packet) + { + + _logger.Debug( + $"Handling packet of type '{nameof(MpPerPlayerPacket)}' " + + $"(SenderId={sender.ConnectionId})" + + $"(PPDEnabled={packet.PPDEnabled}, PPMEnabled={packet.PPMEnabled})." + ); + if(sender.IsServerOwner) + { + _configuration.AllowPerPlayerDifficulties = packet.PPDEnabled; + _configuration.AllowPerPlayerModifiers = packet.PPMEnabled; + _PacketDispatcher.SendToNearbyPlayers(new MpPerPlayerPacket() + { + PPDEnabled = _configuration.AllowPerPlayerDifficulties, + PPMEnabled = _configuration.AllowPerPlayerModifiers, + }, IgnoranceChannelTypes.Reliable); + } + + } + } +} \ No newline at end of file diff --git a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/PingPacketHandler.cs b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/PingPacketHandler.cs index 4b035c3e..a84dbd61 100644 --- a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/PingPacketHandler.cs +++ b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/PingPacketHandler.cs @@ -1,8 +1,7 @@ -using BeatTogether.DedicatedServer.Kernel.Abstractions; +using BeatTogether.DedicatedServer.Ignorance.IgnoranceCore; +using BeatTogether.DedicatedServer.Kernel.Abstractions; using BeatTogether.DedicatedServer.Messaging.Packets; -using BeatTogether.LiteNetLib.Enums; using Serilog; -using System.Threading.Tasks; namespace BeatTogether.DedicatedServer.Kernel.PacketHandlers { @@ -16,7 +15,7 @@ public PingPacketHandler(IPacketDispatcher packetDispatcher) _packetDispatcher = packetDispatcher; } - public override Task Handle(IPlayer sender, PingPacket packet) + public override void Handle(IPlayer sender, PingPacket packet) { _logger.Verbose( $"Handling packet of type '{nameof(PingPacket)}' " + @@ -26,9 +25,7 @@ public override Task Handle(IPlayer sender, PingPacket packet) _packetDispatcher.SendToPlayer(sender, new PongPacket { PingTime = packet.PingTime - }, DeliveryMethod.ReliableOrdered); - - return Task.CompletedTask; + }, IgnoranceChannelTypes.Reliable); } } } diff --git a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/PlayerAvatarPacketHandler.cs b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/PlayerAvatarPacketHandler.cs new file mode 100644 index 00000000..d35a5ab4 --- /dev/null +++ b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/PlayerAvatarPacketHandler.cs @@ -0,0 +1,29 @@ +using BeatTogether.DedicatedServer.Kernel.Abstractions; +using BeatTogether.DedicatedServer.Messaging.Packets; +using Serilog; + +namespace BeatTogether.DedicatedServer.Kernel.PacketHandlers +{ + public sealed class PlayerAvatarPacketHandler : BasePacketHandler + { + private readonly IPacketDispatcher _packetDispatcher; + private readonly IDedicatedInstance _instance; + private readonly ILogger _logger = Log.ForContext(); + + public PlayerAvatarPacketHandler( + IPacketDispatcher packetDispatcher, IDedicatedInstance instance) + { + _packetDispatcher = packetDispatcher; + _instance = instance; + } + + public override void Handle(IPlayer sender, PlayerAvatarPacket packet) + { + _logger.Debug( + $"Handling packet of type '{nameof(PlayerAvatarPacket)}' " + + $"(SenderId={sender.ConnectionId})." + ); + sender.Avatar = packet.PlayerAvatar; + } + } +} diff --git a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/PlayerIdentityPacketHandler.cs b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/PlayerIdentityPacketHandler.cs index df8bfd15..98b9c688 100644 --- a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/PlayerIdentityPacketHandler.cs +++ b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/PlayerIdentityPacketHandler.cs @@ -1,8 +1,7 @@ using System; -using System.Threading.Tasks; +using BeatTogether.DedicatedServer.Ignorance.IgnoranceCore; using BeatTogether.DedicatedServer.Kernel.Abstractions; using BeatTogether.DedicatedServer.Messaging.Packets; -using BeatTogether.LiteNetLib.Enums; using Serilog; namespace BeatTogether.DedicatedServer.Kernel.PacketHandlers @@ -20,21 +19,17 @@ public PlayerIdentityPacketHandler( _instance = instance; } - public override Task Handle(IPlayer sender, PlayerIdentityPacket packet) + public override void Handle(IPlayer sender, PlayerIdentityPacket packet) { _logger.Debug( $"Handling packet of type '{nameof(PlayerIdentityPacket)}' " + $"(SenderId={sender.ConnectionId})." ); - lock (sender.PlayerIdentityLock) - { - sender.Avatar = packet.PlayerAvatar; - sender.State = packet.PlayerState; - sender.Random = packet.Random.Data ?? Array.Empty(); - sender.PublicEncryptionKey = packet.PublicEncryptionKey.Data ?? Array.Empty(); - _packetDispatcher.SendFromPlayer(sender, packet, DeliveryMethod.ReliableOrdered); - } - return Task.CompletedTask; + sender.Avatar = packet.PlayerAvatar; + sender.State = packet.PlayerState; + sender.Random = packet.Random.Data ?? Array.Empty(); + sender.PublicEncryptionKey = packet.PublicEncryptionKey.Data ?? Array.Empty(); + _packetDispatcher.SendFromPlayer(sender, packet, IgnoranceChannelTypes.Reliable); } } } diff --git a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/PlayerLatencyPacketHandler.cs b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/PlayerLatencyPacketHandler.cs index 4bebf942..46ee08d7 100644 --- a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/PlayerLatencyPacketHandler.cs +++ b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/PlayerLatencyPacketHandler.cs @@ -1,8 +1,7 @@ -using BeatTogether.DedicatedServer.Kernel.Abstractions; +using BeatTogether.DedicatedServer.Ignorance.IgnoranceCore; +using BeatTogether.DedicatedServer.Kernel.Abstractions; using BeatTogether.DedicatedServer.Messaging.Packets; -using BeatTogether.LiteNetLib.Enums; using Serilog; -using System.Threading.Tasks; namespace BeatTogether.DedicatedServer.Kernel.PacketHandlers { @@ -17,22 +16,18 @@ public PlayerLatencyPacketHandler( _packetDispatcher = packetDispatcher; } - public override Task Handle(IPlayer sender, PlayerLatencyPacket packet) + public override void Handle(IPlayer sender, PlayerLatencyPacket packet) { _logger.Debug( - $"Handling packet of type '{nameof(SyncTimePacket)}' " + - $"(SenderId={sender.ConnectionId}, SyncTime={packet.Latency})." + $"Handling packet of type '{nameof(PlayerLatencyPacket)}' " + + $"(SenderId={sender.ConnectionId}, Latency={packet.Latency})." ); - lock (sender.LatencyLock) + sender.Latency.Update(packet.Latency); + _packetDispatcher.SendFromPlayer(sender, new PlayerLatencyPacket { - sender.Latency.Update(packet.Latency); - _packetDispatcher.SendFromPlayer(sender, new PlayerLatencyPacket - { - Latency = sender.Latency.CurrentAverage - }, DeliveryMethod.ReliableOrdered); - } - return Task.CompletedTask; + Latency = sender.Latency.CurrentAverage + }, IgnoranceChannelTypes.Reliable); } } } diff --git a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/PlayerSortOrderPacketHandler.cs b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/PlayerSortOrderPacketHandler.cs index d30c7f7b..9bf14e7d 100644 --- a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/PlayerSortOrderPacketHandler.cs +++ b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/PlayerSortOrderPacketHandler.cs @@ -1,8 +1,7 @@ -using BeatTogether.DedicatedServer.Kernel.Abstractions; +using BeatTogether.DedicatedServer.Ignorance.IgnoranceCore; +using BeatTogether.DedicatedServer.Kernel.Abstractions; using BeatTogether.DedicatedServer.Messaging.Packets; -using BeatTogether.LiteNetLib.Enums; using Serilog; -using System.Threading.Tasks; namespace BeatTogether.DedicatedServer.Kernel.PacketHandlers { @@ -16,22 +15,18 @@ public PlayerSortOrderPacketHandler(IPacketDispatcher packetDispatcher) _packetDispatcher = packetDispatcher; } - public override Task Handle(IPlayer sender, PlayerSortOrderPacket packet) + public override void Handle(IPlayer sender, PlayerSortOrderPacket packet) { _logger.Debug( $"Handling packet of type '{nameof(PlayerSortOrderPacket)}' " + $"(SenderId={sender.ConnectionId})." ); - - lock (sender.SortLock) + if (sender.HashedUserId == packet.UserId && sender.SortIndex != packet.SortIndex) //If they send themselves as being in the wrong place, correct them. Although this probably shouldnt have a handler { - if (sender.UserId == packet.UserId && sender.SortIndex != packet.SortIndex) - { - sender.SortIndex = packet.SortIndex; - _packetDispatcher.SendExcludingPlayer(sender, packet, DeliveryMethod.ReliableOrdered); - } + packet.SortIndex = sender.SortIndex; + _packetDispatcher.SendToPlayer(sender, packet, IgnoranceChannelTypes.Reliable); } - return Task.CompletedTask; + } } } diff --git a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/PlayerStatePacketHandler.cs b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/PlayerStatePacketHandler.cs index 8938e6f5..43635cc4 100644 --- a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/PlayerStatePacketHandler.cs +++ b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/PlayerStatePacketHandler.cs @@ -1,26 +1,34 @@ using BeatTogether.DedicatedServer.Kernel.Abstractions; +using BeatTogether.DedicatedServer.Kernel.Managers.Abstractions; using BeatTogether.DedicatedServer.Messaging.Packets; using Serilog; -using System.Threading.Tasks; namespace BeatTogether.DedicatedServer.Kernel.PacketHandlers { public sealed class PlayerStatePacketHandler : BasePacketHandler { private readonly ILogger _logger = Log.ForContext(); + private readonly ILobbyManager _lobbyManager; - public override Task Handle(IPlayer sender, PlayerStatePacket packet) + public PlayerStatePacketHandler(ILobbyManager lobbyManager) + { + _lobbyManager = lobbyManager; + } + + public override void Handle(IPlayer sender, PlayerStatePacket packet) { _logger.Debug( $"Handling packet of type '{nameof(PlayerStatePacket)}' " + - $"(SenderId={sender.ConnectionId}, IsPlayer={packet.PlayerState.Contains("player")}, IsModded={packet.PlayerState.Contains("modded")}, " + - $"IsActive={packet.PlayerState.Contains("is_active")}, WantsToPlayNextLevel={packet.PlayerState.Contains("wants_to_play_next_level")})." + $"(SenderId={sender.ConnectionId}, IsPlayer={packet.PlayerState.Contains("player")}, " + + $"IsActive={packet.PlayerState.Contains("is_active")}, WantsToPlayNextLevel={packet.PlayerState.Contains("wants_to_play_next_level")}, " + + $"IsSpectating={packet.PlayerState.Contains("spectating")}, InMenu={packet.PlayerState.Contains("in_menu")}, " + + $"backgrounded={packet.PlayerState.Contains("backgrounded")}, in_gameplay={packet.PlayerState.Contains("in_gameplay")}, " + + $"was_active_at_level_start={packet.PlayerState.Contains("was_active_at_level_start")}, finished_level={packet.PlayerState.Contains("finished_level")})." ); - lock (sender.StateLock) - { - sender.State = packet.PlayerState; - } - return Task.CompletedTask; + + sender.State = packet.PlayerState; + if ( packet.PlayerState.Contains("wants_to_play_next_level") != sender.State.Contains("wants_to_play_next_level") || packet.PlayerState.Contains("backgrounded") != sender.State.Contains("backgrounded")) + _lobbyManager.UpdateSpectatingPlayers = true; } } } diff --git a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/SyncTimePacketHandler.cs b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/SyncTimePacketHandler.cs index 6f7cf09e..26d4c094 100644 --- a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/SyncTimePacketHandler.cs +++ b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/SyncTimePacketHandler.cs @@ -1,8 +1,7 @@ -using BeatTogether.DedicatedServer.Kernel.Abstractions; +using BeatTogether.DedicatedServer.Ignorance.IgnoranceCore; +using BeatTogether.DedicatedServer.Kernel.Abstractions; using BeatTogether.DedicatedServer.Messaging.Packets; -using BeatTogether.LiteNetLib.Enums; using Serilog; -using System.Threading.Tasks; namespace BeatTogether.DedicatedServer.Kernel.PacketHandlers { @@ -16,7 +15,7 @@ public SyncTimePacketHandler(IPacketDispatcher packetDispatcher) _packetDispatcher = packetDispatcher; } - public override Task Handle(IPlayer sender, SyncTimePacket packet) + public override void Handle(IPlayer sender, SyncTimePacket packet) { _logger.Debug( $"Handling packet of type '{nameof(SyncTimePacket)}' " + @@ -26,8 +25,7 @@ public override Task Handle(IPlayer sender, SyncTimePacket packet) _packetDispatcher.SendToPlayer(sender, new SyncTimePacket { SyncTime = sender.SyncTime - }, DeliveryMethod.ReliableOrdered); - return Task.CompletedTask; + }, IgnoranceChannelTypes.Reliable); } } } diff --git a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/Unconnected/AcknowledgeMessageHandler.cs b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/Unconnected/AcknowledgeMessageHandler.cs deleted file mode 100644 index b54912ab..00000000 --- a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/Unconnected/AcknowledgeMessageHandler.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Threading.Tasks; -using BeatTogether.Core.Messaging.Abstractions; -using BeatTogether.Core.Messaging.Messages; -using BeatTogether.DedicatedServer.Kernel.Abstractions; -using BeatTogether.DedicatedServer.Kernel.Handshake; - -namespace BeatTogether.DedicatedServer.Kernel.PacketHandlers.Unconnected -{ - public class AcknowledgeMessageHandler : BaseHandshakeMessageHandler - { - private readonly IUnconnectedDispatcher _unconnectedDispatcher; - - public AcknowledgeMessageHandler(IUnconnectedDispatcher unconnectedDispatcher) - { - _unconnectedDispatcher = unconnectedDispatcher; - } - - public override Task Handle(HandshakeSession session, AcknowledgeMessage message) - { - _unconnectedDispatcher.Acknowledge(session, message.ResponseId, message.MessageHandled); - return Task.FromResult(default(IMessage?)); - } - - } -} \ No newline at end of file diff --git a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/Unconnected/AuthenticateGameLiftUserRequestHandler.cs b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/Unconnected/AuthenticateGameLiftUserRequestHandler.cs deleted file mode 100644 index ce923161..00000000 --- a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/Unconnected/AuthenticateGameLiftUserRequestHandler.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Threading.Tasks; -using BeatTogether.Core.Messaging.Abstractions; -using BeatTogether.DedicatedServer.Kernel.Abstractions; -using BeatTogether.DedicatedServer.Kernel.Handshake; -using BeatTogether.DedicatedServer.Messaging.Messages.GameLift; - -namespace BeatTogether.DedicatedServer.Kernel.PacketHandlers.Unconnected -{ - public class AuthenticateGameLiftUserRequestHandler : BaseHandshakeMessageHandler - { - private readonly IHandshakeService _handshakeService; - - public AuthenticateGameLiftUserRequestHandler(IHandshakeService handshakeService) - { - _handshakeService = handshakeService; - } - - public override async Task Handle(HandshakeSession session, AuthenticateGameLiftUserRequest message) - => await _handshakeService.AuthenticateGameLiftUser(session, message); - } -} \ No newline at end of file diff --git a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/Unconnected/ClientHelloRequestHandler.cs b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/Unconnected/ClientHelloRequestHandler.cs deleted file mode 100644 index 70794e61..00000000 --- a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/Unconnected/ClientHelloRequestHandler.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Threading.Tasks; -using BeatTogether.Core.Messaging.Abstractions; -using BeatTogether.DedicatedServer.Kernel.Abstractions; -using BeatTogether.DedicatedServer.Kernel.Handshake; -using BeatTogether.DedicatedServer.Messaging.Messages.Handshake; - -namespace BeatTogether.DedicatedServer.Kernel.PacketHandlers.Unconnected -{ - public class ClientHelloRequestHandler : BaseHandshakeMessageHandler - { - private readonly IHandshakeService _handshakeService; - - public ClientHelloRequestHandler(IHandshakeService handshakeService) - { - _handshakeService = handshakeService; - } - - public override async Task Handle(HandshakeSession session, ClientHelloRequest message) => - await _handshakeService.ClientHello(session, message); - } -} \ No newline at end of file diff --git a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/Unconnected/ClientHelloWithCookieRequestHandler.cs b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/Unconnected/ClientHelloWithCookieRequestHandler.cs deleted file mode 100644 index 7ed2f787..00000000 --- a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/Unconnected/ClientHelloWithCookieRequestHandler.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Threading.Tasks; -using BeatTogether.Core.Messaging.Abstractions; -using BeatTogether.DedicatedServer.Kernel.Abstractions; -using BeatTogether.DedicatedServer.Kernel.Handshake; -using BeatTogether.DedicatedServer.Messaging.Messages.Handshake; - -namespace BeatTogether.DedicatedServer.Kernel.PacketHandlers.Unconnected -{ - public class ClientHelloWithCookieRequestHandler : BaseHandshakeMessageHandler - { - private readonly IHandshakeService _handshakeService; - - public ClientHelloWithCookieRequestHandler(IHandshakeService handshakeService) - { - _handshakeService = handshakeService; - } - - public override async Task Handle(HandshakeSession session, ClientHelloWithCookieRequest message) => - await _handshakeService.ClientHelloWithCookie(session, message); - } -} \ No newline at end of file diff --git a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/Unconnected/ClientKeyExchangeRequestHandler.cs b/BeatTogether.DedicatedServer.Kernel/PacketHandlers/Unconnected/ClientKeyExchangeRequestHandler.cs deleted file mode 100644 index bec43716..00000000 --- a/BeatTogether.DedicatedServer.Kernel/PacketHandlers/Unconnected/ClientKeyExchangeRequestHandler.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Threading.Tasks; -using BeatTogether.Core.Messaging.Abstractions; -using BeatTogether.DedicatedServer.Kernel.Abstractions; -using BeatTogether.DedicatedServer.Kernel.Handshake; -using BeatTogether.DedicatedServer.Messaging.Messages.Handshake; - -namespace BeatTogether.DedicatedServer.Kernel.PacketHandlers.Unconnected -{ - public class ClientKeyExchangeRequestHandler : BaseHandshakeMessageHandler - { - private readonly IHandshakeService _handshakeService; - - public ClientKeyExchangeRequestHandler(IHandshakeService handshakeService) - { - _handshakeService = handshakeService; - } - - public override async Task Handle(HandshakeSession session, ClientKeyExchangeRequest message) => - await _handshakeService.ClientKeyExchange(session, message); - } -} \ No newline at end of file diff --git a/BeatTogether.DedicatedServer.Kernel/PacketSource.cs b/BeatTogether.DedicatedServer.Kernel/PacketSource.cs index ab121685..ebcc90de 100644 --- a/BeatTogether.DedicatedServer.Kernel/PacketSource.cs +++ b/BeatTogether.DedicatedServer.Kernel/PacketSource.cs @@ -2,19 +2,20 @@ using System.Net; using BeatTogether.DedicatedServer.Kernel.Abstractions; using BeatTogether.DedicatedServer.Kernel.Configuration; +using BeatTogether.DedicatedServer.Messaging.Enums; +using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession; +using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.GameplayRpc; using BeatTogether.DedicatedServer.Messaging.Registries; using BeatTogether.Extensions; -using BeatTogether.LiteNetLib; -using BeatTogether.LiteNetLib.Abstractions; -using BeatTogether.LiteNetLib.Configuration; -using BeatTogether.LiteNetLib.Enums; -using BeatTogether.LiteNetLib.Sources; +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.DedicatedServer.Messaging.Util; using Krypton.Buffers; using Serilog; +using BeatTogether.DedicatedServer.Ignorance.IgnoranceCore; namespace BeatTogether.DedicatedServer.Kernel { - public sealed class PacketSource : ConnectedMessageSource + public sealed class PacketSource { public const byte LocalConnectionId = 0; public const byte AllConnectionIds = 127; @@ -31,12 +32,7 @@ public PacketSource( IPacketRegistry packetRegistry, IPlayerRegistry playerRegistry, PacketDispatcher packetDispatcher, - InstanceConfiguration instconfiguration, - LiteNetConfiguration configuration, - LiteNetServer server) - : base ( - configuration, - server) + InstanceConfiguration instconfiguration) { _serviceProvider = serviceProvider; _packetRegistry = packetRegistry; @@ -45,7 +41,7 @@ public PacketSource( _configuration = instconfiguration; } - public override void OnReceive(EndPoint remoteEndPoint, ref SpanBufferReader reader, DeliveryMethod method) + public void OnReceive(EndPoint remoteEndPoint, ref SpanBuffer reader, IgnoranceChannelTypes method) { if (!reader.TryReadRoutingHeader(out var routingHeader)) { @@ -64,23 +60,21 @@ public override void OnReceive(EndPoint remoteEndPoint, ref SpanBufferReader rea ); return; } + SpanBuffer HandleRead = new(reader.RemainingData.ToArray()); - //Is this packet meant to be routed? - if (routingHeader.ReceiverId != 0) - RoutePacket(sender, routingHeader, ref reader, method); - while (reader.RemainingSize > 0) + while (HandleRead.RemainingSize > 0) { uint length; - try { length = reader.ReadVarUInt(); } - catch (EndOfBufferException) { _logger.Warning("Packet was an incorrect length"); return; } - if (reader.RemainingSize < length) + try { length = HandleRead.ReadVarUInt(); } + catch (EndOfBufferException) { _logger.Warning("Packet was an incorrect length"); goto RoutePacket; } + if (HandleRead.RemainingSize < length) { - _logger.Warning($"Packet fragmented (RemainingSize={reader.RemainingSize}, Expected={length})."); - return; + _logger.Warning($"Packet fragmented (RemainingSize={HandleRead.RemainingSize}, Expected={length})."); + goto RoutePacket; } - int prevPosition = reader.Offset; + int prevPosition = HandleRead.Offset; INetSerializable? packet; IPacketRegistry packetRegistry = _packetRegistry; while (true) @@ -89,8 +83,8 @@ public override void OnReceive(EndPoint remoteEndPoint, ref SpanBufferReader rea { byte packetId; try - { packetId = reader.ReadByte(); } - catch (EndOfBufferException) { _logger.Warning("Packet was an incorrect length"); return; } + { packetId = HandleRead.ReadByte(); } + catch (EndOfBufferException) { _logger.Warning("Packet was an incorrect length"); goto RoutePacket; } if (packetRegistry.TryCreatePacket(packetId, out packet)) break; if (packetRegistry.TryGetSubPacketRegistry(packetId, out var subPacketRegistry)) @@ -103,8 +97,8 @@ public override void OnReceive(EndPoint remoteEndPoint, ref SpanBufferReader rea { string MPCpacketId; try - { MPCpacketId = reader.ReadString(); } - catch (EndOfBufferException) { _logger.Warning("Packet was an incorrect length"); return; } + { MPCpacketId = HandleRead.ReadString(); } + catch (EndOfBufferException) { _logger.Warning("Packet was an incorrect length"); goto RoutePacket; } if (MPCoreRegistry.TryCreatePacket(MPCpacketId, out packet)) break; } @@ -113,13 +107,37 @@ public override void OnReceive(EndPoint remoteEndPoint, ref SpanBufferReader rea if (packet == null) { + _logger.Debug($"Failed to create packet."); // skip any unprocessed bytes - var processedBytes = reader.Offset - prevPosition; - try { reader.SkipBytes((int)length - processedBytes); } - catch (EndOfBufferException) { _logger.Warning("Packet was an incorrect length"); return; } + var processedBytes = HandleRead.Offset - prevPosition; + try { HandleRead.SkipBytes((int)length - processedBytes); } + catch (EndOfBufferException) { _logger.Warning("Packet was an incorrect length"); goto RoutePacket; } continue; } - + if(packet is NoteSpawnPacket || packet is ObstacleSpawnPacket || packet is SliderSpawnPacket) //Note packet logic + { + if (_configuration.DisableNotes || (_playerRegistry.GetPlayerCount() >= _configuration.DisableNotesPlayerCount) && !_configuration.ForceEnableNotes) + return; + break; + } + else if (packet is NodePoseSyncStatePacket) + { + if ((DateTime.UtcNow.Ticks - sender.TicksAtLastSyncState) / TimeSpan.TicksPerMillisecond < _playerRegistry.GetMillisBetweenPoseSyncStateDeltaPackets()) + { + //_logger.Verbose($"Skipping sync state packet from {sender.ConnectionId} (Secret='{sender.Instance._configuration.Secret}')."); + return; + } + sender.TicksAtLastSyncState = DateTime.UtcNow.Ticks; + } + else if (packet is NodePoseSyncStateDeltaPacket) + { + if ((DateTime.UtcNow.Ticks - sender.TicksAtLastSyncStateDelta) / TimeSpan.TicksPerMillisecond < _playerRegistry.GetMillisBetweenPoseSyncStateDeltaPackets()) + { + //_logger.Verbose($"Skipping sync state packet from {sender.ConnectionId} (Secret='{sender.Instance._configuration.Secret}')."); + return; + } + sender.TicksAtLastSyncStateDelta = DateTime.UtcNow.Ticks; + } var packetType = packet.GetType(); var packetHandlerType = typeof(Abstractions.IPacketHandler<>) .MakeGenericType(packetType); @@ -129,67 +147,78 @@ public override void OnReceive(EndPoint remoteEndPoint, ref SpanBufferReader rea _logger.Verbose($"No handler exists for packet of type '{packetType.Name}'."); // skip any unprocessed bytes - var processedBytes = reader.Offset - prevPosition; - try { reader.SkipBytes((int)length - processedBytes); } - catch (EndOfBufferException) { _logger.Warning("Packet was an incorrect length"); return; } + var processedBytes = HandleRead.Offset - prevPosition; + try { HandleRead.SkipBytes((int)length - processedBytes); } + catch (EndOfBufferException) { _logger.Warning("Packet was an incorrect length"); goto RoutePacket; } continue; } try { - packet.ReadFrom(ref reader); + packet.ReadFrom(ref HandleRead); } catch { // skip any unprocessed bytes - var processedBytes = reader.Offset - prevPosition; - reader.SkipBytes((int)length - processedBytes); + _logger.Debug($"Failed to read packet of type '{packetType.Name}'."); + var processedBytes = HandleRead.Offset - prevPosition; + try { HandleRead.SkipBytes((int)length - processedBytes); } + catch (EndOfBufferException) { _logger.Warning("Packet was an incorrect length"); goto RoutePacket; } continue; } + // Ensure we always skip/rewind our reader + var processedBytesToSkip = HandleRead.Offset - prevPosition; + try { HandleRead.SkipBytes((int)length - processedBytesToSkip); } + catch (EndOfBufferException) { _logger.Warning("Packet was an incorrect length"); goto RoutePacket; } + ((Abstractions.IPacketHandler)packetHandler).Handle(sender, packet); } + RoutePacket: + //Is this packet meant to be routed? + if (routingHeader.ReceiverId != 0) + RoutePacket(sender, routingHeader, ref reader, method); } #region Private Methods private void RoutePacket(IPlayer sender, - (byte SenderId, byte ReceiverId) routingHeader, - ref SpanBufferReader reader, DeliveryMethod deliveryMethod) + (byte SenderId, byte ReceiverId, PacketOption PacketOption) routingHeader, + ref SpanBuffer reader, IgnoranceChannelTypes deliveryMethod) { routingHeader.SenderId = sender.ConnectionId; - var writer = new SpanBufferWriter(stackalloc byte[412]); + var writer = new SpanBuffer(stackalloc byte[412]); if (routingHeader.ReceiverId == AllConnectionIds) { - writer.WriteRoutingHeader(routingHeader.SenderId, routingHeader.ReceiverId); + writer.WriteRoutingHeader(routingHeader.SenderId, routingHeader.ReceiverId, routingHeader.PacketOption); writer.WriteBytes(reader.RemainingData); _logger.Verbose( $"Routing packet from {routingHeader.SenderId} -> all players " + - $"(Secret='{sender.Secret}', DeliveryMethod={deliveryMethod})." + $"PacketOption='{routingHeader.PacketOption}' " + + $"(Secret='{sender.Instance._configuration.Secret}', DeliveryMethod={deliveryMethod})." ); - foreach (var player in _playerRegistry.Players) - if (player != sender) - _packetDispatcher.Send(player.Endpoint, writer, deliveryMethod); + _packetDispatcher.RouteExcludingPlayer(sender, ref writer, deliveryMethod); } else { - writer.WriteRoutingHeader(routingHeader.SenderId, LocalConnectionId); + writer.WriteRoutingHeader(routingHeader.SenderId, LocalConnectionId, routingHeader.PacketOption); writer.WriteBytes(reader.RemainingData); if (!_playerRegistry.TryGetPlayer(routingHeader.ReceiverId, out var receiver)) { _logger.Warning( "Failed to retrieve receiver " + - $"(Secret='{sender.Secret}', ReceiverId={routingHeader.ReceiverId})." + $"(Secret='{sender.Instance._configuration.Secret}', ReceiverId={routingHeader.ReceiverId})." ); return; } _logger.Verbose( $"Routing packet from {routingHeader.SenderId} -> {routingHeader.ReceiverId} " + - $"(Secret='{sender.Secret}', DeliveryMethod={deliveryMethod})." + $"PacketOption='{routingHeader.PacketOption}' " + + $"(Secret='{sender.Instance._configuration.Secret}', DeliveryMethod={deliveryMethod})." ); - _packetDispatcher.Send(receiver.Endpoint, writer, deliveryMethod); + _packetDispatcher.RouteFromPlayerToPlayer(sender, receiver, ref writer, deliveryMethod); } } diff --git a/BeatTogether.DedicatedServer.Kernel/Player.cs b/BeatTogether.DedicatedServer.Kernel/Player.cs index edc3f743..b6b5ddcf 100644 --- a/BeatTogether.DedicatedServer.Kernel/Player.cs +++ b/BeatTogether.DedicatedServer.Kernel/Player.cs @@ -1,7 +1,10 @@ using System; using System.Collections.Concurrent; +using System.Collections.Generic; using System.Net; +using BeatTogether.Core.Enums; using BeatTogether.DedicatedServer.Kernel.Abstractions; +using BeatTogether.DedicatedServer.Kernel.Enums; using BeatTogether.DedicatedServer.Kernel.Types; using BeatTogether.DedicatedServer.Messaging.Enums; using BeatTogether.DedicatedServer.Messaging.Models; @@ -14,67 +17,83 @@ public sealed class Player : IPlayer public IDedicatedInstance Instance { get; } public byte ConnectionId { get; } public byte RemoteConnectionId => 0; - public string Secret { get; } - public string UserId { get; } + public string HashedUserId { get; set; } public string UserName { get; } - public string? PlayerSessionId { get; } - public object LatencyLock { get; set; } = new(); + public string PlayerSessionId { get; set; } + + public uint ENetPeerId { get; set; } + public RollingAverage Latency { get; } = new(30); - public float SyncTime => - Math.Min(Instance.RunTime - Latency.CurrentAverage - _syncTimeOffset, + public long SyncTime => + Math.Min(Instance.RunTime - Latency.CurrentAverage, Instance.RunTime); - public object SortLock { get; set; } = new(); public int SortIndex { get; set; } public byte[]? Random { get; set; } public byte[]? PublicEncryptionKey { get; set; } - public object PlayerIdentityLock { get; set; } = new(); - public AvatarData Avatar { get; set; } = new(); - public object ReadyLock { get; set; } = new(); + public Version PlayerClientVersion { get; set; } = new(); + public Platform PlayerPlatform { get; set; } = Platform.Test; //Unknown + public string PlatformUserId { get; set; } = ""; + public MultiplayerAvatarsData Avatar { get; set; } = new(); public bool IsReady { get; set; } - public object InLobbyLock { get; set; } = new(); public bool InLobby { get; set; } - public object BeatmapLock { get; set; } = new(); public BeatmapIdentifier? BeatmapIdentifier { get; set; } = null; - public object ModifiersLock { get; set; } = new(); public GameplayModifiers Modifiers { get; set; } = new(); - public object StateLock { get; set; } = new(); public PlayerStateHash State { get; set; } = new(); - public bool IsServerOwner => UserId == Instance._configuration.ServerOwnerId; - public bool CanRecommendBeatmaps => true; + public bool IsServerOwner => HashedUserId == Instance._configuration.ServerOwnerId; + public bool CanRecommendBeatmaps => true;// This check is wrong as GameplayServerControlSettings is None in Quickplay to disable Modifier selection //Instance._configuration.GameplayServerControlSettings is not GameplayServerControlSettings.None; public bool CanRecommendModifiers => - Instance._configuration.GameplayServerControlSettings is Enums.GameplayServerControlSettings.AllowModifierSelection or Enums.GameplayServerControlSettings.All; - public bool CanKickVote => UserId == Instance._configuration.ServerOwnerId; + Instance._configuration.GameplayServerConfiguration.GameplayServerControlSettings is GameplayServerControlSettings.AllowModifierSelection or GameplayServerControlSettings.All; + public bool CanKickVote => HashedUserId == Instance._configuration.ServerOwnerId; public bool CanInvite => - Instance._configuration.DiscoveryPolicy is Enums.DiscoveryPolicy.WithCode or Enums.DiscoveryPolicy.Public; + Instance._configuration.GameplayServerConfiguration.DiscoveryPolicy is DiscoveryPolicy.WithCode or DiscoveryPolicy.Public; + public bool ForceLateJoin { get; set; } = false; //Used to force trouble players to late join a mp game/tell them to spectate - public bool IsPlayer => State.Contains("player"); - public bool IsSpectating => State.Contains("spectating"); - public bool WantsToPlayNextLevel => State.Contains("wants_to_play_next_level"); - public bool IsBackgrounded => State.Contains("backgrounded"); - public bool InGameplay => State.Contains("in_gameplay"); - public bool WasActiveAtLevelStart => State.Contains("was_active_at_level_start"); - public bool IsActive => State.Contains("is_active"); - public bool FinishedLevel => State.Contains("finished_level"); - public bool InMenu => State.Contains("in_menu"); - public bool IsModded => State.Contains("modded"); + public bool IsPlayer => State.Contains("player"); //If the user is a player + public bool IsSpectating => State.Contains("spectating"); //True if player is spectating (special case that is not applicable to normal game clients) + public bool WantsToPlayNextLevel => State.Contains("wants_to_play_next_level"); //True if player wants to spectate + public bool IsBackgrounded => State.Contains("backgrounded"); //If user has gone AFK + public bool InGameplay => State.Contains("in_gameplay"); //True while user in gameplay + public bool WasActiveAtLevelStart => State.Contains("was_active_at_level_start"); //True if the player was active at the beatmap level start - playing the level + public bool IsActive => State.Contains("is_active"); //If player is activly playing the beatmap + public bool FinishedLevel => State.Contains("finished_level"); //If the player has finished the level + public bool InMenu => State.Contains("in_menu"); //Should be true while in lobby - private const float _syncTimeOffset = 0.06f; - private ConcurrentDictionary _entitlements = new(); + private readonly ConcurrentDictionary _entitlements = new(); //Set a max amount of like 50 or something. public Player(EndPoint endPoint, IDedicatedInstance instance, - byte connectionId, string secret, string userId, string userName, string? playerSessionId) + byte connectionId, string userId, string userName, string playerSessionId, AccessLevel accessLevel = AccessLevel.Player) { Endpoint = endPoint; Instance = instance; ConnectionId = connectionId; - Secret = secret; - UserId = userId; + HashedUserId = userId; UserName = userName; PlayerSessionId = playerSessionId; + _AccessLevel = accessLevel; + } + public bool IsPatreon { get; set; } = false; + public object MPChatLock { get; set; } = new(); + public bool CanTextChat { get; set; } = false; + public bool CanReceiveVoiceChat { get; set; } = false; + public bool CanTransmitVoiceChat { get; set; } = false; + + private AccessLevel _AccessLevel; + public AccessLevel GetAccessLevel() + { + AccessLevel accessLevel = _AccessLevel; + if (IsServerOwner) + accessLevel = AccessLevel.Manager; + if (IsPatreon) + accessLevel++; + return accessLevel; + } + + public void SetAccessLevel(AccessLevel newLevel) + { + _AccessLevel = newLevel; } - public object EntitlementLock { get; set; } = new(); public EntitlementStatus GetEntitlement(string levelId) => _entitlements.TryGetValue(levelId, out var value) ? value : EntitlementStatus.Unknown; @@ -84,18 +103,10 @@ public void SetEntitlement(string levelId, EntitlementStatus entitlement) public bool UpdateEntitlement { get; set; } = false; public string MapHash { get; set; } = string.Empty; - public bool Chroma { get; set; } = false; - public bool NoodleExtensions { get; set; } = false; - public bool MappingExtensions { get; set; } = false; - public BeatmapDifficulty[] BeatmapDifficulties { get; set; } = Array.Empty(); + public Dictionary BeatmapDifficultiesRequirements { get; set; } = new(); + + public long TicksAtLastSyncStateDelta { get; set; } = 0; //33ms gaps for 30/sec, 66ms gap for 15/sec + public long TicksAtLastSyncState { get; set; } = 0; - public void ResetRecommendedMapRequirements() - { - MapHash= string.Empty; - Chroma = false; - NoodleExtensions = false; - MappingExtensions = false; - BeatmapDifficulties = Array.Empty(); - } } } diff --git a/BeatTogether.DedicatedServer.Kernel/PlayerRegistry.cs b/BeatTogether.DedicatedServer.Kernel/PlayerRegistry.cs index cd4a819e..4fa16d86 100644 --- a/BeatTogether.DedicatedServer.Kernel/PlayerRegistry.cs +++ b/BeatTogether.DedicatedServer.Kernel/PlayerRegistry.cs @@ -1,4 +1,4 @@ -using System.Collections.Concurrent; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Net; @@ -8,50 +8,131 @@ namespace BeatTogether.DedicatedServer.Kernel { public sealed class PlayerRegistry : IPlayerRegistry { - public IPlayer[] Players { get => _playersByUserId.Values.ToArray(); } + + public IPlayer[] Players { get => GetPlayers(); } - private readonly ConcurrentDictionary _playersByRemoteEndPoint = new(); - private readonly ConcurrentDictionary _playersByConnectionId = new(); - private readonly ConcurrentDictionary _playersByUserId = new(); + private readonly object PlayerDictionaries_Lock = new(); + private readonly Dictionary _playersByRemoteEndPoint = new(); + private readonly Dictionary _playersByConnectionId = new(); + private readonly Dictionary _playersByUserId = new(); + + private readonly Dictionary _pendingPlayerSessionData = new(); + + + + public void AddExtraPlayerSessionData(Core.Abstractions.IPlayer playerSessionData) + { + _pendingPlayerSessionData[playerSessionData.PlayerSessionId] = playerSessionData; + } + + public bool RemoveExtraPlayerSessionDataAndApply(Core.Abstractions.IPlayer playerSessionData) + { + + if (_pendingPlayerSessionData.Remove(playerSessionData.PlayerSessionId, out var Value)) + { + playerSessionData.PlayerClientVersion = Value.PlayerClientVersion; + playerSessionData.PlayerPlatform = Value.PlayerPlatform; + playerSessionData.PlatformUserId = Value.PlatformUserId; + return true; + } + playerSessionData.PlayerClientVersion = new System.Version("1.0.0"); + playerSessionData.PlayerPlatform = Core.Enums.Platform.Test; + playerSessionData.PlatformUserId = "ERROR"; + return false; + } + + + public bool RemoveExtraPlayerSessionData(string playerSessionId) + { + return _pendingPlayerSessionData.Remove(playerSessionId, out _); + } + + + private int _PlayerCount = 0; public int GetPlayerCount() { - return _playersByUserId.Count; + lock(PlayerDictionaries_Lock) + { + return _PlayerCount; + } } + + private IPlayer[] GetPlayers() + { + lock (PlayerDictionaries_Lock) + { + return _playersByUserId.Values.ToArray(); + } + } + + public bool AddPlayer(IPlayer player) { - if(_playersByUserId.TryAdd(player.UserId, player)) + lock (PlayerDictionaries_Lock) { - _playersByRemoteEndPoint.TryAdd(player.Endpoint, player); - _playersByConnectionId.TryAdd(player.ConnectionId, player); - return true; + if (_playersByUserId.TryAdd(player.HashedUserId, player)) + { + _playersByRemoteEndPoint.TryAdd(player.Endpoint, player); + _playersByConnectionId.TryAdd(player.ConnectionId, player); + _PlayerCount++; + MillisBetweenPoseSyncStatePackets = _PlayerCount == 1 ? 1000 : (long)(0.94 * _PlayerCount + 15); + MillisBetweenScoreSyncStatePackets = _PlayerCount == 1 ? 1000 : (long)(1.5 * _PlayerCount + 20); + return true; + } } return false; } public void RemovePlayer(IPlayer player) { - _playersByRemoteEndPoint.TryRemove(player.Endpoint, out _); - _playersByUserId.TryRemove(player.UserId, out _); - _playersByConnectionId.TryRemove(player.ConnectionId, out _); + lock (PlayerDictionaries_Lock) + { + if (_playersByUserId.Remove(player.HashedUserId, out _)) + { + _playersByRemoteEndPoint.Remove(player.Endpoint, out _); + _playersByConnectionId.Remove(player.ConnectionId, out _); + _PlayerCount--; + MillisBetweenPoseSyncStatePackets = _PlayerCount == 1 ? 1000 : (long)(0.94 * _PlayerCount + 15); + MillisBetweenScoreSyncStatePackets = _PlayerCount == 1 ? 1000 : (long)(1.5 * _PlayerCount + 20); + } + } } - public IPlayer GetPlayer(EndPoint remoteEndPoint) => - _playersByRemoteEndPoint[remoteEndPoint]; - - public IPlayer GetPlayer(byte connectionId) => - _playersByConnectionId[connectionId]; + public bool TryGetPlayer(EndPoint remoteEndPoint, [MaybeNullWhen(false)] out IPlayer player) + { + lock (PlayerDictionaries_Lock) + { + return _playersByRemoteEndPoint.TryGetValue(remoteEndPoint, out player); + } + } + public bool TryGetPlayer(byte connectionId, [MaybeNullWhen(false)] out IPlayer player) + { + lock (PlayerDictionaries_Lock) + { + return _playersByConnectionId.TryGetValue(connectionId, out player); + } + } + public bool TryGetPlayer(string userId, [MaybeNullWhen(false)] out IPlayer player) + { + lock (PlayerDictionaries_Lock) + { + return _playersByUserId.TryGetValue(userId, out player); + } + } - public IPlayer GetPlayer(string userId) => - _playersByUserId[userId]; - public bool TryGetPlayer(EndPoint remoteEndPoint, [MaybeNullWhen(false)] out IPlayer player) => - _playersByRemoteEndPoint.TryGetValue(remoteEndPoint, out player); - public bool TryGetPlayer(byte connectionId, [MaybeNullWhen(false)] out IPlayer player) => - _playersByConnectionId.TryGetValue(connectionId, out player); + private long MillisBetweenPoseSyncStatePackets = 0; + public long GetMillisBetweenPoseSyncStateDeltaPackets() + { + return MillisBetweenPoseSyncStatePackets; + } - public bool TryGetPlayer(string userId, [MaybeNullWhen(false)] out IPlayer player) => - _playersByUserId.TryGetValue(userId, out player); + private long MillisBetweenScoreSyncStatePackets = 0; + public long GetMillisBetweenScoreSyncStateDeltaPackets() + { + return MillisBetweenScoreSyncStatePackets; + } } } diff --git a/BeatTogether.DedicatedServer.Kernel/Providers/CookieProvider.cs b/BeatTogether.DedicatedServer.Kernel/Providers/CookieProvider.cs index 8eff1e2a..1980f5cb 100644 --- a/BeatTogether.DedicatedServer.Kernel/Providers/CookieProvider.cs +++ b/BeatTogether.DedicatedServer.Kernel/Providers/CookieProvider.cs @@ -7,9 +7,9 @@ public class CookieProvider : ICookieProvider { private const int CookieLength = 32; - private readonly RNGCryptoServiceProvider _rngCryptoServiceProvider; + private readonly RandomNumberGenerator _rngCryptoServiceProvider; - public CookieProvider(RNGCryptoServiceProvider rngCryptoServiceProvider) + public CookieProvider(RandomNumberGenerator rngCryptoServiceProvider) { _rngCryptoServiceProvider = rngCryptoServiceProvider; } diff --git a/BeatTogether.DedicatedServer.Kernel/Providers/RandomProvider.cs b/BeatTogether.DedicatedServer.Kernel/Providers/RandomProvider.cs index 23a51f1d..66f57974 100644 --- a/BeatTogether.DedicatedServer.Kernel/Providers/RandomProvider.cs +++ b/BeatTogether.DedicatedServer.Kernel/Providers/RandomProvider.cs @@ -7,9 +7,9 @@ public class RandomProvider : IRandomProvider { private const int RandomLength = 32; - private readonly RNGCryptoServiceProvider _rngCryptoServiceProvider; + private readonly RandomNumberGenerator _rngCryptoServiceProvider; - public RandomProvider(RNGCryptoServiceProvider rngCryptoServiceProvider) + public RandomProvider(RandomNumberGenerator rngCryptoServiceProvider) { _rngCryptoServiceProvider = rngCryptoServiceProvider; } diff --git a/BeatTogether.DedicatedServer.Kernel/TextCommandRepository.cs b/BeatTogether.DedicatedServer.Kernel/TextCommandRepository.cs new file mode 100644 index 00000000..bce72e1a --- /dev/null +++ b/BeatTogether.DedicatedServer.Kernel/TextCommandRepository.cs @@ -0,0 +1,110 @@ +using BeatTogether.DedicatedServer.Kernel.Abstractions; +using BeatTogether.DedicatedServer.Kernel.Commands; +using BeatTogether.DedicatedServer.Kernel.Enums; +using Serilog; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; + +namespace BeatTogether.DedicatedServer.Kernel +{ + public class TextCommandRepository : ITextCommandRepository + { + public delegate ITextCommand CommandFactory(); + + private readonly ConcurrentDictionary> _Commands = new(); + private readonly ConcurrentDictionary> _SHCommands = new(); + //private readonly ILogger _logger = Log.ForContext(); + public TextCommandRepository() + { + RegisterCommands(); + } + + public bool GetCommand(string[] CommandValues, AccessLevel accessLevel, out ITextCommand Command) + { + bool IsPatreon = (int)accessLevel % 2 == 1; + if ((int)accessLevel == 6) + IsPatreon = true; + CommandFactory CommandDelegate; + while(accessLevel >= 0) + { + if(_Commands.TryGetValue(accessLevel, out var commands)) + { + if(commands.TryGetValue(CommandValues[0], out CommandDelegate!)) + { + Command = CommandDelegate(); + if(CommandValues.Length > 1) + Command.ReadValues(CommandValues[1..]); + return true; + } + } + if (_SHCommands.TryGetValue(accessLevel, out commands)) + { + if (commands.TryGetValue(CommandValues[0], out CommandDelegate!)) + { + Command = CommandDelegate(); + if (CommandValues.Length > 1) + Command.ReadValues(CommandValues[1..]); + return true; + } + } + accessLevel -= IsPatreon ? 2 : 1; + } + Command = null!; + return false; + } + + public string[] GetTextCommandNames(AccessLevel accessLevel) + { + bool IsPatreon = (int)accessLevel % 2 == 1; + if ((int)accessLevel == 6) + IsPatreon = true; + List Commands = new(); + while (accessLevel >= 0) + { + if (_Commands.TryGetValue(accessLevel, out var commands)) + { + Commands.AddRange(commands.Keys); + } + accessLevel -= IsPatreon ? 2 : 1; + } + return Commands.ToArray(); + } + + public void RegisterCommand(AccessLevel accessLevel) where T : class, ITextCommand, new() + { + Type typeFromHandle = typeof(T); + ITextCommand command = new T(); + ConcurrentDictionary Commands; + ConcurrentDictionary SHCommands; + if (!_Commands.TryGetValue(accessLevel, out Commands!)) + { + _Commands.TryAdd(accessLevel, new ConcurrentDictionary()); + _Commands.TryGetValue(accessLevel, out Commands!); + _SHCommands.TryAdd(accessLevel, new ConcurrentDictionary()); + } + _SHCommands.TryGetValue(accessLevel, out SHCommands!); + Commands.TryAdd(command.CommandName, () => new T()); + SHCommands.TryAdd(command.ShortHandName, () => new T()); + } + + + private void RegisterCommands() + { + RegisterCommand(AccessLevel.Player); + + RegisterCommand(AccessLevel.Manager); + RegisterCommand(AccessLevel.Manager); + RegisterCommand(AccessLevel.Manager); + RegisterCommand(AccessLevel.Manager); + RegisterCommand(AccessLevel.Manager); + RegisterCommand(AccessLevel.Manager); + RegisterCommand(AccessLevel.Manager); + RegisterCommand(AccessLevel.Manager); + RegisterCommand(AccessLevel.Manager); + RegisterCommand(AccessLevel.Manager); + RegisterCommand(AccessLevel.Manager); + RegisterCommand(AccessLevel.Manager); + } + } +} diff --git a/BeatTogether.DedicatedServer.Kernel/Types/RollingAverage.cs b/BeatTogether.DedicatedServer.Kernel/Types/RollingAverage.cs index e0314251..0dfef632 100644 --- a/BeatTogether.DedicatedServer.Kernel/Types/RollingAverage.cs +++ b/BeatTogether.DedicatedServer.Kernel/Types/RollingAverage.cs @@ -5,14 +5,12 @@ namespace BeatTogether.DedicatedServer.Kernel.Types public class RollingAverage { private long _currentTotal; - private float _currentAverage; + private long _currentAverage; private readonly long[] _buffer; private int _index; private int _length; - private const long _granularity = 1000L; - - public float CurrentAverage => _currentAverage; + public long CurrentAverage => _currentAverage; public bool HasValue => _length > 0; public RollingAverage(int window) @@ -20,25 +18,24 @@ public RollingAverage(int window) _buffer = new long[window]; } - public void Update(float value) + public void Update(long value) { var currentTotal = _currentTotal; if (_length == _buffer.Length) { currentTotal -= _buffer[_index]; } - var i = (long)(value * 1000f); - _buffer[_index] = i; + _buffer[_index] = value; _index = (_index + 1) % _buffer.Length; _length = Math.Min(_length + 1, _buffer.Length); - currentTotal += i; + currentTotal += value; _currentTotal = currentTotal; - _currentAverage = (float)(currentTotal / (double)(_granularity * _length)); + _currentAverage = (currentTotal / (long)_length); } public void Reset() { - _currentAverage = 0f; + _currentAverage = 0L; _index = 0; _length = 0; _currentTotal = 0L; diff --git a/BeatTogether.DedicatedServer.Messaging/Abstractions/BasePacketRegistry.cs b/BeatTogether.DedicatedServer.Messaging/Abstractions/BasePacketRegistry.cs new file mode 100644 index 00000000..38b90a05 --- /dev/null +++ b/BeatTogether.DedicatedServer.Messaging/Abstractions/BasePacketRegistry.cs @@ -0,0 +1,123 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; + +namespace BeatTogether.DedicatedServer.Messaging.Abstractions +{ + public abstract class BasePacketRegistry : IPacketRegistry + { + public delegate INetSerializable PacketFactory(); + + private readonly Dictionary _types = new(); + private readonly Dictionary> _packetIds = new(); + private readonly Dictionary _subPacketRegistries = new(); + private readonly Dictionary _factories = new(); + + #region Public Methods + + public BasePacketRegistry() => Register(); + + public abstract void Register(); + + /// + public IReadOnlyDictionary> GetAllPacketIds() => + _packetIds; + + /// + /// broke but too lazy to fix + public IEnumerable GetPacketIds(Type type) => + _packetIds[type]; + + /// + /// broke but too lazy to fix + public IEnumerable GetPacketIds() + where T : class, INetSerializable => + GetPacketIds(typeof(T)); + + /// + public Type GetPacketType(object packetId) => + _types[(byte)packetId]; + + /// + public IPacketRegistry GetSubPacketRegistry(object packetRegistryId) => + _subPacketRegistries[(byte)packetRegistryId]; + + /// + public INetSerializable CreatePacket(object packetId) => + _factories[(byte)packetId](); + + /// + public bool TryGetPacketIds(Type type, [MaybeNullWhen(false)] out IEnumerable packetIds) + { + if (_packetIds.TryGetValue(type, out packetIds)) + return true; + foreach (var (id, subRegistry) in _subPacketRegistries) + { + if (subRegistry.TryGetPacketIds(type, out IEnumerable? subPacketIds)) + { + packetIds = Enumerable.Empty().Append((byte)id).Concat(subPacketIds); + return true; + } + } + return false; + } + + /// + public bool TryGetPacketIds([MaybeNullWhen(false)] out IEnumerable packetIds) + where T : class, INetSerializable => + TryGetPacketIds(typeof(T), out packetIds); + + /// + public bool TryGetPacketType(object packetId, [MaybeNullWhen(false)] out Type type) => + _types.TryGetValue((byte)packetId, out type); + + /// + public bool TryGetSubPacketRegistry(object packetRegistryId, [MaybeNullWhen(false)] out IPacketRegistry packetRegistry) => + _subPacketRegistries.TryGetValue((byte)packetRegistryId, out packetRegistry); + + /// + public bool TryCreatePacket(object packetId, [MaybeNullWhen(false)] out INetSerializable packet) + { + if (_factories.TryGetValue((byte)packetId, out var factory)) + { + packet = factory(); + return true; + } + + packet = null; + return false; + } + + #endregion + + #region Private Methods + + protected void AddPacket(object packetId) + where T : class, INetSerializable, new() + { + var type = typeof(T); + if (_types.ContainsKey((byte)packetId) || _packetIds.ContainsKey(type)) + throw new Exception( + $"Duplicate registration for packet of type '{type.Name}' " + + $"(PacketId={packetId})." + ); + + _types[(byte)packetId] = type; + _packetIds[type] = Enumerable.Empty() + .Append((byte)packetId); + _factories[(byte)packetId] = () => new T(); + } + + protected void AddSubPacketRegistry(object packetRegistryId) + where T : class, IPacketRegistry, new() + { + var subPacketRegistry = new T(); + _subPacketRegistries[(byte)packetRegistryId] = subPacketRegistry; + //foreach (var (packetType, packetIds) in subPacketRegistry.GetAllPacketIds()) + // _packetIds[packetType] = packetIds; + } + + #endregion + } +} diff --git a/BeatTogether.DedicatedServer.Messaging/Abstractions/BaseRpcPacket.cs b/BeatTogether.DedicatedServer.Messaging/Abstractions/BaseRpcPacket.cs index 7d0f5964..cf70f3fd 100644 --- a/BeatTogether.DedicatedServer.Messaging/Abstractions/BaseRpcPacket.cs +++ b/BeatTogether.DedicatedServer.Messaging/Abstractions/BaseRpcPacket.cs @@ -1,20 +1,20 @@ -using BeatTogether.LiteNetLib.Abstractions; -using Krypton.Buffers; +using BeatTogether.Extensions; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Abstractions { public abstract class BaseRpcPacket : INetSerializable { - public float SyncTime { get; set; } + public long SyncTime { get; set; } - public virtual void ReadFrom(ref SpanBufferReader reader) + public virtual void ReadFrom(ref SpanBuffer reader) { - SyncTime = reader.ReadFloat32(); + SyncTime = (long)reader.ReadVarULong(); } - public virtual void WriteTo(ref SpanBufferWriter writer) + public virtual void WriteTo(ref SpanBuffer writer) { - writer.WriteFloat32(SyncTime); + writer.WriteVarULong((ulong)SyncTime); } } } diff --git a/BeatTogether.DedicatedServer.Messaging/Abstractions/BaseRpcWithValuesPacket.cs b/BeatTogether.DedicatedServer.Messaging/Abstractions/BaseRpcWithValuesPacket.cs index cbfcefff..495b3793 100644 --- a/BeatTogether.DedicatedServer.Messaging/Abstractions/BaseRpcWithValuesPacket.cs +++ b/BeatTogether.DedicatedServer.Messaging/Abstractions/BaseRpcWithValuesPacket.cs @@ -1,7 +1,8 @@ -using Krypton.Buffers; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Abstractions { + // WARNING do not cast to ulong for any packets that inherit this class public abstract class BaseRpcWithValuesPacket : BaseRpcPacket { public byte HasValues { get; set; } = (1 | 2 | 4 | 8); @@ -30,16 +31,17 @@ public bool HasValue3 set => HasValues |= (byte) (value ? 8 : 0); } - public override void ReadFrom(ref SpanBufferReader reader) + public override void ReadFrom(ref SpanBuffer reader) { base.ReadFrom(ref reader); HasValues = reader.ReadUInt8(); } - public override void WriteTo(ref SpanBufferWriter writer) + public override void WriteTo(ref SpanBuffer writer) { base.WriteTo(ref writer); writer.WriteUInt8(HasValues); } + } } diff --git a/BeatTogether.DedicatedServer.Messaging/Abstractions/INetSerializable.cs b/BeatTogether.DedicatedServer.Messaging/Abstractions/INetSerializable.cs new file mode 100644 index 00000000..7943681b --- /dev/null +++ b/BeatTogether.DedicatedServer.Messaging/Abstractions/INetSerializable.cs @@ -0,0 +1,11 @@ +using BeatTogether.DedicatedServer.Messaging.Util; + +namespace BeatTogether.DedicatedServer.Messaging.Abstractions +{ + public interface INetSerializable + { + void WriteTo(ref SpanBuffer bufferWriter); + + void ReadFrom(ref SpanBuffer bufferReader); + } +} diff --git a/BeatTogether.DedicatedServer.Messaging/Abstractions/IPacketRegistry.cs b/BeatTogether.DedicatedServer.Messaging/Abstractions/IPacketRegistry.cs new file mode 100644 index 00000000..a95ba21a --- /dev/null +++ b/BeatTogether.DedicatedServer.Messaging/Abstractions/IPacketRegistry.cs @@ -0,0 +1,132 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace BeatTogether.DedicatedServer.Messaging.Abstractions +{ + public interface IPacketRegistry + { + /// + /// Retrieves the identifiers of all registered packets. + /// + /// The identifiers of all registered packets. + IReadOnlyDictionary> GetAllPacketIds(); + + /// + /// Retrieves the identifiers associated with the packet + /// of the given . + /// + /// The of packet. + /// The identifiers associated with the packet. + IEnumerable GetPacketIds(Type type); + + /// + /// Retrieves the identifiers associated with the packet of type . + /// + /// The type of packet. + /// The identifiers associated with the packet. + IEnumerable GetPacketIds() + where T : class, INetSerializable; + + /// + /// Retrieves the of the packet + /// associated with the given . + /// + /// + /// The identifier associated with the packet. + /// This must be castable to a . + /// + /// The object of the packet. + Type GetPacketType(object packetId); + + /// + /// Retrieves the instance + /// associated with the given . + /// + /// + /// The identifier associated with the sub packet registry. + /// This must be castable to a . + /// + /// The instance associated with the given identifier. + IPacketRegistry GetSubPacketRegistry(object packetRegistryId); + + /// + /// Creates a new instance of the packet associated with the given . + /// + /// + /// The identifier associated with the packet. + /// This must be castable to a . + /// + /// The packet instance. + INetSerializable CreatePacket(object packetId); + + /// + /// Retrieves the identifiers associated with the packet + /// of the given . + /// + /// The of packet. + /// The identifiers associated with the packet. + /// + /// when the were retrieved successfully; + /// otherwise. + /// + bool TryGetPacketIds(Type type, [MaybeNullWhen(false)] out IEnumerable packetIds); + + /// + /// Retrieves the identifiers associated with the packet of type . + /// + /// The type of packet. + /// The identifiers associated with the packet. + /// + /// when the were retrieved successfully; + /// otherwise. + /// + bool TryGetPacketIds([MaybeNullWhen(false)] out IEnumerable packetIds) + where T : class, INetSerializable; + + /// + /// Retrieves the of the packet + /// associated with the given . + /// + /// + /// The identifier associated with the packet. + /// This must be castable to a . + /// + /// The object of the packet. + /// + /// when the was retrieved successfully; + /// otherwise. + bool TryGetPacketType(object packetId, [MaybeNullWhen(false)] out Type type); + + /// + /// Retrieves the instance + /// associated with the given . + /// + /// + /// The identifier associated with the sub packet registry. + /// This must be castable to a . + /// + /// + /// The instance + /// associated with the given identifier. + /// + /// + /// when the was retrieved successfully; + /// otherwise. + bool TryGetSubPacketRegistry(object packetRegistryId, [MaybeNullWhen(false)] out IPacketRegistry packetRegistry); + + /// + /// Creates a new instance of the packet associated with the given . + /// + /// + /// The identifier associated with the packet. + /// This must be castable to a . + /// + /// The packet instance. + /// + /// when the was created successfully; + /// otherwise. + /// + bool TryCreatePacket(object packetId, [MaybeNullWhen(false)] out INetSerializable packet); + } +} diff --git a/BeatTogether.DedicatedServer.Messaging/BeatTogether.DedicatedServer.Messaging.csproj b/BeatTogether.DedicatedServer.Messaging/BeatTogether.DedicatedServer.Messaging.csproj index d8bbda19..9ed81f49 100644 --- a/BeatTogether.DedicatedServer.Messaging/BeatTogether.DedicatedServer.Messaging.csproj +++ b/BeatTogether.DedicatedServer.Messaging/BeatTogether.DedicatedServer.Messaging.csproj @@ -7,10 +7,14 @@ - + + - + + + + diff --git a/BeatTogether.DedicatedServer.Messaging/Converter/AvatarDataMultiplayerAvatarsDataConverter.cs b/BeatTogether.DedicatedServer.Messaging/Converter/AvatarDataMultiplayerAvatarsDataConverter.cs new file mode 100644 index 00000000..3778d8c2 --- /dev/null +++ b/BeatTogether.DedicatedServer.Messaging/Converter/AvatarDataMultiplayerAvatarsDataConverter.cs @@ -0,0 +1,55 @@ +using BeatTogether.Extensions; +using BeatTogether.DedicatedServer.Messaging.Models; +using BeatTogether.DedicatedServer.Messaging.Structs; +using System.IO; + +namespace BeatTogether.DedicatedServer.Messaging.Converter +{ + public static class AvatarDataMultiplayerAvatarsDataConverter + { + public static AvatarSystemIdentifier BaseGameAvatarSystemTypeIdentifier = new AvatarSystemIdentifier("BeatAvatarSystem"); + public static MultiplayerAvatarData CreateMultiplayerAvatarsData(this AvatarData avatarData) + { + MultiplayerAvatarData multiplayerAvatarData; + using (MemoryStream memoryStream = new MemoryStream()) + { + using (BinaryWriter binaryWriter = new BinaryWriter(memoryStream)) + { + binaryWriter.Write(avatarData.HeadTopId); + binaryWriter.Write(avatarData.HeadTopPrimaryColor); + binaryWriter.Write(avatarData.HeadTopSecondaryColor); + binaryWriter.Write(avatarData.GlassesId); + binaryWriter.Write(avatarData.GlassesColor); + binaryWriter.Write(avatarData.FacialHairId); + binaryWriter.Write(avatarData.FacialHairColor); + binaryWriter.Write(avatarData.HandsId); + binaryWriter.Write(avatarData.HandsColor); + binaryWriter.Write(avatarData.ClothesId); + binaryWriter.Write(avatarData.ClothesPrimaryColor); + binaryWriter.Write(avatarData.ClothesSecondaryColor); + binaryWriter.Write(avatarData.ClothesDetailColor); + binaryWriter.Write(avatarData.SkinColorId); + binaryWriter.Write(avatarData.EyesId); + binaryWriter.Write(avatarData.MouthId); + byte[] array = memoryStream.ToArray(); + multiplayerAvatarData = new MultiplayerAvatarData(BaseGameAvatarSystemTypeIdentifier.AvatarTypeIdentifierHash, array); + } + } + return multiplayerAvatarData; + } + + public static AvatarData CreateAvatarData(this MultiplayerAvatarData multiplayerAvatarsData) + { + AvatarData avatarData; + using (MemoryStream memoryStream = new MemoryStream(multiplayerAvatarsData.Data!)) + { + memoryStream.Position = 0L; + using (BinaryReader binaryReader = new BinaryReader(memoryStream)) + { + avatarData = new AvatarData(binaryReader.ReadString(), binaryReader.ReadColor(), binaryReader.ReadColor(), binaryReader.ReadString(), binaryReader.ReadColor(), binaryReader.ReadString(), binaryReader.ReadColor(), binaryReader.ReadString(), binaryReader.ReadColor(), binaryReader.ReadString(), binaryReader.ReadColor(), binaryReader.ReadColor(), binaryReader.ReadColor(), binaryReader.ReadString(), binaryReader.ReadString(), binaryReader.ReadString()); + } + } + return avatarData; + } + } +} diff --git a/BeatTogether.DedicatedServer.Messaging/Delegates/PacketFactory.cs b/BeatTogether.DedicatedServer.Messaging/Delegates/PacketFactory.cs index d13e324a..5e6efbaf 100644 --- a/BeatTogether.DedicatedServer.Messaging/Delegates/PacketFactory.cs +++ b/BeatTogether.DedicatedServer.Messaging/Delegates/PacketFactory.cs @@ -1,4 +1,4 @@ -using BeatTogether.LiteNetLib.Abstractions; +using BeatTogether.DedicatedServer.Messaging.Abstractions; namespace BeatTogether.DedicatedServer.Messaging.Delegates { diff --git a/BeatTogether.DedicatedServer.Messaging/Enums/EntitlementStatus.cs b/BeatTogether.DedicatedServer.Messaging/Enums/EntitlementStatus.cs index 3fca870a..0bd96c6d 100644 --- a/BeatTogether.DedicatedServer.Messaging/Enums/EntitlementStatus.cs +++ b/BeatTogether.DedicatedServer.Messaging/Enums/EntitlementStatus.cs @@ -1,10 +1,11 @@ namespace BeatTogether.DedicatedServer.Messaging.Enums { + //Unknown public enum EntitlementStatus { - Unknown = 0, - NotOwned = 1, - NotDownloaded = 2, - Ok = 3 + Unknown = 0,//means the server does not current have that users entitlement + NotOwned = 1,//Player cannot download or play the map + NotDownloaded = 2, //Player can play once downloaded + Ok = 3 //Has map } } diff --git a/BeatTogether.DedicatedServer.Messaging/Enums/GameLiftMessageType.cs b/BeatTogether.DedicatedServer.Messaging/Enums/GameLiftMessageType.cs deleted file mode 100644 index 0ef1c819..00000000 --- a/BeatTogether.DedicatedServer.Messaging/Enums/GameLiftMessageType.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace BeatTogether.DedicatedServer.Messaging.Enums -{ - public enum GameLiftMessageType : uint - { - AuthenticateUserRequest, - AuthenticateUserResponse, - MessageReceivedAcknowledge, - MultipartMessage - } -} \ No newline at end of file diff --git a/BeatTogether.DedicatedServer.Messaging/Enums/MultiplayerSessionPacketType.cs b/BeatTogether.DedicatedServer.Messaging/Enums/MultiplayerSessionPacketType.cs index 72efa43d..0e7ce02f 100644 --- a/BeatTogether.DedicatedServer.Messaging/Enums/MultiplayerSessionPacketType.cs +++ b/BeatTogether.DedicatedServer.Messaging/Enums/MultiplayerSessionPacketType.cs @@ -8,6 +8,7 @@ public enum MultiplayerSessionPacketType : byte ScoreSyncState = 3, NodePoseSyncStateDelta = 4, ScoreSyncStateDelta = 5, + OptionalAvatarData = 6, MultiplayerCore = 100 } } diff --git a/BeatTogether.DedicatedServer.Messaging/Enums/MultiplayerGameState.cs b/BeatTogether.DedicatedServer.Messaging/Enums/PacketOption.cs similarity index 50% rename from BeatTogether.DedicatedServer.Messaging/Enums/MultiplayerGameState.cs rename to BeatTogether.DedicatedServer.Messaging/Enums/PacketOption.cs index d2e4238d..eb0840d4 100644 --- a/BeatTogether.DedicatedServer.Messaging/Enums/MultiplayerGameState.cs +++ b/BeatTogether.DedicatedServer.Messaging/Enums/PacketOption.cs @@ -1,9 +1,9 @@ namespace BeatTogether.DedicatedServer.Messaging.Enums { - public enum MultiplayerGameState + public enum PacketOption { None = 0, - Lobby = 1, - Game = 2 + Encrypted = 1, + OnlyFirstDegreeConnections = 2 } } diff --git a/BeatTogether.DedicatedServer.Messaging/Extensions/BinaryReadWriteExtension.cs b/BeatTogether.DedicatedServer.Messaging/Extensions/BinaryReadWriteExtension.cs new file mode 100644 index 00000000..8db6bcf6 --- /dev/null +++ b/BeatTogether.DedicatedServer.Messaging/Extensions/BinaryReadWriteExtension.cs @@ -0,0 +1,23 @@ +using System.IO; +using BeatTogether.DedicatedServer.Messaging.Models; + +namespace BeatTogether.Extensions +{ + public static class BinaryReadWriteExtension + { + // Token: 0x06000069 RID: 105 RVA: 0x00002ED8 File Offset: 0x000010D8 + public static void Write(this BinaryWriter binaryWriter, Color color) + { + binaryWriter.Write(color.r); + binaryWriter.Write(color.g); + binaryWriter.Write(color.b); + binaryWriter.Write(color.a); + } + + // Token: 0x0600006A RID: 106 RVA: 0x00002F0A File Offset: 0x0000110A + public static Color ReadColor(this BinaryReader binaryReader) + { + return new Color(binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle()); + } + } +} diff --git a/BeatTogether.DedicatedServer.Messaging/Extensions/ServiceCollectionExtensions.cs b/BeatTogether.DedicatedServer.Messaging/Extensions/ServiceCollectionExtensions.cs index e374a8c2..4be8279f 100644 --- a/BeatTogether.DedicatedServer.Messaging/Extensions/ServiceCollectionExtensions.cs +++ b/BeatTogether.DedicatedServer.Messaging/Extensions/ServiceCollectionExtensions.cs @@ -1,5 +1,5 @@ using BeatTogether.DedicatedServer.Messaging.Registries; -using BeatTogether.LiteNetLib.Abstractions; +using BeatTogether.DedicatedServer.Messaging.Abstractions; using Microsoft.Extensions.DependencyInjection; namespace BeatTogether.Extensions diff --git a/BeatTogether.DedicatedServer.Messaging/Extensions/SpanBufferExtensions.cs b/BeatTogether.DedicatedServer.Messaging/Extensions/SpanBufferExtensions.cs new file mode 100644 index 00000000..c122d1bb --- /dev/null +++ b/BeatTogether.DedicatedServer.Messaging/Extensions/SpanBufferExtensions.cs @@ -0,0 +1,218 @@ +using BeatTogether.DedicatedServer.Messaging.Enums; +using BeatTogether.DedicatedServer.Messaging.Util; +using System; +using System.Diagnostics.CodeAnalysis; +using System.Drawing; +using System.Net; +using System.Text; + +namespace BeatTogether.Extensions +{ + public static class SpanBufferExtensions + { + public static (byte SenderId, byte ReceiverId, PacketOption packetOptions) ReadRoutingHeader(this ref SpanBuffer reader) => + (reader.ReadUInt8(), reader.ReadUInt8(), (PacketOption)reader.ReadUInt8()); + + public static bool TryReadRoutingHeader(this ref SpanBuffer reader, [MaybeNullWhen(false)] out (byte SenderId, byte ReceiverId, PacketOption packetOptions) routingHeader) + { + if (reader.RemainingSize < 2) + { + routingHeader = default; + return false; + } + + routingHeader = (reader.ReadUInt8(), reader.ReadUInt8(), (PacketOption)reader.ReadUInt8()); + return true; + } + + public static void WriteRoutingHeader(this ref SpanBuffer writer, byte senderId, byte receiverId, PacketOption packetOptions = PacketOption.None) => + writer.WriteRoutingHeader(senderId, receiverId, (byte)packetOptions); + + + + public static void WriteRoutingHeader(this ref SpanBuffer writer, byte senderId, byte receiverId, byte packetOptions) + { + writer.WriteUInt8(senderId); + writer.WriteUInt8(receiverId); + writer.WriteUInt8(packetOptions); + } + + public static ulong ReadVarULong(this ref SpanBuffer bufferReader) + { + ulong num = 0uL; + int num2 = 0; + byte b = bufferReader.ReadByte(); + while (((ulong)b & 0x80uL) != 0L) + { + num |= ((ulong)b & 0x7FuL) << num2; + num2 += 7; + b = bufferReader.ReadByte(); + } + + return num | ((ulong)b << num2); + } + + public static long ReadVarLong(this ref SpanBuffer bufferReader) + { + long num = (long)bufferReader.ReadVarULong(); + if ((num & 1) != 1) + { + return num >> 1; + } + + return -(num >> 1) + 1; + } + + public static uint ReadVarUInt(this ref SpanBuffer bufferReader) + { + return (uint)bufferReader.ReadVarULong(); + } + + public static int ReadVarInt(this ref SpanBuffer bufferReader) + { + return (int)bufferReader.ReadVarLong(); + } + + public static bool TryReadVarULong(this ref SpanBuffer bufferReader, out ulong value) + { + value = 0uL; + int num = 0; + while (num <= 63 && bufferReader.RemainingSize >= 1) + { + byte b = bufferReader.ReadByte(); + value |= (ulong)((long)(b & 0x7F) << num); + num += 7; + if ((b & 0x80) == 0) + { + return true; + } + } + + value = 0uL; + return false; + } + + public static bool TryReadVarUInt(this ref SpanBuffer bufferReader, out uint value) + { + if (bufferReader.TryReadVarULong(out var value2) && value2 >> 32 == 0L) + { + value = (uint)value2; + return true; + } + + value = 0u; + return false; + } + + public static ReadOnlySpan ReadVarBytes(this ref SpanBuffer bufferReader) + { + uint count = bufferReader.ReadVarUInt(); + return bufferReader.ReadBytes((int)count); + } + + public static ReadOnlySpan ReadByteArray(this ref SpanBuffer bufferReader) + { + ushort num = bufferReader.ReadUInt16(); + return bufferReader.ReadBytes(num); + } + + public static string ReadString(this ref SpanBuffer bufferReader, int maxLength = 65535) + { + int num = bufferReader.ReadInt32(); + if (num <= 0 || num > maxLength) + { + return string.Empty; + } + + ReadOnlySpan bytes = bufferReader.ReadBytes(num); + return Encoding.UTF8.GetString(bytes); + } + + public static IPEndPoint ReadIPEndPoint(this ref SpanBuffer bufferReader) + { + if (!IPAddress.TryParse(bufferReader.ReadString(512), out var address)) + { + throw new ArgumentException("Failed to parse IP address"); + } + + int port = bufferReader.ReadInt32(); + return new IPEndPoint(address, port); + } + + public static Color ReadColor(this ref SpanBuffer reader) + { + var r = reader.ReadByte(); + var g = reader.ReadByte(); + var b = reader.ReadByte(); + var a = reader.ReadByte(); + return Color.FromArgb(a, r, g, b); + } + + public static void WriteVarULong(this ref SpanBuffer bufferWriter, ulong value) + { + do + { + byte b = (byte)(value & 0x7F); + value >>= 7; + if (value != 0L) + { + b = (byte)(b | 0x80u); + } + + bufferWriter.WriteUInt8(b); + } + while (value != 0L); + } + + public static void WriteVarLong(this ref SpanBuffer bufferWriter, long value) + { + bufferWriter.WriteVarULong((ulong)((value < 0) ? ((-value << 1) - 1) : (value << 1))); + } + + public static void WriteVarUInt(this ref SpanBuffer buffer, uint value) + { + buffer.WriteVarULong(value); + } + + public static void WriteVarInt(this ref SpanBuffer bufferWriter, int value) + { + bufferWriter.WriteVarLong(value); + } + + public static void WriteVarBytes(this ref SpanBuffer bufferWriter, ReadOnlySpan bytes) + { + bufferWriter.WriteVarUInt((uint)bytes.Length); + bufferWriter.WriteBytes(bytes); + } + + public static void WriteByteArray(this ref SpanBuffer bufferWriter, byte[]? arr = null) + { + ushort num = (arr == null) ? (ushort)0 : ((ushort)arr.Length); + bufferWriter.WriteUInt16(num); + if (arr != null) + { + bufferWriter.WriteBytes(arr); + } + } + + public static void WriteString(this ref SpanBuffer bufferWriter, string value) + { + bufferWriter.WriteInt32(Encoding.UTF8.GetByteCount(value)); + bufferWriter.WriteBytes(Encoding.UTF8.GetBytes(value)); + } + + public static void WriteIPEndPoint(this ref SpanBuffer bufferWriter, IPEndPoint ipEndPoint) + { + bufferWriter.WriteString(ipEndPoint.Address.ToString()); + bufferWriter.WriteInt32(ipEndPoint.Port); + } + + public static void WriteColor(this ref SpanBuffer writer, Color value) + { + writer.WriteUInt8(value.R); + writer.WriteUInt8(value.G); + writer.WriteUInt8(value.B); + writer.WriteUInt8(value.A); + } + } +} diff --git a/BeatTogether.DedicatedServer.Messaging/Extensions/SpanBufferReaderExtensions.cs b/BeatTogether.DedicatedServer.Messaging/Extensions/SpanBufferReaderExtensions.cs index e3dc07ad..7865e03a 100644 --- a/BeatTogether.DedicatedServer.Messaging/Extensions/SpanBufferReaderExtensions.cs +++ b/BeatTogether.DedicatedServer.Messaging/Extensions/SpanBufferReaderExtensions.cs @@ -1,14 +1,18 @@ -using Krypton.Buffers; +using BeatTogether.DedicatedServer.Messaging.Models; +using Krypton.Buffers; +using System; using System.Diagnostics.CodeAnalysis; +using System.Net; +using System.Text; -namespace BeatTogether.Extensions +namespace BeatTogether.DedicatedServer.Messaging.Extensions { public static class SpanBufferReaderExtensions { - public static (byte SenderId, byte ReceiverId) ReadRoutingHeader(this ref SpanBufferReader reader) => - (reader.ReadUInt8(), reader.ReadUInt8()); + public static (byte SenderId, byte ReceiverId, byte PacketOption) ReadRoutingHeader(this ref SpanBufferReader reader) => + (reader.ReadUInt8(), reader.ReadUInt8(), reader.ReadUInt8()); - public static bool TryReadRoutingHeader(this ref SpanBufferReader reader, [MaybeNullWhen(false)] out (byte SenderId, byte ReceiverId) routingHeader) + public static bool TryReadRoutingHeader(this ref SpanBufferReader reader, [MaybeNullWhen(false)] out (byte SenderId, byte ReceiverId, byte PacketOption) routingHeader) { if (reader.RemainingSize < 2) { @@ -16,8 +20,125 @@ public static bool TryReadRoutingHeader(this ref SpanBufferReader reader, [Maybe return false; } - routingHeader = (reader.ReadUInt8(), reader.ReadUInt8()); + routingHeader = (reader.ReadUInt8(), reader.ReadUInt8(), reader.ReadUInt8()); return true; } + + public static void ReadBytesArray(this ref SpanBufferReader reader, ref byte[]? array) + { + int length = (int)reader.ReadVarUInt(); + array = reader.ReadBytes(length).ToArray(); + } + + public static void ReadColor(this ref SpanBufferReader reader, ref Models.Color color) + { + color.r = reader.ReadFloat32(); + color.g = reader.ReadFloat32(); + color.b = reader.ReadFloat32(); + color.a = reader.ReadFloat32(); + } + + public static void ReadColorNoAlpha(this ref SpanBufferReader reader, ref ColorNoAlpha color) + { + color.r = reader.ReadFloat32(); + color.g = reader.ReadFloat32(); + color.b = reader.ReadFloat32(); + } + + public static void ReadBeatmapIdentifier(this ref SpanBufferReader reader, ref BeatmapIdentifier beatmapIdentifier) + { + beatmapIdentifier.LevelId = reader.ReadString(); + beatmapIdentifier.Characteristic = reader.ReadString(); + beatmapIdentifier.Difficulty = (BeatmapDifficulty)reader.ReadVarUInt(); + } + public static ulong ReadVarULong(this ref SpanBufferReader bufferReader) + { + var value = 0UL; + var shift = 0; + var b = bufferReader.ReadByte(); + while ((b & 128UL) != 0UL) + { + value |= (b & 127UL) << shift; + shift += 7; + b = bufferReader.ReadByte(); + } + return value | (ulong)b << shift; + } + + public static long ReadVarLong(this ref SpanBufferReader bufferReader) + { + var varULong = (long)bufferReader.ReadVarULong(); + if ((varULong & 1L) != 1L) + return varULong >> 1; + return -(varULong >> 1) + 1L; + } + + public static uint ReadVarUInt(this ref SpanBufferReader bufferReader) + => (uint)bufferReader.ReadVarULong(); + + public static int ReadVarInt(this ref SpanBufferReader bufferReader) + => (int)bufferReader.ReadVarLong(); + + public static bool TryReadVarULong(this ref SpanBufferReader bufferReader, out ulong value) + { + value = 0UL; + var shift = 0; + while (shift <= 63 && bufferReader.RemainingSize >= 1) + { + var b = bufferReader.ReadByte(); + value |= (ulong)(b & 127) << shift; + shift += 7; + if ((b & 128) == 0) + return true; + } + + value = 0UL; + return false; + } + + public static bool TryReadVarUInt(this ref SpanBufferReader bufferReader, out uint value) + { + ulong num; + if (bufferReader.TryReadVarULong(out num) && (num >> 32) == 0UL) + { + value = (uint)num; + return true; + } + + value = 0U; + return false; + } + + public static ReadOnlySpan ReadVarBytes(this ref SpanBufferReader bufferReader) + { + var length = bufferReader.ReadVarUInt(); + return bufferReader.ReadBytes((int)length); + } + + public static string ReadString(this ref SpanBufferReader bufferReader, int maxLength = 65535) + { + var length = bufferReader.ReadInt32(); + if (length <= 0 | length > maxLength) + return string.Empty; + var bytes = bufferReader.ReadBytes(length); + return Encoding.UTF8.GetString(bytes); + } + + public static IPEndPoint ReadIPEndPoint(this ref SpanBufferReader bufferReader) + { + if (!IPAddress.TryParse(bufferReader.ReadString(512), out var address)) + throw new ArgumentException("Failed to parse IP address"); + var port = bufferReader.ReadInt32(); + return new IPEndPoint(address, port); + } + + public static System.Drawing.Color ReadColor(this ref SpanBufferReader reader) + { + var r = reader.ReadByte(); + var g = reader.ReadByte(); + var b = reader.ReadByte(); + var a = reader.ReadByte(); + return System.Drawing.Color.FromArgb(a, r, g, b); + } } } diff --git a/BeatTogether.DedicatedServer.Messaging/Extensions/SpanBufferWriterExtensions.cs b/BeatTogether.DedicatedServer.Messaging/Extensions/SpanBufferWriterExtensions.cs index 7edf1f30..7e6ced42 100644 --- a/BeatTogether.DedicatedServer.Messaging/Extensions/SpanBufferWriterExtensions.cs +++ b/BeatTogether.DedicatedServer.Messaging/Extensions/SpanBufferWriterExtensions.cs @@ -1,13 +1,58 @@ using Krypton.Buffers; +using System; +using System.Drawing; +using System.Net; +using System.Text; -namespace BeatTogether.Extensions +namespace BeatTogether.DedicatedServer.Messaging.Extensions { public static class SpanBufferWriterExtensions { - public static void WriteRoutingHeader(this ref SpanBufferWriter writer, byte senderId, byte receiverId) + public static void WriteVarULong(this ref SpanBufferWriter bufferWriter, ulong value) { - writer.WriteUInt8(senderId); - writer.WriteUInt8(receiverId); + do + { + var b = (byte)(value & 127UL); + value >>= 7; + if (value != 0UL) + b |= 128; + bufferWriter.WriteUInt8(b); + } while (value != 0UL); + } + + public static void WriteVarLong(this ref SpanBufferWriter bufferWriter, long value) + => bufferWriter.WriteVarULong((value < 0L ? (ulong)((-value << 1) - 1L) : (ulong)(value << 1))); + + public static void WriteVarUInt(this ref SpanBufferWriter buffer, uint value) + => buffer.WriteVarULong(value); + + public static void WriteVarInt(this ref SpanBufferWriter bufferWriter, int value) + => bufferWriter.WriteVarLong(value); + + public static void WriteVarBytes(this ref SpanBufferWriter bufferWriter, ReadOnlySpan bytes) + { + bufferWriter.WriteVarUInt((uint)bytes.Length); + bufferWriter.WriteBytes(bytes); + } + + public static void WriteString(this ref SpanBufferWriter bufferWriter, string value) + { + bufferWriter.WriteInt32(Encoding.UTF8.GetByteCount(value)); + bufferWriter.WriteBytes(Encoding.UTF8.GetBytes(value)); + } + + public static void WriteIPEndPoint(this ref SpanBufferWriter bufferWriter, IPEndPoint ipEndPoint) + { + bufferWriter.WriteString(ipEndPoint.Address.ToString()); + bufferWriter.WriteInt32(ipEndPoint.Port); + } + + public static void WriteColor(this ref SpanBufferWriter writer, Color value) + { + writer.WriteUInt8(value.R); + writer.WriteUInt8(value.G); + writer.WriteUInt8(value.B); + writer.WriteUInt8(value.A); } } } diff --git a/BeatTogether.DedicatedServer.Messaging/Messages/GameLift/AuthenticateGameLiftUserRequest.cs b/BeatTogether.DedicatedServer.Messaging/Messages/GameLift/AuthenticateGameLiftUserRequest.cs deleted file mode 100644 index d5936156..00000000 --- a/BeatTogether.DedicatedServer.Messaging/Messages/GameLift/AuthenticateGameLiftUserRequest.cs +++ /dev/null @@ -1,32 +0,0 @@ -using BeatTogether.Core.Messaging.Abstractions; -using BeatTogether.LiteNetLib.Abstractions; -using BeatTogether.LiteNetLib.Extensions; -using Krypton.Buffers; - -namespace BeatTogether.DedicatedServer.Messaging.Messages.GameLift -{ - public sealed class AuthenticateGameLiftUserRequest : IEncryptedMessage, IReliableRequest, IReliableResponse - { - public uint SequenceId { get; set; } - public uint RequestId { get; set; } - public uint ResponseId { get; set; } - - public string UserId { get; set; } - public string UserName { get; set; } - public string PlayerSessionId { get; set; } - - public void ReadFrom(ref SpanBufferReader reader) - { - UserId = reader.ReadString(); - UserName = reader.ReadString(); - PlayerSessionId = reader.ReadString(); - } - - public void WriteTo(ref SpanBufferWriter writer) - { - writer.WriteString(UserId); - writer.WriteString(UserName); - writer.WriteString(PlayerSessionId); - } - } -} diff --git a/BeatTogether.DedicatedServer.Messaging/Messages/GameLift/AuthenticateGameLiftUserResponse.cs b/BeatTogether.DedicatedServer.Messaging/Messages/GameLift/AuthenticateGameLiftUserResponse.cs deleted file mode 100644 index 1493cca9..00000000 --- a/BeatTogether.DedicatedServer.Messaging/Messages/GameLift/AuthenticateGameLiftUserResponse.cs +++ /dev/null @@ -1,32 +0,0 @@ -using BeatTogether.Core.Messaging.Abstractions; -using Krypton.Buffers; - -namespace BeatTogether.DedicatedServer.Messaging.Messages.GameLift -{ - public enum AuthenticateUserResult : byte - { - Success = 0, - Failed = 1, - UnknownError = 2 - } - - public sealed class AuthenticateGameLiftUserResponse : IEncryptedMessage, IReliableRequest, IReliableResponse - { - public uint SequenceId { get; set; } - public uint RequestId { get; set; } - public uint ResponseId { get; set; } - public AuthenticateUserResult Result { get; set; } - - public bool Success => Result == AuthenticateUserResult.Success; - - public void WriteTo(ref SpanBufferWriter bufferWriter) - { - bufferWriter.WriteUInt8((byte)Result); - } - - public void ReadFrom(ref SpanBufferReader bufferReader) - { - Result = (AuthenticateUserResult)bufferReader.ReadByte(); - } - } -} diff --git a/BeatTogether.DedicatedServer.Messaging/Messages/Handshake/ChangeCipherSpecRequest.cs b/BeatTogether.DedicatedServer.Messaging/Messages/Handshake/ChangeCipherSpecRequest.cs deleted file mode 100644 index f01f6a6d..00000000 --- a/BeatTogether.DedicatedServer.Messaging/Messages/Handshake/ChangeCipherSpecRequest.cs +++ /dev/null @@ -1,19 +0,0 @@ -using BeatTogether.Core.Messaging.Abstractions; -using Krypton.Buffers; - -namespace BeatTogether.DedicatedServer.Messaging.Messages.Handshake -{ - public sealed class ChangeCipherSpecRequest : IMessage, IReliableRequest, IReliableResponse - { - public uint RequestId { get; set; } - public uint ResponseId { get; set; } - - public void WriteTo(ref SpanBufferWriter bufferWriter) - { - } - - public void ReadFrom(ref SpanBufferReader bufferReader) - { - } - } -} diff --git a/BeatTogether.DedicatedServer.Messaging/Messages/Handshake/ClientHelloRequest.cs b/BeatTogether.DedicatedServer.Messaging/Messages/Handshake/ClientHelloRequest.cs deleted file mode 100644 index 0d145997..00000000 --- a/BeatTogether.DedicatedServer.Messaging/Messages/Handshake/ClientHelloRequest.cs +++ /dev/null @@ -1,21 +0,0 @@ -using BeatTogether.Core.Messaging.Abstractions; -using Krypton.Buffers; - -namespace BeatTogether.DedicatedServer.Messaging.Messages.Handshake -{ - public sealed class ClientHelloRequest : IMessage, IReliableRequest - { - public uint RequestId { get; set; } - public byte[] Random { get; set; } - - public void WriteTo(ref SpanBufferWriter bufferWriter) - { - bufferWriter.WriteBytes(Random); - } - - public void ReadFrom(ref SpanBufferReader bufferReader) - { - Random = bufferReader.ReadBytes(32).ToArray(); - } - } -} \ No newline at end of file diff --git a/BeatTogether.DedicatedServer.Messaging/Messages/Handshake/ClientHelloWithCookieRequest.cs b/BeatTogether.DedicatedServer.Messaging/Messages/Handshake/ClientHelloWithCookieRequest.cs deleted file mode 100644 index b0811e3c..00000000 --- a/BeatTogether.DedicatedServer.Messaging/Messages/Handshake/ClientHelloWithCookieRequest.cs +++ /dev/null @@ -1,27 +0,0 @@ -using BeatTogether.Core.Messaging.Abstractions; -using Krypton.Buffers; - -namespace BeatTogether.DedicatedServer.Messaging.Messages.Handshake -{ - public sealed class ClientHelloWithCookieRequest : IMessage, IReliableRequest - { - public uint RequestId { get; set; } - public uint CertificateResponseId { get; set; } - public byte[] Random { get; set; } - public byte[] Cookie { get; set; } - - public void WriteTo(ref SpanBufferWriter bufferWriter) - { - bufferWriter.WriteUInt32(CertificateResponseId); - bufferWriter.WriteBytes(Random); - bufferWriter.WriteBytes(Cookie); - } - - public void ReadFrom(ref SpanBufferReader bufferReader) - { - CertificateResponseId = bufferReader.ReadUInt32(); - Random = bufferReader.ReadBytes(32).ToArray(); - Cookie = bufferReader.ReadBytes(32).ToArray(); - } - } -} diff --git a/BeatTogether.DedicatedServer.Messaging/Messages/Handshake/ClientKeyExchangeRequest.cs b/BeatTogether.DedicatedServer.Messaging/Messages/Handshake/ClientKeyExchangeRequest.cs deleted file mode 100644 index 34a40101..00000000 --- a/BeatTogether.DedicatedServer.Messaging/Messages/Handshake/ClientKeyExchangeRequest.cs +++ /dev/null @@ -1,23 +0,0 @@ -using BeatTogether.Core.Messaging.Abstractions; -using BeatTogether.Extensions; -using Krypton.Buffers; - -namespace BeatTogether.DedicatedServer.Messaging.Messages.Handshake -{ - public sealed class ClientKeyExchangeRequest : IMessage, IReliableRequest, IReliableResponse - { - public uint RequestId { get; set; } - public uint ResponseId { get; set; } - public byte[] ClientPublicKey { get; set; } - - public void WriteTo(ref SpanBufferWriter bufferWriter) - { - bufferWriter.WriteVarBytes(ClientPublicKey); - } - - public void ReadFrom(ref SpanBufferReader bufferReader) - { - ClientPublicKey = bufferReader.ReadVarBytes().ToArray(); - } - } -} diff --git a/BeatTogether.DedicatedServer.Messaging/Messages/Handshake/HelloVerifyRequest.cs b/BeatTogether.DedicatedServer.Messaging/Messages/Handshake/HelloVerifyRequest.cs deleted file mode 100644 index cd4addda..00000000 --- a/BeatTogether.DedicatedServer.Messaging/Messages/Handshake/HelloVerifyRequest.cs +++ /dev/null @@ -1,22 +0,0 @@ -using BeatTogether.Core.Messaging.Abstractions; -using Krypton.Buffers; - -namespace BeatTogether.DedicatedServer.Messaging.Messages.Handshake -{ - public sealed class HelloVerifyRequest : IMessage, IRequest, IResponse - { - public uint RequestId { get; set; } - public uint ResponseId { get; set; } - public byte[] Cookie { get; set; } - - public void WriteTo(ref SpanBufferWriter bufferWriter) - { - bufferWriter.WriteBytes(Cookie); - } - - public void ReadFrom(ref SpanBufferReader bufferReader) - { - Cookie = bufferReader.ReadBytes(32).ToArray(); - } - } -} diff --git a/BeatTogether.DedicatedServer.Messaging/Messages/Handshake/ServerCertificateRequest.cs b/BeatTogether.DedicatedServer.Messaging/Messages/Handshake/ServerCertificateRequest.cs deleted file mode 100644 index 42f2585f..00000000 --- a/BeatTogether.DedicatedServer.Messaging/Messages/Handshake/ServerCertificateRequest.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System.Collections.Generic; -using BeatTogether.Core.Messaging.Abstractions; -using BeatTogether.Extensions; -using Krypton.Buffers; - -namespace BeatTogether.DedicatedServer.Messaging.Messages.Handshake -{ - public sealed class ServerCertificateRequest : IMessage, IReliableRequest, IReliableResponse - { - public uint RequestId { get; set; } - public uint ResponseId { get; set; } - public List Certificates { get; set; } - - public void WriteTo(ref SpanBufferWriter bufferWriter) - { - bufferWriter.WriteVarUInt((uint)Certificates.Count); - foreach (var certificate in Certificates) - bufferWriter.WriteVarBytes(certificate); - } - - public void ReadFrom(ref SpanBufferReader bufferReader) - { - Certificates = new List(); - var certificateCount = bufferReader.ReadVarUInt(); - for (var i = 0; i < certificateCount; i++) - Certificates.Add(bufferReader.ReadVarBytes().ToArray()); - } - } -} diff --git a/BeatTogether.DedicatedServer.Messaging/Messages/Handshake/ServerHelloRequest.cs b/BeatTogether.DedicatedServer.Messaging/Messages/Handshake/ServerHelloRequest.cs deleted file mode 100644 index fee39237..00000000 --- a/BeatTogether.DedicatedServer.Messaging/Messages/Handshake/ServerHelloRequest.cs +++ /dev/null @@ -1,29 +0,0 @@ -using BeatTogether.Core.Messaging.Abstractions; -using BeatTogether.Extensions; -using Krypton.Buffers; - -namespace BeatTogether.DedicatedServer.Messaging.Messages.Handshake -{ - public sealed class ServerHelloRequest : IMessage, IReliableRequest, IReliableResponse - { - public uint RequestId { get; set; } - public uint ResponseId { get; set; } - public byte[] Random { get; set; } - public byte[] PublicKey { get; set; } - public byte[] Signature { get; set; } - - public void WriteTo(ref SpanBufferWriter bufferWriter) - { - bufferWriter.WriteBytes(Random); - bufferWriter.WriteVarBytes(PublicKey); - bufferWriter.WriteVarBytes(Signature); - } - - public void ReadFrom(ref SpanBufferReader bufferReader) - { - Random = bufferReader.ReadBytes(32).ToArray(); - PublicKey = bufferReader.ReadVarBytes().ToArray(); - Signature = bufferReader.ReadVarBytes().ToArray(); - } - } -} diff --git a/BeatTogether.DedicatedServer.Messaging/Models/AvatarData.cs b/BeatTogether.DedicatedServer.Messaging/Models/AvatarData.cs index 3ea5a80e..72cc8db7 100644 --- a/BeatTogether.DedicatedServer.Messaging/Models/AvatarData.cs +++ b/BeatTogether.DedicatedServer.Messaging/Models/AvatarData.cs @@ -1,69 +1,93 @@ -using BeatTogether.LiteNetLib.Abstractions; -using BeatTogether.LiteNetLib.Extensions; -using Krypton.Buffers; -using System.Drawing; +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.DedicatedServer.Messaging.Util; +using System; namespace BeatTogether.DedicatedServer.Messaging.Models { public sealed class AvatarData : INetSerializable { public string HeadTopId { get; set; } = "BedHead"; - public Color HeadTopPrimaryColor { get; set; } - public Color HeadTopSecondaryColor { get; set; } + public Color HeadTopPrimaryColor { get; set; } = new Color(); + public Color HeadTopSecondaryColor { get; set; } = new Color(); public string GlassesId { get; set; } = "Default"; - public Color GlassesColor { get; set; } + public Color GlassesColor { get; set; } = new Color(); public string FacialHairId { get; set; } = "None"; - public Color FacialHairColor { get; set; } + public Color FacialHairColor { get; set; } = new Color(); public string HandsId { get; set; } = "BareHands"; - public Color HandsColor { get; set; } + public Color HandsColor { get; set; } = new Color(); public string ClothesId { get; set; } = "Hoodie"; - public Color ClothesPrimaryColor { get; set; } - public Color ClothesSecondaryColor { get; set; } - public Color ClothesDetailColor { get; set; } + public Color ClothesPrimaryColor { get; set; } = new Color(); + public Color ClothesSecondaryColor { get; set; } = new Color(); + public Color ClothesDetailColor { get; set; } = new Color(); public string SkinColorId { get; set; } = "Default"; public string EyesId { get; set; } = "Eyes1"; public string MouthId { get; set; } = "Mouth8"; - public void ReadFrom(ref SpanBufferReader reader) + public AvatarData() { } + + public AvatarData(string headTopId, Color headTopPrimaryColor, Color headTopSecondaryColor, string glassesId, Color glassesColor, string facialHairId, Color facialHairColor, string handsId, Color handsColor, string clothesId, Color clothesPrimaryColor, Color clothesSecondaryColor, Color clothesDetailColor, string skinColorId, string eyesId, string mouthId) + { + HeadTopId = headTopId; + HeadTopPrimaryColor = headTopPrimaryColor; + HeadTopSecondaryColor = headTopSecondaryColor; + GlassesId = glassesId; + GlassesColor = glassesColor; + FacialHairId = facialHairId; + FacialHairColor = facialHairColor; + HandsId = handsId; + HandsColor = handsColor; + ClothesId = clothesId; + ClothesPrimaryColor = clothesPrimaryColor; + ClothesSecondaryColor = clothesSecondaryColor; + ClothesDetailColor = clothesDetailColor; + SkinColorId = skinColorId; + EyesId = eyesId; + MouthId = mouthId; + } + + //TODO: Move class as it's no longer a INetSerializable + [Obsolete("This method is obsolete and will be removed soon. Use MultiplayerAvatarsData.ReadFrom instead.")] + public void ReadFrom(ref SpanBuffer reader) { - HeadTopId = reader.ReadString(); - HeadTopPrimaryColor = reader.ReadColor(); - HandsColor = reader.ReadColor(); - ClothesId = reader.ReadString(); - ClothesPrimaryColor = reader.ReadColor(); - ClothesSecondaryColor = reader.ReadColor(); - ClothesDetailColor = reader.ReadColor(); - reader.SkipBytes(8); - EyesId = reader.ReadString(); - MouthId = reader.ReadString(); - GlassesColor = reader.ReadColor(); - FacialHairColor = reader.ReadColor(); - HeadTopSecondaryColor = reader.ReadColor(); - GlassesId = reader.ReadString(); - FacialHairId = reader.ReadString(); - HandsId = reader.ReadString(); - SkinColorId = GlassesId; // Don't ask + //HeadTopId = reader.ReadString(); + //HeadTopPrimaryColor = reader.ReadColor(); + //HandsColor = reader.ReadColor(); + //ClothesId = reader.ReadString(); + //ClothesPrimaryColor = reader.ReadColor(); + //ClothesSecondaryColor = reader.ReadColor(); + //ClothesDetailColor = reader.ReadColor(); + //reader.SkipBytes(8); + //EyesId = reader.ReadString(); + //MouthId = reader.ReadString(); + //GlassesColor = reader.ReadColor(); + //FacialHairColor = reader.ReadColor(); + //HeadTopSecondaryColor = reader.ReadColor(); + //GlassesId = reader.ReadString(); + //FacialHairId = reader.ReadString(); + //HandsId = reader.ReadString(); + //SkinColorId = GlassesId; // Don't ask } - public void WriteTo(ref SpanBufferWriter writer) + [Obsolete("This method is obsolete and will be removed soon. Use MultiplayerAvatarsData.WriteTo instead.")] + public void WriteTo(ref SpanBuffer writer) { - writer.WriteString(HeadTopId); - writer.WriteColor(HeadTopPrimaryColor); - writer.WriteColor(HandsColor); - writer.WriteString(ClothesId); - writer.WriteColor(ClothesPrimaryColor); - writer.WriteColor(ClothesSecondaryColor); - writer.WriteColor(ClothesDetailColor); - writer.WriteColor(new Color()); - writer.WriteColor(new Color()); - writer.WriteString(EyesId); - writer.WriteString(MouthId); - writer.WriteColor(GlassesColor); - writer.WriteColor(FacialHairColor); - writer.WriteColor(HeadTopSecondaryColor); - writer.WriteString(GlassesId); - writer.WriteString(FacialHairId); - writer.WriteString(HandsId); + //writer.WriteString(HeadTopId); + //writer.WriteColor(HeadTopPrimaryColor); + //writer.WriteColor(HandsColor); + //writer.WriteString(ClothesId); + //writer.WriteColor(ClothesPrimaryColor); + //writer.WriteColor(ClothesSecondaryColor); + //writer.WriteColor(ClothesDetailColor); + //writer.WriteColor(new Color()); + //writer.WriteColor(new Color()); + //writer.WriteString(EyesId); + //writer.WriteString(MouthId); + //writer.WriteColor(GlassesColor); + //writer.WriteColor(FacialHairColor); + //writer.WriteColor(HeadTopSecondaryColor); + //writer.WriteString(GlassesId); + //writer.WriteString(FacialHairId); + //writer.WriteString(HandsId); } } } diff --git a/BeatTogether.DedicatedServer.Messaging/Models/BeatmapIdentifier.cs b/BeatTogether.DedicatedServer.Messaging/Models/BeatmapIdentifier.cs index a0911673..9503c20e 100644 --- a/BeatTogether.DedicatedServer.Messaging/Models/BeatmapIdentifier.cs +++ b/BeatTogether.DedicatedServer.Messaging/Models/BeatmapIdentifier.cs @@ -1,6 +1,6 @@ -using BeatTogether.LiteNetLib.Abstractions; -using BeatTogether.LiteNetLib.Extensions; -using Krypton.Buffers; +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.Extensions; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Models { @@ -10,14 +10,14 @@ public sealed class BeatmapIdentifier : INetSerializable public string Characteristic { get; set; } = null!; public BeatmapDifficulty Difficulty { get; set; } - public void ReadFrom(ref SpanBufferReader reader) + public void ReadFrom(ref SpanBuffer reader) { LevelId = reader.ReadString(); Characteristic = reader.ReadString(); Difficulty = (BeatmapDifficulty)reader.ReadVarUInt(); } - public void WriteTo(ref SpanBufferWriter writer) + public void WriteTo(ref SpanBuffer writer) { writer.WriteString(LevelId); writer.WriteString(Characteristic); diff --git a/BeatTogether.DedicatedServer.Messaging/Models/BitMask128.cs b/BeatTogether.DedicatedServer.Messaging/Models/BitMask128.cs index 4d22c22b..0cf66c08 100644 --- a/BeatTogether.DedicatedServer.Messaging/Models/BitMask128.cs +++ b/BeatTogether.DedicatedServer.Messaging/Models/BitMask128.cs @@ -1,5 +1,5 @@ -using BeatTogether.LiteNetLib.Abstractions; -using Krypton.Buffers; +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Models { @@ -10,20 +10,38 @@ public class BitMask128 : INetSerializable public ulong Top { get; set; } public ulong Bottom { get; set; } + public BitMask128(ulong top, ulong bottom) + { + Top = top; + Bottom = bottom; + } + + public BitMask128() { } + public bool Contains(string value, int hashCount = 3, int hashBits = 8) { - uint num = MurmurHash2(value); + uint hash = MurmurHash2(value); for (int i = 0; i < hashCount; i++) { - if (GetBits((int)((ulong)num % (ulong)((long)BitCount)), 1) == 0UL) + if (GetBits((int)(hash % (ulong)((long)BitCount)), 1) == 0UL) { return false; } - num >>= hashBits; + hash >>= hashBits; } return true; } + public void WriteToBitMask(string value, int hashCount = 3, int hashBits = 8) + { + ulong hash = MurmurHash2(value); + for(int i = 0; i < hashCount; i++) + { + SetBits((int)(hash % (ulong)((long)BitCount)), 1UL); + hash >>= hashBits; + } + } + public ulong GetBits(int offset, int count) { ulong num = (1UL << count) - 1UL; @@ -31,13 +49,20 @@ public ulong GetBits(int offset, int count) return (ShiftRight(Top, num2) | ShiftRight(Bottom, offset)) & num; } - public void ReadFrom(ref SpanBufferReader reader) + public BitMask128 SetBits(int offset, ulong bits) + { + ulong d = Top; + int num = offset - 64; + return new BitMask128(d | ShiftLeft(bits, num), Bottom | ShiftLeft(bits, offset)); + } + + public void ReadFrom(ref SpanBuffer reader) { Top = reader.ReadUInt64(); Bottom = reader.ReadUInt64(); } - public void WriteTo(ref SpanBufferWriter writer) + public void WriteTo(ref SpanBuffer writer) { writer.WriteUInt64(Top); writer.WriteUInt64(Bottom); diff --git a/BeatTogether.DedicatedServer.Messaging/Models/BitMask256.cs b/BeatTogether.DedicatedServer.Messaging/Models/BitMask256.cs new file mode 100644 index 00000000..b3c9ea95 --- /dev/null +++ b/BeatTogether.DedicatedServer.Messaging/Models/BitMask256.cs @@ -0,0 +1,142 @@ +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.DedicatedServer.Messaging.Util; + +namespace BeatTogether.DedicatedServer.Messaging.Models +{ + public class BitMask256 : INetSerializable + { + public ulong D0 { get; set; } + public ulong D1 { get; set; } + public ulong D2 { get; set; } + public ulong D3 { get; set; } + + public const int BitCount = 256; + + public static BitMask256 MaxValue => new(ulong.MaxValue, ulong.MaxValue, ulong.MaxValue, ulong.MaxValue); + public static BitMask256 MinValue => new(); + + public BitMask256(ulong d0 = 0U, ulong d1 = 0U, ulong d2 = 0U, ulong d3 = 0U) + { + D0 = d0; + D1 = d1; + D2 = d2; + D3 = d3; + } + + #region Bits + + public BitMask256 SetBits(int offset, ulong bits) + { + ulong d = D0; + int num = offset - 192; + ulong num2 = d | ShiftLeft(bits, num); + ulong d2 = D1; + int num3 = offset - 128; + ulong num4 = d2 | ShiftLeft(bits, num3); + ulong d3 = D2; + int num5 = offset - 64; + return new BitMask256(num2, num4, d3 | ShiftLeft(bits, num5), D3 | ShiftLeft(bits, offset)); + } + + public ulong GetBits(int offset, int count) + { + ulong num = (1UL << count) - 1UL; + int num2 = offset - 192; + ulong num3 = ShiftRight(D0, num2); + int num4 = offset - 128; + ulong num5 = num3 | ShiftRight(D1, num4); + int num6 = offset - 64; + return (num5 | ShiftRight(D2, num6) | ShiftRight(D3, offset)) & num; + } + + #endregion + + #region Network + + public void WriteTo(ref SpanBuffer bufferWriter) + { + bufferWriter.WriteUInt64(D0); + bufferWriter.WriteUInt64(D1); + bufferWriter.WriteUInt64(D2); + bufferWriter.WriteUInt64(D3); + } + + public void ReadFrom(ref SpanBuffer bufferReader) + { + D0 = bufferReader.ReadUInt64(); + D1 = bufferReader.ReadUInt64(); + D2 = bufferReader.ReadUInt64(); + D3 = bufferReader.ReadUInt64(); + } + + #endregion + + private static uint MurmurHash2(string key) + { + uint num = (uint)key.Length; + uint num2 = 33U ^ num; + int num3 = 0; + while (num >= 4U) + { + uint num4 = (uint)((uint)key[num3 + 3] << 24 | (uint)key[num3 + 2] << 16 | (uint)key[num3 + 1] << 8 | key[num3]); + num4 *= 1540483477U; + num4 ^= num4 >> 24; + num4 *= 1540483477U; + num2 *= 1540483477U; + num2 ^= num4; + num3 += 4; + num -= 4U; + } + switch (num) + { + case 1U: + num2 ^= (uint)key[num3]; + num2 *= 1540483477U; + break; + case 2U: + num2 ^= (uint)((uint)key[num3 + 1] << 8); + num2 ^= (uint)key[num3]; + num2 *= 1540483477U; + break; + case 3U: + num2 ^= (uint)((uint)key[num3 + 2] << 16); + num2 ^= (uint)((uint)key[num3 + 1] << 8); + num2 ^= (uint)key[num3]; + num2 *= 1540483477U; + break; + } + num2 ^= num2 >> 13; + num2 *= 1540483477U; + return num2 ^ num2 >> 15; + } + + + public static ulong ShiftLeft(ulong value, in int shift) + { + if (shift < 0) + { + int num = -shift; + return ShiftRight(value, num); + } + if (shift < 64) + { + return value << shift; + } + return 0UL; + } + + public static ulong ShiftRight(ulong value, in int shift) + { + if (shift < 0) + { + int num = -shift; + return ShiftLeft(value, num); + } + if (shift < 64) + { + return value >> shift; + } + return 0UL; + } + } +} \ No newline at end of file diff --git a/BeatTogether.DedicatedServer.Messaging/Models/ByteArray.cs b/BeatTogether.DedicatedServer.Messaging/Models/ByteArray.cs index 11bbdc4c..885ece12 100644 --- a/BeatTogether.DedicatedServer.Messaging/Models/ByteArray.cs +++ b/BeatTogether.DedicatedServer.Messaging/Models/ByteArray.cs @@ -1,7 +1,6 @@ -using BeatTogether.LiteNetLib.Abstractions; -using BeatTogether.LiteNetLib.Extensions; -using Krypton.Buffers; -using System; +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.Extensions; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Models { @@ -9,13 +8,13 @@ public sealed class ByteArray : INetSerializable { public byte[]? Data { get; set; } = null; - public void ReadFrom(ref SpanBufferReader reader) + public void ReadFrom(ref SpanBuffer reader) { int length = (int)reader.ReadVarUInt(); Data = reader.ReadBytes(length).ToArray(); } - public void WriteTo(ref SpanBufferWriter writer) + public void WriteTo(ref SpanBuffer writer) { writer.WriteVarUInt((Data != null) ? (uint)Data.Length : 0); writer.WriteBytes(Data); diff --git a/BeatTogether.DedicatedServer.Messaging/Models/Color.cs b/BeatTogether.DedicatedServer.Messaging/Models/Color.cs new file mode 100644 index 00000000..b2710739 --- /dev/null +++ b/BeatTogether.DedicatedServer.Messaging/Models/Color.cs @@ -0,0 +1,39 @@ +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.DedicatedServer.Messaging.Util; + +namespace BeatTogether.DedicatedServer.Messaging.Models +{ + public sealed class Color : INetSerializable + { + public float r { get; set; } + public float g { get; set; } + public float b { get; set; } + public float a { get; set; } + + public Color(float r, float g, float b, float a) + { + this.r = r; + this.g = g; + this.b = b; + this.a = a; + } + + public Color() { } + + public void ReadFrom(ref SpanBuffer reader) + { + r = reader.ReadFloat32(); + g = reader.ReadFloat32(); + b = reader.ReadFloat32(); + a = reader.ReadFloat32(); + } + + public void WriteTo(ref SpanBuffer writer) + { + writer.WriteFloat32(r); + writer.WriteFloat32(g); + writer.WriteFloat32(b); + writer.WriteFloat32(a); + } + } +} diff --git a/BeatTogether.DedicatedServer.Messaging/Models/ColorNoAlpha.cs b/BeatTogether.DedicatedServer.Messaging/Models/ColorNoAlpha.cs index a1721669..e5bfdfde 100644 --- a/BeatTogether.DedicatedServer.Messaging/Models/ColorNoAlpha.cs +++ b/BeatTogether.DedicatedServer.Messaging/Models/ColorNoAlpha.cs @@ -1,5 +1,5 @@ -using BeatTogether.LiteNetLib.Abstractions; -using Krypton.Buffers; +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Models { @@ -9,14 +9,14 @@ public sealed class ColorNoAlpha : INetSerializable public float g { get; set; } public float b { get; set; } - public void ReadFrom(ref SpanBufferReader reader) + public void ReadFrom(ref SpanBuffer reader) { r = reader.ReadFloat32(); g = reader.ReadFloat32(); b = reader.ReadFloat32(); } - public void WriteTo(ref SpanBufferWriter writer) + public void WriteTo(ref SpanBuffer writer) { writer.WriteFloat32(r); writer.WriteFloat32(g); diff --git a/BeatTogether.DedicatedServer.Messaging/Models/ColorScheme.cs b/BeatTogether.DedicatedServer.Messaging/Models/ColorScheme.cs index 3415ab4c..5f7590c9 100644 --- a/BeatTogether.DedicatedServer.Messaging/Models/ColorScheme.cs +++ b/BeatTogether.DedicatedServer.Messaging/Models/ColorScheme.cs @@ -1,5 +1,5 @@ -using BeatTogether.LiteNetLib.Abstractions; -using Krypton.Buffers; +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Models { @@ -13,7 +13,7 @@ public sealed class ColorScheme : INetSerializable public ColorNoAlpha EnvironmentColor0Boost { get; set; } = new(); public ColorNoAlpha EnvironmentColor1Boost { get; set; } = new(); - public void ReadFrom(ref SpanBufferReader reader) + public void ReadFrom(ref SpanBuffer reader) { SaberAColor.ReadFrom(ref reader); SaberBColor.ReadFrom(ref reader); @@ -24,7 +24,7 @@ public void ReadFrom(ref SpanBufferReader reader) EnvironmentColor1Boost.ReadFrom(ref reader); } - public void WriteTo(ref SpanBufferWriter writer) + public void WriteTo(ref SpanBuffer writer) { SaberAColor.WriteTo(ref writer); SaberBColor.WriteTo(ref writer); diff --git a/BeatTogether.DedicatedServer.Messaging/Models/ConnectionRequestData.cs b/BeatTogether.DedicatedServer.Messaging/Models/ConnectionRequestData.cs index 08dfc565..904a01a2 100644 --- a/BeatTogether.DedicatedServer.Messaging/Models/ConnectionRequestData.cs +++ b/BeatTogether.DedicatedServer.Messaging/Models/ConnectionRequestData.cs @@ -1,74 +1,34 @@ -using System; -using BeatTogether.LiteNetLib.Abstractions; -using BeatTogether.LiteNetLib.Extensions; -using Krypton.Buffers; +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.Extensions; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Models { public sealed class ConnectionRequestData : INetSerializable { - public const string SessionIdPrefix = "ps:bt$"; + //public const string SessionIdPrefix = "ps:bt$"; - // Beat Saber public string UserId { get; set; } = null!; public string UserName { get; set; } = null!; public bool IsConnectionOwner { get; set; } - - // BasicConnectionRequestHandler - public string? Secret { get; set; } = null!; - - // GameLiftClientConnectionRequestHandler - public string? PlayerSessionId { get; set; } = null!; + public string PlayerSessionId { get; set; } = null!; - public void ReadFrom(ref SpanBufferReader reader) + public void ReadFrom(ref SpanBuffer reader) { - Secret = null; - PlayerSessionId = null; - - var initialOffset = reader.Offset; - - // Try to read as a GameLift connection request - try - { - UserId = reader.ReadString(); - UserName = reader.ReadString(); - IsConnectionOwner = reader.ReadBool(); - PlayerSessionId = reader.ReadString(); - } - catch (Exception ex) { } - - if (PlayerSessionId != null && PlayerSessionId.StartsWith(SessionIdPrefix)) - // Read OK, valid session identifier - return; - - // Rewind, try to read as basic request - reader.SkipBytes(initialOffset - reader.Offset); - - Secret = reader.ReadString(); + //read as a GameLift connection request UserId = reader.ReadString(); UserName = reader.ReadString(); IsConnectionOwner = reader.ReadBool(); - PlayerSessionId = null; + PlayerSessionId = reader.ReadString(); } - public void WriteTo(ref SpanBufferWriter writer) + public void WriteTo(ref SpanBuffer writer) { - if (!string.IsNullOrEmpty(PlayerSessionId)) - { - // GameLift - writer.WriteString(UserId); - writer.WriteString(UserName); - writer.WriteBool(IsConnectionOwner); - writer.WriteString(PlayerSessionId); - } - else - { - // Basic - writer.WriteString(Secret ?? ""); - writer.WriteString(UserId); - writer.WriteString(UserName); - writer.WriteBool(IsConnectionOwner); - } + // GameLift + writer.WriteString(UserId); + writer.WriteString(UserName); + writer.WriteBool(IsConnectionOwner); + writer.WriteString(PlayerSessionId); } } } diff --git a/BeatTogether.DedicatedServer.Messaging/Models/GameplayModifiers.cs b/BeatTogether.DedicatedServer.Messaging/Models/GameplayModifiers.cs index 3a195bf4..3283e223 100644 --- a/BeatTogether.DedicatedServer.Messaging/Models/GameplayModifiers.cs +++ b/BeatTogether.DedicatedServer.Messaging/Models/GameplayModifiers.cs @@ -1,5 +1,5 @@ -using BeatTogether.LiteNetLib.Abstractions; -using Krypton.Buffers; +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Models { @@ -23,7 +23,7 @@ public sealed class GameplayModifiers : INetSerializable public bool ZenMode { get; set; } public bool SmallCubes { get; set; } - public void ReadFrom(ref SpanBufferReader reader) + public void ReadFrom(ref SpanBuffer reader) { int @int = reader.ReadInt32(); Energy = (EnergyType)(@int & 15); @@ -45,7 +45,7 @@ public void ReadFrom(ref SpanBufferReader reader) SmallCubes = (@int & 67108864) != 0; } - public void WriteTo(ref SpanBufferWriter writer) + public void WriteTo(ref SpanBuffer writer) { int num = 0; num |= (int)(Energy & (EnergyType)15); diff --git a/BeatTogether.DedicatedServer.Messaging/Models/LevelCompletionResults.cs b/BeatTogether.DedicatedServer.Messaging/Models/LevelCompletionResults.cs index b44e79ed..26149a2f 100644 --- a/BeatTogether.DedicatedServer.Messaging/Models/LevelCompletionResults.cs +++ b/BeatTogether.DedicatedServer.Messaging/Models/LevelCompletionResults.cs @@ -1,7 +1,7 @@ using BeatTogether.DedicatedServer.Messaging.Enums; -using BeatTogether.LiteNetLib.Abstractions; -using BeatTogether.LiteNetLib.Extensions; -using Krypton.Buffers; +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.Extensions; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Models { @@ -31,8 +31,9 @@ public sealed class LevelCompletionResults : INetSerializable public float AverageCutScoreForNotesWithFullScoreScoringType { get; set; } public int MaxCombo { get; set; } public float EndSongTime { get; set; } + public bool Invalidated { get; set; } - public void ReadFrom(ref SpanBufferReader reader) + public void ReadFrom(ref SpanBuffer reader) { GameplayModifiers.ReadFrom(ref reader); ModifiedScore = reader.ReadVarInt(); @@ -58,9 +59,10 @@ public void ReadFrom(ref SpanBufferReader reader) AverageCutScoreForNotesWithFullScoreScoringType = reader.ReadFloat32(); MaxCombo = reader.ReadVarInt(); EndSongTime = reader.ReadFloat32(); + Invalidated = reader.ReadBool(); // Added in 1.37.1 } - public void WriteTo(ref SpanBufferWriter writer) + public void WriteTo(ref SpanBuffer writer) { GameplayModifiers.WriteTo(ref writer); writer.WriteVarInt(ModifiedScore); @@ -86,6 +88,7 @@ public void WriteTo(ref SpanBufferWriter writer) writer.WriteFloat32(AverageCutScoreForNotesWithFullScoreScoringType); writer.WriteVarInt(MaxCombo); writer.WriteFloat32(EndSongTime); + writer.WriteBool(Invalidated); // Added in 1.37.1 } } } diff --git a/BeatTogether.DedicatedServer.Messaging/Models/MultiplayerAvatarsData.cs b/BeatTogether.DedicatedServer.Messaging/Models/MultiplayerAvatarsData.cs new file mode 100644 index 00000000..3cdc7c24 --- /dev/null +++ b/BeatTogether.DedicatedServer.Messaging/Models/MultiplayerAvatarsData.cs @@ -0,0 +1,91 @@ +using BeatTogether.DedicatedServer.Messaging.Converter; +using BeatTogether.DedicatedServer.Messaging.Structs; +using BeatTogether.Extensions; +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.DedicatedServer.Messaging.Util; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace BeatTogether.DedicatedServer.Messaging.Models +{ + public sealed class MultiplayerAvatarsData : INetSerializable + { + public List? AvatarsData { get; set; } + public BitMask128 SupportedAvatarTypeIdHashesBloomFilter { get; set; } = AddBloomFilterEntryHash(new BitMask128(), AvatarDataMultiplayerAvatarsDataConverter.BaseGameAvatarSystemTypeIdentifier.AvatarTypeIdentifierHash, 3, 8); + + public MultiplayerAvatarsData() + { + AvatarData defaultAvatar = new AvatarData(); + AvatarsData = new List + { + defaultAvatar.CreateMultiplayerAvatarsData() + }; + } + + public MultiplayerAvatarsData(List multiplayerAvatarsData, IEnumerable supportedAvatarTypeIdHashes) + { + AvatarsData = multiplayerAvatarsData; + SupportedAvatarTypeIdHashesBloomFilter = ToBloomFilter(supportedAvatarTypeIdHashes, 3, 8); + } + + // Token: 0x060000A2 RID: 162 RVA: 0x00003D91 File Offset: 0x00001F91 + public MultiplayerAvatarsData(List multiplayerAvatarsData, BitMask128 supportedAvatarTypeIdHashesBloomFilter) + { + AvatarsData = multiplayerAvatarsData; + SupportedAvatarTypeIdHashesBloomFilter = supportedAvatarTypeIdHashesBloomFilter; + } + + public void WriteTo(ref SpanBuffer writer) + { + WriteToAvatarsData(ref writer); + SupportedAvatarTypeIdHashesBloomFilter.WriteTo(ref writer); + } + public void WriteToAvatarsData(ref SpanBuffer writer) + { + if (AvatarsData == null) + { + writer.WriteInt32(0); + return; + } + writer.WriteInt32(AvatarsData.Count); + foreach (MultiplayerAvatarData multiplayerAvatarData in AvatarsData) + { + writer.WriteUInt32(multiplayerAvatarData.AvatarTypeIdentifierHash); + writer.WriteByteArray(multiplayerAvatarData.Data); + } + } + + public void ReadFrom(ref SpanBuffer reader) + { + AvatarsData = ReadFromAvatarsData(ref reader); + SupportedAvatarTypeIdHashesBloomFilter.ReadFrom(ref reader); + } + public static List ReadFromAvatarsData(ref SpanBuffer reader) + { + int @int = reader.ReadInt32(); + List list = new List(@int); + for (int i = 0; i < @int; i++) + { + uint @uint = reader.ReadUInt32(); + ReadOnlySpan byteArray = reader.ReadByteArray(); + list.Add(new MultiplayerAvatarData(@uint, byteArray.ToArray())); + } + return list; + } + public static BitMask128 ToBloomFilter(IEnumerable hashes, int hashCount = 3, int hashBits = 8) + { + return hashes.Aggregate(new BitMask128(), (BitMask128 bloomFilter, uint hash) => AddBloomFilterEntryHash(bloomFilter, hash, hashCount, hashBits)); + } + + public static BitMask128 AddBloomFilterEntryHash(BitMask128 bitMask, uint hash, int hashCount = 3, int hashBits = 8) + { + for (int i = 0; i < hashCount; i++) + { + bitMask = bitMask.SetBits((int)((ulong)hash % (ulong)((long)bitMask.BitCount)), 1UL); + hash >>= hashBits; + } + return bitMask; + } + } +} diff --git a/BeatTogether.DedicatedServer.Messaging/Models/MultiplayerLevelCompletionResults.cs b/BeatTogether.DedicatedServer.Messaging/Models/MultiplayerLevelCompletionResults.cs index 8ff648c5..8d4e1eaf 100644 --- a/BeatTogether.DedicatedServer.Messaging/Models/MultiplayerLevelCompletionResults.cs +++ b/BeatTogether.DedicatedServer.Messaging/Models/MultiplayerLevelCompletionResults.cs @@ -1,7 +1,7 @@ using BeatTogether.DedicatedServer.Messaging.Enums; -using BeatTogether.LiteNetLib.Abstractions; -using BeatTogether.LiteNetLib.Extensions; -using Krypton.Buffers; +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.Extensions; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Models { @@ -11,7 +11,7 @@ public sealed class MultiplayerLevelCompletionResults : INetSerializable public MultiplayerPlayerLevelEndReason PlayerLevelEndReason { get; set; } public LevelCompletionResults LevelCompletionResults { get; set; } = new(); - public void ReadFrom(ref SpanBufferReader reader) + public void ReadFrom(ref SpanBuffer reader) { PlayerLevelEndState = (MultiplayerPlayerLevelEndState) reader.ReadVarInt(); PlayerLevelEndReason = (MultiplayerPlayerLevelEndReason) reader.ReadVarInt(); @@ -20,7 +20,7 @@ public void ReadFrom(ref SpanBufferReader reader) LevelCompletionResults.ReadFrom(ref reader); } - public void WriteTo(ref SpanBufferWriter writer) + public void WriteTo(ref SpanBuffer writer) { writer.WriteVarInt((int) PlayerLevelEndState); writer.WriteVarInt((int) PlayerLevelEndReason); diff --git a/BeatTogether.DedicatedServer.Messaging/Models/NodePoseSyncState.cs b/BeatTogether.DedicatedServer.Messaging/Models/NodePoseSyncState.cs index 4f147893..41a55e85 100644 --- a/BeatTogether.DedicatedServer.Messaging/Models/NodePoseSyncState.cs +++ b/BeatTogether.DedicatedServer.Messaging/Models/NodePoseSyncState.cs @@ -1,5 +1,5 @@ -using BeatTogether.LiteNetLib.Abstractions; -using Krypton.Buffers; +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Models { @@ -9,14 +9,14 @@ public sealed class NodePoseSyncState : INetSerializable public Pose LeftController { get; set; } public Pose RightController { get; set; } - public void ReadFrom(ref SpanBufferReader reader) + public void ReadFrom(ref SpanBuffer reader) { Head.ReadFrom(ref reader); LeftController.ReadFrom(ref reader); RightController.ReadFrom(ref reader); } - public void WriteTo(ref SpanBufferWriter writer) + public void WriteTo(ref SpanBuffer writer) { Head.WriteTo(ref writer); LeftController.WriteTo(ref writer); diff --git a/BeatTogether.DedicatedServer.Messaging/Models/NoteCutInfo.cs b/BeatTogether.DedicatedServer.Messaging/Models/NoteCutInfo.cs index 0ac8954a..94701734 100644 --- a/BeatTogether.DedicatedServer.Messaging/Models/NoteCutInfo.cs +++ b/BeatTogether.DedicatedServer.Messaging/Models/NoteCutInfo.cs @@ -1,7 +1,7 @@ using BeatTogether.DedicatedServer.Messaging.Enums; -using BeatTogether.LiteNetLib.Abstractions; -using BeatTogether.LiteNetLib.Extensions; -using Krypton.Buffers; +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.Extensions; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Models { @@ -23,7 +23,7 @@ public sealed class NoteCutInfo : INetSerializable public float TimeToNextNote { get; set; } public Vector3 MoveVec { get; set; } - public void ReadFrom(ref SpanBufferReader reader) + public void ReadFrom(ref SpanBuffer reader) { CutWasOk = reader.ReadUInt8(); SaberSpeed = reader.ReadFloat32(); @@ -42,7 +42,7 @@ public void ReadFrom(ref SpanBufferReader reader) MoveVec.ReadFrom(ref reader); } - public void WriteTo(ref SpanBufferWriter writer) + public void WriteTo(ref SpanBuffer writer) { writer.WriteUInt8(CutWasOk); writer.WriteFloat32(SaberSpeed); diff --git a/BeatTogether.DedicatedServer.Messaging/Models/NoteMissInfo.cs b/BeatTogether.DedicatedServer.Messaging/Models/NoteMissInfo.cs index 0588997a..9e194d5e 100644 --- a/BeatTogether.DedicatedServer.Messaging/Models/NoteMissInfo.cs +++ b/BeatTogether.DedicatedServer.Messaging/Models/NoteMissInfo.cs @@ -1,6 +1,6 @@ -using BeatTogether.LiteNetLib.Abstractions; -using BeatTogether.LiteNetLib.Extensions; -using Krypton.Buffers; +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.Extensions; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Models { @@ -11,7 +11,7 @@ public sealed class NoteMissInfo : INetSerializable public int NoteLineIndex { get; set; } public float NoteTime { get; set; } - public void ReadFrom(ref SpanBufferReader reader) + public void ReadFrom(ref SpanBuffer reader) { ColorType = reader.ReadVarInt(); NoteLineLayer = reader.ReadVarInt(); @@ -19,7 +19,7 @@ public void ReadFrom(ref SpanBufferReader reader) NoteTime = reader.ReadFloat32(); } - public void WriteTo(ref SpanBufferWriter writer) + public void WriteTo(ref SpanBuffer writer) { writer.WriteVarInt(ColorType); writer.WriteVarInt(NoteLineLayer); diff --git a/BeatTogether.DedicatedServer.Messaging/Models/PlayerPermissionConfiguration.cs b/BeatTogether.DedicatedServer.Messaging/Models/PlayerPermissionConfiguration.cs index 35184bc5..014b1a07 100644 --- a/BeatTogether.DedicatedServer.Messaging/Models/PlayerPermissionConfiguration.cs +++ b/BeatTogether.DedicatedServer.Messaging/Models/PlayerPermissionConfiguration.cs @@ -1,6 +1,6 @@ -using BeatTogether.LiteNetLib.Abstractions; -using BeatTogether.LiteNetLib.Extensions; -using Krypton.Buffers; +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.Extensions; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Models { @@ -13,7 +13,7 @@ public sealed class PlayerPermissionConfiguration : INetSerializable public bool HasKickVotePermission { get; set; } public bool HasInvitePermission { get; set; } - public void ReadFrom(ref SpanBufferReader reader) + public void ReadFrom(ref SpanBuffer reader) { UserId = reader.ReadString(); byte num = reader.ReadUInt8(); @@ -24,7 +24,7 @@ public void ReadFrom(ref SpanBufferReader reader) HasInvitePermission = (num & 16) > 0; } - public void WriteTo(ref SpanBufferWriter writer) + public void WriteTo(ref SpanBuffer writer) { writer.WriteString(UserId); int num = (IsServerOwner ? 1 : 0) | (HasRecommendBeatmapsPermission ? 2 : 0) | (HasRecommendGameplayModifiersPermission ? 4 : 0) | (HasKickVotePermission ? 8 : 0) | (HasInvitePermission ? 16 : 0); diff --git a/BeatTogether.DedicatedServer.Messaging/Models/PlayerSpecificSettings.cs b/BeatTogether.DedicatedServer.Messaging/Models/PlayerSpecificSettings.cs index 1899e6e4..0b9ce624 100644 --- a/BeatTogether.DedicatedServer.Messaging/Models/PlayerSpecificSettings.cs +++ b/BeatTogether.DedicatedServer.Messaging/Models/PlayerSpecificSettings.cs @@ -1,6 +1,6 @@ -using BeatTogether.LiteNetLib.Abstractions; -using BeatTogether.LiteNetLib.Extensions; -using Krypton.Buffers; +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.Extensions; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Models { @@ -14,7 +14,7 @@ public sealed class PlayerSpecificSettings : INetSerializable public float HeadPosToPlayerHeightOffset { get; set; } public ColorScheme ColorScheme { get; set; } = new(); - public void ReadFrom(ref SpanBufferReader reader) + public void ReadFrom(ref SpanBuffer reader) { UserId = reader.ReadString(); UserName = reader.ReadString(); @@ -25,7 +25,7 @@ public void ReadFrom(ref SpanBufferReader reader) ColorScheme.ReadFrom(ref reader); } - public void WriteTo(ref SpanBufferWriter writer) + public void WriteTo(ref SpanBuffer writer) { writer.WriteString(UserId); writer.WriteString(UserName); diff --git a/BeatTogether.DedicatedServer.Messaging/Models/PlayerSpecificSettingsAtStart.cs b/BeatTogether.DedicatedServer.Messaging/Models/PlayerSpecificSettingsAtStart.cs index 965cb2a4..49e35c43 100644 --- a/BeatTogether.DedicatedServer.Messaging/Models/PlayerSpecificSettingsAtStart.cs +++ b/BeatTogether.DedicatedServer.Messaging/Models/PlayerSpecificSettingsAtStart.cs @@ -1,5 +1,5 @@ -using BeatTogether.LiteNetLib.Abstractions; -using Krypton.Buffers; +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Models { @@ -7,7 +7,7 @@ public sealed class PlayerSpecificSettingsAtStart : INetSerializable { public PlayerSpecificSettings[] ActivePlayerSpecificSettingsAtStart { get; set; } = null!; - public void ReadFrom(ref SpanBufferReader reader) + public void ReadFrom(ref SpanBuffer reader) { int count = reader.ReadInt32(); ActivePlayerSpecificSettingsAtStart = new PlayerSpecificSettings[count]; @@ -17,7 +17,7 @@ public void ReadFrom(ref SpanBufferReader reader) } } - public void WriteTo(ref SpanBufferWriter writer) + public void WriteTo(ref SpanBuffer writer) { writer.WriteInt32(ActivePlayerSpecificSettingsAtStart.Length); foreach(PlayerSpecificSettings playerSpecificSettings in ActivePlayerSpecificSettingsAtStart) diff --git a/BeatTogether.DedicatedServer.Messaging/Models/PlayersPermissionConfiguration.cs b/BeatTogether.DedicatedServer.Messaging/Models/PlayersPermissionConfiguration.cs index bd510d05..e8e7b0f3 100644 --- a/BeatTogether.DedicatedServer.Messaging/Models/PlayersPermissionConfiguration.cs +++ b/BeatTogether.DedicatedServer.Messaging/Models/PlayersPermissionConfiguration.cs @@ -1,6 +1,5 @@ -using BeatTogether.LiteNetLib.Abstractions; -using Krypton.Buffers; -using System; +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Models { @@ -8,7 +7,7 @@ public sealed class PlayersPermissionConfiguration : INetSerializable { public PlayerPermissionConfiguration[] PlayersPermission = null!; - public void ReadFrom(ref SpanBufferReader reader) + public void ReadFrom(ref SpanBuffer reader) { PlayersPermission = new PlayerPermissionConfiguration[reader.ReadInt32()]; for (int i = 0; i < PlayersPermission.Length; i++) @@ -17,7 +16,7 @@ public void ReadFrom(ref SpanBufferReader reader) } } - public void WriteTo(ref SpanBufferWriter writer) + public void WriteTo(ref SpanBuffer writer) { writer.WriteInt32(PlayersPermission.Length); for (int i = 0; i < PlayersPermission.Length; i++) diff --git a/BeatTogether.DedicatedServer.Messaging/Models/Pose.cs b/BeatTogether.DedicatedServer.Messaging/Models/Pose.cs index 4c50a0f5..e0847b83 100644 --- a/BeatTogether.DedicatedServer.Messaging/Models/Pose.cs +++ b/BeatTogether.DedicatedServer.Messaging/Models/Pose.cs @@ -1,5 +1,5 @@ -using BeatTogether.LiteNetLib.Abstractions; -using Krypton.Buffers; +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Models { @@ -8,13 +8,13 @@ public struct Pose : INetSerializable public Vector3 Position { get; set; } public Quaternion Rotation { get; set; } - public void ReadFrom(ref SpanBufferReader reader) + public void ReadFrom(ref SpanBuffer reader) { Position.ReadFrom(ref reader); Rotation.ReadFrom(ref reader); } - public void WriteTo(ref SpanBufferWriter writer) + public void WriteTo(ref SpanBuffer writer) { Position.WriteTo(ref writer); Rotation.WriteTo(ref writer); diff --git a/BeatTogether.DedicatedServer.Messaging/Models/ProcessingPacketInfo.cs b/BeatTogether.DedicatedServer.Messaging/Models/ProcessingPacketInfo.cs deleted file mode 100644 index b3e4ce8e..00000000 --- a/BeatTogether.DedicatedServer.Messaging/Models/ProcessingPacketInfo.cs +++ /dev/null @@ -1,11 +0,0 @@ -using BeatTogether.LiteNetLib.Abstractions; - -namespace BeatTogether.DedicatedServer.Messaging.Models -{ - public struct ProcessingPacketInfo - { - public uint length; - public int startPosition; - public INetSerializable packet; - } -} diff --git a/BeatTogether.DedicatedServer.Messaging/Models/Quaternion.cs b/BeatTogether.DedicatedServer.Messaging/Models/Quaternion.cs index 4f08c9fd..b93cf37b 100644 --- a/BeatTogether.DedicatedServer.Messaging/Models/Quaternion.cs +++ b/BeatTogether.DedicatedServer.Messaging/Models/Quaternion.cs @@ -1,6 +1,6 @@ -using BeatTogether.LiteNetLib.Abstractions; -using BeatTogether.LiteNetLib.Extensions; -using Krypton.Buffers; +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.Extensions; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Models { @@ -10,14 +10,14 @@ public struct Quaternion : INetSerializable public int b; public int c; - public void ReadFrom(ref SpanBufferReader reader) + public void ReadFrom(ref SpanBuffer reader) { a = reader.ReadVarInt(); b = reader.ReadVarInt(); c = reader.ReadVarInt(); } - public void WriteTo(ref SpanBufferWriter writer) + public void WriteTo(ref SpanBuffer writer) { writer.WriteVarInt(a); writer.WriteVarInt(b); diff --git a/BeatTogether.DedicatedServer.Messaging/Models/SongPackMask.cs b/BeatTogether.DedicatedServer.Messaging/Models/SongPackMask.cs index 10434cd8..779310bd 100644 --- a/BeatTogether.DedicatedServer.Messaging/Models/SongPackMask.cs +++ b/BeatTogether.DedicatedServer.Messaging/Models/SongPackMask.cs @@ -1,6 +1,6 @@ namespace BeatTogether.DedicatedServer.Messaging.Models { - public sealed class SongPackMask : BitMask128 + public sealed class SongPackMask : BitMask256 { } } diff --git a/BeatTogether.DedicatedServer.Messaging/Models/StandardScoreSyncState.cs b/BeatTogether.DedicatedServer.Messaging/Models/StandardScoreSyncState.cs index d5a16e70..9b33eaa1 100644 --- a/BeatTogether.DedicatedServer.Messaging/Models/StandardScoreSyncState.cs +++ b/BeatTogether.DedicatedServer.Messaging/Models/StandardScoreSyncState.cs @@ -1,6 +1,6 @@ -using BeatTogether.LiteNetLib.Abstractions; -using BeatTogether.LiteNetLib.Extensions; -using Krypton.Buffers; +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.Extensions; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Models { @@ -12,7 +12,7 @@ public sealed class StandardScoreSyncState : INetSerializable public int Combo { get; set; } public int Multiplier { get; set; } - public void ReadFrom(ref SpanBufferReader reader) + public void ReadFrom(ref SpanBuffer reader) { ModifiedScore = reader.ReadVarInt(); RawScore = reader.ReadVarInt(); @@ -21,7 +21,7 @@ public void ReadFrom(ref SpanBufferReader reader) Multiplier = reader.ReadVarInt(); } - public void WriteTo(ref SpanBufferWriter writer) + public void WriteTo(ref SpanBuffer writer) { writer.WriteVarInt(ModifiedScore); writer.WriteVarInt(RawScore); diff --git a/BeatTogether.DedicatedServer.Messaging/Models/Vector3.cs b/BeatTogether.DedicatedServer.Messaging/Models/Vector3.cs index 44aa1659..cba2e83f 100644 --- a/BeatTogether.DedicatedServer.Messaging/Models/Vector3.cs +++ b/BeatTogether.DedicatedServer.Messaging/Models/Vector3.cs @@ -1,6 +1,6 @@ -using BeatTogether.LiteNetLib.Abstractions; -using BeatTogether.LiteNetLib.Extensions; -using Krypton.Buffers; +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.Extensions; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Models { @@ -10,14 +10,14 @@ public struct Vector3 : INetSerializable public int y; public int z; - public void ReadFrom(ref SpanBufferReader reader) + public void ReadFrom(ref SpanBuffer reader) { x = reader.ReadVarInt(); y = reader.ReadVarInt(); z = reader.ReadVarInt(); } - public void WriteTo(ref SpanBufferWriter writer) + public void WriteTo(ref SpanBuffer writer) { writer.WriteVarInt(x); writer.WriteVarInt(y); diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/KickPlayerPacket.cs b/BeatTogether.DedicatedServer.Messaging/Packets/KickPlayerPacket.cs index fa3af2dc..9542d897 100644 --- a/BeatTogether.DedicatedServer.Messaging/Packets/KickPlayerPacket.cs +++ b/BeatTogether.DedicatedServer.Messaging/Packets/KickPlayerPacket.cs @@ -1,7 +1,7 @@ using BeatTogether.DedicatedServer.Messaging.Enums; -using BeatTogether.LiteNetLib.Abstractions; -using BeatTogether.LiteNetLib.Extensions; -using Krypton.Buffers; +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.Extensions; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Packets { @@ -9,12 +9,12 @@ public sealed class KickPlayerPacket : INetSerializable { public DisconnectedReason DisconnectedReason { get; set; } - public void WriteTo(ref SpanBufferWriter writer) + public void WriteTo(ref SpanBuffer writer) { writer.WriteVarInt((int)DisconnectedReason); } - public void ReadFrom(ref SpanBufferReader reader) + public void ReadFrom(ref SpanBuffer reader) { DisconnectedReason = (DisconnectedReason)reader.ReadVarInt(); } diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/GameplayRpc/LevelFinishedPacket.cs b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/GameplayRpc/LevelFinishedPacket.cs index d7247584..e9d0bb28 100644 --- a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/GameplayRpc/LevelFinishedPacket.cs +++ b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/GameplayRpc/LevelFinishedPacket.cs @@ -1,6 +1,6 @@ using BeatTogether.DedicatedServer.Messaging.Abstractions; using BeatTogether.DedicatedServer.Messaging.Models; -using Krypton.Buffers; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.GameplayRpc { @@ -8,14 +8,14 @@ public sealed class LevelFinishedPacket : BaseRpcWithValuesPacket { public MultiplayerLevelCompletionResults Results { get; set; } = new(); - public override void ReadFrom(ref SpanBufferReader reader) + public override void ReadFrom(ref SpanBuffer reader) { base.ReadFrom(ref reader); if (HasValue0) Results.ReadFrom(ref reader); } - public override void WriteTo(ref SpanBufferWriter writer) + public override void WriteTo(ref SpanBuffer writer) { base.WriteTo(ref writer); Results.WriteTo(ref writer); diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/GameplayRpc/NoteCutPacket.cs b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/GameplayRpc/NoteCutPacket.cs index ddb5344f..224d3632 100644 --- a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/GameplayRpc/NoteCutPacket.cs +++ b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/GameplayRpc/NoteCutPacket.cs @@ -1,6 +1,6 @@ using BeatTogether.DedicatedServer.Messaging.Abstractions; using BeatTogether.DedicatedServer.Messaging.Models; -using Krypton.Buffers; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.GameplayRpc { @@ -9,7 +9,7 @@ public sealed class NoteCutPacket : BaseRpcWithValuesPacket public float SongTime { get; set; } public NoteCutInfo Info { get; set; } = new(); - public override void ReadFrom(ref SpanBufferReader reader) + public override void ReadFrom(ref SpanBuffer reader) { base.ReadFrom(ref reader); if (HasValue0) @@ -18,7 +18,7 @@ public override void ReadFrom(ref SpanBufferReader reader) Info.ReadFrom(ref reader); } - public override void WriteTo(ref SpanBufferWriter writer) + public override void WriteTo(ref SpanBuffer writer) { base.WriteTo(ref writer); writer.WriteFloat32(SongTime); diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/GameplayRpc/NoteMissPacket.cs b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/GameplayRpc/NoteMissPacket.cs index ec9a0c6e..e08c95a5 100644 --- a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/GameplayRpc/NoteMissPacket.cs +++ b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/GameplayRpc/NoteMissPacket.cs @@ -1,6 +1,6 @@ using BeatTogether.DedicatedServer.Messaging.Abstractions; using BeatTogether.DedicatedServer.Messaging.Models; -using Krypton.Buffers; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.GameplayRpc { @@ -9,7 +9,7 @@ public sealed class NoteMissPacket : BaseRpcWithValuesPacket public float SongTime { get; set; } public NoteMissInfo Info { get; set; } = new(); - public override void ReadFrom(ref SpanBufferReader reader) + public override void ReadFrom(ref SpanBuffer reader) { base.ReadFrom(ref reader); if (HasValue0) @@ -18,7 +18,7 @@ public override void ReadFrom(ref SpanBufferReader reader) Info.ReadFrom(ref reader); } - public override void WriteTo(ref SpanBufferWriter writer) + public override void WriteTo(ref SpanBuffer writer) { base.WriteTo(ref writer); writer.WriteFloat32(SongTime); diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/GameplayRpc/NoteSpawn.cs b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/GameplayRpc/NoteSpawn.cs new file mode 100644 index 00000000..4aad7a04 --- /dev/null +++ b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/GameplayRpc/NoteSpawn.cs @@ -0,0 +1,17 @@ +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.DedicatedServer.Messaging.Util; + +namespace BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.GameplayRpc +{ + public sealed class NoteSpawnPacket : BaseRpcWithValuesPacket + { + public override void ReadFrom(ref SpanBuffer reader) + { + base.ReadFrom(ref reader); + } + public override void WriteTo(ref SpanBuffer writer) + { + base.WriteTo(ref writer); + } + } +} diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/GameplayRpc/ObstacleSpawnPacket.cs b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/GameplayRpc/ObstacleSpawnPacket.cs new file mode 100644 index 00000000..499271c5 --- /dev/null +++ b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/GameplayRpc/ObstacleSpawnPacket.cs @@ -0,0 +1,17 @@ +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.DedicatedServer.Messaging.Util; + +namespace BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.GameplayRpc +{ + public sealed class ObstacleSpawnPacket : BaseRpcWithValuesPacket + { + public override void ReadFrom(ref SpanBuffer reader) + { + base.ReadFrom(ref reader); + } + public override void WriteTo(ref SpanBuffer writer) + { + base.WriteTo(ref writer); + } + } +} diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/GameplayRpc/SetGameplaySceneReadyPacket.cs b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/GameplayRpc/SetGameplaySceneReadyPacket.cs index 123053c9..1a1e872d 100644 --- a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/GameplayRpc/SetGameplaySceneReadyPacket.cs +++ b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/GameplayRpc/SetGameplaySceneReadyPacket.cs @@ -1,6 +1,6 @@ using BeatTogether.DedicatedServer.Messaging.Abstractions; using BeatTogether.DedicatedServer.Messaging.Models; -using Krypton.Buffers; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.GameplayRpc { @@ -8,14 +8,14 @@ public sealed class SetGameplaySceneReadyPacket : BaseRpcWithValuesPacket { public PlayerSpecificSettings PlayerSpecificSettings { get; set; } = new(); - public override void ReadFrom(ref SpanBufferReader reader) + public override void ReadFrom(ref SpanBuffer reader) { base.ReadFrom(ref reader); if (HasValue0) PlayerSpecificSettings.ReadFrom(ref reader); } - public override void WriteTo(ref SpanBufferWriter writer) + public override void WriteTo(ref SpanBuffer writer) { base.WriteTo(ref writer); PlayerSpecificSettings.WriteTo(ref writer); diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/GameplayRpc/SetGameplaySceneSyncFinishedPacket.cs b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/GameplayRpc/SetGameplaySceneSyncFinishedPacket.cs index 2ad436b2..01565a05 100644 --- a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/GameplayRpc/SetGameplaySceneSyncFinishedPacket.cs +++ b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/GameplayRpc/SetGameplaySceneSyncFinishedPacket.cs @@ -1,7 +1,7 @@ using BeatTogether.DedicatedServer.Messaging.Abstractions; using BeatTogether.DedicatedServer.Messaging.Models; -using BeatTogether.LiteNetLib.Extensions; -using Krypton.Buffers; +using BeatTogether.Extensions; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.GameplayRpc { @@ -10,7 +10,7 @@ public sealed class SetGameplaySceneSyncFinishedPacket : BaseRpcWithValuesPacket public PlayerSpecificSettingsAtStart PlayersAtStart { get; set; } = null!; public string SessionGameId { get; set; } = null!; - public override void ReadFrom(ref SpanBufferReader reader) + public override void ReadFrom(ref SpanBuffer reader) { base.ReadFrom(ref reader); if (HasValue0) @@ -19,7 +19,7 @@ public override void ReadFrom(ref SpanBufferReader reader) SessionGameId = reader.ReadString(); } - public override void WriteTo(ref SpanBufferWriter writer) + public override void WriteTo(ref SpanBuffer writer) { base.WriteTo(ref writer); PlayersAtStart.WriteTo(ref writer); diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/GameplayRpc/SetPlayerDidConnectLatePacket.cs b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/GameplayRpc/SetPlayerDidConnectLatePacket.cs index 1e3fbb9b..dcfe5035 100644 --- a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/GameplayRpc/SetPlayerDidConnectLatePacket.cs +++ b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/GameplayRpc/SetPlayerDidConnectLatePacket.cs @@ -1,7 +1,7 @@ using BeatTogether.DedicatedServer.Messaging.Abstractions; using BeatTogether.DedicatedServer.Messaging.Models; -using BeatTogether.LiteNetLib.Extensions; -using Krypton.Buffers; +using BeatTogether.Extensions; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.GameplayRpc { @@ -11,7 +11,7 @@ public sealed class SetPlayerDidConnectLatePacket : BaseRpcWithValuesPacket public PlayerSpecificSettingsAtStart PlayersAtStart { get; set; } = new(); public string SessionGameId { get; set; } = null!; - public override void ReadFrom(ref SpanBufferReader reader) + public override void ReadFrom(ref SpanBuffer reader) { base.ReadFrom(ref reader); if (HasValue0) @@ -22,7 +22,7 @@ public override void ReadFrom(ref SpanBufferReader reader) SessionGameId = reader.ReadString(); } - public override void WriteTo(ref SpanBufferWriter writer) + public override void WriteTo(ref SpanBuffer writer) { base.WriteTo(ref writer); writer.WriteString(UserId); diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/GameplayRpc/SetSongStartTimePacket.cs b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/GameplayRpc/SetSongStartTimePacket.cs index fd76aacd..81a17305 100644 --- a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/GameplayRpc/SetSongStartTimePacket.cs +++ b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/GameplayRpc/SetSongStartTimePacket.cs @@ -1,23 +1,24 @@ using BeatTogether.DedicatedServer.Messaging.Abstractions; -using Krypton.Buffers; +using BeatTogether.Extensions; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.GameplayRpc { public sealed class SetSongStartTimePacket : BaseRpcWithValuesPacket { - public float StartTime { get; set; } + public long StartTime { get; set; } - public override void ReadFrom(ref SpanBufferReader reader) + public override void ReadFrom(ref SpanBuffer reader) { base.ReadFrom(ref reader); if (HasValue0) - StartTime = reader.ReadFloat32(); + StartTime = reader.ReadVarLong(); } - public override void WriteTo(ref SpanBufferWriter writer) + public override void WriteTo(ref SpanBuffer writer) { base.WriteTo(ref writer); - writer.WriteFloat32(StartTime); + writer.WriteVarLong(StartTime); } } } diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/GameplayRpc/SliderSpawnPacket.cs b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/GameplayRpc/SliderSpawnPacket.cs new file mode 100644 index 00000000..9de2db5a --- /dev/null +++ b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/GameplayRpc/SliderSpawnPacket.cs @@ -0,0 +1,17 @@ +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.DedicatedServer.Messaging.Util; + +namespace BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.GameplayRpc +{ + public sealed class SliderSpawnPacket : BaseRpcWithValuesPacket + { + public override void ReadFrom(ref SpanBuffer reader) + { + base.ReadFrom(ref reader); + } + public override void WriteTo(ref SpanBuffer writer) + { + base.WriteTo(ref writer); + } + } +} diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MPChatPackets/MpcBasePacket.cs b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MPChatPackets/MpcBasePacket.cs new file mode 100644 index 00000000..5dd52672 --- /dev/null +++ b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MPChatPackets/MpcBasePacket.cs @@ -0,0 +1,26 @@ +using BeatTogether.Extensions; +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.DedicatedServer.Messaging.Util; + +namespace BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MPChatPackets +{ + public class MpcBasePacket : INetSerializable + { + + public uint ProtocolVersion; + + public virtual void WriteTo(ref SpanBuffer writer) + { + ProtocolVersion = 1; + + writer.WriteVarUInt(ProtocolVersion); + } + + public virtual void ReadFrom(ref SpanBuffer reader) + { + ProtocolVersion = reader.ReadVarUInt(); + } + } +} + + diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MPChatPackets/MpcCapabilitiesPacket.cs b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MPChatPackets/MpcCapabilitiesPacket.cs new file mode 100644 index 00000000..f165e7bf --- /dev/null +++ b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MPChatPackets/MpcCapabilitiesPacket.cs @@ -0,0 +1,30 @@ +using BeatTogether.DedicatedServer.Messaging.Util; + +namespace BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MPChatPackets +{ + public class MpcCapabilitiesPacket : MpcBasePacket + { + public bool CanTextChat; + public bool CanReceiveVoiceChat; + public bool CanTransmitVoiceChat; + + public override void WriteTo(ref SpanBuffer writer) + { + base.WriteTo(ref writer); + writer.WriteBool(CanTextChat); + writer.WriteBool(CanReceiveVoiceChat); + writer.WriteBool(CanTransmitVoiceChat); + } + + public override void ReadFrom(ref SpanBuffer reader) + { + base.ReadFrom(ref reader); + + CanTextChat = reader.ReadBool(); + CanReceiveVoiceChat = reader.ReadBool(); + CanTransmitVoiceChat = reader.ReadBool(); + } + } +} + + diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MPChatPackets/MpcTextChatPacket.cs b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MPChatPackets/MpcTextChatPacket.cs new file mode 100644 index 00000000..98345d8c --- /dev/null +++ b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MPChatPackets/MpcTextChatPacket.cs @@ -0,0 +1,25 @@ +using BeatTogether.Extensions; +using BeatTogether.DedicatedServer.Messaging.Util; + +namespace BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MPChatPackets +{ + public class MpcTextChatPacket : MpcBasePacket + { + public string Text = string.Empty; + + public override void WriteTo(ref SpanBuffer writer) + { + base.WriteTo(ref writer); + writer.WriteString(Text); + } + + public override void ReadFrom(ref SpanBuffer reader) + { + base.ReadFrom(ref reader); + + Text = reader.ReadString(); + } + } +} + + diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MPChatPackets/MpcVoicePacket.cs b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MPChatPackets/MpcVoicePacket.cs new file mode 100644 index 00000000..ef9fd13b --- /dev/null +++ b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MPChatPackets/MpcVoicePacket.cs @@ -0,0 +1,18 @@ +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.DedicatedServer.Messaging.Util; + +namespace BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MPChatPackets +{ + public class MpcVoicePacket : INetSerializable + { + public void ReadFrom(ref SpanBuffer bufferReader) + { + } + + public void WriteTo(ref SpanBuffer bufferWriter) + { + } + } +} + + diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/ClearSelectedBeatmap.cs b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/ClearSelectedBeatmap.cs index 5c9db5ca..57489e70 100644 --- a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/ClearSelectedBeatmap.cs +++ b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/ClearSelectedBeatmap.cs @@ -1,5 +1,4 @@ using BeatTogether.DedicatedServer.Messaging.Abstractions; -using Krypton.Buffers; namespace BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MenuRpc { diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/GetIsEntitledToLevelPacket.cs b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/GetIsEntitledToLevelPacket.cs index 3360ffe0..9b59a522 100644 --- a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/GetIsEntitledToLevelPacket.cs +++ b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/GetIsEntitledToLevelPacket.cs @@ -1,6 +1,6 @@ using BeatTogether.DedicatedServer.Messaging.Abstractions; -using BeatTogether.LiteNetLib.Extensions; -using Krypton.Buffers; +using BeatTogether.Extensions; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MenuRpc { @@ -8,14 +8,14 @@ public sealed class GetIsEntitledToLevelPacket : BaseRpcWithValuesPacket { public string LevelId { get; set; } = null!; - public override void ReadFrom(ref SpanBufferReader reader) + public override void ReadFrom(ref SpanBuffer reader) { base.ReadFrom(ref reader); if (HasValue0) LevelId = reader.ReadString(); } - public override void WriteTo(ref SpanBufferWriter writer) + public override void WriteTo(ref SpanBuffer writer) { base.WriteTo(ref writer); writer.WriteString(LevelId); diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/RequestKickPlayerPacket.cs b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/RequestKickPlayerPacket.cs index 0bb874fd..d34b32a3 100644 --- a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/RequestKickPlayerPacket.cs +++ b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/RequestKickPlayerPacket.cs @@ -1,6 +1,6 @@ using BeatTogether.DedicatedServer.Messaging.Abstractions; -using BeatTogether.LiteNetLib.Extensions; -using Krypton.Buffers; +using BeatTogether.Extensions; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MenuRpc { @@ -8,14 +8,14 @@ public sealed class RequestKickPlayerPacket : BaseRpcWithValuesPacket { public string KickedPlayerId { get; set; } = null!; - public override void ReadFrom(ref SpanBufferReader reader) + public override void ReadFrom(ref SpanBuffer reader) { base.ReadFrom(ref reader); if (HasValue0) KickedPlayerId = reader.ReadString(); } - public override void WriteTo(ref SpanBufferWriter writer) + public override void WriteTo(ref SpanBuffer writer) { base.WriteTo(ref writer); writer.WriteString(KickedPlayerId); diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetCountdownEndTimePacket.cs b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetCountdownEndTimePacket.cs index 666c460e..6964fe18 100644 --- a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetCountdownEndTimePacket.cs +++ b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetCountdownEndTimePacket.cs @@ -1,23 +1,24 @@ using BeatTogether.DedicatedServer.Messaging.Abstractions; -using Krypton.Buffers; +using BeatTogether.Extensions; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MenuRpc { public sealed class SetCountdownEndTimePacket : BaseRpcWithValuesPacket { - public float CountdownTime { get; set; } + public long CountdownTime { get; set; } - public override void ReadFrom(ref SpanBufferReader reader) + public override void ReadFrom(ref SpanBuffer reader) { base.ReadFrom(ref reader); if (HasValue0) - CountdownTime = reader.ReadFloat32(); + CountdownTime = reader.ReadVarLong(); } - public override void WriteTo(ref SpanBufferWriter writer) + public override void WriteTo(ref SpanBuffer writer) { base.WriteTo(ref writer); - writer.WriteFloat32(CountdownTime); + writer.WriteVarLong(CountdownTime); } } } diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetIsEntitledToLevelPacket.cs b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetIsEntitledToLevelPacket.cs index 051d0d11..b49f23a3 100644 --- a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetIsEntitledToLevelPacket.cs +++ b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetIsEntitledToLevelPacket.cs @@ -1,7 +1,7 @@ using BeatTogether.DedicatedServer.Messaging.Abstractions; using BeatTogether.DedicatedServer.Messaging.Enums; -using BeatTogether.LiteNetLib.Extensions; -using Krypton.Buffers; +using BeatTogether.Extensions; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MenuRpc { @@ -10,7 +10,7 @@ public sealed class SetIsEntitledToLevelPacket : BaseRpcWithValuesPacket public string LevelId { get; set; } = null!; public EntitlementStatus Entitlement { get; set; } - public override void ReadFrom(ref SpanBufferReader reader) + public override void ReadFrom(ref SpanBuffer reader) { base.ReadFrom(ref reader); if (HasValue0) @@ -19,7 +19,7 @@ public override void ReadFrom(ref SpanBufferReader reader) Entitlement = (EntitlementStatus)reader.ReadVarInt(); } - public override void WriteTo(ref SpanBufferWriter writer) + public override void WriteTo(ref SpanBuffer writer) { base.WriteTo(ref writer); writer.WriteString(LevelId); diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetIsInLobbyPacket.cs b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetIsInLobbyPacket.cs index 5f732c73..3e76fe33 100644 --- a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetIsInLobbyPacket.cs +++ b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetIsInLobbyPacket.cs @@ -1,5 +1,5 @@ using BeatTogether.DedicatedServer.Messaging.Abstractions; -using Krypton.Buffers; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MenuRpc { @@ -7,14 +7,14 @@ public sealed class SetIsInLobbyPacket : BaseRpcWithValuesPacket { public bool IsInLobby { get; set; } - public override void ReadFrom(ref SpanBufferReader reader) + public override void ReadFrom(ref SpanBuffer reader) { base.ReadFrom(ref reader); if (HasValue0) IsInLobby = reader.ReadBool(); } - public override void WriteTo(ref SpanBufferWriter writer) + public override void WriteTo(ref SpanBuffer writer) { base.WriteTo(ref writer); writer.WriteBool(IsInLobby); diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetIsReadyPacket.cs b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetIsReadyPacket.cs index 1022f621..c6f37b3e 100644 --- a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetIsReadyPacket.cs +++ b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetIsReadyPacket.cs @@ -1,5 +1,5 @@ using BeatTogether.DedicatedServer.Messaging.Abstractions; -using Krypton.Buffers; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MenuRpc { @@ -7,14 +7,14 @@ public sealed class SetIsReadyPacket : BaseRpcWithValuesPacket { public bool IsReady { get; set; } - public override void ReadFrom(ref SpanBufferReader reader) + public override void ReadFrom(ref SpanBuffer reader) { base.ReadFrom(ref reader); if (HasValue0) IsReady = reader.ReadBool(); } - public override void WriteTo(ref SpanBufferWriter writer) + public override void WriteTo(ref SpanBuffer writer) { base.WriteTo(ref writer); writer.WriteBool(IsReady); diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetIsStartButtonEnabledPacket.cs b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetIsStartButtonEnabledPacket.cs index bc2cdcba..bc3fc098 100644 --- a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetIsStartButtonEnabledPacket.cs +++ b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetIsStartButtonEnabledPacket.cs @@ -1,7 +1,7 @@ using BeatTogether.DedicatedServer.Messaging.Abstractions; using BeatTogether.DedicatedServer.Messaging.Enums; -using BeatTogether.LiteNetLib.Extensions; -using Krypton.Buffers; +using BeatTogether.Extensions; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MenuRpc { @@ -9,14 +9,14 @@ public sealed class SetIsStartButtonEnabledPacket : BaseRpcWithValuesPacket { public CannotStartGameReason Reason { get; set; } - public override void ReadFrom(ref SpanBufferReader reader) + public override void ReadFrom(ref SpanBuffer reader) { base.ReadFrom(ref reader); if (HasValue0) Reason = (CannotStartGameReason)reader.ReadVarInt(); } - public override void WriteTo(ref SpanBufferWriter writer) + public override void WriteTo(ref SpanBuffer writer) { base.WriteTo(ref writer); writer.WriteVarInt((int)Reason); diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetMultiplayerGameStatePacket.cs b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetMultiplayerGameStatePacket.cs index bc3d6bda..8727e360 100644 --- a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetMultiplayerGameStatePacket.cs +++ b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetMultiplayerGameStatePacket.cs @@ -1,7 +1,7 @@ using BeatTogether.DedicatedServer.Messaging.Abstractions; -using BeatTogether.DedicatedServer.Messaging.Enums; -using BeatTogether.LiteNetLib.Extensions; -using Krypton.Buffers; +using BeatTogether.Extensions; +using BeatTogether.DedicatedServer.Messaging.Util; +using BeatTogether.Core.Enums; namespace BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MenuRpc { @@ -9,14 +9,14 @@ public sealed class SetMultiplayerGameStatePacket : BaseRpcWithValuesPacket { public MultiplayerGameState State { get; set; } - public override void ReadFrom(ref SpanBufferReader reader) + public override void ReadFrom(ref SpanBuffer reader) { base.ReadFrom(ref reader); if (HasValue0) State = (MultiplayerGameState)reader.ReadVarInt(); } - public override void WriteTo(ref SpanBufferWriter writer) + public override void WriteTo(ref SpanBuffer writer) { base.WriteTo(ref writer); writer.WriteVarInt((int)State); diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetOwnedSongPacksPacket.cs b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetOwnedSongPacksPacket.cs index 53d18136..f7849354 100644 --- a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetOwnedSongPacksPacket.cs +++ b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetOwnedSongPacksPacket.cs @@ -1,6 +1,6 @@ using BeatTogether.DedicatedServer.Messaging.Abstractions; using BeatTogether.DedicatedServer.Messaging.Models; -using Krypton.Buffers; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MenuRpc { @@ -8,14 +8,14 @@ public sealed class SetOwnedSongPacksPacket : BaseRpcWithValuesPacket { public SongPackMask SongPackMask { get; set; } = new(); - public override void ReadFrom(ref SpanBufferReader reader) + public override void ReadFrom(ref SpanBuffer reader) { base.ReadFrom(ref reader); if (HasValue0) SongPackMask.ReadFrom(ref reader); } - public override void WriteTo(ref SpanBufferWriter writer) + public override void WriteTo(ref SpanBuffer writer) { base.WriteTo(ref writer); SongPackMask?.WriteTo(ref writer); diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetPlayersMissingEntitlementsToLevelPacket.cs b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetPlayersMissingEntitlementsToLevelPacket.cs index 69ec767d..d2634bc2 100644 --- a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetPlayersMissingEntitlementsToLevelPacket.cs +++ b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetPlayersMissingEntitlementsToLevelPacket.cs @@ -1,7 +1,7 @@ using System; using BeatTogether.DedicatedServer.Messaging.Abstractions; -using BeatTogether.LiteNetLib.Extensions; -using Krypton.Buffers; +using BeatTogether.Extensions; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MenuRpc { @@ -9,7 +9,7 @@ public sealed class SetPlayersMissingEntitlementsToLevelPacket : BaseRpcWithValu { public string[] PlayersWithoutEntitlements { get; set; } = Array.Empty(); - public override void ReadFrom(ref SpanBufferReader reader) + public override void ReadFrom(ref SpanBuffer reader) { base.ReadFrom(ref reader); @@ -23,7 +23,7 @@ public override void ReadFrom(ref SpanBufferReader reader) } } - public override void WriteTo(ref SpanBufferWriter writer) + public override void WriteTo(ref SpanBuffer writer) { base.WriteTo(ref writer); diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetPlayersPermissionConfigurationPacket.cs b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetPlayersPermissionConfigurationPacket.cs index afab76f8..bb576bea 100644 --- a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetPlayersPermissionConfigurationPacket.cs +++ b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetPlayersPermissionConfigurationPacket.cs @@ -1,6 +1,6 @@ using BeatTogether.DedicatedServer.Messaging.Abstractions; using BeatTogether.DedicatedServer.Messaging.Models; -using Krypton.Buffers; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MenuRpc { @@ -8,14 +8,14 @@ public sealed class SetPlayersPermissionConfigurationPacket : BaseRpcWithValuesP { public PlayersPermissionConfiguration PermissionConfiguration { get; set; } = new(); - public override void ReadFrom(ref SpanBufferReader reader) + public override void ReadFrom(ref SpanBuffer reader) { base.ReadFrom(ref reader); if (HasValue0) PermissionConfiguration.ReadFrom(ref reader); } - public override void WriteTo(ref SpanBufferWriter writer) + public override void WriteTo(ref SpanBuffer writer) { base.WriteTo(ref writer); PermissionConfiguration.WriteTo(ref writer); diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetRecommendedBeatmapPacket.cs b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetRecommendedBeatmapPacket.cs index 1bfc8fd9..72bf8979 100644 --- a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetRecommendedBeatmapPacket.cs +++ b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetRecommendedBeatmapPacket.cs @@ -1,6 +1,6 @@ using BeatTogether.DedicatedServer.Messaging.Abstractions; using BeatTogether.DedicatedServer.Messaging.Models; -using Krypton.Buffers; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MenuRpc { @@ -8,14 +8,14 @@ public sealed class SetRecommendedBeatmapPacket : BaseRpcWithValuesPacket { public BeatmapIdentifier BeatmapIdentifier { get; set; } = new(); - public override void ReadFrom(ref SpanBufferReader reader) + public override void ReadFrom(ref SpanBuffer reader) { base.ReadFrom(ref reader); if (HasValue0) BeatmapIdentifier.ReadFrom(ref reader); } - public override void WriteTo(ref SpanBufferWriter writer) + public override void WriteTo(ref SpanBuffer writer) { base.WriteTo(ref writer); BeatmapIdentifier.WriteTo(ref writer); diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetRecommendedModifiersPacket.cs b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetRecommendedModifiersPacket.cs index 9efbe120..546fe871 100644 --- a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetRecommendedModifiersPacket.cs +++ b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetRecommendedModifiersPacket.cs @@ -1,6 +1,6 @@ using BeatTogether.DedicatedServer.Messaging.Abstractions; using BeatTogether.DedicatedServer.Messaging.Models; -using Krypton.Buffers; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MenuRpc { @@ -8,14 +8,14 @@ public sealed class SetRecommendedModifiersPacket : BaseRpcWithValuesPacket { public GameplayModifiers Modifiers { get; set; } = new(); - public override void ReadFrom(ref SpanBufferReader reader) + public override void ReadFrom(ref SpanBuffer reader) { base.ReadFrom(ref reader); if (HasValue0) Modifiers.ReadFrom(ref reader); } - public override void WriteTo(ref SpanBufferWriter writer) + public override void WriteTo(ref SpanBuffer writer) { base.WriteTo(ref writer); Modifiers.WriteTo(ref writer); diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetSelectedBeatmap.cs b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetSelectedBeatmap.cs index a99d5761..5f00bdb1 100644 --- a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetSelectedBeatmap.cs +++ b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetSelectedBeatmap.cs @@ -1,6 +1,6 @@ using BeatTogether.DedicatedServer.Messaging.Abstractions; using BeatTogether.DedicatedServer.Messaging.Models; -using Krypton.Buffers; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MenuRpc { @@ -8,14 +8,14 @@ public sealed class SetSelectedBeatmap : BaseRpcWithValuesPacket { public BeatmapIdentifier Beatmap { get; set; } = new(); - public override void ReadFrom(ref SpanBufferReader reader) + public override void ReadFrom(ref SpanBuffer reader) { base.ReadFrom(ref reader); if (HasValue0) Beatmap.ReadFrom(ref reader); } - public override void WriteTo(ref SpanBufferWriter writer) + public override void WriteTo(ref SpanBuffer writer) { base.WriteTo(ref writer); Beatmap.WriteTo(ref writer); diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetSelectedGameplayModifiers.cs b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetSelectedGameplayModifiers.cs index 4105932b..b9d61765 100644 --- a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetSelectedGameplayModifiers.cs +++ b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetSelectedGameplayModifiers.cs @@ -1,6 +1,6 @@ using BeatTogether.DedicatedServer.Messaging.Abstractions; using BeatTogether.DedicatedServer.Messaging.Models; -using Krypton.Buffers; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MenuRpc { @@ -8,14 +8,14 @@ public sealed class SetSelectedGameplayModifiers : BaseRpcWithValuesPacket { public GameplayModifiers Modifiers { get; set; } = new(); - public override void ReadFrom(ref SpanBufferReader reader) + public override void ReadFrom(ref SpanBuffer reader) { base.ReadFrom(ref reader); if (HasValue0) Modifiers.ReadFrom(ref reader); } - public override void WriteTo(ref SpanBufferWriter writer) + public override void WriteTo(ref SpanBuffer writer) { base.WriteTo(ref writer); Modifiers.WriteTo(ref writer); diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetStartGameTimePacket.cs b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetStartGameTimePacket.cs index 7fde4a41..169b8f66 100644 --- a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetStartGameTimePacket.cs +++ b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/SetStartGameTimePacket.cs @@ -1,23 +1,24 @@ using BeatTogether.DedicatedServer.Messaging.Abstractions; -using Krypton.Buffers; +using BeatTogether.Extensions; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MenuRpc { public sealed class SetStartGameTimePacket : BaseRpcWithValuesPacket { - public float StartTime { get; set; } + public long StartTime { get; set; } - public override void ReadFrom(ref SpanBufferReader reader) + public override void ReadFrom(ref SpanBuffer reader) { base.ReadFrom(ref reader); if (HasValue0) - StartTime = reader.ReadFloat32(); + StartTime = reader.ReadVarLong(); } - public override void WriteTo(ref SpanBufferWriter writer) + public override void WriteTo(ref SpanBuffer writer) { base.WriteTo(ref writer); - writer.WriteFloat32(StartTime); + writer.WriteVarLong(StartTime); } } } diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/StartLevelPacket.cs b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/StartLevelPacket.cs index 731e6d3b..0de6f601 100644 --- a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/StartLevelPacket.cs +++ b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MenuRpc/StartLevelPacket.cs @@ -1,6 +1,7 @@ using BeatTogether.DedicatedServer.Messaging.Abstractions; using BeatTogether.DedicatedServer.Messaging.Models; -using Krypton.Buffers; +using BeatTogether.Extensions; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MenuRpc { @@ -8,9 +9,9 @@ public sealed class StartLevelPacket : BaseRpcWithValuesPacket { public BeatmapIdentifier Beatmap { get; set; } = new(); public GameplayModifiers Modifiers { get; set; } = new(); - public float StartTime { get; set; } + public long StartTime { get; set; } - public override void ReadFrom(ref SpanBufferReader reader) + public override void ReadFrom(ref SpanBuffer reader) { base.ReadFrom(ref reader); if (HasValue0) @@ -18,15 +19,15 @@ public override void ReadFrom(ref SpanBufferReader reader) if (HasValue1) Modifiers.ReadFrom(ref reader); if (HasValue2) - StartTime = reader.ReadFloat32(); + StartTime = reader.ReadVarLong(); } - public override void WriteTo(ref SpanBufferWriter writer) + public override void WriteTo(ref SpanBuffer writer) { base.WriteTo(ref writer); Beatmap.WriteTo(ref writer); Modifiers.WriteTo(ref writer); - writer.WriteFloat32(StartTime); + writer.WriteVarLong(StartTime); } } } diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MpCorePackets/DediPacketSetNewManager.cs b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MpCorePackets/DediPacketSetNewManager.cs index 145b4e7a..010c1be0 100644 --- a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MpCorePackets/DediPacketSetNewManager.cs +++ b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MpCorePackets/DediPacketSetNewManager.cs @@ -1,6 +1,6 @@ -using BeatTogether.LiteNetLib.Abstractions; -using BeatTogether.LiteNetLib.Extensions; -using Krypton.Buffers; +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.Extensions; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MpCorePackets { @@ -8,12 +8,12 @@ public sealed class DediPacketSetNewManagerPacket : INetSerializable { public string NewManagerID { get; set; } = null!; - public void WriteTo(ref SpanBufferWriter bufferWriter) + public void WriteTo(ref SpanBuffer bufferWriter) { bufferWriter.WriteString(NewManagerID); } - public void ReadFrom(ref SpanBufferReader bufferReader) + public void ReadFrom(ref SpanBuffer bufferReader) { NewManagerID = bufferReader.ReadString(); } diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MpCorePackets/GetMpPerPlayerPacket.cs b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MpCorePackets/GetMpPerPlayerPacket.cs new file mode 100644 index 00000000..a15f2f5b --- /dev/null +++ b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MpCorePackets/GetMpPerPlayerPacket.cs @@ -0,0 +1,16 @@ +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.DedicatedServer.Messaging.Util; + +namespace BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MpCorePackets +{ + public sealed class GetMpPerPlayerPacket : INetSerializable + { + public void ReadFrom(ref SpanBuffer bufferReader) + { + } + + public void WriteTo(ref SpanBuffer bufferWriter) + { + } + } +} \ No newline at end of file diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MpCorePackets/GetMpPlayerData.cs b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MpCorePackets/GetMpPlayerData.cs new file mode 100644 index 00000000..bb835900 --- /dev/null +++ b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MpCorePackets/GetMpPlayerData.cs @@ -0,0 +1,16 @@ +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.DedicatedServer.Messaging.Util; + +namespace BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MpCorePackets +{ + public sealed class GetMpPlayerData : INetSerializable + { + public void ReadFrom(ref SpanBuffer bufferReader) + { + } + + public void WriteTo(ref SpanBuffer bufferWriter) + { + } + } +} diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MpCorePackets/MpBeatmapPacket.cs b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MpCorePackets/MpBeatmapPacket.cs index 58d5c935..dbfbd98f 100644 --- a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MpCorePackets/MpBeatmapPacket.cs +++ b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MpCorePackets/MpBeatmapPacket.cs @@ -1,6 +1,6 @@ -using BeatTogether.LiteNetLib.Abstractions; -using BeatTogether.LiteNetLib.Extensions; -using Krypton.Buffers; +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.Extensions; +using BeatTogether.DedicatedServer.Messaging.Util; using System.Collections.Generic; namespace BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MpCorePackets @@ -20,13 +20,11 @@ public sealed class MpBeatmapPacket : INetSerializable public Dictionary requirements = new(); - public void WriteTo(ref SpanBufferWriter bufferWriter) + public void WriteTo(ref SpanBuffer bufferWriter) { throw new System.NotImplementedException(); } - - - public void ReadFrom(ref SpanBufferReader bufferReader) + public void ReadFrom(ref SpanBuffer bufferReader) { levelHash = bufferReader.ReadString(); songName = bufferReader.ReadString(); @@ -49,7 +47,7 @@ public void ReadFrom(ref SpanBufferReader bufferReader) reqsForDifficulty[j] = bufferReader.ReadString(); requirements[difficulty] = reqsForDifficulty; } - + //contributors byte count = bufferReader.ReadByte(); for (int i = 0; i < count; i++) { @@ -57,7 +55,7 @@ public void ReadFrom(ref SpanBufferReader bufferReader) bufferReader.ReadString(); bufferReader.ReadString(); } - + //difficulty colors byte count2 = bufferReader.ReadByte(); for (int i = 0; i < count2; i++) { @@ -65,6 +63,5 @@ public void ReadFrom(ref SpanBufferReader bufferReader) bufferReader.ReadColor(); } } - } } diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MpCorePackets/MpNodePoseSyncStatePacket.cs b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MpCorePackets/MpNodePoseSyncStatePacket.cs new file mode 100644 index 00000000..3eb37b11 --- /dev/null +++ b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MpCorePackets/MpNodePoseSyncStatePacket.cs @@ -0,0 +1,24 @@ +using BeatTogether.Extensions; +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.DedicatedServer.Messaging.Util; + +namespace BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MpCorePackets +{ + public sealed class MpNodePoseSyncStatePacket : INetSerializable + { + public long deltaUpdateFrequency; + public long fullStateUpdateFrequency; + + public void WriteTo(ref SpanBuffer bufferWriter) + { + bufferWriter.WriteVarLong(deltaUpdateFrequency); + bufferWriter.WriteVarLong(fullStateUpdateFrequency); + } + public void ReadFrom(ref SpanBuffer bufferReader) + { + deltaUpdateFrequency = bufferReader.ReadVarLong(); + fullStateUpdateFrequency = bufferReader.ReadVarLong(); + } + } + +} diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MpCorePackets/MpPerPlayerPacket.cs b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MpCorePackets/MpPerPlayerPacket.cs new file mode 100644 index 00000000..3877d5a0 --- /dev/null +++ b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MpCorePackets/MpPerPlayerPacket.cs @@ -0,0 +1,23 @@ +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.DedicatedServer.Messaging.Util; + +namespace BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MpCorePackets +{ + public sealed class MpPerPlayerPacket : INetSerializable + { + public bool PPDEnabled; + public bool PPMEnabled; + + public void ReadFrom(ref SpanBuffer bufferReader) + { + PPDEnabled = bufferReader.ReadBool(); + PPMEnabled = bufferReader.ReadBool(); + } + + public void WriteTo(ref SpanBuffer bufferWriter) + { + bufferWriter.WriteBool(PPDEnabled); + bufferWriter.WriteBool(PPMEnabled); + } + } +} \ No newline at end of file diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MpCorePackets/MpPlayerData.cs b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MpCorePackets/MpPlayerData.cs new file mode 100644 index 00000000..73156606 --- /dev/null +++ b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MpCorePackets/MpPlayerData.cs @@ -0,0 +1,35 @@ +using BeatTogether.Extensions; +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.DedicatedServer.Messaging.Util; + +namespace BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MpCorePackets +{ + public enum Platform + { + Unknown = 0, + Steam = 1, + OculusPC = 2, + OculusQuest = 3, + PS4 = 4 + } + + public sealed class MpPlayerData : INetSerializable + { + public string PlatformID = string.Empty; + public Platform Platform; + public string ClientVersion = string.Empty; + + public void WriteTo(ref SpanBuffer bufferWriter) + { + bufferWriter.WriteString(PlatformID); + bufferWriter.WriteInt32((int)Platform); + bufferWriter.WriteString(ClientVersion.Replace("_","-")); + } + public void ReadFrom(ref SpanBuffer bufferReader) + { + PlatformID = bufferReader.ReadString(); + Platform = (Platform)bufferReader.ReadInt32(); + ClientVersion = bufferReader.ReadString(); + } + } +} diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MpCorePackets/MpScoreSyncStatePacket.cs b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MpCorePackets/MpScoreSyncStatePacket.cs new file mode 100644 index 00000000..5c254dab --- /dev/null +++ b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/MpCorePackets/MpScoreSyncStatePacket.cs @@ -0,0 +1,24 @@ +using BeatTogether.Extensions; +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.DedicatedServer.Messaging.Util; + +namespace BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MpCorePackets +{ + public sealed class MpScoreSyncStatePacket : INetSerializable + { + public long deltaUpdateFrequency; + public long fullStateUpdateFrequency; + + public void WriteTo(ref SpanBuffer bufferWriter) + { + bufferWriter.WriteVarLong(deltaUpdateFrequency); + bufferWriter.WriteVarLong(fullStateUpdateFrequency); + } + public void ReadFrom(ref SpanBuffer bufferReader) + { + deltaUpdateFrequency = bufferReader.ReadVarLong(); + fullStateUpdateFrequency = bufferReader.ReadVarLong(); + } + } + +} diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/NodePoseSyncStateDeltaPacket.cs b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/NodePoseSyncStateDeltaPacket.cs index 4d066fa3..417cfd23 100644 --- a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/NodePoseSyncStateDeltaPacket.cs +++ b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/NodePoseSyncStateDeltaPacket.cs @@ -1,7 +1,7 @@ using BeatTogether.DedicatedServer.Messaging.Models; -using BeatTogether.LiteNetLib.Abstractions; -using BeatTogether.LiteNetLib.Extensions; -using Krypton.Buffers; +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.Extensions; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession { @@ -11,7 +11,7 @@ public sealed class NodePoseSyncStateDeltaPacket : INetSerializable public int TimeOffsetMs { get; set; } public NodePoseSyncState Delta { get; set; } = new(); - public void ReadFrom(ref SpanBufferReader reader) + public void ReadFrom(ref SpanBuffer reader) { SyncStateId = reader.ReadUInt8(); TimeOffsetMs = reader.ReadVarInt(); @@ -20,7 +20,7 @@ public void ReadFrom(ref SpanBufferReader reader) Delta.ReadFrom(ref reader); } - public void WriteTo(ref SpanBufferWriter writer) + public void WriteTo(ref SpanBuffer writer) { writer.WriteUInt8(SyncStateId); writer.WriteVarInt(TimeOffsetMs); diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/NodePoseSyncStatePacket.cs b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/NodePoseSyncStatePacket.cs index c3aefe4c..88fb4012 100644 --- a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/NodePoseSyncStatePacket.cs +++ b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/NodePoseSyncStatePacket.cs @@ -1,26 +1,27 @@ using BeatTogether.DedicatedServer.Messaging.Models; -using BeatTogether.LiteNetLib.Abstractions; -using Krypton.Buffers; +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.Extensions; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession { public sealed class NodePoseSyncStatePacket : INetSerializable { public byte SyncStateId { get; set; } - public float Time { get; set; } + public long Time { get; set; } public NodePoseSyncState State { get; set; } = new(); - public void ReadFrom(ref SpanBufferReader reader) + public void ReadFrom(ref SpanBuffer reader) { SyncStateId = reader.ReadUInt8(); - Time = reader.ReadFloat32(); + Time = (long)reader.ReadVarULong(); State.ReadFrom(ref reader); } - public void WriteTo(ref SpanBufferWriter writer) + public void WriteTo(ref SpanBuffer writer) { writer.WriteUInt8(SyncStateId); - writer.WriteFloat32(Time); + writer.WriteVarULong((ulong)Time); State.WriteTo(ref writer); } } diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/ScoreSyncStateDeltaPacket.cs b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/ScoreSyncStateDeltaPacket.cs index 17cb1827..0e1ab249 100644 --- a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/ScoreSyncStateDeltaPacket.cs +++ b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/ScoreSyncStateDeltaPacket.cs @@ -1,7 +1,7 @@ using BeatTogether.DedicatedServer.Messaging.Models; -using BeatTogether.LiteNetLib.Abstractions; -using BeatTogether.LiteNetLib.Extensions; -using Krypton.Buffers; +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.Extensions; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession { @@ -11,7 +11,7 @@ public sealed class ScoreSyncStateDeltaPacket : INetSerializable public int TimeOffsetMs { get; set; } public StandardScoreSyncState Delta { get; set; } = new(); - public void ReadFrom(ref SpanBufferReader reader) + public void ReadFrom(ref SpanBuffer reader) { SyncStateId = reader.ReadUInt8(); TimeOffsetMs = reader.ReadVarInt(); @@ -19,7 +19,7 @@ public void ReadFrom(ref SpanBufferReader reader) Delta.ReadFrom(ref reader); } - public void WriteTo(ref SpanBufferWriter writer) + public void WriteTo(ref SpanBuffer writer) { writer.WriteUInt8(SyncStateId); writer.WriteVarInt(TimeOffsetMs); diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/ScoreSyncStatePacket.cs b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/ScoreSyncStatePacket.cs index c06b6c5b..b5ee181f 100644 --- a/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/ScoreSyncStatePacket.cs +++ b/BeatTogether.DedicatedServer.Messaging/Packets/MultiplayerSession/ScoreSyncStatePacket.cs @@ -1,26 +1,27 @@ using BeatTogether.DedicatedServer.Messaging.Models; -using BeatTogether.LiteNetLib.Abstractions; -using Krypton.Buffers; +using BeatTogether.Extensions; +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession { public sealed class ScoreSyncStatePacket : INetSerializable { public byte SyncStateId { get; set; } - public float Time { get; set; } + public long Time { get; set; } public StandardScoreSyncState State { get; set; } = new(); - public void ReadFrom(ref SpanBufferReader reader) + public void ReadFrom(ref SpanBuffer reader) { SyncStateId = reader.ReadUInt8(); - Time = reader.ReadFloat32(); + Time = (long)reader.ReadVarULong(); State.ReadFrom(ref reader); } - public void WriteTo(ref SpanBufferWriter writer) + public void WriteTo(ref SpanBuffer writer) { writer.WriteUInt8(SyncStateId); - writer.WriteFloat32(Time); + writer.WriteVarULong((ulong)Time); State.WriteTo(ref writer); } } diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/PingPacket.cs b/BeatTogether.DedicatedServer.Messaging/Packets/PingPacket.cs index 83ddc396..f8dfec9b 100644 --- a/BeatTogether.DedicatedServer.Messaging/Packets/PingPacket.cs +++ b/BeatTogether.DedicatedServer.Messaging/Packets/PingPacket.cs @@ -1,20 +1,21 @@ -using BeatTogether.LiteNetLib.Abstractions; -using Krypton.Buffers; +using BeatTogether.Extensions; +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Packets { public sealed class PingPacket : INetSerializable { - public float PingTime { get; set; } + public long PingTime { get; set; } - public void WriteTo(ref SpanBufferWriter writer) + public void WriteTo(ref SpanBuffer writer) { - writer.WriteFloat32(PingTime); + writer.WriteVarULong((ulong)PingTime); } - public void ReadFrom(ref SpanBufferReader reader) + public void ReadFrom(ref SpanBuffer reader) { - PingTime = reader.ReadFloat32(); + PingTime = (long)reader.ReadVarULong(); } } } diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/PlayerAvatarPacket.cs b/BeatTogether.DedicatedServer.Messaging/Packets/PlayerAvatarPacket.cs new file mode 100644 index 00000000..93c43cde --- /dev/null +++ b/BeatTogether.DedicatedServer.Messaging/Packets/PlayerAvatarPacket.cs @@ -0,0 +1,21 @@ +using BeatTogether.DedicatedServer.Messaging.Models; +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.DedicatedServer.Messaging.Util; + +namespace BeatTogether.DedicatedServer.Messaging.Packets +{ + public sealed class PlayerAvatarPacket : INetSerializable + { + public MultiplayerAvatarsData PlayerAvatar { get; set; } = new(); + + public void ReadFrom(ref SpanBuffer reader) + { + PlayerAvatar.ReadFrom(ref reader); + } + + public void WriteTo(ref SpanBuffer writer) + { + PlayerAvatar.WriteTo(ref writer); + } + } +} diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/PlayerConnectedPacket.cs b/BeatTogether.DedicatedServer.Messaging/Packets/PlayerConnectedPacket.cs index be966fba..3921f2ad 100644 --- a/BeatTogether.DedicatedServer.Messaging/Packets/PlayerConnectedPacket.cs +++ b/BeatTogether.DedicatedServer.Messaging/Packets/PlayerConnectedPacket.cs @@ -1,6 +1,6 @@ -using BeatTogether.LiteNetLib.Abstractions; -using BeatTogether.LiteNetLib.Extensions; -using Krypton.Buffers; +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.Extensions; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Packets { @@ -11,7 +11,7 @@ public sealed class PlayerConnectedPacket : INetSerializable public string UserName { get; set; } = null!; public bool IsConnectionOwner { get; set; } - public void ReadFrom(ref SpanBufferReader reader) + public void ReadFrom(ref SpanBuffer reader) { RemoteConnectionId = reader.ReadUInt8(); UserId = reader.ReadString(); @@ -19,7 +19,7 @@ public void ReadFrom(ref SpanBufferReader reader) IsConnectionOwner = reader.ReadBool(); } - public void WriteTo(ref SpanBufferWriter writer) + public void WriteTo(ref SpanBuffer writer) { writer.WriteUInt8(RemoteConnectionId); writer.WriteString(UserId); diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/PlayerDisconnectedPacket.cs b/BeatTogether.DedicatedServer.Messaging/Packets/PlayerDisconnectedPacket.cs index 4cd677d3..601b7b34 100644 --- a/BeatTogether.DedicatedServer.Messaging/Packets/PlayerDisconnectedPacket.cs +++ b/BeatTogether.DedicatedServer.Messaging/Packets/PlayerDisconnectedPacket.cs @@ -1,7 +1,7 @@ using BeatTogether.DedicatedServer.Messaging.Enums; -using BeatTogether.LiteNetLib.Abstractions; -using BeatTogether.LiteNetLib.Extensions; -using Krypton.Buffers; +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.Extensions; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Packets { @@ -9,12 +9,12 @@ public sealed class PlayerDisconnectedPacket : INetSerializable { public DisconnectedReason DisconnectedReason { get; set; } - public void ReadFrom(ref SpanBufferReader reader) + public void ReadFrom(ref SpanBuffer reader) { DisconnectedReason = (DisconnectedReason)reader.ReadVarInt(); } - public void WriteTo(ref SpanBufferWriter writer) + public void WriteTo(ref SpanBuffer writer) { writer.WriteVarInt((int)DisconnectedReason); } diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/PlayerIdentityPacket.cs b/BeatTogether.DedicatedServer.Messaging/Packets/PlayerIdentityPacket.cs index 7ec7763e..da9879db 100644 --- a/BeatTogether.DedicatedServer.Messaging/Packets/PlayerIdentityPacket.cs +++ b/BeatTogether.DedicatedServer.Messaging/Packets/PlayerIdentityPacket.cs @@ -1,17 +1,16 @@ using BeatTogether.DedicatedServer.Messaging.Models; -using BeatTogether.LiteNetLib.Abstractions; -using Krypton.Buffers; - +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Packets { public sealed class PlayerIdentityPacket : INetSerializable { public PlayerStateHash PlayerState { get; set; } = new(); - public AvatarData PlayerAvatar { get; set; } = new(); + public MultiplayerAvatarsData PlayerAvatar { get; set; } = new(); public ByteArray Random { get; set; } = new(); public ByteArray PublicEncryptionKey { get; set; } = new(); - public void ReadFrom(ref SpanBufferReader reader) + public void ReadFrom(ref SpanBuffer reader) { PlayerState.ReadFrom(ref reader); PlayerAvatar.ReadFrom(ref reader); @@ -19,7 +18,7 @@ public void ReadFrom(ref SpanBufferReader reader) PublicEncryptionKey.ReadFrom(ref reader); } - public void WriteTo(ref SpanBufferWriter writer) + public void WriteTo(ref SpanBuffer writer) { PlayerState.WriteTo(ref writer); PlayerAvatar.WriteTo(ref writer); diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/PlayerLatencyPacket.cs b/BeatTogether.DedicatedServer.Messaging/Packets/PlayerLatencyPacket.cs index 9d9ea872..f95371ad 100644 --- a/BeatTogether.DedicatedServer.Messaging/Packets/PlayerLatencyPacket.cs +++ b/BeatTogether.DedicatedServer.Messaging/Packets/PlayerLatencyPacket.cs @@ -1,20 +1,21 @@ -using BeatTogether.LiteNetLib.Abstractions; -using Krypton.Buffers; +using BeatTogether.Extensions; +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Packets { public sealed class PlayerLatencyPacket : INetSerializable { - public float Latency { get; set; } + public long Latency { get; set; } - public void ReadFrom(ref SpanBufferReader reader) + public void ReadFrom(ref SpanBuffer reader) { - Latency = reader.ReadFloat32(); + Latency = (long)reader.ReadVarULong(); } - public void WriteTo(ref SpanBufferWriter writer) + public void WriteTo(ref SpanBuffer writer) { - writer.WriteFloat32(Latency); + writer.WriteVarULong((ulong)Latency); } } } diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/PlayerSortOrderPacket.cs b/BeatTogether.DedicatedServer.Messaging/Packets/PlayerSortOrderPacket.cs index 5f2d50ac..bf740655 100644 --- a/BeatTogether.DedicatedServer.Messaging/Packets/PlayerSortOrderPacket.cs +++ b/BeatTogether.DedicatedServer.Messaging/Packets/PlayerSortOrderPacket.cs @@ -1,6 +1,6 @@ -using BeatTogether.LiteNetLib.Abstractions; -using BeatTogether.LiteNetLib.Extensions; -using Krypton.Buffers; +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.Extensions; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Packets { @@ -9,13 +9,13 @@ public sealed class PlayerSortOrderPacket : INetSerializable public string UserId { get; set; } = null!; public int SortIndex { get; set; } - public void ReadFrom(ref SpanBufferReader reader) + public void ReadFrom(ref SpanBuffer reader) { UserId = reader.ReadString(); SortIndex = reader.ReadVarInt(); } - public void WriteTo(ref SpanBufferWriter writer) + public void WriteTo(ref SpanBuffer writer) { writer.WriteString(UserId); writer.WriteVarInt(SortIndex); diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/PlayerStatePacket.cs b/BeatTogether.DedicatedServer.Messaging/Packets/PlayerStatePacket.cs index ac182094..80f34fa4 100644 --- a/BeatTogether.DedicatedServer.Messaging/Packets/PlayerStatePacket.cs +++ b/BeatTogether.DedicatedServer.Messaging/Packets/PlayerStatePacket.cs @@ -1,6 +1,6 @@ using BeatTogether.DedicatedServer.Messaging.Models; -using BeatTogether.LiteNetLib.Abstractions; -using Krypton.Buffers; +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Packets { @@ -8,12 +8,12 @@ public sealed class PlayerStatePacket : INetSerializable { public PlayerStateHash PlayerState { get; set; } = new(); - public void ReadFrom(ref SpanBufferReader reader) + public void ReadFrom(ref SpanBuffer reader) { PlayerState.ReadFrom(ref reader); } - public void WriteTo(ref SpanBufferWriter writer) + public void WriteTo(ref SpanBuffer writer) { PlayerState.WriteTo(ref writer); } diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/PongPacket.cs b/BeatTogether.DedicatedServer.Messaging/Packets/PongPacket.cs index 9e15a1f5..f29b7454 100644 --- a/BeatTogether.DedicatedServer.Messaging/Packets/PongPacket.cs +++ b/BeatTogether.DedicatedServer.Messaging/Packets/PongPacket.cs @@ -1,20 +1,21 @@ -using BeatTogether.LiteNetLib.Abstractions; -using Krypton.Buffers; +using BeatTogether.Extensions; +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Packets { public sealed class PongPacket : INetSerializable { - public float PingTime { get; set; } + public long PingTime { get; set; } - public void WriteTo(ref SpanBufferWriter writer) + public void WriteTo(ref SpanBuffer writer) { - writer.WriteFloat32(PingTime); + writer.WriteVarULong((ulong)PingTime); } - public void ReadFrom(ref SpanBufferReader reader) + public void ReadFrom(ref SpanBuffer reader) { - PingTime = reader.ReadFloat32(); + PingTime = (long)reader.ReadVarULong(); } } } diff --git a/BeatTogether.DedicatedServer.Messaging/Packets/SyncTimePacket.cs b/BeatTogether.DedicatedServer.Messaging/Packets/SyncTimePacket.cs index 7ed76d1c..ee5d31ee 100644 --- a/BeatTogether.DedicatedServer.Messaging/Packets/SyncTimePacket.cs +++ b/BeatTogether.DedicatedServer.Messaging/Packets/SyncTimePacket.cs @@ -1,20 +1,21 @@ -using BeatTogether.LiteNetLib.Abstractions; -using Krypton.Buffers; +using BeatTogether.Extensions; +using BeatTogether.DedicatedServer.Messaging.Abstractions; +using BeatTogether.DedicatedServer.Messaging.Util; namespace BeatTogether.DedicatedServer.Messaging.Packets { public sealed class SyncTimePacket : INetSerializable { - public float SyncTime { get; set; } + public long SyncTime { get; set; } - public void WriteTo(ref SpanBufferWriter writer) + public void WriteTo(ref SpanBuffer writer) { - writer.WriteFloat32(SyncTime); + writer.WriteVarULong((ulong)SyncTime); } - public void ReadFrom(ref SpanBufferReader reader) + public void ReadFrom(ref SpanBuffer reader) { - SyncTime = reader.ReadFloat32(); + SyncTime = (long)reader.ReadVarULong(); } } } diff --git a/BeatTogether.DedicatedServer.Messaging/Registries/GameplayRpcPacketRegistry.cs b/BeatTogether.DedicatedServer.Messaging/Registries/GameplayRpcPacketRegistry.cs index a53a54b3..8b76666e 100644 --- a/BeatTogether.DedicatedServer.Messaging/Registries/GameplayRpcPacketRegistry.cs +++ b/BeatTogether.DedicatedServer.Messaging/Registries/GameplayRpcPacketRegistry.cs @@ -1,6 +1,6 @@ using BeatTogether.DedicatedServer.Messaging.Enums; using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.GameplayRpc; -using BeatTogether.LiteNetLib.Abstractions; +using BeatTogether.DedicatedServer.Messaging.Abstractions; namespace BeatTogether.DedicatedServer.Messaging.Registries { @@ -20,6 +20,9 @@ public override void Register() AddPacket(GameplayRpcPacketType.LevelFinished); AddPacket(GameplayRpcPacketType.NoteCut); AddPacket(GameplayRpcPacketType.NoteMissed); + AddPacket(GameplayRpcPacketType.ObstacleSpawned); + AddPacket(GameplayRpcPacketType.NoteSpawned); + AddPacket(GameplayRpcPacketType.SliderSpawned); } } } diff --git a/BeatTogether.DedicatedServer.Messaging/Registries/MenuRpcPacketRegistry.cs b/BeatTogether.DedicatedServer.Messaging/Registries/MenuRpcPacketRegistry.cs index e7aa2e19..f7655f43 100644 --- a/BeatTogether.DedicatedServer.Messaging/Registries/MenuRpcPacketRegistry.cs +++ b/BeatTogether.DedicatedServer.Messaging/Registries/MenuRpcPacketRegistry.cs @@ -1,6 +1,6 @@ using BeatTogether.DedicatedServer.Messaging.Enums; using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MenuRpc; -using BeatTogether.LiteNetLib.Abstractions; +using BeatTogether.DedicatedServer.Messaging.Abstractions; namespace BeatTogether.DedicatedServer.Messaging.Registries { diff --git a/BeatTogether.DedicatedServer.Messaging/Registries/MultiplayerCorePacketRegistry.cs b/BeatTogether.DedicatedServer.Messaging/Registries/MultiplayerCorePacketRegistry.cs index efdd1c42..949f535e 100644 --- a/BeatTogether.DedicatedServer.Messaging/Registries/MultiplayerCorePacketRegistry.cs +++ b/BeatTogether.DedicatedServer.Messaging/Registries/MultiplayerCorePacketRegistry.cs @@ -1,5 +1,6 @@ -using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MpCorePackets; -using BeatTogether.LiteNetLib.Abstractions; +using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MPChatPackets; +using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession.MpCorePackets; +using BeatTogether.DedicatedServer.Messaging.Abstractions; using System; using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; @@ -8,13 +9,20 @@ namespace BeatTogether.DedicatedServer.Messaging.Registries { public class MultiplayerCorePacketRegistry : BasePacketRegistry { - private readonly ConcurrentDictionary _factories = new(); - public override void Register() { AddPacket(); AddPacket(); + AddPacket(); + AddPacket(); + AddPacket(); + AddPacket(); + AddPacket(); + AddPacket(); + AddPacket(); + AddPacket(); + AddPacket(); } public bool TryCreatePacket(string packetId, [MaybeNullWhen(false)] out INetSerializable packet) @@ -28,11 +36,9 @@ public bool TryCreatePacket(string packetId, [MaybeNullWhen(false)] out INetSeri packet = null; return false; } - protected void AddPacket() where T : class, INetSerializable, new() { Type typeFromHandle = typeof(T); - _factories[typeFromHandle.Name] = () => new T(); } } diff --git a/BeatTogether.DedicatedServer.Messaging/Registries/MultiplayerSessionPacketRegistry.cs b/BeatTogether.DedicatedServer.Messaging/Registries/MultiplayerSessionPacketRegistry.cs index 8183ecff..27c82779 100644 --- a/BeatTogether.DedicatedServer.Messaging/Registries/MultiplayerSessionPacketRegistry.cs +++ b/BeatTogether.DedicatedServer.Messaging/Registries/MultiplayerSessionPacketRegistry.cs @@ -1,6 +1,6 @@ using BeatTogether.DedicatedServer.Messaging.Enums; using BeatTogether.DedicatedServer.Messaging.Packets.MultiplayerSession; -using BeatTogether.LiteNetLib.Abstractions; +using BeatTogether.DedicatedServer.Messaging.Abstractions; namespace BeatTogether.DedicatedServer.Messaging.Registries { diff --git a/BeatTogether.DedicatedServer.Messaging/Registries/PacketRegistry.cs b/BeatTogether.DedicatedServer.Messaging/Registries/PacketRegistry.cs index 796bc499..af6f3ef7 100644 --- a/BeatTogether.DedicatedServer.Messaging/Registries/PacketRegistry.cs +++ b/BeatTogether.DedicatedServer.Messaging/Registries/PacketRegistry.cs @@ -1,6 +1,6 @@ using BeatTogether.DedicatedServer.Messaging.Enums; using BeatTogether.DedicatedServer.Messaging.Packets; -using BeatTogether.LiteNetLib.Abstractions; +using BeatTogether.DedicatedServer.Messaging.Abstractions; namespace BeatTogether.DedicatedServer.Messaging.Registries { @@ -14,6 +14,7 @@ public override void Register() AddPacket(PacketType.PlayerLatencyUpdate); AddPacket(PacketType.PlayerDisconnected); AddPacket(PacketType.PlayerSortOrderUpdate); + AddPacket(PacketType.PlayerAvatarUpdate); AddPacket(PacketType.KickPlayer); AddPacket(PacketType.PlayerStateUpdate); AddSubPacketRegistry(PacketType.MultiplayerSession); diff --git a/BeatTogether.DedicatedServer.Messaging/Registries/Unconnected/GameLiftMessageRegistry.cs b/BeatTogether.DedicatedServer.Messaging/Registries/Unconnected/GameLiftMessageRegistry.cs deleted file mode 100644 index 354be19b..00000000 --- a/BeatTogether.DedicatedServer.Messaging/Registries/Unconnected/GameLiftMessageRegistry.cs +++ /dev/null @@ -1,20 +0,0 @@ -using BeatTogether.Core.Messaging.Implementations.Registries; -using BeatTogether.Core.Messaging.Messages; -using BeatTogether.DedicatedServer.Messaging.Enums; -using BeatTogether.DedicatedServer.Messaging.Messages.GameLift; - -namespace BeatTogether.DedicatedServer.Messaging.Registries.Unconnected -{ - public class GameLiftMessageRegistry : BaseMessageRegistry - { - public override uint MessageGroup => 3U; - - public GameLiftMessageRegistry() - { - Register(GameLiftMessageType.AuthenticateUserRequest); - Register(GameLiftMessageType.AuthenticateUserResponse); - Register(GameLiftMessageType.MessageReceivedAcknowledge); - Register(GameLiftMessageType.MultipartMessage); - } - } -} diff --git a/BeatTogether.DedicatedServer.Messaging/Registries/Unconnected/HandshakeMessageRegistry.cs b/BeatTogether.DedicatedServer.Messaging/Registries/Unconnected/HandshakeMessageRegistry.cs deleted file mode 100644 index 9bba5d8c..00000000 --- a/BeatTogether.DedicatedServer.Messaging/Registries/Unconnected/HandshakeMessageRegistry.cs +++ /dev/null @@ -1,25 +0,0 @@ -using BeatTogether.Core.Messaging.Implementations.Registries; -using BeatTogether.Core.Messaging.Messages; -using BeatTogether.DedicatedServer.Messaging.Messages.Handshake; -using BeatTogether.MasterServer.Messaging.Enums; - -namespace BeatTogether.DedicatedServer.Messaging.Registries.Unconnected -{ - public class HandshakeMessageRegistry : BaseMessageRegistry - { - public override uint MessageGroup => 3192347326U; - - public HandshakeMessageRegistry() - { - Register(HandshakeMessageType.ClientHelloRequest); - Register(HandshakeMessageType.HelloVerifyRequest); - Register(HandshakeMessageType.ClientHelloWithCookieRequest); - Register(HandshakeMessageType.ServerHelloRequest); - Register(HandshakeMessageType.ServerCertificateRequest); - Register(HandshakeMessageType.ClientKeyExchangeRequest); - Register(HandshakeMessageType.ChangeCipherSpecRequest); - Register(HandshakeMessageType.MessageReceivedAcknowledge); - Register(HandshakeMessageType.MultipartMessage); - } - } -} diff --git a/BeatTogether.DedicatedServer.Messaging/Structs/AvatarSystemIdentifier.cs b/BeatTogether.DedicatedServer.Messaging/Structs/AvatarSystemIdentifier.cs new file mode 100644 index 00000000..8ae8643a --- /dev/null +++ b/BeatTogether.DedicatedServer.Messaging/Structs/AvatarSystemIdentifier.cs @@ -0,0 +1,60 @@ + +namespace BeatTogether.DedicatedServer.Messaging.Structs +{ + public struct AvatarSystemIdentifier + { + public string AvatarSystemTypeIdentifier { get; set; } + public uint AvatarTypeIdentifierHash { get; set; } + + public AvatarSystemIdentifier(string avatarSystemTypeIdentifier) + { + AvatarSystemTypeIdentifier = avatarSystemTypeIdentifier; + AvatarTypeIdentifierHash = AvatarSystemIdentifier.HashAvatarSystemTypeMultiplier(AvatarSystemTypeIdentifier); + } + + public static uint HashAvatarSystemTypeMultiplier(string avatarSystemTypeIdentifier) + { + return MurmurHash2(avatarSystemTypeIdentifier); + } + + public static uint MurmurHash2(string key) + { + uint num = (uint)key.Length; + uint num2 = 33U ^ num; + int num3 = 0; + while (num >= 4U) + { + uint num4 = (uint)(((uint)key[num3 + 3] << 24) | ((uint)key[num3 + 2] << 16) | ((uint)key[num3 + 1] << 8) | key[num3]); + num4 *= 1540483477U; + num4 ^= num4 >> 24; + num4 *= 1540483477U; + num2 *= 1540483477U; + num2 ^= num4; + num3 += 4; + num -= 4U; + } + switch (num) + { + case 1U: + num2 ^= (uint)key[num3]; + num2 *= 1540483477U; + break; + case 2U: + num2 ^= (uint)((uint)key[num3 + 1] << 8); + num2 ^= (uint)key[num3]; + num2 *= 1540483477U; + break; + case 3U: + num2 ^= (uint)((uint)key[num3 + 2] << 16); + num2 ^= (uint)((uint)key[num3 + 1] << 8); + num2 ^= (uint)key[num3]; + num2 *= 1540483477U; + break; + } + num2 ^= num2 >> 13; + num2 *= 1540483477U; + return num2 ^ (num2 >> 15); + } + } + +} diff --git a/BeatTogether.DedicatedServer.Messaging/Structs/MultiplayerAvatarData.cs b/BeatTogether.DedicatedServer.Messaging/Structs/MultiplayerAvatarData.cs new file mode 100644 index 00000000..c29a7329 --- /dev/null +++ b/BeatTogether.DedicatedServer.Messaging/Structs/MultiplayerAvatarData.cs @@ -0,0 +1,16 @@ + +namespace BeatTogether.DedicatedServer.Messaging.Structs +{ + public readonly struct MultiplayerAvatarData + { + public byte[]? Data { get; } + public uint AvatarTypeIdentifierHash { get; } + + public MultiplayerAvatarData(uint avatarTypeIdentifierHash, byte[]? data) + { + AvatarTypeIdentifierHash = avatarTypeIdentifierHash; + Data = data; + } + + } +} diff --git a/BeatTogether.DedicatedServer.Messaging/Util/SpanBuffer.cs b/BeatTogether.DedicatedServer.Messaging/Util/SpanBuffer.cs new file mode 100644 index 00000000..5a2f461d --- /dev/null +++ b/BeatTogether.DedicatedServer.Messaging/Util/SpanBuffer.cs @@ -0,0 +1,488 @@ +using System; +using System.Buffers.Binary; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; +using Krypton.Buffers; + +namespace BeatTogether.DedicatedServer.Messaging.Util +{ + public ref struct SpanBuffer + { + private readonly bool _resize; + + private Span _buffer; + + private int _offset; + public int Offset => _offset; + public ReadOnlySpan ReadOnlyData => _buffer.Slice(0, _offset); + public Span Data => _buffer.Slice(0, _offset); + public int Size => _offset; + public readonly int RemainingSize => _buffer.Length - _offset; + public ReadOnlySpan RemainingData => _buffer.Slice(_offset); + + public SpanBuffer(Span buffer, bool resize = true) + { + _resize = resize; + _buffer = buffer; + _offset = 0; + } + + public SpanBuffer(int size, bool resize = true) + { + _resize = resize; + _buffer = new byte[size].AsSpan(); + _offset = 0; + } + public void SetOffset(int NewOffset) + { + _offset = NewOffset; + } + + #region Writing To buffer + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void Reserve(int length) + { + int num = _offset + length; + if (num > _buffer.Length) + { + ResizeBuffer(num); + } + } + + private void ResizeBuffer(int neededLength) + { + if (!_resize) + { + throw new OutOfSpaceException(_buffer.Length, _offset, neededLength); + } + + byte[] array = new byte[neededLength * 2]; + _buffer.CopyTo(array.AsSpan()); + _buffer = array.AsSpan(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteBool(bool x) + { + Reserve(1); + _buffer[_offset++] = (byte)(x ? 1 : 0); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteInt8(sbyte x) + { + Reserve(1); + _buffer[_offset++] = (byte)x; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteUInt8(byte x) + { + Reserve(1); + _buffer[_offset++] = x; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteInt16(short x) + { + Reserve(2); + BinaryPrimitives.WriteInt16LittleEndian(_buffer.Slice(_offset), x); + _offset += 2; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteUInt16(ushort x) + { + Reserve(2); + BinaryPrimitives.WriteUInt16LittleEndian(_buffer.Slice(_offset), x); + _offset += 2; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteInt32(int x) + { + Reserve(4); + BinaryPrimitives.WriteInt32LittleEndian(_buffer.Slice(_offset), x); + _offset += 4; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteUInt32(uint x) + { + Reserve(4); + BinaryPrimitives.WriteUInt32LittleEndian(_buffer.Slice(_offset), x); + _offset += 4; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteUInt64(ulong x) + { + Reserve(8); + BinaryPrimitives.WriteUInt64LittleEndian(_buffer.Slice(_offset), x); + _offset += 8; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteInt64(long x) + { + Reserve(8); + BinaryPrimitives.WriteInt64LittleEndian(_buffer.Slice(_offset), x); + _offset += 8; + } + + public void WriteFloat32(float x) + { + if (!BitConverter.IsLittleEndian) + { + throw new NotImplementedException(); + } + + Reserve(4); + MemoryMarshal.Write(_buffer.Slice(_offset), ref x); + _offset += 4; + } + + public void WriteFloat64(double x) + { + if (!BitConverter.IsLittleEndian) + { + throw new NotImplementedException(); + } + + Reserve(8); + MemoryMarshal.Write(_buffer.Slice(_offset), ref x); + _offset += 8; + } + + public void WriteGuid(Guid guid) + { + Reserve(16); + guid.TryWriteBytes(_buffer.Slice(_offset)); + _offset += 16; + } + + public void WriteString(string str, Encoding encoding) + { + int byteCount = encoding.GetByteCount(str); + Reserve(byteCount + 2); + BinaryPrimitives.WriteUInt16LittleEndian(_buffer.Slice(_offset), (ushort)byteCount); + _offset += 2; + Span bytes = _buffer.Slice(_offset, byteCount); + encoding.GetBytes(str.AsSpan(), bytes); + _offset += byteCount; + } + + public void WriteUTF8String(string str) + { + WriteString(str, Encoding.UTF8); + } + + public void WriteUTF16String(string str) + { + WriteString(str, Encoding.Unicode); + } + + public void WriteBytes(ReadOnlySpan x) + { + Reserve(x.Length); + x.CopyTo(_buffer.Slice(_offset)); + _offset += x.Length; + } + public void PadBytes(int n) + { + Reserve(n); + _offset += n; + } + + public void Dispose() + { + _buffer = Span.Empty; + _offset = 0; + } + + public static implicit operator ReadOnlySpan(SpanBuffer buffer) + { + return buffer.ReadOnlyData; + } + #endregion + + #region ReadingFromBuffer + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ThrowIfEndOfBuffer(int neededSize) + { + if (_offset + neededSize > _buffer.Length) + { + throw new EndOfBufferException(_buffer.Length, _offset, neededSize); + } + } + + public Span ReadBytes(int count) + { + ThrowIfEndOfBuffer(count); + Span result = _buffer.Slice(_offset, count); + _offset += count; + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public byte ReadByte() + { + ThrowIfEndOfBuffer(1); + return _buffer[_offset++]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public byte ReadUInt8() + { + return ReadByte(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public sbyte ReadInt8() + { + return (sbyte)ReadByte(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool ReadBool() + { + if (ReadUInt8() != 1) + { + return false; + } + + return true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ushort ReadUInt16() + { + ThrowIfEndOfBuffer(2); + ushort result = BinaryPrimitives.ReadUInt16LittleEndian(_buffer.Slice(_offset)); + _offset += 2; + return result; + } + + public Span ReadUInt16Slice(int count) + { + ThrowIfEndOfBuffer(2 * count); + Span readOnlySpan = MemoryMarshal.Cast(_buffer.Slice(_offset)); + _offset += 2 * count; + if (BitConverter.IsLittleEndian) + { + return readOnlySpan.Slice(0, count); + } + + ushort[] array = new ushort[count]; + for (int i = 0; i < count; i++) + { + array[i] = BinaryPrimitives.ReverseEndianness(readOnlySpan[i]); + } + + return array; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public short ReadInt16() + { + ThrowIfEndOfBuffer(2); + short result = BinaryPrimitives.ReadInt16LittleEndian(_buffer.Slice(_offset)); + _offset += 2; + return result; + } + + public ReadOnlySpan ReadInt16Slice(int count) + { + ThrowIfEndOfBuffer(2 * count); + ReadOnlySpan readOnlySpan = MemoryMarshal.Cast(_buffer.Slice(_offset)); + _offset += 2 * count; + if (BitConverter.IsLittleEndian) + { + return readOnlySpan.Slice(0, count); + } + + short[] array = new short[count]; + for (int i = 0; i < count; i++) + { + array[i] = BinaryPrimitives.ReverseEndianness(readOnlySpan[i]); + } + + return array; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public uint ReadUInt32() + { + ThrowIfEndOfBuffer(4); + uint result = BinaryPrimitives.ReadUInt32LittleEndian(_buffer.Slice(_offset)); + _offset += 4; + return result; + } + + public ReadOnlySpan ReadUInt32Slice(int count) + { + ThrowIfEndOfBuffer(4 * count); + ReadOnlySpan readOnlySpan = MemoryMarshal.Cast(_buffer.Slice(_offset)); + _offset += 4 * count; + if (BitConverter.IsLittleEndian) + { + return readOnlySpan.Slice(0, count); + } + + uint[] array = new uint[count]; + for (int i = 0; i < count; i++) + { + array[i] = BinaryPrimitives.ReverseEndianness(readOnlySpan[i]); + } + + return array; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int ReadInt32() + { + ThrowIfEndOfBuffer(4); + int result = BinaryPrimitives.ReadInt32LittleEndian(_buffer.Slice(_offset)); + _offset += 4; + return result; + } + + public ReadOnlySpan ReadInt32Slice(int count) + { + ThrowIfEndOfBuffer(4 * count); + ReadOnlySpan readOnlySpan = MemoryMarshal.Cast(_buffer.Slice(_offset)); + _offset += 4 * count; + if (BitConverter.IsLittleEndian) + { + return readOnlySpan.Slice(0, count); + } + + int[] array = new int[count]; + for (int i = 0; i < count; i++) + { + array[i] = BinaryPrimitives.ReverseEndianness(readOnlySpan[i]); + } + + return array; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ulong ReadUInt64() + { + ThrowIfEndOfBuffer(8); + ulong result = BinaryPrimitives.ReadUInt64LittleEndian(_buffer.Slice(_offset)); + _offset += 8; + return result; + } + + public ReadOnlySpan ReadUInt64Slice(int count) + { + ThrowIfEndOfBuffer(8 * count); + ReadOnlySpan readOnlySpan = MemoryMarshal.Cast(_buffer.Slice(_offset)); + _offset += 8 * count; + if (BitConverter.IsLittleEndian) + { + return readOnlySpan.Slice(0, count); + } + + ulong[] array = new ulong[count]; + for (int i = 0; i < count; i++) + { + array[i] = BinaryPrimitives.ReverseEndianness(readOnlySpan[i]); + } + + return array; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public long ReadInt64() + { + ThrowIfEndOfBuffer(8); + long result = BinaryPrimitives.ReadInt64LittleEndian(_buffer.Slice(_offset)); + _offset += 8; + return result; + } + + public ReadOnlySpan ReadInt64Slice(int count) + { + ThrowIfEndOfBuffer(8 * count); + ReadOnlySpan readOnlySpan = MemoryMarshal.Cast(_buffer.Slice(_offset)); + _offset += 8 * count; + if (BitConverter.IsLittleEndian) + { + return readOnlySpan.Slice(0, count); + } + + long[] array = new long[count]; + for (int i = 0; i < count; i++) + { + array[i] = BinaryPrimitives.ReverseEndianness(readOnlySpan[i]); + } + + return array; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float ReadFloat32() + { + if (!BitConverter.IsLittleEndian) + { + throw new NotImplementedException(); + } + + ThrowIfEndOfBuffer(4); + float result = MemoryMarshal.Read(_buffer.Slice(_offset)); + _offset += 4; + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public double ReadFloat64() + { + if (!BitConverter.IsLittleEndian) + { + throw new NotImplementedException(); + } + + ThrowIfEndOfBuffer(8); + double result = MemoryMarshal.Read(_buffer.Slice(_offset)); + _offset += 8; + return result; + } + + public Guid ReadGuid() + { + ThrowIfEndOfBuffer(16); + Guid result = new Guid(_buffer.Slice(_offset, 16)); + _offset += 16; + return result; + } + + public string ReadString(Encoding encoding) + { + ushort count = ReadUInt16(); + ReadOnlySpan bytes = ReadBytes(count); + return encoding.GetString(bytes); + } + + public string ReadUTF8String() + { + return ReadString(Encoding.UTF8); + } + + public string ReadUTF16String() + { + return ReadString(Encoding.Unicode); + } + + public void SkipBytes(int count) + { + ThrowIfEndOfBuffer(count); + _offset += count; + } + #endregion + } +} \ No newline at end of file diff --git a/BeatTogether.DedicatedServer.Node/Abstractions/IInstanceFactory.cs b/BeatTogether.DedicatedServer.Node/Abstractions/IInstanceFactory.cs deleted file mode 100644 index c6345130..00000000 --- a/BeatTogether.DedicatedServer.Node/Abstractions/IInstanceFactory.cs +++ /dev/null @@ -1,25 +0,0 @@ -using BeatTogether.DedicatedServer.Interface.Models; -using BeatTogether.DedicatedServer.Kernel.Abstractions; - -namespace BeatTogether.DedicatedServer.Node.Abstractions -{ - public interface IInstanceFactory - { - public IDedicatedInstance? CreateInstance( - string secret, - string managerId, - GameplayServerConfiguration config, - bool permanentManager = false,//If a user links there account to discord and uses a bot to make a lobby, then can enter there userId - float instanceTimeout = 0f, - string ServerName = "", - float resultScreenTime = 20.0f, - float BeatmapStartTime = 5.0f, - float PlayersReadyCountdownTime = 0f, - bool AllowPerPlayerModifiers = false, - bool AllowPerPlayerDifficulties = false, - bool AllowChroma = true, - bool AllowME = true, - bool AllowNE = true - ); - } -} diff --git a/BeatTogether.DedicatedServer.Node/Abstractions/IInstanceRegistry.cs b/BeatTogether.DedicatedServer.Node/Abstractions/IInstanceRegistry.cs deleted file mode 100644 index 99a5928c..00000000 --- a/BeatTogether.DedicatedServer.Node/Abstractions/IInstanceRegistry.cs +++ /dev/null @@ -1,12 +0,0 @@ -using BeatTogether.DedicatedServer.Kernel.Abstractions; -using System.Diagnostics.CodeAnalysis; - -namespace BeatTogether.DedicatedServer.Node.Abstractions -{ - public interface IInstanceRegistry - { - public bool AddInstance(IDedicatedInstance instance); - public bool RemoveInstance(IDedicatedInstance instance); - public bool TryGetInstance(string secret, [MaybeNullWhen(false)] out IDedicatedInstance instance); - } -} diff --git a/BeatTogether.DedicatedServer.Node/Abstractions/INodeService.cs b/BeatTogether.DedicatedServer.Node/Abstractions/INodeService.cs deleted file mode 100644 index 33e39416..00000000 --- a/BeatTogether.DedicatedServer.Node/Abstractions/INodeService.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace BeatTogether.DedicatedServer.Node.Abstractions -{ - public interface INodeService - { - } -} diff --git a/BeatTogether.DedicatedServer.Node/BeatTogether.DedicatedServer.Node.csproj b/BeatTogether.DedicatedServer.Node/BeatTogether.DedicatedServer.Node.csproj index b2cbd542..06d26b42 100644 --- a/BeatTogether.DedicatedServer.Node/BeatTogether.DedicatedServer.Node.csproj +++ b/BeatTogether.DedicatedServer.Node/BeatTogether.DedicatedServer.Node.csproj @@ -7,18 +7,18 @@ BeatTogether Team BeatTogether https://github.com/beattogether/BeatTogether.DedicatedServer - 1.1.0 + 1.1.1 enable - - + + - + diff --git a/BeatTogether.DedicatedServer.Node/Configuration/NodeConfiguration.cs b/BeatTogether.DedicatedServer.Node/Configuration/NodeConfiguration.cs index a7d8b419..13d80cb4 100644 --- a/BeatTogether.DedicatedServer.Node/Configuration/NodeConfiguration.cs +++ b/BeatTogether.DedicatedServer.Node/Configuration/NodeConfiguration.cs @@ -1,10 +1,10 @@ -namespace BeatTogether.DedicatedServer.Node.Configuration +using System; + +namespace BeatTogether.DedicatedServer.Node.Configuration { public sealed class NodeConfiguration { - public string HostName { get; set; } = "127.0.0.1"; - public string NodeVersion { get; } = "1.3.0"; - public int BasePort { get; set; } = 30000; - public int MaximumSlots { get; set; } = 10000; + public string HostEndpoint { get; set; } = "127.0.0.1"; + public Version NodeVersion { get; } = new Version(2,0,0); } } diff --git a/BeatTogether.DedicatedServer.Node/Extensions/HostBuilderExtensions.cs b/BeatTogether.DedicatedServer.Node/Extensions/HostBuilderExtensions.cs index 0d6c31f1..e99cf440 100644 --- a/BeatTogether.DedicatedServer.Node/Extensions/HostBuilderExtensions.cs +++ b/BeatTogether.DedicatedServer.Node/Extensions/HostBuilderExtensions.cs @@ -1,15 +1,8 @@ -using System.Security.Cryptography; using Autobus; +using BeatTogether.Core.ServerMessaging; using BeatTogether.DedicatedServer.Interface; -using BeatTogether.DedicatedServer.Kernel.Abstractions; -using BeatTogether.DedicatedServer.Kernel.Encryption; -using BeatTogether.DedicatedServer.Kernel.Encryption.Abstractions; -using BeatTogether.DedicatedServer.Kernel.Extensions; -using BeatTogether.DedicatedServer.Kernel.Providers; -using BeatTogether.DedicatedServer.Node.Abstractions; using BeatTogether.DedicatedServer.Node.Configuration; using BeatTogether.Extensions; -using BeatTogether.LiteNetLib.Abstractions; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -22,24 +15,12 @@ public static IHostBuilder UseDedicatedServerNode(this IHostBuilder hostBuilder) .ConfigureAppConfiguration() .UseSerilog() .UseAutobus() - .UseDedicatedInstances() .ConfigureServices((hostBuilderContext, services) => services - .AddCoreSecurity() - .AddConfiguration("Node") - .AddTransient() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton(services => - services.GetRequiredService()) - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddServiceKernel() - .AddHostedService() + .AddConfiguration("ServerConfiguration") + .AddServiceKernel() + .AddSingleton() + .AddHostedService() ); } } \ No newline at end of file diff --git a/BeatTogether.DedicatedServer.Node/ForwardServerEventsLayer.cs b/BeatTogether.DedicatedServer.Node/ForwardServerEventsLayer.cs new file mode 100644 index 00000000..f455cae4 --- /dev/null +++ b/BeatTogether.DedicatedServer.Node/ForwardServerEventsLayer.cs @@ -0,0 +1,57 @@ +using Autobus; +using BeatTogether.Core.Abstractions; +using BeatTogether.Core.ServerMessaging; +using BeatTogether.DedicatedServer.Interface.Events; +using System; +using System.Linq; + +namespace BeatTogether.DedicatedServer.Node +{ + public class ForwardServerEventsLayer : ILayer1 + { + + private readonly IAutobus _autobus; + //private readonly ILogger _logger = Log.ForContext(); + + public ForwardServerEventsLayer( + IAutobus autobus) + { + _autobus = autobus; + } + + public void InstanceClosed(IServerInstance instance) + { + _autobus.Publish(new MatchmakingServerStoppedEvent(instance.Secret)); + } + + public void InstanceConfigChanged(IServerInstance instance) + { + _autobus.Publish(new UpdateInstanceConfigEvent(new Core.ServerMessaging.Models.Server(instance))); + } + + public void InstanceCreated(IServerInstance instance) + { + throw new NotImplementedException(); + } + + public void InstancePlayersChanged(IServerInstance instance) + { + _autobus.Publish(new UpdatePlayersEvent(instance.Secret, instance.PlayerHashes.ToArray())); + } + + public void InstanceStateChanged(IServerInstance instance) + { + _autobus.Publish(new ServerInGameplayEvent(instance.Secret, instance.GameState)); + } + + public void PlayerJoined(IServerInstance instance, IPlayer player) + { + _autobus.Publish(new PlayerJoinEvent(instance.Secret, player.HashedUserId)); + } + + public void PlayerLeft(IServerInstance instance, IPlayer player) + { + _autobus.Publish(new PlayerLeaveServerEvent(instance.Secret, player.HashedUserId)); + } + } +} diff --git a/BeatTogether.DedicatedServer.Node/InstanceFactory.cs b/BeatTogether.DedicatedServer.Node/InstanceFactory.cs deleted file mode 100644 index 00b6d410..00000000 --- a/BeatTogether.DedicatedServer.Node/InstanceFactory.cs +++ /dev/null @@ -1,87 +0,0 @@ -using BeatTogether.DedicatedServer.Interface.Models; -using BeatTogether.DedicatedServer.Kernel.Abstractions; -using BeatTogether.DedicatedServer.Kernel.Configuration; -using BeatTogether.DedicatedServer.Kernel.Enums; -using BeatTogether.DedicatedServer.Node.Abstractions; -using Microsoft.Extensions.DependencyInjection; -using System; - -namespace BeatTogether.DedicatedServer.Node -{ - public sealed class InstanceFactory : IInstanceFactory - { - private readonly IInstanceRegistry _instanceRegistry; - private readonly IServiceProvider _serviceProvider; - private readonly IPortAllocator _portAllocator; - - public InstanceFactory( - IInstanceRegistry instanceRegistry, - IServiceProvider serviceProvider, - IPortAllocator portAllocator) - { - _instanceRegistry = instanceRegistry; - _serviceProvider = serviceProvider; - _portAllocator = portAllocator; - } - - public IDedicatedInstance? CreateInstance( - string secret, - string managerId, - GameplayServerConfiguration config, - bool permanentManager, - float instanceTimeout, - string ServerName, - float resultScreenTime, - float BeatmapStartTime, - float PlayersReadyCountdownTime, - bool AllowPerPlayerModifiers, - bool AllowPerPlayerDifficulties, - bool AllowChroma, - bool AllowME, - bool AllowNE - ) - { - var port = _portAllocator.AcquirePort(); - if (!port.HasValue) - return null; - - var scope = _serviceProvider.CreateScope(); - - var instanceConfig = scope.ServiceProvider.GetRequiredService(); - instanceConfig.Port = (int)port!; - instanceConfig.Secret = secret; - instanceConfig.ServerOwnerId = managerId; - instanceConfig.MaxPlayerCount = Math.Min(config.MaxPlayerCount,250); //max size of 254, id 127 routes packets to all, max is 250, last 4 ID's will be reserved for future features - instanceConfig.DiscoveryPolicy = (DiscoveryPolicy)config.DiscoveryPolicy; - instanceConfig.InvitePolicy = (InvitePolicy)config.InvitePolicy; - instanceConfig.GameplayServerMode = (GameplayServerMode)config.GameplayServerMode; - instanceConfig.SongSelectionMode = (SongSelectionMode)config.SongSelectionMode; - instanceConfig.GameplayServerControlSettings = (GameplayServerControlSettings)config.GameplayServerControlSettings; - instanceConfig.DestroyInstanceTimeout = instanceTimeout; - instanceConfig.ServerName = ServerName; - instanceConfig.CountdownConfig.BeatMapStartCountdownTime = Math.Max(BeatmapStartTime,0f); - instanceConfig.CountdownConfig.ResultsScreenTime = Math.Max(resultScreenTime,0f); - instanceConfig.AllowChroma = AllowChroma; - instanceConfig.AllowMappingExtensions = AllowME; - instanceConfig.AllowNoodleExtensions = AllowNE; - instanceConfig.AllowPerPlayerDifficulties = AllowPerPlayerDifficulties; - instanceConfig.AllowPerPlayerModifiers = AllowPerPlayerModifiers; - if (permanentManager) - instanceConfig.SetConstantManagerFromUserId = managerId; - instanceConfig.CountdownConfig.CountdownTimePlayersReady = Math.Max(PlayersReadyCountdownTime,0f); - if (instanceConfig.CountdownConfig.CountdownTimePlayersReady == 0f) - instanceConfig.CountdownConfig.CountdownTimePlayersReady = instanceConfig.GameplayServerMode == GameplayServerMode.Managed ? 15.0f : 30.0f; - var instance = scope.ServiceProvider.GetRequiredService(); - if (!_instanceRegistry.AddInstance(instance)) - return null; - instance.StopEvent += HandleStopEvent; - return instance; - } - - private void HandleStopEvent(IDedicatedInstance Instance) - { - _instanceRegistry.RemoveInstance(Instance); - _portAllocator.ReleasePort(Instance.Port); - } - } -} diff --git a/BeatTogether.DedicatedServer.Node/InstanceRegistry.cs b/BeatTogether.DedicatedServer.Node/InstanceRegistry.cs deleted file mode 100644 index a405f177..00000000 --- a/BeatTogether.DedicatedServer.Node/InstanceRegistry.cs +++ /dev/null @@ -1,22 +0,0 @@ -using BeatTogether.DedicatedServer.Kernel.Abstractions; -using BeatTogether.DedicatedServer.Node.Abstractions; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; - -namespace BeatTogether.DedicatedServer.Node -{ - public sealed class InstanceRegistry : IInstanceRegistry - { - private readonly ConcurrentDictionary _instances = new(); - - public bool AddInstance(IDedicatedInstance instance) => - _instances.TryAdd(instance._configuration.Secret, instance); - - public bool RemoveInstance(IDedicatedInstance instance) => - _instances.TryRemove(instance._configuration.Secret, out _); - - public bool TryGetInstance(string secret, [MaybeNullWhen(false)] out IDedicatedInstance instance) => - _instances.TryGetValue(secret, out instance); - } -} diff --git a/BeatTogether.DedicatedServer.Node/MasterServerEventHandler.cs b/BeatTogether.DedicatedServer.Node/MasterServerEventHandler.cs deleted file mode 100644 index 14c53d10..00000000 --- a/BeatTogether.DedicatedServer.Node/MasterServerEventHandler.cs +++ /dev/null @@ -1,135 +0,0 @@ -using System; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Autobus; -using BeatTogether.DedicatedServer.Interface.Events; -using BeatTogether.DedicatedServer.Kernel.Abstractions; -using BeatTogether.DedicatedServer.Kernel.Encryption; -using BeatTogether.DedicatedServer.Node.Abstractions; -using BeatTogether.DedicatedServer.Node.Configuration; -using BeatTogether.MasterServer.Interface.Events; -using Microsoft.Extensions.Hosting; -using Serilog; - -namespace BeatTogether.DedicatedServer.Node -{ - public sealed class MasterServerEventHandler : IHostedService - { - private readonly IAutobus _autobus; - private readonly PacketEncryptionLayer _packetEncryptionLayer; - private readonly ILogger _logger = Log.ForContext(); - private readonly NodeConfiguration _configuration; - private readonly IInstanceRegistry _instanceRegistry; - - public MasterServerEventHandler( - IAutobus autobus, - PacketEncryptionLayer packetEncryptionLayer, - NodeConfiguration nodeConfiguration, - IInstanceRegistry instanceRegistry) - { - _autobus = autobus; - _packetEncryptionLayer = packetEncryptionLayer; - _configuration = nodeConfiguration; - _instanceRegistry = instanceRegistry; - } - - #region Start/Stop - - public Task StartAsync(CancellationToken cancellationToken) - { - _autobus.Subscribe(HandlePlayerConnectedToMatchmaking); - _autobus.Subscribe(HandleCheckNode); - _autobus.Subscribe(HandleDisconnectPlayer); - _autobus.Subscribe(HandleCloseServer); - _autobus.Publish(new NodeStartedEvent(_configuration.HostName, _configuration.NodeVersion)); - _logger.Information("Dedicated node version: " + _configuration.NodeVersion + " starting: " + _configuration.HostName); - return Task.CompletedTask; - } - - public Task StopAsync(CancellationToken cancellationToken) - { - _autobus.Unsubscribe(HandlePlayerConnectedToMatchmaking); - _autobus.Unsubscribe(HandleCheckNode); - _autobus.Unsubscribe(HandleDisconnectPlayer); - _autobus.Unsubscribe(HandleCloseServer); - return Task.CompletedTask; - } - - #endregion - - #region Handlers - - private Task HandlePlayerConnectedToMatchmaking(PlayerConnectedToMatchmakingServerEvent @event) - { - if (@event.NodeEndpoint != _configuration.HostName) - return Task.CompletedTask; - - var remoteEndPoint = IPEndPoint.Parse(@event.RemoteEndPoint); - var random = @event.Random; - var publicKey = @event.PublicKey; - var playerSessionId = @event.PlayerSessionId; - var serverSecret = @event.Secret; - - // Clients connecting via Graph API will connect directly to us to negotiate encryption parameters - var hasEncryptionParams = random != null && publicKey != null && random.Length > 0 && publicKey.Length > 0; - - if (hasEncryptionParams) - { - _logger.Verbose( - "Adding encrypted end point " + - $"(RemoteEndPoint='{remoteEndPoint}', " + - $"Random='{BitConverter.ToString(random)}', " + - $"PublicKey='{BitConverter.ToString(publicKey)}')." - ); - _packetEncryptionLayer.AddEncryptedEndPoint(remoteEndPoint, random, publicKey); - } - else - { - _logger.Verbose( - "Master server notified us of connecting graph client " + - $"(RemoteEndPoint='{remoteEndPoint}', " + - $"PlayerSessionId='{playerSessionId}')." - ); - - TryGetDedicatedInstance(serverSecret)? - .GetHandshakeSessionRegistry() - .AddPendingPlayerSessionId(playerSessionId); - } - - _autobus.Publish(new NodeReceivedPlayerEncryptionEvent(_configuration.HostName, @event.RemoteEndPoint)); - return Task.CompletedTask; - } - - private Task HandleCheckNode(CheckNodesEvent checkNodesEvent) - { - _autobus.Publish(new NodeOnlineEvent(_configuration.HostName, _configuration.NodeVersion)); - return Task.CompletedTask; - } - - private Task HandleDisconnectPlayer(DisconnectPlayerFromMatchmakingServerEvent disconnectEvent) - { - TryGetDedicatedInstance(disconnectEvent.Secret)?.DisconnectPlayer(disconnectEvent.UserId); - - if (!string.IsNullOrEmpty(disconnectEvent.UserEndPoint)) - _packetEncryptionLayer.RemoveEncryptedEndPoint(IPEndPoint.Parse(disconnectEvent.UserEndPoint)); - - return Task.CompletedTask; - } - - private Task HandleCloseServer(CloseServerInstanceEvent closeEvent) - { - TryGetDedicatedInstance(closeEvent.Secret)?.Stop(); - return Task.CompletedTask; - } - - #endregion - - #region Util - - private IDedicatedInstance? TryGetDedicatedInstance(string secret) => - _instanceRegistry.TryGetInstance(secret, out var instance) ? instance : null; - - #endregion - } -} diff --git a/BeatTogether.DedicatedServer.Node/Models/PlayerFromMessage.cs b/BeatTogether.DedicatedServer.Node/Models/PlayerFromMessage.cs new file mode 100644 index 00000000..c6b39aef --- /dev/null +++ b/BeatTogether.DedicatedServer.Node/Models/PlayerFromMessage.cs @@ -0,0 +1,24 @@ +using BeatTogether.Core.Abstractions; +using BeatTogether.Core.Enums; +using System; + +namespace BeatTogether.DedicatedServer.Node.Models +{ + public class PlayerFromMessage : IPlayer + { + public string HashedUserId { get; set; } + public string PlatformUserId { get; set; } + public string PlayerSessionId { get; set; } + public Platform PlayerPlatform { get; set; } + public Version PlayerClientVersion { get; set; } + + public PlayerFromMessage(Core.ServerMessaging.Models.Player player) + { + HashedUserId = player.HashedUserId; + PlatformUserId = player.PlatformUserId; + PlayerSessionId = player.PlayerSessionId; + PlayerPlatform = player.PlayerPlatform; + PlayerClientVersion = new Version(player.PlayerClientVersion); + } + } +} diff --git a/BeatTogether.DedicatedServer.Node/Models/ServerFromMessage.cs b/BeatTogether.DedicatedServer.Node/Models/ServerFromMessage.cs new file mode 100644 index 00000000..45c6907e --- /dev/null +++ b/BeatTogether.DedicatedServer.Node/Models/ServerFromMessage.cs @@ -0,0 +1,81 @@ +using BeatTogether.Core.Abstractions; +using BeatTogether.Core.Enums; +using BeatTogether.Core.Models; +using System.Collections.Generic; +using System.Net; + +namespace BeatTogether.DedicatedServer.Node.Models +{ + public class ServerFromMessage : IServerInstance + { + public string ServerName { get; set; } + + public string Secret { get; set; } + + public string Code { get; set; } + + public string InstanceId { get; set; } + + public MultiplayerGameState GameState { get; set; } + + public BeatmapDifficultyMask BeatmapDifficultyMask { get; set; } + + public GameplayModifiersMask GameplayModifiersMask { get; set; } + + public GameplayServerConfiguration GameplayServerConfiguration { get; set; } + + public string SongPackMasks { get; set; } + + public string ManagerId { get; set; } + + public bool PermanentManager { get; set; } + + public long ServerStartJoinTimeout { get; set; } + + public bool NeverCloseServer { get; set; } + + public long ResultScreenTime { get; set; } + + public long BeatmapStartTime { get; set; } + + public long PlayersReadyCountdownTime { get; set; } + + public bool AllowPerPlayerModifiers { get; set; } + + public bool AllowPerPlayerDifficulties { get; set; } + + public bool AllowChroma { get; set; } + + public bool AllowME { get; set; } + + public bool AllowNE { get; set; } + + public IPEndPoint InstanceEndPoint { get; set; } = null!; + public HashSet PlayerHashes { get; set; } = null!; + + public ServerFromMessage(Core.ServerMessaging.Models.Server instance) + { + ServerName = instance.ServerName; + Secret = instance.Secret; + Code = instance.Code; + InstanceId = instance.InstanceId; + GameState = instance.GameState; + BeatmapDifficultyMask = instance.BeatmapDifficultyMask; + GameplayModifiersMask = instance.GameplayModifiersMask; + GameplayServerConfiguration = instance.GameplayServerConfiguration; + SongPackMasks = instance.SongPackMasks; + ManagerId = instance.ManagerId; + PermanentManager = instance.PermanentManager; + ServerStartJoinTimeout = instance.ServerStartJoinTimeout; + NeverCloseServer = instance.NeverCloseServer; + ResultScreenTime = instance.ResultScreenTime; + BeatmapStartTime = instance.BeatmapStartTime; + PlayersReadyCountdownTime = instance.PlayersReadyCountdownTime; + AllowPerPlayerDifficulties = instance.AllowPerPlayerDifficulties; + AllowPerPlayerModifiers = instance.AllowPerPlayerModifiers; + AllowChroma = instance.AllowChroma; + AllowME = instance.AllowME; + AllowNE = instance.AllowNE; + } + } +} diff --git a/BeatTogether.DedicatedServer.Node/NodeMessageEventHandler.cs b/BeatTogether.DedicatedServer.Node/NodeMessageEventHandler.cs new file mode 100644 index 00000000..6dbb3743 --- /dev/null +++ b/BeatTogether.DedicatedServer.Node/NodeMessageEventHandler.cs @@ -0,0 +1,100 @@ +using System.Threading; +using System.Threading.Tasks; +using Autobus; +using BeatTogether.Core.Abstractions; +using BeatTogether.Core.Extensions; +using BeatTogether.Core.ServerMessaging.Models; +using BeatTogether.DedicatedServer.Interface.Events; +using BeatTogether.DedicatedServer.Node.Configuration; +using BeatTogether.DedicatedServer.Node.Models; +using BeatTogether.MasterServer.Interface.Events; +using BinaryRecords; +using Microsoft.Extensions.Hosting; +using Serilog; + +namespace BeatTogether.DedicatedServer.Node +{ + public sealed class NodeMessageEventHandler : IHostedService + { + private readonly IAutobus _autobus; + private readonly ILogger _logger = Log.ForContext(); + private readonly NodeConfiguration _configuration; + private readonly ILayer2 _Layer2; + + public NodeMessageEventHandler( + IAutobus autobus, + NodeConfiguration nodeConfiguration, + ILayer2 layer2) + { + _autobus = autobus; + _configuration = nodeConfiguration; + _Layer2 = layer2; + + BinarySerializer.AddGeneratorProvider( + (Player value, ref BinaryBufferWriter buffer) => BinaryBufferWriterExtensions.WritePlayer(ref buffer, value), + (ref BinaryBufferReader bufferReader) => BinaryBufferReaderExtensions.ReadPlayer(ref bufferReader) + ); + BinarySerializer.AddGeneratorProvider( + (Server value, ref BinaryBufferWriter buffer) => BinaryBufferWriterExtensions.WriteServer(ref buffer, value), + (ref BinaryBufferReader bufferReader) => BinaryBufferReaderExtensions.ReadServer(ref bufferReader) + ); + } + + #region Start/Stop + + public Task StartAsync(CancellationToken cancellationToken) + { + _autobus.Subscribe(HandlePlayerConnectedToMatchmaking); + _autobus.Subscribe(HandleCheckNode); + _autobus.Subscribe(HandleDisconnectPlayer); + _autobus.Subscribe(HandleCloseServer); + _autobus.Publish(new NodeStartedEvent(_configuration.HostEndpoint, _configuration.NodeVersion.ToString())); + _logger.Information("Dedicated node version: " + _configuration.NodeVersion.ToString() + ". Host Endpoint: " + _configuration.HostEndpoint); + return Task.CompletedTask; + } + + public Task StopAsync(CancellationToken cancellationToken) + { + _autobus.Unsubscribe(HandlePlayerConnectedToMatchmaking); + _autobus.Unsubscribe(HandleCheckNode); + _autobus.Unsubscribe(HandleDisconnectPlayer); + _autobus.Unsubscribe(HandleCloseServer); + return Task.CompletedTask; + } + + #endregion + + #region Handlers + + private async Task HandlePlayerConnectedToMatchmaking(PlayerSessionDataSendToDediEvent SessionDataEvent) + { + if (SessionDataEvent.NodeEndpoint != _configuration.HostEndpoint) + return; + + Core.Abstractions.IPlayer player = new PlayerFromMessage(SessionDataEvent.Player); + if (!await _Layer2.SetPlayerSessionData(SessionDataEvent.serverInstanceSecret, player)) + return; + + _autobus.Publish(new NodeReceivedPlayerSessionDataEvent(_configuration.HostEndpoint, SessionDataEvent.Player.PlayerSessionId)); + return; + } + + private Task HandleCheckNode(CheckNodesEvent checkNodesEvent) + { + _autobus.Publish(new NodeOnlineEvent(_configuration.HostEndpoint, _configuration.NodeVersion.ToString())); + return Task.CompletedTask; + } + + private async Task HandleDisconnectPlayer(DisconnectPlayerFromMatchmakingServerEvent disconnectEvent) + { + await _Layer2.DisconnectPlayer(disconnectEvent.Secret, disconnectEvent.HashedUserId); + } + + private async Task HandleCloseServer(CloseServerInstanceEvent closeEvent) + { + await _Layer2.CloseInstance(closeEvent.Secret); + } + + #endregion + } +} diff --git a/BeatTogether.DedicatedServer.Node/NodeService.cs b/BeatTogether.DedicatedServer.Node/NodeService.cs index 2ec32121..b97098f5 100644 --- a/BeatTogether.DedicatedServer.Node/NodeService.cs +++ b/BeatTogether.DedicatedServer.Node/NodeService.cs @@ -1,205 +1,49 @@ -using Autobus; +using System.Threading.Tasks; +using BeatTogether.Core.Abstractions; using BeatTogether.DedicatedServer.Interface; -using BeatTogether.DedicatedServer.Interface.Events; using BeatTogether.DedicatedServer.Interface.Requests; using BeatTogether.DedicatedServer.Interface.Responses; -using BeatTogether.DedicatedServer.Kernel.Encryption; -using BeatTogether.DedicatedServer.Node.Abstractions; -using BeatTogether.DedicatedServer.Node.Configuration; -using BeatTogether.DedicatedServer.Kernel.Abstractions; +using BeatTogether.DedicatedServer.Node.Models; using Serilog; -using System; -using System.Threading.Tasks; -using System.Net; -using BeatTogether.DedicatedServer.Messaging.Models; namespace BeatTogether.DedicatedServer.Node { - public sealed class NodeService : IMatchmakingService + public sealed class NodeMatchmakingService : IMatchmakingService { - private readonly NodeConfiguration _configuration; - private readonly IInstanceFactory _instanceFactory; - private readonly PacketEncryptionLayer _packetEncryptionLayer; - private readonly IAutobus _autobus; - private readonly ILogger _logger = Log.ForContext(); + private readonly ILayer2 _Layer2; + private readonly ILogger _logger = Log.ForContext(); - public NodeService( - NodeConfiguration configuration, - IInstanceFactory instanceFactory, - PacketEncryptionLayer packetEncryptionLayer, - IAutobus autobus) + public NodeMatchmakingService( + ILayer2 layer2) { - _configuration = configuration; - _instanceFactory = instanceFactory; - _packetEncryptionLayer = packetEncryptionLayer; - _autobus = autobus; + _Layer2 = layer2; } public async Task CreateMatchmakingServer(CreateMatchmakingServerRequest request) { - _logger.Debug($"Received request to create matchmaking server. " + - $"(Secret={request.Secret}, " + - $"ManagerId={request.ManagerId}, " + - $"MaxPlayerCount={request.Configuration.MaxPlayerCount}, " + - $"DiscoveryPolicy={request.Configuration.DiscoveryPolicy}, " + - $"InvitePolicy={request.Configuration.InvitePolicy}, " + - $"GameplayServerMode={request.Configuration.GameplayServerMode}, " + - $"SongSelectionMode={request.Configuration.SongSelectionMode}, " + - $"GameplayServerControlSettings={request.Configuration.GameplayServerControlSettings})"); + _logger.Debug($"Received request to create matchmaking server from node messaging. " + + $"(Secret={request.Server.Secret}, " + + $"Code={request.Server.Code}, " + + $"ManagerId={request.Server.ManagerId}, " + + $"MaxPlayerCount={request.Server.GameplayServerConfiguration.MaxPlayerCount}, " + + $"DiscoveryPolicy={request.Server.GameplayServerConfiguration.DiscoveryPolicy}, " + + $"InvitePolicy={request.Server.GameplayServerConfiguration.InvitePolicy}, " + + $"GameplayServerMode={request.Server.GameplayServerConfiguration.GameplayServerMode}, " + + $"SongSelectionMode={request.Server.GameplayServerConfiguration.SongSelectionMode}, " + + $"GameplayServerControlSettings={request.Server.GameplayServerConfiguration.GameplayServerControlSettings})"); - var matchmakingServer = _instanceFactory.CreateInstance( - request.Secret, - request.ManagerId, - request.Configuration, - request.PermanentManager, - request.Timeout, - request.ServerName, - request.resultScreenTime, - request.BeatmapStartTime, - request.PlayersReadyCountdownTime , - request.AllowPerPlayerModifiers, - request.AllowPerPlayerDifficulties, - request.AllowChroma, - request.AllowME, - request.AllowNE - ); - if (matchmakingServer is null) - return new CreateMatchmakingServerResponse(CreateMatchmakingServerError.NoAvailableSlots, string.Empty, Array.Empty(), Array.Empty()); + IServerInstance serverInstance = new ServerFromMessage(request.Server); - matchmakingServer.PlayerConnectedEvent += HandleUpdatePlayerEvent; - matchmakingServer.PlayerDisconnectedEvent += HandlePlayerDisconnectEvent; - matchmakingServer.PlayerDisconnectBeforeJoining += HandlePlayerCountChange;//Updates master server player count - //matchmakingServer.StartEvent += HandleStartEvent; - matchmakingServer.StopEvent += HandleStopEvent; - matchmakingServer.GameIsInLobby += HandleGameInLobbyEvent; - //matchmakingServer.StateChangedEvent += HandleStateChangedEvent; //For master server to check if a game is ongoing or not - //matchmakingServer.UpdateBeatmapEvent += HandleBeatmapChangedEvent; - //matchmakingServer.UpdateInstanceEvent += HandleConfigChangeEvent; - //matchmakingServer.LevelFinishedEvent += HandleLevelFinishedEvent; - await matchmakingServer.Start(); + var result = await _Layer2.CreateInstance(serverInstance); + + if(!result) + return new CreateMatchmakingServerResponse(CreateMatchmakingServerError.NoAvailableSlots, string.Empty); + return new CreateMatchmakingServerResponse( CreateMatchmakingServerError.None, - $"{_configuration.HostName}:{matchmakingServer.Port}", - _packetEncryptionLayer.Random, - _packetEncryptionLayer.KeyPair.PublicKey + $"{serverInstance.InstanceEndPoint}" ); } - - - #region EventHandlers - /* - private void HandleLevelFinishedEvent(string secret, BeatmapIdentifier beatmap, List<(string, BeatmapDifficulty, LevelCompletionResults)> Results) - { - Interface.Models.BeatmapIdentifier beatmapIdentifier = new(beatmap.LevelId, beatmap.Characteristic, (Interface.Models.BeatmapDifficulty)beatmap.Difficulty); - List<(string, Interface.Models.BeatmapDifficulty, Interface.Models.LevelCompletionResults)> FinalResults = new(); - foreach (var item in Results) - { - FinalResults.Add((item.Item1, (Interface.Models.BeatmapDifficulty)item.Item2, LevelCompletionCast(item.Item3))); - } - _autobus.Publish(new LevelCompletionResultsEvent(secret, beatmapIdentifier, FinalResults)); - } - */ - private void HandleGameInLobbyEvent(string secret, bool state) - { - //_autobus.Publish(new UpdateStatusEvent(secret, (Interface.Enums.CountdownState)countdownState, (Interface.Enums.MultiplayerGameState)gameState, (Interface.Enums.GameplayState)GameplayState)); - _autobus.Publish(new ServerInGameplayEvent(secret, !state)); - } - /* - private void HandleStateChangedEvent(string secret, CountdownState countdownState, MultiplayerGameState gameState, GameplayManagerState GameplayState) - { - //_autobus.Publish(new UpdateStatusEvent(secret, (Interface.Enums.CountdownState)countdownState, (Interface.Enums.MultiplayerGameState)gameState, (Interface.Enums.GameplayState)GameplayState)); - _autobus.Publish(new ServerInGameplayEvent(secret, gameState == MultiplayerGameState.Game)); - } - - private void HandleBeatmapChangedEvent(string secret, BeatmapIdentifier? beatmap, GameplayModifiers modifiers, bool IsGameplay, DateTime StartTime) - { - _autobus.Publish(new SelectedBeatmapEvent(secret, beatmap is not null ? beatmap.LevelId : string.Empty, beatmap is not null ? beatmap.Characteristic : string.Empty, beatmap is not null ? (uint)beatmap.Difficulty : uint.MinValue, IsGameplay, GameplayCast(modifiers), StartTime)); - } - */ - public static Interface.Models.GameplayModifiers GameplayCast(GameplayModifiers v) - { - return new Interface.Models.GameplayModifiers((Interface.Models.EnergyType)v.Energy, v.NoFailOn0Energy, v.DemoNoFail, v.InstaFail, v.FailOnSaberClash, (Interface.Models.EnabledObstacleType)v.EnabledObstacle, v.DemoNoObstacles, v.FastNotes, v.StrictAngles, v.DisappearingArrows, v.GhostNotes, v.NoBombs, (Interface.Models.SongSpeed)v.Speed, v.NoArrows, v.ProMode, v.ZenMode, v.SmallCubes); - } - public static Interface.Models.LevelCompletionResults LevelCompletionCast(LevelCompletionResults y) - { - return new(GameplayCast(y.GameplayModifiers), y.ModifiedScore, y.MultipliedScore, (Interface.Models.Rank)y.Rank, y.FullCombo, y.LeftSaberMovementDistance, y.RightSaberMovementDistance, y.LeftHandMovementDistance, y.RightHandMovementDistance, (Interface.Models.LevelEndStateType)y.LevelEndStateType, (Interface.Models.LevelEndAction)y.LevelEndAction, y.Energy, y.GoodCutsCount, y.BadCutsCount, y.MissedCount, y.NotGoodCount, y.OkCount, y.MaxCutScore, y.TotalCutScore, y.GoodCutsCountForNotesWithFullScoreScoringType, y.AverageCenterDistanceCutScoreForNotesWithFullScoreScoringType, y.AverageCutScoreForNotesWithFullScoreScoringType, y.MaxCombo, y.EndSongTime); - } - public static Interface.Models.AvatarData AvatarCast(AvatarData v) - { - return new( - v.HeadTopId, - v.HeadTopPrimaryColor, - v.HeadTopSecondaryColor, - v.GlassesId, - v.GlassesColor, - v.FacialHairId, - v.FacialHairColor, - v.HandsId, - v.HandsColor, - v.ClothesId, - v.ClothesPrimaryColor, - v.ClothesSecondaryColor, - v.ClothesDetailColor, - v.SkinColorId, - v.EyesId, - v.MouthId); - } - - /* - private void HandleConfigChangeEvent(IDedicatedInstance inst) - { - _autobus.Publish(new UpdateServerEvent( - inst._configuration.Secret, - new Interface.Models.GameplayServerConfiguration( - inst._configuration.MaxPlayerCount, - (Interface.Enums.DiscoveryPolicy)inst._configuration.DiscoveryPolicy, - (Interface.Enums.InvitePolicy)inst._configuration.InvitePolicy, - (Interface.Enums.GameplayServerMode)inst._configuration.GameplayServerMode, - (Interface.Enums.SongSelectionMode)inst._configuration.SongSelectionMode, - (Interface.Enums.GameplayServerControlSettings)inst._configuration.GameplayServerControlSettings - ), - inst._configuration.Port, - inst._configuration.ManagerId, - inst._configuration.ServerId, - inst._configuration.ServerName, - inst._configuration.DestroyInstanceTimeout, - inst._configuration.SetConstantManagerFromUserId, - inst._configuration.AllowPerPlayerDifficulties, - inst._configuration.AllowPerPlayerModifiers, - inst._configuration.AllowChroma, - inst._configuration.AllowMappingExtensions, - inst._configuration.AllowNoodleExtensions, - inst._configuration.KickPlayersWithoutEntitlementTimeout, - inst._configuration.CountdownConfig.CountdownTimePlayersReady, - inst._configuration.CountdownConfig.BeatMapStartCountdownTime, - inst._configuration.CountdownConfig.ResultsScreenTime)); - } - - private void HandleStartEvent(IDedicatedInstance inst) - { - HandleConfigChangeEvent(inst); - } - */ - private void HandleStopEvent(IDedicatedInstance inst) - { - _autobus.Publish(new MatchmakingServerStoppedEvent(inst._configuration.Secret));//Tells the master server and api server that the server has stopped - } - private void HandleUpdatePlayerEvent(IPlayer player, int PlayerCount) - { - _autobus.Publish(new PlayerLeaveServerEvent(player.Secret, string.Empty, string.Empty, PlayerCount)); - //_autobus.Publish(new PlayerJoinEvent(player.Secret, player.Endpoint.ToString()!, player.UserId, player.UserName, player.ConnectionId, player.SortIndex, AvatarCast(player.Avatar))); - } - private void HandlePlayerDisconnectEvent(IPlayer player, int count) //Updates master server player count And removes the players encryption data from the server - { - _packetEncryptionLayer.RemoveEncryptedEndPoint((IPEndPoint)player.Endpoint); - _autobus.Publish(new PlayerLeaveServerEvent(player.Secret, player.UserId, ((IPEndPoint)player.Endpoint).ToString(), count)); - } - private void HandlePlayerCountChange(string Secret, EndPoint endPoint, int count) //Updates master server player count - { - _packetEncryptionLayer.RemoveEncryptedEndPoint((IPEndPoint)endPoint); - _autobus.Publish(new PlayerLeaveServerEvent(Secret,string.Empty, string.Empty, count)); - } - #endregion } } \ No newline at end of file diff --git a/BeatTogether.DedicatedServer.sln b/BeatTogether.DedicatedServer.sln index be9c0639..ef5f959d 100644 --- a/BeatTogether.DedicatedServer.sln +++ b/BeatTogether.DedicatedServer.sln @@ -13,6 +13,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BeatTogether.DedicatedServe EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BeatTogether.DedicatedServer.Node", "BeatTogether.DedicatedServer.Node\BeatTogether.DedicatedServer.Node.csproj", "{D2B3DC3B-FECD-4E06-8B20-7A60A0BE3C2E}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BeatTogether.DedicatedServer.Ignorance", "BeatTogether.DedicatedServer.Ignorance\BeatTogether.DedicatedServer.Ignorance.csproj", "{775C10CC-FE88-41DD-A9E3-7F66CBA87470}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BeatTogether.DedicatedServer.Instancing", "BeatTogether.DedicatedServer.Instancing\BeatTogether.DedicatedServer.Instancing.csproj", "{2C9A5AAD-D817-4410-BBF2-2EA765C31C1B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -39,6 +43,14 @@ Global {D2B3DC3B-FECD-4E06-8B20-7A60A0BE3C2E}.Debug|Any CPU.Build.0 = Debug|Any CPU {D2B3DC3B-FECD-4E06-8B20-7A60A0BE3C2E}.Release|Any CPU.ActiveCfg = Release|Any CPU {D2B3DC3B-FECD-4E06-8B20-7A60A0BE3C2E}.Release|Any CPU.Build.0 = Release|Any CPU + {775C10CC-FE88-41DD-A9E3-7F66CBA87470}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {775C10CC-FE88-41DD-A9E3-7F66CBA87470}.Debug|Any CPU.Build.0 = Debug|Any CPU + {775C10CC-FE88-41DD-A9E3-7F66CBA87470}.Release|Any CPU.ActiveCfg = Release|Any CPU + {775C10CC-FE88-41DD-A9E3-7F66CBA87470}.Release|Any CPU.Build.0 = Release|Any CPU + {2C9A5AAD-D817-4410-BBF2-2EA765C31C1B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2C9A5AAD-D817-4410-BBF2-2EA765C31C1B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2C9A5AAD-D817-4410-BBF2-2EA765C31C1B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2C9A5AAD-D817-4410-BBF2-2EA765C31C1B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/BeatTogether.DedicatedServer/BeatTogether.DedicatedServer.csproj b/BeatTogether.DedicatedServer/BeatTogether.DedicatedServer.csproj index f9defa64..d2e91a76 100644 --- a/BeatTogether.DedicatedServer/BeatTogether.DedicatedServer.csproj +++ b/BeatTogether.DedicatedServer/BeatTogether.DedicatedServer.csproj @@ -29,6 +29,7 @@ + diff --git a/BeatTogether.DedicatedServer/Program.cs b/BeatTogether.DedicatedServer/Program.cs index aacc2d0b..f6a45f76 100644 --- a/BeatTogether.DedicatedServer/Program.cs +++ b/BeatTogether.DedicatedServer/Program.cs @@ -1,4 +1,5 @@ -using BeatTogether.DedicatedServer.Node.Extensions; +using BeatTogether.DedicatedServer.Instancing.Extensions; +using BeatTogether.DedicatedServer.Node.Extensions; using Microsoft.Extensions.Hosting; namespace BeatTogether.DedicatedServer @@ -9,6 +10,6 @@ public static void Main(string[] args) => CreateHostBuilder(args).Build().Run(); public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args).UseDedicatedServerNode(); + Host.CreateDefaultBuilder(args).UseDedicatedServerNode().UseDedicatedServerInstancing(); } } diff --git a/BeatTogether.DedicatedServer/appsettings.Development.json b/BeatTogether.DedicatedServer/appsettings.Development.json index 2b1699ff..27727fad 100644 --- a/BeatTogether.DedicatedServer/appsettings.Development.json +++ b/BeatTogether.DedicatedServer/appsettings.Development.json @@ -5,6 +5,9 @@ "Overrides": { "Microsoft": "Warning" } + }, + "ServerConfiguration": { + "HostEndpoint": "127.0.0.1" } } } diff --git a/BeatTogether.DedicatedServer/appsettings.json b/BeatTogether.DedicatedServer/appsettings.json index 0c9c125c..12a9f2bf 100644 --- a/BeatTogether.DedicatedServer/appsettings.json +++ b/BeatTogether.DedicatedServer/appsettings.json @@ -2,6 +2,9 @@ "Serilog": { "File": { "Path": "logs/BeatTogether.DedicatedServer-{Date}.log" + }, + "ServerConfiguration": { + "HostEndpoint": "127.0.0.1" } } }