Skip to content

Commit

Permalink
Merge pull request #68 from nowsprinting/feature/initialize_async
Browse files Browse the repository at this point in the history
Support calling async method to InitializeOnLaunchAutopilotAttribute
  • Loading branch information
asurato authored Sep 20, 2024
2 parents 3ee1bee + fc381bb commit f1d7941
Show file tree
Hide file tree
Showing 10 changed files with 147 additions and 51 deletions.
2 changes: 1 addition & 1 deletion Editor/UI/Settings/AutopilotSettingsEditor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ internal void Launch(AutopilotState state)
if (EditorApplication.isPlaying)
{
state.launchFrom = LaunchType.EditorPlayMode;
Launcher.Run();
Launcher.Run().Forget();
}
else
{
Expand Down
20 changes: 19 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -481,9 +481,27 @@ Note that it is convenient to set the `[CreateAssetMenu]` attribute to create an

### Game title-specific pre-processing

If your title requires its own initialization process, add the `InitializeOnLaunchAutopilot` attribute to the static method that does the initialization.
If your title requires its own initialization process, add the `InitializeOnLaunchAutopilot` attribute to the `public static` method that does the initialization.
An added method is called from the autopilot launch process.

```csharp
[InitializeOnLaunchAutopilot]
public static void InitializeOnLaunchAutopilotMethod()
{
// initialize code for your game.
}
```

Async methods are also supported.

```csharp
[InitializeOnLaunchAutopilot]
public static async UniTask InitializeOnLaunchAutopilotMethod()
{
// initialize code for your game.
}
```

Note that the autopilot launch process is performed with `RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)` (default for `RuntimeInitializeOnLoadMethod`).
Also, `RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)` implements the initialization process for Configurable Enter Play Mode.

Expand Down
20 changes: 19 additions & 1 deletion README_ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -484,9 +484,27 @@ Assembly Definition File (asmdef) のAuto Referencedをoff、Define Constraints

### タイトル独自事前処理

タイトル独自の初期化処理が必要な場合、初期化を行なうstaticメソッドに `InitializeOnLaunchAutopilot` 属性を付与してください。
タイトル独自の初期化処理が必要な場合、初期化を行なう `public static` メソッドに `InitializeOnLaunchAutopilot` 属性を付与してください。
オートパイロットの起動処理の中でメソッドを呼び出します。

```csharp
[InitializeOnLaunchAutopilot]
public static void InitializeOnLaunchAutopilotMethod()
{
// プロジェクト固有の初期化処理
}
```

非同期メソッドにも対応しています。

```csharp
[InitializeOnLaunchAutopilot]
public static async UniTask InitializeOnLaunchAutopilotMethod()
{
// プロジェクト固有の初期化処理
}
```

なお、オートパイロットの起動処理は、`RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)``RuntimeInitializeOnLoadMethod`のデフォルト)で実行しています。
また`RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)`で、Configurable Enter Play Modeのための初期化処理を実装しています。

Expand Down
22 changes: 18 additions & 4 deletions Runtime/Launcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

using System;
using System.Linq;
using System.Threading.Tasks;
using Cysharp.Threading.Tasks;
using DeNA.Anjin.Attributes;
using DeNA.Anjin.Settings;
using DeNA.Anjin.Utilities;
Expand Down Expand Up @@ -33,7 +35,8 @@ internal static void ResetEventHandlers()
/// Run autopilot
/// </summary>
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
public static void Run()
// ReSharper disable once Unity.IncorrectMethodSignature
public static async UniTaskVoid Run()
{
var state = AutopilotState.Instance;
if (!state.IsRunning)
Expand All @@ -48,20 +51,31 @@ public static void Run()

ScreenshotStore.CleanDirectories();

CallAttachedInitializeOnLaunchAutopilotAttributeMethods();
await CallAttachedInitializeOnLaunchAutopilotAttributeMethods();

var autopilot = new GameObject(nameof(Autopilot)).AddComponent<Autopilot>();
Object.DontDestroyOnLoad(autopilot);
}

