diff --git a/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/Archipelago.meta b/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/Archipelago.meta new file mode 100644 index 0000000000..98d28a1a2a --- /dev/null +++ b/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/Archipelago.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e9f7a825678c443cb1eaaeeaa64b6059 +timeCreated: 1713371082 \ No newline at end of file diff --git a/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/ArchipelagoIslandRoom.cs b/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/Archipelago/ArchipelagoIslandRoomStrategy.cs similarity index 62% rename from Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/ArchipelagoIslandRoom.cs rename to Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/Archipelago/ArchipelagoIslandRoomStrategy.cs index cf236318be..0c34ff7ca9 100644 --- a/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/ArchipelagoIslandRoom.cs +++ b/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/Archipelago/ArchipelagoIslandRoomStrategy.cs @@ -1,54 +1,37 @@ -using Cysharp.Threading.Tasks; +using Cysharp.Threading.Tasks; using DCL.Character; using DCL.Diagnostics; -using DCL.Multiplayer.Connections.Archipelago.AdapterAddress.Current; using DCL.Multiplayer.Connections.Archipelago.LiveConnections; using DCL.Multiplayer.Connections.Archipelago.SignFlow; +using DCL.Multiplayer.Connections.Rooms; using DCL.Multiplayer.Connections.Rooms.Connective; using DCL.Multiplayer.Connections.Typing; using DCL.Utilities.Extensions; using DCL.Web3.Identities; -using LiveKit.Internal.FFIClients.Pools; -using LiveKit.Internal.FFIClients.Pools.Memory; -using LiveKit.Rooms; using System; -using System.Buffers; -using System.Net.WebSockets; using System.Threading; using UnityEngine; using Utility.Multithreading; namespace DCL.Multiplayer.Connections.Archipelago.Rooms { - public class ArchipelagoIslandRoom : IArchipelagoIslandRoom + internal class ArchipelagoIslandRoomStrategy : IRealmRoomStrategy { private readonly IWeb3IdentityCache web3IdentityCache; private readonly IArchipelagoSignFlow signFlow; private readonly ICharacterObject characterObject; - private readonly IConnectiveRoom connectiveRoom; - private readonly ICurrentAdapterAddress currentAdapterAddress; + private readonly string currentAdapterAddress; - private ConnectToRoomAsyncDelegate? connectToRoomAsyncDelegate; + public IConnectiveRoom ConnectiveRoom { get; } - public ArchipelagoIslandRoom(ICharacterObject characterObject, IWeb3IdentityCache web3IdentityCache, IMultiPool multiPool, ICurrentAdapterAddress currentAdapterAddress) : this( - web3IdentityCache, - new LiveConnectionArchipelagoSignFlow( - new WebSocketArchipelagoLiveConnection( - () => new ClientWebSocket(), - new ArrayMemoryPool(ArrayPool.Shared!) - ).WithLog(), - new ArrayMemoryPool(ArrayPool.Shared!), - multiPool - ).WithLog(), - characterObject, - currentAdapterAddress - ) { } + private ConnectToRoomAsyncDelegate? connectToRoomAsyncDelegate; - public ArchipelagoIslandRoom( + public ArchipelagoIslandRoomStrategy( + InteriorRoom sharedRoom, IWeb3IdentityCache web3IdentityCache, IArchipelagoSignFlow signFlow, ICharacterObject characterObject, - ICurrentAdapterAddress currentAdapterAddress + string currentAdapterAddress ) { this.web3IdentityCache = web3IdentityCache; @@ -56,29 +39,9 @@ ICurrentAdapterAddress currentAdapterAddress this.characterObject = characterObject; this.currentAdapterAddress = currentAdapterAddress; - connectiveRoom = new RenewableConnectiveRoom( - () => new ConnectiveRoom( - PrewarmAsync, - SendHeartbeatAsync - ) - ); + ConnectiveRoom = new ConnectiveRoom(sharedRoom, PrewarmAsync, SendHeartbeatAsync); } - public void Start() => - connectiveRoom.Start(); - - public UniTask StopAsync() => - UniTask.WhenAll( - //signFlow.DisconnectAsync(CancellationToken.None), - connectiveRoom.StopAsync() - ); - - public IConnectiveRoom.State CurrentState() => - connectiveRoom.CurrentState(); - - public IRoom Room() => - connectiveRoom.Room(); - private async UniTask PrewarmAsync(CancellationToken token) { await ConnectToArchipelagoAsync(token); @@ -102,15 +65,14 @@ private async UniTask SendHeartbeatAsync(ConnectToRoomAsyncDelegate connectDeleg private void OnNewConnectionString(string connectionString, CancellationToken token) { - if (CurrentState() is IConnectiveRoom.State.Stopped) throw new InvalidOperationException("Room is not running"); + if (ConnectiveRoom.CurrentState() is IConnectiveRoom.State.Stopped) throw new InvalidOperationException("Room is not running"); connectToRoomAsyncDelegate.EnsureNotNull("Connection delegate is not passed yet"); connectToRoomAsyncDelegate!(connectionString, token).Forget(); } private async UniTask ConnectToArchipelagoAsync(CancellationToken token) { - string adapterUrl = await currentAdapterAddress.AdapterUrlAsync(token); - LightResult welcomePeerId = await WelcomePeerIdAsync(adapterUrl, token); + LightResult welcomePeerId = await WelcomePeerIdAsync(currentAdapterAddress, token); welcomePeerId.EnsureSuccess("Cannot authorize with current address and signature, peer id is invalid"); } diff --git a/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/Archipelago/ArchipelagoIslandRoomStrategy.cs.meta b/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/Archipelago/ArchipelagoIslandRoomStrategy.cs.meta new file mode 100644 index 0000000000..275f7bf02c --- /dev/null +++ b/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/Archipelago/ArchipelagoIslandRoomStrategy.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 56f4410ba45d47a1ac123ddea6c10d75 +timeCreated: 1713369725 \ No newline at end of file diff --git a/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/ArchipelagoIslandRoom.cs.meta b/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/ArchipelagoIslandRoom.cs.meta deleted file mode 100644 index 64ce19ec4e..0000000000 --- a/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/ArchipelagoIslandRoom.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: ee4c9d3efc1d4f419d156f3c9d4658d1 -timeCreated: 1707999014 \ No newline at end of file diff --git a/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/Fixed/FixedConnectiveRoom.cs b/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/Fixed/FixedConnectionRoomStrategy.cs similarity index 67% rename from Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/Fixed/FixedConnectiveRoom.cs rename to Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/Fixed/FixedConnectionRoomStrategy.cs index b48d979a7c..d1cc0da452 100644 --- a/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/Fixed/FixedConnectiveRoom.cs +++ b/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/Fixed/FixedConnectionRoomStrategy.cs @@ -1,47 +1,39 @@ -using Cysharp.Threading.Tasks; +using Cysharp.Threading.Tasks; using DCL.Diagnostics; -using DCL.Multiplayer.Connections.Archipelago.AdapterAddress.Current; +using DCL.Multiplayer.Connections.Rooms; using DCL.Multiplayer.Connections.Rooms.Connective; using DCL.WebRequests; -using LiveKit.Rooms; using System; using System.Threading; using UnityEngine; namespace DCL.Multiplayer.Connections.Archipelago.Rooms.Fixed { - public class FixedConnectiveRoom : IConnectiveRoom + /// + /// No need for any abstractions - it's a unique behaviour that we can't replace + /// + internal class FixedConnectionRoomStrategy : IRealmRoomStrategy { private readonly IWebRequestController webRequests; - private readonly IConnectiveRoom connectiveRoom; - private readonly ICurrentAdapterAddress currentAdapterAddress; + private readonly string currentAdapterAddress; - public FixedConnectiveRoom(IWebRequestController webRequests, ICurrentAdapterAddress currentAdapterAddress) + public IConnectiveRoom ConnectiveRoom { get; } + + public FixedConnectionRoomStrategy(InteriorRoom sharedRoom, IWebRequestController webRequests, string currentAdapterAddress) { this.webRequests = webRequests; this.currentAdapterAddress = currentAdapterAddress; - connectiveRoom = new ConnectiveRoom( + ConnectiveRoom = new ConnectiveRoom( + sharedRoom, static _ => UniTask.CompletedTask, RunConnectCycleStepAsync ); } - public void Start() => - connectiveRoom.Start(); - - public UniTask StopAsync() => - connectiveRoom.StopAsync(); - - public IConnectiveRoom.State CurrentState() => - connectiveRoom.CurrentState(); - - public IRoom Room() => - connectiveRoom.Room(); - private async UniTask RunConnectCycleStepAsync(ConnectToRoomAsyncDelegate connectToRoomAsyncDelegate, CancellationToken token) { - if (connectiveRoom.CurrentState() is not IConnectiveRoom.State.Running) + if (ConnectiveRoom.CurrentState() is not IConnectiveRoom.State.Running) { string connectionString = await ConnectionStringAsync(token); await connectToRoomAsyncDelegate(connectionString, token); @@ -50,9 +42,8 @@ private async UniTask RunConnectCycleStepAsync(ConnectToRoomAsyncDelegate connec private async UniTask ConnectionStringAsync(CancellationToken token) { - string adapterUrl = await currentAdapterAddress.AdapterUrlAsync(token); string metadata = FixedMetadata.Default.ToJson(); - GenericPostRequest result = await webRequests.SignedFetchPostAsync(adapterUrl, metadata, token); + GenericPostRequest result = await webRequests.SignedFetchPostAsync(currentAdapterAddress, metadata, token); AdapterResponse response = await result.CreateFromJson(WRJsonParser.Unity); string connectionString = response.fixedAdapter; ReportHub.WithReport(ReportCategory.ARCHIPELAGO_REQUEST).Log($"String is: {connectionString}"); diff --git a/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/Fixed/FixedConnectionRoomStrategy.cs.meta b/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/Fixed/FixedConnectionRoomStrategy.cs.meta new file mode 100644 index 0000000000..303e089a9d --- /dev/null +++ b/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/Fixed/FixedConnectionRoomStrategy.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 9dd46460c4e64c83abedd2b5f92165e8 +timeCreated: 1713367811 \ No newline at end of file diff --git a/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/Fixed/FixedConnectiveRoom.cs.meta b/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/Fixed/FixedConnectiveRoom.cs.meta deleted file mode 100644 index 5552167337..0000000000 --- a/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/Fixed/FixedConnectiveRoom.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 3a853ff612c9460682f8f8d4e75a8e77 -timeCreated: 1713216475 \ No newline at end of file diff --git a/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/ForkArchipelagoIslandRoom.cs b/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/ForkArchipelagoIslandRoom.cs deleted file mode 100644 index 39db329276..0000000000 --- a/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/ForkArchipelagoIslandRoom.cs +++ /dev/null @@ -1,58 +0,0 @@ -using Cysharp.Threading.Tasks; -using DCL.Multiplayer.Connections.Archipelago.AdapterAddress.Current; -using DCL.Multiplayer.Connections.Rooms; -using DCL.Multiplayer.Connections.Rooms.Connective; -using LiveKit.Rooms; -using System; -using System.Threading; - -namespace DCL.Multiplayer.Connections.Archipelago.Rooms -{ - public class ForkArchipelagoIslandRoom : IArchipelagoIslandRoom - { - private readonly ICurrentAdapterAddress currentAdapterAddress; - private readonly Func wssRoomFactory; - private readonly Func httpsRoomFactory; - - private readonly InteriorRoom interiorRoom = new (); - private IConnectiveRoom? current; - - public ForkArchipelagoIslandRoom(ICurrentAdapterAddress currentAdapterAddress, Func wssRoomFactory, Func httpsRoomFactory) - { - this.currentAdapterAddress = currentAdapterAddress; - this.wssRoomFactory = wssRoomFactory; - this.httpsRoomFactory = httpsRoomFactory; - } - - public void Start() - { - StartAsync().Forget(); - } - - private async UniTaskVoid StartAsync() - { - if (current != null && current.CurrentState() is not IConnectiveRoom.State.Stopped) - throw new InvalidOperationException("First stop previous room before starting a new one"); - - string adapterUrl = await currentAdapterAddress.AdapterUrlAsync(CancellationToken.None); - - if (adapterUrl.Contains("wss://")) - current = wssRoomFactory(); - else if (adapterUrl.Contains("https://")) - current = httpsRoomFactory(); - else - throw new InvalidOperationException($"Cannot determine the protocol from the about url: {adapterUrl}"); - - current!.Start(); - interiorRoom.Assign(current.Room(), out _); - } - - public UniTask StopAsync() => current?.StopAsync() ?? throw new InvalidOperationException("Nothing to stop"); - - public IConnectiveRoom.State CurrentState() => - current?.CurrentState() ?? IConnectiveRoom.State.Stopped; - - public IRoom Room() => - interiorRoom; - } -} diff --git a/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/ForkArchipelagoIslandRoom.cs.meta b/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/ForkArchipelagoIslandRoom.cs.meta deleted file mode 100644 index 51a3a77df9..0000000000 --- a/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/ForkArchipelagoIslandRoom.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: c017f3ab35cf4c77b7615967b4fbb854 -timeCreated: 1713213531 \ No newline at end of file diff --git a/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/IArchipelagoIslandRoom.cs b/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/IArchipelagoIslandRoom.cs deleted file mode 100644 index 0013bf4281..0000000000 --- a/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/IArchipelagoIslandRoom.cs +++ /dev/null @@ -1,57 +0,0 @@ -using Cysharp.Threading.Tasks; -using DCL.Character; -using DCL.Multiplayer.Connections.Archipelago.AdapterAddress.Current; -using DCL.Multiplayer.Connections.Archipelago.Rooms.Fixed; -using DCL.Multiplayer.Connections.Rooms; -using DCL.Multiplayer.Connections.Rooms.Connective; -using DCL.Web3.Identities; -using DCL.WebRequests; -using LiveKit.Internal.FFIClients.Pools; -using LiveKit.Rooms; - -namespace DCL.Multiplayer.Connections.Archipelago.Rooms -{ - public interface IArchipelagoIslandRoom : IConnectiveRoom - { - public static IArchipelagoIslandRoom NewDefault( - IWeb3IdentityCache identityCache, - IMultiPool multiPool, - ICharacterObject characterObject, - ICurrentAdapterAddress currentAdapterAddress, - IWebRequestController webRequestController - ) => - new RenewableArchipelagoIslandRoom( - () => new ForkArchipelagoIslandRoom( - currentAdapterAddress, - () => new ArchipelagoIslandRoom( - characterObject, - identityCache, - multiPool, - currentAdapterAddress - ), - () => new FixedConnectiveRoom( - webRequestController, - currentAdapterAddress - ) - ) - ); - - class Fake : IArchipelagoIslandRoom - { - public void Start() - { - //ignore - } - - public UniTask StopAsync() => - UniTask.CompletedTask; - - //ignore - public State CurrentState() => - State.Stopped; - - public IRoom Room() => - NullRoom.INSTANCE; - } - } -} diff --git a/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/IArchipelagoIslandRoom.cs.meta b/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/IArchipelagoIslandRoom.cs.meta deleted file mode 100644 index df428cdb50..0000000000 --- a/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/IArchipelagoIslandRoom.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 8d0b4a37242d4672882795107319ac8e -timeCreated: 1707998935 \ No newline at end of file diff --git a/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/IRealmRoomStrategy.cs b/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/IRealmRoomStrategy.cs new file mode 100644 index 0000000000..676afd489b --- /dev/null +++ b/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/IRealmRoomStrategy.cs @@ -0,0 +1,9 @@ +using DCL.Multiplayer.Connections.Rooms.Connective; + +namespace DCL.Multiplayer.Connections.Archipelago.Rooms +{ + internal interface IRealmRoomStrategy + { + IConnectiveRoom ConnectiveRoom { get; } + } +} diff --git a/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/IRealmRoomStrategy.cs.meta b/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/IRealmRoomStrategy.cs.meta new file mode 100644 index 0000000000..0ef20ebe1a --- /dev/null +++ b/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/IRealmRoomStrategy.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 4572e4e7a3494b2f967083b7b278c0a9 +timeCreated: 1713372845 \ No newline at end of file diff --git a/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/IRealmRoomsProvider.cs b/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/IRealmRoomsProvider.cs new file mode 100644 index 0000000000..6d8a6b3dbe --- /dev/null +++ b/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/IRealmRoomsProvider.cs @@ -0,0 +1,6 @@ +namespace DCL.Multiplayer.Connections.Archipelago.Rooms +{ + public interface IRealmRoomsProvider : IRoomProvider + { + } +} diff --git a/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/IRealmRoomsProvider.cs.meta b/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/IRealmRoomsProvider.cs.meta new file mode 100644 index 0000000000..42e093f133 --- /dev/null +++ b/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/IRealmRoomsProvider.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f2a3377220074417a07366f1889e7133 +timeCreated: 1713376510 \ No newline at end of file diff --git a/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/IRoomProvider.cs b/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/IRoomProvider.cs new file mode 100644 index 0000000000..4fe9d58513 --- /dev/null +++ b/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/IRoomProvider.cs @@ -0,0 +1,38 @@ +using Cysharp.Threading.Tasks; +using DCL.Multiplayer.Connections.Rooms; +using DCL.Multiplayer.Connections.Rooms.Connective; +using LiveKit.Rooms; +using System.Threading; + +namespace DCL.Multiplayer.Connections.Archipelago.Rooms +{ + /// + /// This interface is not connected to . + /// It has some common fields but its contract can mutate independently + /// + public interface IRoomProvider + { + IRoom Room(); + + IConnectiveRoom.State CurrentState(); + + UniTask StartAsync(CancellationToken ct); + + UniTask StopAsync(CancellationToken ct); + + public class Fake : IRoomProvider + { + public IRoom Room() => + NullRoom.INSTANCE; + + public IConnectiveRoom.State CurrentState() => + IConnectiveRoom.State.Stopped; + + public UniTask StartAsync(CancellationToken ct) => + UniTask.CompletedTask; + + public UniTask StopAsync(CancellationToken ct) => + UniTask.CompletedTask; + } + } +} diff --git a/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/IRoomProvider.cs.meta b/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/IRoomProvider.cs.meta new file mode 100644 index 0000000000..ee2e7db37b --- /dev/null +++ b/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/IRoomProvider.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b0aa5beeb4b14e64ba618dc7a59bed3b +timeCreated: 1713376442 \ No newline at end of file diff --git a/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/RealmRoomsProvider.cs b/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/RealmRoomsProvider.cs new file mode 100644 index 0000000000..bcdc030d34 --- /dev/null +++ b/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/RealmRoomsProvider.cs @@ -0,0 +1,86 @@ +using Cysharp.Threading.Tasks; +using DCL.Character; +using DCL.Multiplayer.Connections.Archipelago.AdapterAddress.Current; +using DCL.Multiplayer.Connections.Archipelago.Rooms.Fixed; +using DCL.Multiplayer.Connections.Archipelago.SignFlow; +using DCL.Multiplayer.Connections.Rooms; +using DCL.Multiplayer.Connections.Rooms.Connective; +using DCL.Web3.Identities; +using DCL.WebRequests; +using LiveKit.Rooms; +using System; +using System.Threading; + +namespace DCL.Multiplayer.Connections.Archipelago.Rooms +{ + /// + /// Constructs either a fixed connective room or a dynamic one based on the archipelago. + /// It has nothing to do with the room itself - it operates on the higher level + /// + public class RealmRoomsProvider : IRealmRoomsProvider + { + private readonly IWeb3IdentityCache identityCache; + private readonly ICharacterObject characterObject; + private readonly IWebRequestController webRequestController; + private readonly IArchipelagoSignFlow archipelagoSignFlow; + private readonly ICurrentAdapterAddress currentAdapterAddress; + + /// + /// Maintain a single instance to be able to reconfigure and share with all consumers + /// + private readonly InteriorRoom sharedRoom = new (); + + private IRealmRoomStrategy? currentStrategy; + + public RealmRoomsProvider( + IWeb3IdentityCache identityCache, + ICharacterObject characterObject, + IWebRequestController webRequestController, + IArchipelagoSignFlow archipelagoSignFlow, + ICurrentAdapterAddress currentAdapterAddress) + { + this.identityCache = identityCache; + this.characterObject = characterObject; + this.webRequestController = webRequestController; + this.archipelagoSignFlow = archipelagoSignFlow; + this.currentAdapterAddress = currentAdapterAddress; + } + + public IRoom Room() => + sharedRoom; + + public IConnectiveRoom.State CurrentState() => + currentStrategy?.ConnectiveRoom.CurrentState() ?? IConnectiveRoom.State.Stopped; + + /// + /// Should be called from RealmController + /// + /// + /// + public async UniTask StartAsync(CancellationToken ct) + { + if (currentStrategy != null && currentStrategy.ConnectiveRoom.CurrentState() is not IConnectiveRoom.State.Stopped) + throw new InvalidOperationException("First stop previous room before starting a new one"); + + string adapterUrl = await currentAdapterAddress.AdapterUrlAsync(ct); + + if (adapterUrl.Contains("wss://")) + currentStrategy = CreateFixedConnectionRoomStrategy(adapterUrl); + else if (adapterUrl.Contains("https://")) + currentStrategy = CreateArchipelagoIslandRoomStrategy(adapterUrl); + else + throw new InvalidOperationException($"Cannot determine the protocol from the about url: {adapterUrl}"); + + currentStrategy.ConnectiveRoom.Start(); + } + + public UniTask StopAsync(CancellationToken ct) => + currentStrategy?.ConnectiveRoom.StopAsync(ct) ?? UniTask.CompletedTask; + + private FixedConnectionRoomStrategy CreateFixedConnectionRoomStrategy(string adapterUrl) => + new (sharedRoom, webRequestController, adapterUrl); + + private ArchipelagoIslandRoomStrategy CreateArchipelagoIslandRoomStrategy(string adapterUrl) => + new (sharedRoom, identityCache, archipelagoSignFlow, characterObject, adapterUrl); + } +} diff --git a/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/RealmRoomsProvider.cs.meta b/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/RealmRoomsProvider.cs.meta new file mode 100644 index 0000000000..55a1781b50 --- /dev/null +++ b/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/RealmRoomsProvider.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 5bef39165c54484f8cf07daf9a7dadfd +timeCreated: 1713368208 \ No newline at end of file diff --git a/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/RenewableArchipelagoIslandRoom.cs b/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/RenewableArchipelagoIslandRoom.cs deleted file mode 100644 index 91b56a579d..0000000000 --- a/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/RenewableArchipelagoIslandRoom.cs +++ /dev/null @@ -1,39 +0,0 @@ -using Cysharp.Threading.Tasks; -using DCL.Multiplayer.Connections.Rooms; -using DCL.Multiplayer.Connections.Rooms.Connective; -using LiveKit.Rooms; -using System; - -namespace DCL.Multiplayer.Connections.Archipelago.Rooms -{ - public class RenewableArchipelagoIslandRoom : IArchipelagoIslandRoom - { - private readonly Func factory; - private readonly InteriorRoom room = new (); - private IArchipelagoIslandRoom? current; - - public RenewableArchipelagoIslandRoom(Func factory) - { - this.factory = factory; - } - - public void Start() - { - current = factory()!; - room.Assign(current.Room(), out _); - current.Start(); - } - - public async UniTask StopAsync() - { - await (current?.StopAsync() ?? UniTask.CompletedTask); - room.Assign(NullRoom.INSTANCE, out _); - } - - public IConnectiveRoom.State CurrentState() => - current?.CurrentState() ?? IConnectiveRoom.State.Stopped; - - public IRoom Room() => - room; - } -} diff --git a/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/RenewableArchipelagoIslandRoom.cs.meta b/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/RenewableArchipelagoIslandRoom.cs.meta deleted file mode 100644 index be6fb8e8e0..0000000000 --- a/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/RenewableArchipelagoIslandRoom.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 4ee694b99397430090c7f0e4d993e982 -timeCreated: 1712852621 \ No newline at end of file diff --git a/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/RoomProviderExtensions.cs b/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/RoomProviderExtensions.cs new file mode 100644 index 0000000000..8737aa26ee --- /dev/null +++ b/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/RoomProviderExtensions.cs @@ -0,0 +1,17 @@ +using Cysharp.Threading.Tasks; +using DCL.Multiplayer.Connections.Rooms.Connective; +using System.Threading; + +namespace DCL.Multiplayer.Connections.Archipelago.Rooms +{ + public static class RoomProviderExtensions + { + public static UniTask StartIfNeededAsync(this IRoomProvider room, CancellationToken ct) => + room.CurrentState() is IConnectiveRoom.State.Stopped ? room.StartAsync(ct) : UniTask.CompletedTask; + + public static string ParticipantCountInfo(this IRoomProvider room) => + room.CurrentState() is IConnectiveRoom.State.Running + ? room.Room().Participants.RemoteParticipantSids().Count.ToString() + : "Not connected"; + } +} diff --git a/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/RoomProviderExtensions.cs.meta b/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/RoomProviderExtensions.cs.meta new file mode 100644 index 0000000000..968f943ca3 --- /dev/null +++ b/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/RoomProviderExtensions.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: cd40151dc1b948639c739c4a990bdccf +timeCreated: 1713376477 \ No newline at end of file diff --git a/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/WorldAboutUrl/Current/ICurrentWorldAboutUrl.cs b/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/WorldAboutUrl/Current/ICurrentWorldAboutUrl.cs index 71c93acf43..d5a1f73682 100644 --- a/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/WorldAboutUrl/Current/ICurrentWorldAboutUrl.cs +++ b/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/WorldAboutUrl/Current/ICurrentWorldAboutUrl.cs @@ -2,6 +2,9 @@ namespace DCL.Multiplayer.Connections.Archipelago.WorldAboutUrl.Current { + /// + /// TODO why so many abstractions? + /// public interface ICurrentWorldAboutUrl { string AboutUrl(); diff --git a/Explorer/Assets/DCL/Multiplayer/Connections/Demo/ArchipelagoRoomPlayground.cs b/Explorer/Assets/DCL/Multiplayer/Connections/Demo/ArchipelagoRoomPlayground.cs index dde1b695dc..7b01bdfead 100644 --- a/Explorer/Assets/DCL/Multiplayer/Connections/Demo/ArchipelagoRoomPlayground.cs +++ b/Explorer/Assets/DCL/Multiplayer/Connections/Demo/ArchipelagoRoomPlayground.cs @@ -8,6 +8,7 @@ using DCL.Multiplayer.Connections.FfiClients; using DCL.Multiplayer.Connections.GateKeeper.Rooms; using DCL.Multiplayer.Connections.Pools; +using DCL.Multiplayer.Connections.Rooms; using DCL.Multiplayer.Connections.Systems; using DCL.UserInAppInitializationFlow; using DCL.Web3.Identities; @@ -60,15 +61,17 @@ private async UniTaskVoid LaunchAsync() IWeb3IdentityCache? identityCache = await ArchipelagoFakeIdentityCache.NewAsync(); - var archipelagoIslandRoom = new ArchipelagoIslandRoom( + var archipelagoRoomProvider = new RealmRoomsProvider( identityCache, - signFlow, loonCharacterObject, + IWebRequestController.DEFAULT, + signFlow, ICurrentAdapterAddress.NewDefault(IWebRequestController.DEFAULT, new IRealmData.Fake()) ); + var realFlowLoadingStatus = new RealFlowLoadingStatus(); realFlowLoadingStatus.SetStage(RealFlowLoadingStatus.Stage.Completed); - system = new ConnectionRoomsSystem(world, archipelagoIslandRoom, new IGateKeeperSceneRoom.Fake(), realFlowLoadingStatus); + system = new ConnectionRoomsSystem(world, archipelagoRoomProvider, new IGateKeeperSceneRoomProvider.Fake(), realFlowLoadingStatus); while (this) { diff --git a/Explorer/Assets/DCL/Multiplayer/Connections/Demo/GateKeeperRoomPlayground.cs b/Explorer/Assets/DCL/Multiplayer/Connections/Demo/GateKeeperRoomPlayground.cs index c10ffc827c..d434e71167 100644 --- a/Explorer/Assets/DCL/Multiplayer/Connections/Demo/GateKeeperRoomPlayground.cs +++ b/Explorer/Assets/DCL/Multiplayer/Connections/Demo/GateKeeperRoomPlayground.cs @@ -36,11 +36,11 @@ private async UniTaskVoid LaunchAsync() var places = new PlacesAPIService.PlacesAPIService(new PlacesAPIClient(webRequests)); var realmData = new IRealmData.Fake(); - new GateKeeperSceneRoom( + new GateKeeperSceneRoomProvider( webRequests, new LogMetaDataSource(new MetaDataSource(realmData, character, places)), gateKeeperUrl - ).Start(); + ).StartAsync(destroyCancellationToken); } } } diff --git a/Explorer/Assets/DCL/Multiplayer/Connections/GateKeeper/Rooms/GateKeeperSceneRoom.cs b/Explorer/Assets/DCL/Multiplayer/Connections/GateKeeper/Rooms/GateKeeperSceneRoomProvider.cs similarity index 80% rename from Explorer/Assets/DCL/Multiplayer/Connections/GateKeeper/Rooms/GateKeeperSceneRoom.cs rename to Explorer/Assets/DCL/Multiplayer/Connections/GateKeeper/Rooms/GateKeeperSceneRoomProvider.cs index fd64858cfa..f34700edea 100644 --- a/Explorer/Assets/DCL/Multiplayer/Connections/GateKeeper/Rooms/GateKeeperSceneRoom.cs +++ b/Explorer/Assets/DCL/Multiplayer/Connections/GateKeeper/Rooms/GateKeeperSceneRoomProvider.cs @@ -1,6 +1,7 @@ using Cysharp.Threading.Tasks; using DCL.Diagnostics; using DCL.Multiplayer.Connections.GateKeeper.Meta; +using DCL.Multiplayer.Connections.Rooms; using DCL.Multiplayer.Connections.Rooms.Connective; using DCL.WebRequests; using LiveKit.Rooms; @@ -9,15 +10,17 @@ namespace DCL.Multiplayer.Connections.GateKeeper.Rooms { - public class GateKeeperSceneRoom : IGateKeeperSceneRoom + public class GateKeeperSceneRoomProvider : IGateKeeperSceneRoomProvider { private readonly IWebRequestController webRequests; private readonly IMetaDataSource metaDataSource; - private readonly IConnectiveRoom connectiveRoom; + private readonly ConnectiveRoom connectiveRoom; private readonly string sceneHandleUrl; private MetaData? previousMetaData; - public GateKeeperSceneRoom( + private readonly InteriorRoom interiorRoom; + + public GateKeeperSceneRoomProvider( IWebRequestController webRequests, IMetaDataSource metaDataSource, string sceneHandleUrl = "https://comms-gatekeeper.decentraland.zone/get-scene-adapter" @@ -28,22 +31,26 @@ public GateKeeperSceneRoom( this.sceneHandleUrl = sceneHandleUrl; connectiveRoom = new ConnectiveRoom( + interiorRoom = new InteriorRoom(), // not shared room static _ => UniTask.CompletedTask, RunConnectCycleStepAsync ); } - public void Start() => - connectiveRoom.Start(); - - public UniTask StopAsync() => - connectiveRoom.StopAsync(); + public IRoom Room() => + interiorRoom; public IConnectiveRoom.State CurrentState() => connectiveRoom.CurrentState(); - public IRoom Room() => - connectiveRoom.Room(); + public UniTask StartAsync(CancellationToken ct) + { + connectiveRoom.Start(); + return UniTask.CompletedTask; + } + + public UniTask StopAsync(CancellationToken ct) => + connectiveRoom.StopAsync(ct); private async UniTask RunConnectCycleStepAsync(ConnectToRoomAsyncDelegate connectToRoomAsyncDelegate, CancellationToken token) { diff --git a/Explorer/Assets/DCL/Multiplayer/Connections/GateKeeper/Rooms/GateKeeperSceneRoom.cs.meta b/Explorer/Assets/DCL/Multiplayer/Connections/GateKeeper/Rooms/GateKeeperSceneRoomProvider.cs.meta similarity index 100% rename from Explorer/Assets/DCL/Multiplayer/Connections/GateKeeper/Rooms/GateKeeperSceneRoom.cs.meta rename to Explorer/Assets/DCL/Multiplayer/Connections/GateKeeper/Rooms/GateKeeperSceneRoomProvider.cs.meta diff --git a/Explorer/Assets/DCL/Multiplayer/Connections/GateKeeper/Rooms/IGateKeeperSceneRoom.cs b/Explorer/Assets/DCL/Multiplayer/Connections/GateKeeper/Rooms/IGateKeeperSceneRoom.cs deleted file mode 100644 index a71acb231c..0000000000 --- a/Explorer/Assets/DCL/Multiplayer/Connections/GateKeeper/Rooms/IGateKeeperSceneRoom.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Cysharp.Threading.Tasks; -using DCL.Multiplayer.Connections.Rooms; -using DCL.Multiplayer.Connections.Rooms.Connective; -using LiveKit.Rooms; - -namespace DCL.Multiplayer.Connections.GateKeeper.Rooms -{ - public interface IGateKeeperSceneRoom : IConnectiveRoom - { - class Fake : IGateKeeperSceneRoom - { - public void Start() - { - //ignore - } - - public UniTask StopAsync() => - UniTask.CompletedTask; - - //ignore - public State CurrentState() => - State.Stopped; - - public IRoom Room() => - NullRoom.INSTANCE; - } - } -} diff --git a/Explorer/Assets/DCL/Multiplayer/Connections/GateKeeper/Rooms/IGateKeeperSceneRoomProvider.cs b/Explorer/Assets/DCL/Multiplayer/Connections/GateKeeper/Rooms/IGateKeeperSceneRoomProvider.cs new file mode 100644 index 0000000000..85ee45898f --- /dev/null +++ b/Explorer/Assets/DCL/Multiplayer/Connections/GateKeeper/Rooms/IGateKeeperSceneRoomProvider.cs @@ -0,0 +1,9 @@ +using DCL.Multiplayer.Connections.Archipelago.Rooms; + +namespace DCL.Multiplayer.Connections.GateKeeper.Rooms +{ + public interface IGateKeeperSceneRoomProvider : IRoomProvider + { + class Fake : IRoomProvider.Fake, IGateKeeperSceneRoomProvider { } + } +} diff --git a/Explorer/Assets/DCL/Multiplayer/Connections/GateKeeper/Rooms/IGateKeeperSceneRoom.cs.meta b/Explorer/Assets/DCL/Multiplayer/Connections/GateKeeper/Rooms/IGateKeeperSceneRoomProvider.cs.meta similarity index 100% rename from Explorer/Assets/DCL/Multiplayer/Connections/GateKeeper/Rooms/IGateKeeperSceneRoom.cs.meta rename to Explorer/Assets/DCL/Multiplayer/Connections/GateKeeper/Rooms/IGateKeeperSceneRoomProvider.cs.meta diff --git a/Explorer/Assets/DCL/Multiplayer/Connections/RoomHubs/IRoomHub.cs b/Explorer/Assets/DCL/Multiplayer/Connections/RoomHubs/IRoomHub.cs index 11e0714af6..bf038452a0 100644 --- a/Explorer/Assets/DCL/Multiplayer/Connections/RoomHubs/IRoomHub.cs +++ b/Explorer/Assets/DCL/Multiplayer/Connections/RoomHubs/IRoomHub.cs @@ -1,6 +1,7 @@ using Cysharp.Threading.Tasks; using DCL.Multiplayer.Connections.Rooms; using LiveKit.Rooms; +using System.Threading; namespace DCL.Multiplayer.Connections.RoomHubs { @@ -10,9 +11,9 @@ public interface IRoomHub IRoom SceneRoom(); - UniTask StartAsync(); + UniTask StartAsync(CancellationToken ct); - UniTask StopAsync(); + UniTask StopAsync(CancellationToken ct); class Fake : IRoomHub { @@ -22,10 +23,10 @@ public IRoom IslandRoom() => public IRoom SceneRoom() => NullRoom.INSTANCE; - public UniTask StartAsync() => + public UniTask StartAsync(CancellationToken ct) => UniTask.CompletedTask; - public UniTask StopAsync() => + public UniTask StopAsync(CancellationToken ct) => UniTask.CompletedTask; public void Reconnect() diff --git a/Explorer/Assets/DCL/Multiplayer/Connections/RoomHubs/RoomHub.cs b/Explorer/Assets/DCL/Multiplayer/Connections/RoomHubs/RoomHub.cs index 66aeac342a..035ac58f1c 100644 --- a/Explorer/Assets/DCL/Multiplayer/Connections/RoomHubs/RoomHub.cs +++ b/Explorer/Assets/DCL/Multiplayer/Connections/RoomHubs/RoomHub.cs @@ -1,15 +1,17 @@ using Cysharp.Threading.Tasks; -using DCL.Multiplayer.Connections.Rooms.Connective; +using DCL.Multiplayer.Connections.Archipelago.Rooms; +using DCL.Multiplayer.Connections.GateKeeper.Rooms; using LiveKit.Rooms; +using System.Threading; namespace DCL.Multiplayer.Connections.RoomHubs { public class RoomHub : IRoomHub { - private readonly IConnectiveRoom archipelagoIslandRoom; - private readonly IConnectiveRoom gateKeeperSceneRoom; + private readonly IRealmRoomsProvider archipelagoIslandRoom; + private readonly IGateKeeperSceneRoomProvider gateKeeperSceneRoom; - public RoomHub(IConnectiveRoom archipelagoIslandRoom, IConnectiveRoom gateKeeperSceneRoom) + public RoomHub(IRealmRoomsProvider archipelagoIslandRoom, IGateKeeperSceneRoomProvider gateKeeperSceneRoom) { this.archipelagoIslandRoom = archipelagoIslandRoom; this.gateKeeperSceneRoom = gateKeeperSceneRoom; @@ -21,17 +23,15 @@ public IRoom IslandRoom() => public IRoom SceneRoom() => gateKeeperSceneRoom.Room(); - public UniTask StartAsync() - { - archipelagoIslandRoom.Start(); - gateKeeperSceneRoom.Start(); - return UniTask.CompletedTask; - } + public UniTask StartAsync(CancellationToken ct) => + UniTask.WhenAll( + archipelagoIslandRoom.StartAsync(ct), + gateKeeperSceneRoom.StartAsync(ct)); - public UniTask StopAsync() => + public UniTask StopAsync(CancellationToken ct) => UniTask.WhenAll( - archipelagoIslandRoom.StopAsync(), - gateKeeperSceneRoom.StopAsync() + archipelagoIslandRoom.StopAsync(ct), + gateKeeperSceneRoom.StopAsync(ct) ); } } diff --git a/Explorer/Assets/DCL/Multiplayer/Connections/Rooms/Connective/ConnectiveRoom.cs b/Explorer/Assets/DCL/Multiplayer/Connections/Rooms/Connective/ConnectiveRoom.cs index 1e72ccce49..6277c365f9 100644 --- a/Explorer/Assets/DCL/Multiplayer/Connections/Rooms/Connective/ConnectiveRoom.cs +++ b/Explorer/Assets/DCL/Multiplayer/Connections/Rooms/Connective/ConnectiveRoom.cs @@ -25,14 +25,17 @@ namespace DCL.Multiplayer.Connections.Rooms.Connective public delegate UniTask ConnectToRoomAsyncDelegate(string connectionString, CancellationToken token); + /// + /// Represents the core of the room behaviour + /// public class ConnectiveRoom : IConnectiveRoom { private readonly PrewarmAsyncDelegate prewarmAsync; private readonly CycleStepDelegate runConnectCycleStepAsync; - private readonly InteriorRoom room = new (); private readonly TimeSpan heartbeatsInterval = TimeSpan.FromSeconds(1); private readonly Atomic roomState = new (IConnectiveRoom.State.Stopped); + private readonly IObjectPool roomPool = new ObjectPool( () => new LogRoom( new Room( @@ -49,13 +52,17 @@ public class ConnectiveRoom : IConnectiveRoom ) ); + private readonly InteriorRoom room; + private CancellationTokenSource? cancellationTokenSource; public ConnectiveRoom( + InteriorRoom interiorRoom, PrewarmAsyncDelegate prewarmAsync, CycleStepDelegate runConnectCycleStepAsync ) { + room = interiorRoom; this.prewarmAsync = prewarmAsync; this.runConnectCycleStepAsync = runConnectCycleStepAsync; } @@ -69,7 +76,7 @@ public void Start() RunAsync().Forget(); } - public async UniTask StopAsync() + public async UniTask StopAsync(CancellationToken ct) { if (CurrentState() is IConnectiveRoom.State.Stopped or IConnectiveRoom.State.Stopping) throw new InvalidOperationException("Room is already stopped"); @@ -84,13 +91,10 @@ public async UniTask StopAsync() public IConnectiveRoom.State CurrentState() => roomState.Value(); - public IRoom Room() => - room; - private async UniTask NewCancellationTokenAsync() { if (cancellationTokenSource != null) - await StopAsync(); + await StopAsync(cancellationTokenSource.Token); cancellationTokenSource = new CancellationTokenSource(); return cancellationTokenSource.Token; @@ -111,7 +115,7 @@ private async UniTaskVoid RunAsync() private async UniTask TryConnectToRoomAsync(string connectionString, CancellationToken token) { - var newRoom = roomPool.Get()!; + IRoom newRoom = roomPool.Get()!; var credentials = new ConnectionStringCredentials(connectionString); bool connectResult = await newRoom.ConnectAsync(credentials, token); diff --git a/Explorer/Assets/DCL/Multiplayer/Connections/Rooms/Connective/IConnectiveRoom.cs b/Explorer/Assets/DCL/Multiplayer/Connections/Rooms/Connective/IConnectiveRoom.cs index 2f5792824e..21ed4788d2 100644 --- a/Explorer/Assets/DCL/Multiplayer/Connections/Rooms/Connective/IConnectiveRoom.cs +++ b/Explorer/Assets/DCL/Multiplayer/Connections/Rooms/Connective/IConnectiveRoom.cs @@ -1,8 +1,11 @@ using Cysharp.Threading.Tasks; -using LiveKit.Rooms; +using System.Threading; namespace DCL.Multiplayer.Connections.Rooms.Connective { + /// + /// This interface became redundant but I keep it if we want to mock in the future + /// public interface IConnectiveRoom { enum State @@ -10,46 +13,13 @@ enum State Stopped, Starting, Running, - Stopping + Stopping, } void Start(); - UniTask StopAsync(); + UniTask StopAsync(CancellationToken ct); State CurrentState(); - - IRoom Room(); - - class Fake : IConnectiveRoom - { - public void Start() - { - //ignore - } - - public UniTask StopAsync() => - UniTask.CompletedTask; - - public State CurrentState() => - State.Stopped; - - public IRoom Room() => - NullRoom.INSTANCE; - } - } - - public static class GateKeeperSceneRoomExtensions - { - public static void StartIfNot(this IConnectiveRoom room) - { - if (room.CurrentState() is IConnectiveRoom.State.Stopped) - room.Start(); - } - - public static string ParticipantCountInfo(this IConnectiveRoom room) => - room.CurrentState() is IConnectiveRoom.State.Running - ? room.Room().Participants.RemoteParticipantSids().Count.ToString() - : "Not connected"; } } diff --git a/Explorer/Assets/DCL/Multiplayer/Connections/Rooms/Connective/RenewableConnectiveRoom.cs b/Explorer/Assets/DCL/Multiplayer/Connections/Rooms/Connective/RenewableConnectiveRoom.cs deleted file mode 100644 index 48ed2ce1a8..0000000000 --- a/Explorer/Assets/DCL/Multiplayer/Connections/Rooms/Connective/RenewableConnectiveRoom.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Cysharp.Threading.Tasks; -using LiveKit.Rooms; -using System; - -namespace DCL.Multiplayer.Connections.Rooms.Connective -{ - public class RenewableConnectiveRoom : IConnectiveRoom - { - private readonly Func factory; - private readonly InteriorRoom room = new (); - private IConnectiveRoom? current; - - public RenewableConnectiveRoom(Func factory) - { - this.factory = factory; - } - - public void Start() - { - current = factory(); - current!.Start(); - room.Assign(current.Room(), out _); - } - - public async UniTask StopAsync() - { - await (current?.StopAsync() ?? UniTask.CompletedTask); - current = null; - room.Assign(NullRoom.INSTANCE, out _); - } - - public IConnectiveRoom.State CurrentState() => - current?.CurrentState() ?? IConnectiveRoom.State.Stopped; - - public IRoom Room() => - room; - } -} diff --git a/Explorer/Assets/DCL/Multiplayer/Connections/Rooms/Connective/RenewableConnectiveRoom.cs.meta b/Explorer/Assets/DCL/Multiplayer/Connections/Rooms/Connective/RenewableConnectiveRoom.cs.meta deleted file mode 100644 index 5f2c046cb9..0000000000 --- a/Explorer/Assets/DCL/Multiplayer/Connections/Rooms/Connective/RenewableConnectiveRoom.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: df60aa396cbb4c8d81f2ca1592213fcf -timeCreated: 1712847489 \ No newline at end of file diff --git a/Explorer/Assets/DCL/Multiplayer/Connections/Systems/ConnectionRoomsSystem.cs b/Explorer/Assets/DCL/Multiplayer/Connections/Systems/ConnectionRoomsSystem.cs index 6742becb71..e15139d4d5 100644 --- a/Explorer/Assets/DCL/Multiplayer/Connections/Systems/ConnectionRoomsSystem.cs +++ b/Explorer/Assets/DCL/Multiplayer/Connections/Systems/ConnectionRoomsSystem.cs @@ -1,41 +1,51 @@ using Arch.Core; using Arch.SystemGroups; using Arch.SystemGroups.DefaultSystemGroups; +using Cysharp.Threading.Tasks; using DCL.Multiplayer.Connections.Archipelago.Rooms; using DCL.Multiplayer.Connections.GateKeeper.Rooms; -using DCL.Multiplayer.Connections.Rooms.Connective; using DCL.UserInAppInitializationFlow; using ECS.Abstract; +using System.Threading; +using Utility; namespace DCL.Multiplayer.Connections.Systems { [UpdateInGroup(typeof(PresentationSystemGroup))] public partial class ConnectionRoomsSystem : BaseUnityLoopSystem { - private readonly IArchipelagoIslandRoom archipelagoIslandRoom; - private readonly IGateKeeperSceneRoom gateKeeperSceneRoom; + private readonly IRealmRoomsProvider archipelagoIslandRoom; + private readonly IGateKeeperSceneRoomProvider gateKeeperSceneRoomProvider; private readonly IReadOnlyRealFlowLoadingStatus loadingStatus; + private CancellationTokenSource cancellationTokenSource = new (); + private bool alreadyStarted; public ConnectionRoomsSystem( World world, - IArchipelagoIslandRoom archipelagoIslandRoom, - IGateKeeperSceneRoom gateKeeperSceneRoom, + IRealmRoomsProvider archipelagoIslandRoom, + IGateKeeperSceneRoomProvider gateKeeperSceneRoomProvider, IReadOnlyRealFlowLoadingStatus loadingStatus) : base(world) { this.archipelagoIslandRoom = archipelagoIslandRoom; - this.gateKeeperSceneRoom = gateKeeperSceneRoom; + this.gateKeeperSceneRoomProvider = gateKeeperSceneRoomProvider; this.loadingStatus = loadingStatus; } + public override void Dispose() + { + cancellationTokenSource.SafeCancelAndDispose(); + cancellationTokenSource = null; + } + protected override void Update(float t) { // Don't connect to the rooms until the loading process has finished if (loadingStatus.CurrentStage != RealFlowLoadingStatus.Stage.Completed || alreadyStarted) return; - archipelagoIslandRoom.StartIfNot(); - gateKeeperSceneRoom.StartIfNot(); + archipelagoIslandRoom.StartIfNeededAsync(cancellationTokenSource.Token).Forget(); + gateKeeperSceneRoomProvider.StartIfNeededAsync(cancellationTokenSource.Token).Forget(); alreadyStarted = true; } } diff --git a/Explorer/Assets/DCL/Multiplayer/Connections/Systems/Debug/DebugWidgetRoomDisplay.cs b/Explorer/Assets/DCL/Multiplayer/Connections/Systems/Debug/DebugWidgetRoomDisplay.cs index 3c2c3dd448..7b2c2ce139 100644 --- a/Explorer/Assets/DCL/Multiplayer/Connections/Systems/Debug/DebugWidgetRoomDisplay.cs +++ b/Explorer/Assets/DCL/Multiplayer/Connections/Systems/Debug/DebugWidgetRoomDisplay.cs @@ -1,5 +1,6 @@ using DCL.DebugUtilities; using DCL.DebugUtilities.UIBindings; +using DCL.Multiplayer.Connections.Archipelago.Rooms; using DCL.Multiplayer.Connections.Rooms.Connective; using System; @@ -7,7 +8,7 @@ namespace DCL.Multiplayer.Connections.Systems.Debug { public class DebugWidgetRoomDisplay : IRoomDisplay { - private readonly IConnectiveRoom room; + private readonly IRoomProvider room; private readonly ElementBinding stateScene; private readonly ElementBinding remoteParticipantsScene; private readonly ElementBinding selfSid; @@ -18,13 +19,13 @@ public class DebugWidgetRoomDisplay : IRoomDisplay public DebugWidgetRoomDisplay( string roomName, - IConnectiveRoom connectiveRoom, + IRoomProvider roomProvider, IDebugContainerBuilder debugBuilder, Action? postBuildAction = null ) : this( - connectiveRoom, debugBuilder.AddWidget(roomName)!, postBuildAction + roomProvider, debugBuilder.AddWidget(roomName)!, postBuildAction ) { } - public DebugWidgetRoomDisplay(IConnectiveRoom connectiveRoom, DebugWidgetBuilder widgetBuilder, Action? postBuildAction = null) + public DebugWidgetRoomDisplay(IRoomProvider connectiveRoom, DebugWidgetBuilder widgetBuilder, Action? postBuildAction = null) { room = connectiveRoom; selfSid = new ElementBinding(string.Empty); @@ -48,27 +49,6 @@ public DebugWidgetRoomDisplay(IConnectiveRoom connectiveRoom, DebugWidgetBuilder postBuildAction?.Invoke(widgetBuilder); } - public DebugWidgetRoomDisplay( - IConnectiveRoom room, - ElementBinding stateScene, - ElementBinding remoteParticipantsScene, - ElementBinding selfSid, - ElementBinding connectionQuality, - ElementBinding roomSid, - ElementBinding selfMetadata, - ElementBinding connectiveState - ) - { - this.room = room; - this.stateScene = stateScene; - this.remoteParticipantsScene = remoteParticipantsScene; - this.selfSid = selfSid; - this.connectionQuality = connectionQuality; - this.roomSid = roomSid; - this.selfMetadata = selfMetadata; - this.connectiveState = connectiveState; - } - public void Update() { connectionQuality.SetAndUpdate(room.Room().Participants.LocalParticipant().ConnectionQuality.ToString()); diff --git a/Explorer/Assets/DCL/Multiplayer/Connections/Systems/DebugRoomsSystem.cs b/Explorer/Assets/DCL/Multiplayer/Connections/Systems/DebugRoomsSystem.cs index c94b222eb3..8520447664 100644 --- a/Explorer/Assets/DCL/Multiplayer/Connections/Systems/DebugRoomsSystem.cs +++ b/Explorer/Assets/DCL/Multiplayer/Connections/Systems/DebugRoomsSystem.cs @@ -19,8 +19,8 @@ public partial class DebugRoomsSystem : BaseUnityLoopSystem public DebugRoomsSystem( World world, - IArchipelagoIslandRoom archipelagoIslandRoom, - IGateKeeperSceneRoom gateKeeperSceneRoom, + IRealmRoomsProvider archipelagoIslandRoom, + IGateKeeperSceneRoomProvider gateKeeperSceneRoomProvider, IReadOnlyEntityParticipantTable entityParticipantTable, IRemotePoses remotePoses, IDebugContainerBuilder debugBuilder @@ -28,7 +28,7 @@ IDebugContainerBuilder debugBuilder { var gateKeeperRoomDisplay = new DebugWidgetRoomDisplay( "Room: Scene", - gateKeeperSceneRoom, + gateKeeperSceneRoomProvider, debugBuilder ); diff --git a/Explorer/Assets/DCL/PluginSystem/Global/InputPlugin.cs b/Explorer/Assets/DCL/PluginSystem/Global/InputPlugin.cs index 367c6cc2c9..2ca7dd344a 100644 --- a/Explorer/Assets/DCL/PluginSystem/Global/InputPlugin.cs +++ b/Explorer/Assets/DCL/PluginSystem/Global/InputPlugin.cs @@ -4,8 +4,7 @@ using DCL.Input; using DCL.Input.Component; using DCL.Input.Systems; -using DCL.Multiplayer.Emotes; -using UnityEngine.EventSystems; +using DCL.Multiplayer.Emotes.Interfaces; using UpdateEmoteInputSystem = DCL.AvatarRendering.Emotes.UpdateEmoteInputSystem; namespace DCL.PluginSystem.Global @@ -13,10 +12,10 @@ namespace DCL.PluginSystem.Global public class InputPlugin : IDCLGlobalPluginWithoutSettings { private readonly DCLInput dclInput; - private readonly MultiplayerEmotesMessageBus messageBus; + private readonly IEmotesMessageBus messageBus; private readonly IEventSystem eventSystem; - public InputPlugin(DCLInput dclInput, MultiplayerEmotesMessageBus messageBus, IEventSystem eventSystem) + public InputPlugin(DCLInput dclInput, IEmotesMessageBus messageBus, IEventSystem eventSystem) { this.dclInput = dclInput; this.eventSystem = eventSystem; diff --git a/Explorer/Assets/DCL/PluginSystem/Global/MultiplayerPlugin.cs b/Explorer/Assets/DCL/PluginSystem/Global/MultiplayerPlugin.cs index 62e91a443a..3f13403795 100644 --- a/Explorer/Assets/DCL/PluginSystem/Global/MultiplayerPlugin.cs +++ b/Explorer/Assets/DCL/PluginSystem/Global/MultiplayerPlugin.cs @@ -26,8 +26,8 @@ namespace DCL.PluginSystem.Global { public class MultiplayerPlugin : IDCLGlobalPluginWithoutSettings { - private readonly IArchipelagoIslandRoom archipelagoIslandRoom; - private readonly IGateKeeperSceneRoom gateKeeperSceneRoom; + private readonly IRealmRoomsProvider archipelagoIslandRoom; + private readonly IGateKeeperSceneRoomProvider gateKeeperSceneRoomProvider; private readonly IRoomHub roomHub; private readonly IMessagePipesHub messagePipesHub; private readonly IProfileRepository profileRepository; @@ -41,8 +41,8 @@ public class MultiplayerPlugin : IDCLGlobalPluginWithoutSettings private readonly IRealmData realmData; public MultiplayerPlugin( - IArchipelagoIslandRoom archipelagoIslandRoom, - IGateKeeperSceneRoom gateKeeperSceneRoom, + IRealmRoomsProvider archipelagoIslandRoom, + IGateKeeperSceneRoomProvider gateKeeperSceneRoomProvider, IRoomHub roomHub, IProfileRepository profileRepository, IProfileBroadcast profileBroadcast, @@ -57,7 +57,7 @@ IRemoteEntities remoteEntities ) { this.archipelagoIslandRoom = archipelagoIslandRoom; - this.gateKeeperSceneRoom = gateKeeperSceneRoom; + this.gateKeeperSceneRoomProvider = gateKeeperSceneRoomProvider; this.roomHub = roomHub; this.profileRepository = profileRepository; this.profileBroadcast = profileBroadcast; @@ -82,8 +82,8 @@ public void InjectToWorld(ref ArchSystemsWorldBuilder builder, #if !NO_LIVEKIT_MODE IFFIClient.Default.EnsureInitialize(); - DebugRoomsSystem.InjectToWorld(ref builder, archipelagoIslandRoom, gateKeeperSceneRoom, entityParticipantTable, remotePoses, debugContainerBuilder); - ConnectionRoomsSystem.InjectToWorld(ref builder, archipelagoIslandRoom, gateKeeperSceneRoom, realFlowLoadingStatus); + DebugRoomsSystem.InjectToWorld(ref builder, archipelagoIslandRoom, gateKeeperSceneRoomProvider, entityParticipantTable, remotePoses, debugContainerBuilder); + ConnectionRoomsSystem.InjectToWorld(ref builder, archipelagoIslandRoom, gateKeeperSceneRoomProvider, realFlowLoadingStatus); MultiplayerProfilesSystem.InjectToWorld(ref builder, new RemoteAnnouncements(messagePipesHub), diff --git a/Explorer/Assets/Scripts/Global/Dynamic/DynamicWorldContainer.cs b/Explorer/Assets/Scripts/Global/Dynamic/DynamicWorldContainer.cs index 7fe300e184..01aa6e1a02 100644 --- a/Explorer/Assets/Scripts/Global/Dynamic/DynamicWorldContainer.cs +++ b/Explorer/Assets/Scripts/Global/Dynamic/DynamicWorldContainer.cs @@ -1,7 +1,5 @@ using CommunicationData.URLHelpers; using Cysharp.Threading.Tasks; -using DCL.AsyncLoadReporting; -using DCL.Audio; using DCL.AvatarRendering.Emotes; using DCL.AvatarRendering.Emotes.Equipped; using DCL.AvatarRendering.Wearables; @@ -15,21 +13,10 @@ using DCL.DebugUtilities.UIBindings; using DCL.Input; using DCL.LOD.Systems; -using DCL.Multiplayer.Connections.Archipelago.AdapterAddress.Current; -using DCL.Multiplayer.Connections.Archipelago.Rooms; -using DCL.Multiplayer.Connections.GateKeeper.Meta; -using DCL.Multiplayer.Connections.GateKeeper.Rooms; using DCL.Multiplayer.Connections.Messaging.Hubs; -using DCL.Multiplayer.Connections.RoomHubs; using DCL.Multiplayer.Deduplication; -using DCL.Multiplayer.Emotes; -using DCL.Multiplayer.Emotes.Interfaces; -using DCL.Multiplayer.Movement; using DCL.Multiplayer.Movement.Systems; using DCL.Multiplayer.Profiles.BroadcastProfiles; -using DCL.Multiplayer.Profiles.Entities; -using DCL.Multiplayer.Profiles.Poses; -using DCL.Multiplayer.Profiles.Tables; using DCL.Nametags; using DCL.NftInfoAPIService; using DCL.ParcelsService; @@ -45,21 +32,16 @@ using ECS.Prioritization.Components; using ECS.SceneLifeCycle.Realm; using Global.Dynamic.ChatCommands; -using LiveKit.Internal.FFIClients.Pools; -using LiveKit.Internal.FFIClients.Pools.Memory; using MVC; using MVC.PopupsController.PopupCloser; using SceneRunner.EmptyScene; using System; -using System.Buffers; using System.Text.RegularExpressions; using System.Collections.Generic; using System.Threading; using DCL.Profiles.Self; using DCL.SceneLoadingScreens.LoadingScreen; using UnityEngine.EventSystems; -using UnityEngine.Pool; -using Utility.PriorityQueue; using Object = UnityEngine.Object; namespace Global.Dynamic @@ -92,25 +74,19 @@ public class DynamicWorldContainer : IDCLPlugin public RealUserInitializationFlowController UserInAppInitializationFlow { get; private set; } = null!; - // TODO move multiplayer related dependencies to a separate container - public IChatMessagesBus ChatMessagesBus { get; private set; } = null!; - - public IEmotesMessageBus EmotesMessageBus { get; private set; } = null!; + public MultiplayerContainer MultiplayerContainer { get; private set; } = null!; - public IMessagePipesHub MessagePipesHub { get; private set; } = null!; + public IChatMessagesBus ChatMessagesBus { get; private set; } = null!; public IProfileBroadcast ProfileBroadcast { get; private set; } = null!; - public IRoomHub RoomHub { get; private set; } - - public MultiplayerMovementMessageBus MultiplayerMovementMessageBus { get; private set; } = null!; public void Dispose() { MvcManager.Dispose(); ChatMessagesBus.Dispose(); ProfileBroadcast.Dispose(); - MessagePipesHub.Dispose(); + MultiplayerContainer.Dispose(); } public UniTask InitializeAsync(DynamicWorldSettings settings, CancellationToken ct) @@ -182,8 +158,6 @@ async UniTask InitializeContainersAsync(IPluginSettingsContainer settingsContain var landscapePlugin = new LandscapePlugin(staticContainer.AssetsProvisioner, debugBuilder, mapRendererContainer.TextureContainer, dynamicWorldParams.EnableLandscape); - var multiPool = new ThreadSafeMultiPool(); - var memoryPool = new ArrayMemoryPool(ArrayPool.Shared!); var realFlowLoadingStatus = new RealFlowLoadingStatus(); var emotesCache = new MemoryEmotesCache(); @@ -206,19 +180,6 @@ async UniTask InitializeContainersAsync(IPluginSettingsContainer settingsContain landscapePlugin ); - var metaDataSource = new LogMetaDataSource(new MetaDataSource(realmData, staticContainer.CharacterContainer.CharacterObject, placesAPIService)); - var gateKeeperSceneRoom = new GateKeeperSceneRoom(staticContainer.WebRequestsContainer.WebRequestController, metaDataSource); - - var currentAdapterAddress = ICurrentAdapterAddress.NewDefault(staticContainer.WebRequestsContainer.WebRequestController, realmData); - - var archipelagoIslandRoom = IArchipelagoIslandRoom.NewDefault( - identityCache, - multiPool, - staticContainer.CharacterContainer.CharacterObject, - currentAdapterAddress, - staticContainer.WebRequestsContainer.WebRequestController - ); - container.RealmController = new RealmController( identityCache, staticContainer.WebRequestsContainer.WebRequestController, @@ -229,32 +190,19 @@ async UniTask InitializeContainersAsync(IPluginSettingsContainer settingsContain realmData, staticContainer.ScenesCache); - container.RoomHub = new RoomHub(archipelagoIslandRoom, gateKeeperSceneRoom); - container.MessagePipesHub = new MessagePipesHub(container.RoomHub, multiPool, memoryPool); - - var entityParticipantTable = new EntityParticipantTable(); - - var queuePoolFullMovementMessage = new ObjectPool>( - () => new SimplePriorityQueue(), - actionOnRelease: x => x.Clear() - ); - - var remoteEntities = new RemoteEntities( - container.RoomHub, - entityParticipantTable, - staticContainer.ComponentsContainer.ComponentPoolsRegistry, - queuePoolFullMovementMessage - ); ILoadingScreen loadingScreen = new LoadingScreen(container.MvcManager); + var multiplayerContainer = MultiplayerContainer.Create(staticContainer, realmData, identityCache, placesAPIService); + container.MultiplayerContainer = multiplayerContainer; + IRealmNavigator realmNavigator = new RealmNavigator( loadingScreen, mapRendererContainer.MapRenderer, container.RealmController, parcelServiceContainer.TeleportController, - container.RoomHub, - remoteEntities, + multiplayerContainer.RoomHub, + multiplayerContainer.RemoteEntities, staticContainer.GlobalWorldProxy ); @@ -265,50 +213,36 @@ async UniTask InitializeContainersAsync(IPluginSettingsContainer settingsContain { DebugPanelChatCommand.REGEX, () => new DebugPanelChatCommand(container.DebugContainer.Builder) }, }; - container.ChatMessagesBus = new MultiplayerChatMessagesBus(container.MessagePipesHub, container.ProfileRepository, new MessageDeduplication()) + container.ChatMessagesBus = new MultiplayerChatMessagesBus(multiplayerContainer.MessagePipesHub, container.ProfileRepository, new MessageDeduplication()) .WithSelfResend(identityCache, container.ProfileRepository) .WithCommands(chatCommandsFactory) .WithDebugPanel(debugBuilder); container.ProfileBroadcast = new DebounceProfileBroadcast( new EnsureSelfPublishedProfileBroadcast( - new ProfileBroadcast(container.MessagePipesHub), + new ProfileBroadcast(multiplayerContainer.MessagePipesHub), selfProfile, realmData ) ); - var multiplayerEmotesMessageBus = new MultiplayerEmotesMessageBus(container.MessagePipesHub, entityParticipantTable, identityCache); - - var remotePoses = new DebounceRemotePoses( - new RemotePoses(container.RoomHub) - ); - var eventSystem = new UnityEventSystem(EventSystem.current.EnsureNotNull()); var globalPlugins = new List { - new MultiplayerPlugin( - archipelagoIslandRoom, - gateKeeperSceneRoom, - container.RoomHub, - container.ProfileRepository, - container.ProfileBroadcast, - debugBuilder, - realFlowLoadingStatus, - entityParticipantTable, - container.MessagePipesHub, - remotePoses, - staticContainer.CharacterContainer.CharacterObject, + multiplayerContainer.CreateMultiplayerPlugin( + staticContainer, + container, realmData, - remoteEntities + realFlowLoadingStatus, + debugBuilder ), new CharacterMotionPlugin(staticContainer.AssetsProvisioner, staticContainer.CharacterContainer.CharacterObject, debugBuilder), - new InputPlugin(dclInput, multiplayerEmotesMessageBus, eventSystem), + new InputPlugin(dclInput, multiplayerContainer.EmotesMessageBus, eventSystem), new GlobalInteractionPlugin(dclInput, dynamicWorldDependencies.RootUIDocument, staticContainer.AssetsProvisioner, staticContainer.EntityCollidersGlobalCache, exposedGlobalDataContainer.GlobalInputEvents), new CharacterCameraPlugin(staticContainer.AssetsProvisioner, realmSamplingData, exposedGlobalDataContainer.ExposedCameraData), new WearablePlugin(staticContainer.AssetsProvisioner, staticContainer.WebRequestsContainer.WebRequestController, realmData, ASSET_BUNDLES_URL, staticContainer.CacheCleaner, wearableCatalog), - new EmotePlugin(staticContainer.WebRequestsContainer.WebRequestController, emotesCache, realmData, multiplayerEmotesMessageBus, debugBuilder, staticContainer.AssetsProvisioner), + new EmotePlugin(staticContainer.WebRequestsContainer.WebRequestController, emotesCache, realmData, multiplayerContainer.EmotesMessageBus, debugBuilder, staticContainer.AssetsProvisioner), new ProfilingPlugin(staticContainer.ProfilingProvider, staticContainer.SingletonSharedDependencies.FrameTimeBudget, staticContainer.SingletonSharedDependencies.MemoryBudget, debugBuilder), new AvatarPlugin( staticContainer.ComponentsContainer.ComponentPoolsRegistry, @@ -321,14 +255,14 @@ async UniTask InitializeContainersAsync(IPluginSettingsContainer settingsContain staticContainer.CacheCleaner, chatEntryConfiguration, new DefaultFaceFeaturesHandler(wearableCatalog), - entityParticipantTable, + multiplayerContainer.EntityParticipantTable, nametagsData, container.DefaultTexturesContainer.TextureArrayContainerFactory ), new ProfilePlugin(container.ProfileRepository, profileCache, staticContainer.CacheCleaner, new ProfileIntentionCache()), new MapRendererPlugin(mapRendererContainer.MapRenderer), new MinimapPlugin(staticContainer.AssetsProvisioner, container.MvcManager, mapRendererContainer, placesAPIService), - new ChatPlugin(staticContainer.AssetsProvisioner, container.MvcManager, container.ChatMessagesBus, entityParticipantTable, nametagsData, dclInput, eventSystem), + new ChatPlugin(staticContainer.AssetsProvisioner, container.MvcManager, container.ChatMessagesBus, multiplayerContainer.EntityParticipantTable, nametagsData, dclInput, eventSystem), new ExplorePanelPlugin( staticContainer.AssetsProvisioner, container.MvcManager, @@ -365,7 +299,7 @@ async UniTask InitializeContainersAsync(IPluginSettingsContainer settingsContain staticContainer.CharacterContainer.CreateGlobalPlugin(), staticContainer.QualityContainer.CreatePlugin(), landscapePlugin, - new MultiplayerMovementPlugin(staticContainer.AssetsProvisioner, new MultiplayerMovementMessageBus(container.MessagePipesHub, entityParticipantTable)), + multiplayerContainer.CreateMultiplayerMovementPlugin(staticContainer), container.LODContainer.LODPlugin, container.LODContainer.RoadPlugin }; @@ -381,8 +315,8 @@ async UniTask InitializeContainersAsync(IPluginSettingsContainer settingsContain globalPlugins, debugBuilder, staticContainer.ScenesCache, - multiplayerEmotesMessageBus, - container.MessagePipesHub); + multiplayerContainer.EmotesMessageBus, + multiplayerContainer.MessagePipesHub); container.GlobalPlugins = globalPlugins; container.EmptyScenesWorldFactory = new EmptyScenesWorldFactory(staticContainer.SingletonSharedDependencies, staticContainer.ECSWorldPlugins); diff --git a/Explorer/Assets/Scripts/Global/Dynamic/MainSceneLoader.cs b/Explorer/Assets/Scripts/Global/Dynamic/MainSceneLoader.cs index f63370d95c..0810192438 100644 --- a/Explorer/Assets/Scripts/Global/Dynamic/MainSceneLoader.cs +++ b/Explorer/Assets/Scripts/Global/Dynamic/MainSceneLoader.cs @@ -177,10 +177,10 @@ private async UniTask InitializeFlowAsync(CancellationToken ct) } var webRequestController = staticContainer!.WebRequestsContainer.WebRequestController; - var roomHub = dynamicWorldContainer!.RoomHub; + var roomHub = dynamicWorldContainer!.MultiplayerContainer.RoomHub; sceneSharedContainer = SceneSharedContainer.Create(in staticContainer!, dynamicWorldContainer!.MvcManager, - identityCache, dynamicWorldContainer.ProfileRepository, webRequestController, roomHub, dynamicWorldContainer.RealmController.GetRealm(), dynamicWorldContainer.MessagePipesHub); + identityCache, dynamicWorldContainer.ProfileRepository, webRequestController, roomHub, dynamicWorldContainer.RealmController.GetRealm(), dynamicWorldContainer.MultiplayerContainer.MessagePipesHub); // Initialize global plugins var anyFailure = false; diff --git a/Explorer/Assets/Scripts/Global/Dynamic/MultiplayerContainer.cs b/Explorer/Assets/Scripts/Global/Dynamic/MultiplayerContainer.cs new file mode 100644 index 0000000000..8e7de0fc73 --- /dev/null +++ b/Explorer/Assets/Scripts/Global/Dynamic/MultiplayerContainer.cs @@ -0,0 +1,132 @@ +using DCL.DebugUtilities; +using DCL.Multiplayer.Connections.Archipelago.AdapterAddress.Current; +using DCL.Multiplayer.Connections.Archipelago.LiveConnections; +using DCL.Multiplayer.Connections.Archipelago.Rooms; +using DCL.Multiplayer.Connections.Archipelago.SignFlow; +using DCL.Multiplayer.Connections.GateKeeper.Meta; +using DCL.Multiplayer.Connections.GateKeeper.Rooms; +using DCL.Multiplayer.Connections.Messaging.Hubs; +using DCL.Multiplayer.Connections.RoomHubs; +using DCL.Multiplayer.Emotes; +using DCL.Multiplayer.Emotes.Interfaces; +using DCL.Multiplayer.Movement; +using DCL.Multiplayer.Movement.Systems; +using DCL.Multiplayer.Profiles.Entities; +using DCL.Multiplayer.Profiles.Poses; +using DCL.Multiplayer.Profiles.Tables; +using DCL.PlacesAPIService; +using DCL.PluginSystem.Global; +using DCL.UserInAppInitializationFlow; +using DCL.Web3.Identities; +using ECS; +using LiveKit.Internal.FFIClients.Pools; +using LiveKit.Internal.FFIClients.Pools.Memory; +using System.Buffers; +using System.Net.WebSockets; +using UnityEngine.Pool; +using Utility.PriorityQueue; + +namespace Global.Dynamic +{ + public class MultiplayerContainer + { + public IRoomHub RoomHub { get; private set; } + public IMessagePipesHub MessagePipesHub { get; private set; } = null!; + public EntityParticipantTable EntityParticipantTable { get; private set; } = null!; + public RemoteEntities RemoteEntities { get; private set; } = null!; + public IRealmRoomsProvider RealmRoomsProvider { get; private set; } = null!; + public IEmotesMessageBus EmotesMessageBus { get; private set; } = null!; + + private IGateKeeperSceneRoomProvider gateKeeperSceneRoomProvider = null!; + private IRemotePoses remotePoses = null!; + + public MultiplayerPlugin CreateMultiplayerPlugin( + StaticContainer staticContainer, + DynamicWorldContainer dynamicWorldContainer, + IRealmData realmData, + RealFlowLoadingStatus realFlowLoadingStatus, + DebugContainerBuilder debugBuilder) + { + return new MultiplayerPlugin( + RealmRoomsProvider, + gateKeeperSceneRoomProvider, + RoomHub, + dynamicWorldContainer.ProfileRepository, + dynamicWorldContainer.ProfileBroadcast, + debugBuilder, + realFlowLoadingStatus, + EntityParticipantTable, + MessagePipesHub, + remotePoses, + staticContainer.CharacterContainer.CharacterObject, + realmData, + RemoteEntities + ); + } + + public MultiplayerMovementPlugin CreateMultiplayerMovementPlugin(StaticContainer staticContainer) => + new (staticContainer.AssetsProvisioner, new MultiplayerMovementMessageBus(MessagePipesHub, EntityParticipantTable)); + + public void Dispose() + { + MessagePipesHub?.Dispose(); + } + + public static MultiplayerContainer Create(StaticContainer staticContainer, IRealmData realmData, + IWeb3IdentityCache identityCache, IPlacesAPIService placesAPIService) + { + var container = new MultiplayerContainer(); + + var entityParticipantTable = new EntityParticipantTable(); + var multiPool = new ThreadSafeMultiPool(); + var memoryPool = new ArrayMemoryPool(ArrayPool.Shared!); + var metaDataSource = new LogMetaDataSource(new MetaDataSource(realmData, staticContainer.CharacterContainer.CharacterObject, placesAPIService)); + + var currentAdapterAddress = ICurrentAdapterAddress.NewDefault(staticContainer.WebRequestsContainer.WebRequestController, realmData); + + container.gateKeeperSceneRoomProvider = new GateKeeperSceneRoomProvider(staticContainer.WebRequestsContainer.WebRequestController, metaDataSource); + + var webSocketArchipelagoLiveConnection = + new WebSocketArchipelagoLiveConnection(() => new ClientWebSocket(), memoryPool) + .WithLog(); + + var liveConnectionArchipelagoSignFlow = new LiveConnectionArchipelagoSignFlow( + webSocketArchipelagoLiveConnection, + memoryPool, + multiPool + ).WithLog(); + + container.RealmRoomsProvider = new RealmRoomsProvider( + identityCache, + staticContainer.CharacterContainer.CharacterObject, + staticContainer.WebRequestsContainer.WebRequestController, + liveConnectionArchipelagoSignFlow, + currentAdapterAddress); + + container.RoomHub = new RoomHub(container.RealmRoomsProvider, container.gateKeeperSceneRoomProvider); + container.MessagePipesHub = new MessagePipesHub(container.RoomHub, multiPool, memoryPool); + + var queuePoolFullMovementMessage = new ObjectPool>( + () => new SimplePriorityQueue(), + actionOnRelease: x => x.Clear() + ); + + var remoteEntities = new RemoteEntities( + container.RoomHub, + entityParticipantTable, + staticContainer.ComponentsContainer.ComponentPoolsRegistry, + queuePoolFullMovementMessage + ); + + container.remotePoses = new DebounceRemotePoses( + new RemotePoses(container.RoomHub) + ); + + container.RemoteEntities = remoteEntities; + container.EntityParticipantTable = entityParticipantTable; + container.EmotesMessageBus = new MultiplayerEmotesMessageBus(container.MessagePipesHub, entityParticipantTable, identityCache); + + return container; + } + } +} diff --git a/Explorer/Assets/Scripts/Global/Dynamic/MultiplayerContainer.cs.meta b/Explorer/Assets/Scripts/Global/Dynamic/MultiplayerContainer.cs.meta new file mode 100644 index 0000000000..b9fff033dc --- /dev/null +++ b/Explorer/Assets/Scripts/Global/Dynamic/MultiplayerContainer.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a04befd46fab4214a786d89ec0118ead +timeCreated: 1713374336 \ No newline at end of file diff --git a/Explorer/Assets/Scripts/Global/Dynamic/RealmNavigator.cs b/Explorer/Assets/Scripts/Global/Dynamic/RealmNavigator.cs index 03ca395589..729f918c0b 100644 --- a/Explorer/Assets/Scripts/Global/Dynamic/RealmNavigator.cs +++ b/Explorer/Assets/Scripts/Global/Dynamic/RealmNavigator.cs @@ -62,9 +62,9 @@ public async UniTask TryChangeRealmAsync(URLDomain realm, CancellationToke await loadingScreen.ShowWhileExecuteTaskAsync(async loadReport => { remoteEntities.ForceRemoveAll(world); - await roomHub.StopAsync(); + await roomHub.StopAsync(ct); await realmController.SetRealmAsync(realm, Vector2Int.zero, loadReport, ct); - await roomHub.StartAsync(); + await roomHub.StartAsync(ct); }, ct );