using System; using System.Collections; using Unity.BossRoom.ApplicationLifecycle.Messages; using Unity.BossRoom.ConnectionManagement; using Unity.BossRoom.Gameplay.GameState; using Unity.BossRoom.Gameplay.Messages; using Unity.BossRoom.Infrastructure; using Unity.BossRoom.UnityServices; using Unity.BossRoom.UnityServices.Auth; using Unity.BossRoom.UnityServices.Lobbies; using Unity.BossRoom.Utils; using Unity.Netcode; using UnityEngine; using UnityEngine.SceneManagement; using VContainer; using VContainer.Unity; namespace Unity.BossRoom.ApplicationLifecycle { /// /// An entry point to the application, where we bind all the common dependencies to the root DI scope. /// public class ApplicationController : LifetimeScope { [SerializeField] UpdateRunner m_UpdateRunner; [SerializeField] ConnectionManager m_ConnectionManager; [SerializeField] NetworkManager m_NetworkManager; LocalLobby m_LocalLobby; LobbyServiceFacade m_LobbyServiceFacade; IDisposable m_Subscriptions; protected override void Configure(IContainerBuilder builder) { base.Configure(builder); builder.RegisterComponent(m_UpdateRunner); builder.RegisterComponent(m_ConnectionManager); builder.RegisterComponent(m_NetworkManager); //the following singletons represent the local representations of the lobby that we're in and the user that we are //they can persist longer than the lifetime of the UI in MainMenu where we set up the lobby that we create or join builder.Register(Lifetime.Singleton); builder.Register(Lifetime.Singleton); builder.Register(Lifetime.Singleton); builder.Register(Lifetime.Singleton); //these message channels are essential and persist for the lifetime of the lobby and relay services // Registering as instance to prevent code stripping on iOS builder.RegisterInstance(new MessageChannel()).AsImplementedInterfaces(); builder.RegisterInstance(new MessageChannel()).AsImplementedInterfaces(); builder.RegisterInstance(new MessageChannel()).AsImplementedInterfaces(); builder.RegisterInstance(new MessageChannel()).AsImplementedInterfaces(); //these message channels are essential and persist for the lifetime of the lobby and relay services //they are networked so that the clients can subscribe to those messages that are published by the server builder.RegisterComponent(new NetworkedMessageChannel()).AsImplementedInterfaces(); builder.RegisterComponent(new NetworkedMessageChannel()).AsImplementedInterfaces(); #if UNITY_EDITOR || DEVELOPMENT_BUILD builder.RegisterComponent(new NetworkedMessageChannel()).AsImplementedInterfaces(); #endif //this message channel is essential and persists for the lifetime of the lobby and relay services builder.RegisterInstance(new MessageChannel()).AsImplementedInterfaces(); //buffered message channels hold the latest received message in buffer and pass to any new subscribers builder.RegisterInstance(new BufferedMessageChannel()).AsImplementedInterfaces(); //all the lobby service stuff, bound here so that it persists through scene loads builder.Register(Lifetime.Singleton); //a manager entity that allows us to do anonymous authentication with unity services //LobbyServiceFacade is registered as entrypoint because it wants a callback after container is built to do it's initialization builder.RegisterEntryPoint(Lifetime.Singleton).AsSelf(); } private void Start() { m_LocalLobby = Container.Resolve(); m_LobbyServiceFacade = Container.Resolve(); var quitApplicationSub = Container.Resolve>(); var subHandles = new DisposableGroup(); subHandles.Add(quitApplicationSub.Subscribe(QuitGame)); m_Subscriptions = subHandles; Application.wantsToQuit += OnWantToQuit; DontDestroyOnLoad(gameObject); DontDestroyOnLoad(m_UpdateRunner.gameObject); Application.targetFrameRate = 120; SceneManager.LoadScene("MainMenu"); } protected override void OnDestroy() { if (m_Subscriptions != null) { m_Subscriptions.Dispose(); } if (m_LobbyServiceFacade != null) { m_LobbyServiceFacade.EndTracking(); } base.OnDestroy(); } /// /// In builds, if we are in a lobby and try to send a Leave request on application quit, it won't go through if we're quitting on the same frame. /// So, we need to delay just briefly to let the request happen (though we don't need to wait for the result). /// private IEnumerator LeaveBeforeQuit() { // We want to quit anyways, so if anything happens while trying to leave the Lobby, log the exception then carry on try { m_LobbyServiceFacade.EndTracking(); } catch (Exception e) { Debug.LogError(e.Message); } yield return null; Application.Quit(); } private bool OnWantToQuit() { Application.wantsToQuit -= OnWantToQuit; var canQuit = m_LocalLobby != null && string.IsNullOrEmpty(m_LocalLobby.LobbyID); if (!canQuit) { StartCoroutine(LeaveBeforeQuit()); } return canQuit; } private void QuitGame(QuitApplicationMessage msg) { #if UNITY_EDITOR UnityEditor.EditorApplication.isPlaying = false; #else Application.Quit(); #endif } } }