private static void CallAttachedInitializeOnLaunchAutopilotAttributeMethods()
private static async UniTask CallAttachedInitializeOnLaunchAutopilotAttributeMethods()
{
foreach (var methodInfo in AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(x => x.GetTypes())
.SelectMany(x => x.GetMethods())
.Where(x => x.GetCustomAttributes(typeof(InitializeOnLaunchAutopilotAttribute), false).Any()))
{
methodInfo.Invoke(null, null); // static method only
switch (methodInfo.ReturnType.Name)
{
case nameof(Task):
await (Task)methodInfo.Invoke(null, null);
break;
case nameof(UniTask):
await (UniTask)methodInfo.Invoke(null, null);
break;
default:
methodInfo.Invoke(null, null); // static method only
break;
}
}
}

Expand Down
3 changes: 0 additions & 3 deletions Tests/Editor/Fakes.meta

This file was deleted.

23 changes: 0 additions & 23 deletions Tests/Editor/Fakes/FakeInitializeOnLaunchAutopilot.cs

This file was deleted.

17 changes: 0 additions & 17 deletions Tests/Editor/UI/Settings/AutopilotSettingsEditorTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

using System.Collections;
using System.Diagnostics.CodeAnalysis;
using DeNA.Anjin.Editor.Fakes;
using DeNA.Anjin.Settings;
using NUnit.Framework;
using UnityEditor;
Expand Down Expand Up @@ -66,21 +65,5 @@ public IEnumerator Launch_OnEditMode_RunAutopilotOnPlayMode()
var autopilot = Object.FindObjectOfType<Autopilot>();
Assert.That((bool)autopilot.gameObject, Is.True, "Autopilot object is alive");
}

[UnityTest]
public IEnumerator Launch_CallMethodWithInitializeOnLaunchAutopilotAttribute()
{
FakeInitializeOnLaunchAutopilot.Reset();

var testSettings = AssetDatabase.LoadAssetAtPath<AutopilotSettings>(
"Packages/com.dena.anjin/Tests/TestAssets/AutopilotSettingsForTests.asset");
var editor = (AutopilotSettingsEditor)UnityEditor.Editor.CreateEditor(testSettings);
var state = AutopilotState.Instance;
editor.Launch(state); // Note: Can not call editor.OnInspectorGUI() and GUILayout.Button()

yield return new WaitForDomainReload(); // Wait for domain reloading by switching play mode

Assert.That(FakeInitializeOnLaunchAutopilot.IsCallInitializeOnLaunchAutopilotMethod, Is.True);
}
}
}
48 changes: 47 additions & 1 deletion Tests/Runtime/LauncherTest.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
// Copyright (c) 2023 DeNA Co., Ltd.
// This software is released under the MIT License.

using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
using DeNA.Anjin.Editor.UI.Settings;
using DeNA.Anjin.Settings;
using DeNA.Anjin.TestDoubles;
using NUnit.Framework;
using UnityEditor;
using UnityEngine;
#if !UNITY_2020_1_OR_NEWER
using UnityEngine.TestTools;
#endif

