using System.Collections.Generic; using System.Linq; using System.Threading; using Cysharp.Threading.Tasks; using RebootKit.Engine.Foundation; using UnityEngine; using UnityEngine.AddressableAssets; using UnityEngine.Assertions; using UnityEngine.ResourceManagement.AsyncOperations; using UnityEngine.ResourceManagement.ResourceProviders; using UnityEngine.SceneManagement; using Logger = RebootKit.Engine.Foundation.Logger; namespace RebootKit.Engine.Services.Simulation { public interface IWorldContext { } public class WorldService : IService { static readonly Logger s_Logger = new Logger(nameof(WorldService)); enum WorldState { Unloaded, Loading, Loaded } WorldState m_WorldState = WorldState.Unloaded; WorldConfig m_Config; AsyncOperationHandle m_SceneInstance; public IWorldContext Context { get; private set; } readonly List m_Actors = new List(); public void Dispose() { Unload(); } public async UniTask LoadAsync(WorldConfig worldConfig, CancellationToken cancellationToken) { await UniTask.WaitWhile(() => m_WorldState == WorldState.Loading, cancellationToken: cancellationToken); Unload(); m_WorldState = WorldState.Loading; m_Config = worldConfig; m_SceneInstance = worldConfig.mainScene.LoadSceneAsync(LoadSceneMode.Additive, false); await m_SceneInstance.ToUniTask(cancellationToken: cancellationToken); await m_SceneInstance.Result.ActivateAsync(); SceneManager.SetActiveScene(m_SceneInstance.Result.Scene); foreach (GameObject root in m_SceneInstance.Result.Scene.GetRootGameObjects()) { if (root.TryGetComponent(out IWorldContext worldContext)) { Assert.IsNull(Context, "WorldContext is already set. There should be only one WorldContext in the scene."); Context = worldContext; } foreach (Actor actor in root.GetComponentsInChildren()) { RegisterActor(actor); } } m_WorldState = WorldState.Loaded; } public async UniTask LoadAsync(WorldConfig worldConfig, CancellationToken cancellationToken) where TRequiredActor : Actor { await LoadAsync(worldConfig, cancellationToken); TRequiredActor actor = m_Actors.FirstOrDefault(t => t is TRequiredActor) as TRequiredActor; if (actor is null) { s_Logger.Error($"Actor of type {typeof(TRequiredActor)} not found in the scene"); return null; } return actor; } public void Unload() { KillAllActors(); if (m_SceneInstance.IsValid()) { m_SceneInstance.Release(); m_SceneInstance = default; } m_WorldState = WorldState.Unloaded; Context = null; } public async UniTask SpawnActor(AssetReferenceT asset, CancellationToken cancellationToken) where TActor : Actor { GameObject gameObject = await Addressables.InstantiateAsync(asset); if (cancellationToken.IsCancellationRequested) { asset.ReleaseInstance(gameObject); return null; } if (gameObject.TryGetComponent(out TActor actor)) { return actor; } asset.ReleaseInstance(gameObject); return null; } public void RegisterActor(Actor actor) { m_Actors.Add(actor); } public void KillActor(Actor actor) { Addressables.ReleaseInstance(actor.gameObject); } public void KillAllActors() { foreach (Actor actor in m_Actors) { Addressables.ReleaseInstance(actor.gameObject); } m_Actors.Clear(); } } }