namespace DeNA.Anjin
{
#if !UNITY_2020_1_OR_NEWER
[UnityPlatform(RuntimePlatform.OSXEditor, RuntimePlatform.WindowsEditor)] // Fail on Unity 2019 Linux editor
#endif
[SuppressMessage("ApiDesign", "RS0030")]
public class LauncherTest
{
Expand Down Expand Up @@ -51,7 +57,7 @@ public async Task Launch_StopAutopilotThatRunInPlayMode_KeepPlayMode()
var state = AutopilotState.Instance;
editor.Launch(state);

await Task.Delay(2200); // 2sec+overhead
await Task.Delay(5000); // 2sec+overhead

Assert.That(state.IsRunning, Is.False, "AutopilotState is terminated");
Assert.That(EditorApplication.isPlaying, Is.True, "Keep play mode");
Expand All @@ -65,12 +71,52 @@ public async Task Stop_TerminateAutopilotAndKeepPlayMode()
var editor = (AutopilotSettingsEditor)UnityEditor.Editor.CreateEditor(testSettings);
var state = AutopilotState.Instance;
editor.Launch(state);

await Task.Delay(2000);
await editor.Stop();
// Note: If Autopilot stops for life before Stop, a NullReference exception is raised here.

Assert.That(state.IsRunning, Is.False, "AutopilotState is terminated");
Assert.That(EditorApplication.isPlaying, Is.True, "Keep play mode");
}

[Test]
public async Task Launch_InitializeOnLaunchAutopilotAttribute_Called()
{
SpyInitializeOnLaunchAutopilot.Reset();

var settings = ScriptableObject.CreateInstance(typeof(AutopilotSettings)) as AutopilotSettings;
settings.sceneAgentMaps = new List<SceneAgentMap>();
settings.lifespanSec = 1;
await LauncherFromTest.AutopilotAsync(settings); // TODO: Renamed in another PR

Assert.That(SpyInitializeOnLaunchAutopilot.IsCallInitializeOnLaunchAutopilotMethod, Is.True);
}

[Test]
public async Task Launch_InitializeOnLaunchAutopilotAttributeAttachToAsyncMethod_Called()
{
SpyInitializeOnLaunchAutopilot.Reset();

var settings = ScriptableObject.CreateInstance(typeof(AutopilotSettings)) as AutopilotSettings;
settings.sceneAgentMaps = new List<SceneAgentMap>();
settings.lifespanSec = 1;
await LauncherFromTest.AutopilotAsync(settings); // TODO: Renamed in another PR

Assert.That(SpyInitializeOnLaunchAutopilot.IsCallInitializeOnLaunchAutopilotMethodAsync, Is.True);
}

[Test]
public async Task Launch_InitializeOnLaunchAutopilotAttributeAttachToUniTaskAsyncMethod_Called()
{
SpyInitializeOnLaunchAutopilot.Reset();

var settings = ScriptableObject.CreateInstance(typeof(AutopilotSettings)) as AutopilotSettings;
settings.sceneAgentMaps = new List<SceneAgentMap>();
settings.lifespanSec = 1;
await LauncherFromTest.AutopilotAsync(settings); // TODO: Renamed in another PR

Assert.That(SpyInitializeOnLaunchAutopilot.IsCallInitializeOnLaunchAutopilotMethodUniTaskAsync, Is.True);
}
}
}
43 changes: 43 additions & 0 deletions Tests/Runtime/TestDoubles/SpyInitializeOnLaunchAutopilot.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright (c) 2023 DeNA Co., Ltd.
// This software is released under the MIT License.

using System.Threading.Tasks;
using Cysharp.Threading.Tasks;
using DeNA.Anjin.Attributes;

namespace DeNA.Anjin.TestDoubles
{
public static class SpyInitializeOnLaunchAutopilot
{
public static bool IsCallInitializeOnLaunchAutopilotMethod { get; private set; }
public static bool IsCallInitializeOnLaunchAutopilotMethodAsync { get; private set; }
public static bool IsCallInitializeOnLaunchAutopilotMethodUniTaskAsync { get; private set; }

public static void Reset()
{
IsCallInitializeOnLaunchAutopilotMethod = false;
IsCallInitializeOnLaunchAutopilotMethodAsync = false;
IsCallInitializeOnLaunchAutopilotMethodUniTaskAsync = false;
}

[InitializeOnLaunchAutopilot]
public static void InitializeOnLaunchAutopilotMethod()
{
IsCallInitializeOnLaunchAutopilotMethod = true;
}

[InitializeOnLaunchAutopilot]
public static async Task InitializeOnLaunchAutopilotMethodAsync()
{
await Task.Delay(0);
IsCallInitializeOnLaunchAutopilotMethodAsync = true;
}

[InitializeOnLaunchAutopilot]
public static async UniTask InitializeOnLaunchAutopilotMethodUniTaskAsync()
{
await UniTask.Delay(0);
IsCallInitializeOnLaunchAutopilotMethodUniTaskAsync = true;
}
}
}

0 comments on commit f1d7941

Please sign in to comment.