Fusionヘッドレスモード
このドキュメントでは、ヘッドレス Unity アプリケーションから Photon Fusion を起動するためのユーティリティスクリプトを紹介します。 グラフィックインターフェースを使用せずにFusionを起動することは、特別なことではありません。これらのスクリプトは、その方法の一つです。
HeadlessController スクリプト
以下のスクリプトは、ゲームモード Server
で新しい Fusion Runner をインスタンス化し起動します。
HeadlessUtils
スクリプト(下記参照)を使って、バイナリが "Headless Mode" で起動されたかどうかを確認し、どのシーンが読み込まれるべきかを検証します。
ゲームサーバーの初期化に失敗した場合、アプリケーションはゼロ以外の終了コードで終了します。
using UnityEngine;
using Fusion;
using System.Collections.Generic;
using Fusion.Sockets;
using UnityEngine.SceneManagement;
public class HeadlessController : MonoBehaviour, INetworkRunnerCallbacks {
[SerializeField]
private NetworkRunner _runnerPrefab;
void Awake() {
Application.targetFrameRate = 30;
}
async void Start() {
// Quit if not in Headless mode
if (HeadlessUtils.IsHeadlessMode() == false) {
Application.Quit(1);
}
// Get game scene to be loaded
var sceneToLoad = HeadlessUtils.GetArg("-scene") ?? SceneManager.GetActiveScene().name;
Debug.Log($"Starting Server");
// Create a new Fusion Runner
var runner = Instantiate(_runnerPrefab);
// Basic Setup
runner.name = $"DedicatedServer";
runner.AddCallbacks(this); // register callbacks
// Start the Server
var result = await runner.StartGame(new StartGameArgs() {
GameMode = GameMode.Server, // for dedicated servers,
Scene = SceneManager.GetSceneByName(sceneToLoad).buildIndex,
SceneObjectProvider = gameObject.AddComponent<NetworkSceneManagerDefault>()
});
// Check if all went fine
if (result.Ok) {
Debug.Log($"Runner Start DONE");
} else {
// Quit the application if startup fails
Debug.LogError($"Error while starting Server: {result.ShutdownReason}");
// it can be used any error code that can be read by an external application
// using 0 means all went fine
Application.Quit(1);
}
}
// Fusion INetworkRunnerCallbacks implementation
public void OnShutdown(NetworkRunner runner, ShutdownReason shutdownReason) {
Debug.LogWarning($"{nameof(OnShutdown)}: {nameof(shutdownReason)}: {shutdownReason}");
// Quit normally
Application.Quit(0);
}
public void OnPlayerJoined(NetworkRunner runner, PlayerRef player) => Debug.LogWarning($"{nameof(OnPlayerJoined)}: {nameof(player)}: {player}");
public void OnPlayerLeft(NetworkRunner runner, PlayerRef player) => Debug.LogWarning($"{nameof(OnPlayerLeft)}: {nameof(player)}: {player}");
public void OnConnectRequest(NetworkRunner runner, NetworkRunnerCallbackArgs.ConnectRequest request, byte[] token) => Debug.LogWarning($"{nameof(OnConnectRequest)}: {nameof(NetworkRunnerCallbackArgs.ConnectRequest)}: {request.RemoteAddress}");
public void OnSceneLoadDone(NetworkRunner runner) => Debug.LogWarning($"{nameof(OnSceneLoadDone)}: {nameof(runner.CurrentScene)}: {runner.CurrentScene}");
public void OnSceneLoadStart(NetworkRunner runner) => Debug.LogWarning($"{nameof(OnSceneLoadStart)}: {nameof(runner.CurrentScene)}: {runner.CurrentScene}");
public void OnInput(NetworkRunner runner, NetworkInput input) { }
public void OnInputMissing(NetworkRunner runner, PlayerRef player, NetworkInput input) { }
public void OnConnectedToServer(NetworkRunner runner) { }
public void OnDisconnectedFromServer(NetworkRunner runner) { }
public void OnConnectFailed(NetworkRunner runner, NetAddress remoteAddress, NetConnectFailedReason reason) { }
public void OnUserSimulationMessage(NetworkRunner runner, SimulationMessagePtr message) { }
public void OnSessionListUpdated(NetworkRunner runner, List<SessionInfo> sessionList) { }
public void OnCustomAuthenticationResponse(NetworkRunner runner, Dictionary<string, object> data) { }
public void OnReliableDataReceived(NetworkRunner runner, PlayerRef player, System.ArraySegment<byte> data) { }
}
HeadlessUtils スクリプト
このスクリプトには、Unity バイナリが "Headless Mode" で起動されたかどうかを確認するメソッド (IsHeadlessMode
) と、特定の Command Line Argument の値を取得するメソッド (GetArg
) の二つだけが含まれており、ヘッドレスゲームサーバーにカスタム起動設定を渡すのに便利です。
using System;
public class HeadlessUtils {
/// <summary>
/// Signal if the executable was started in Headless mode by using the "-batchmode -nographics" command-line arguments
/// <see cref="https://docs.unity3d.com/Manual/PlayerCommandLineArguments.html"/>
/// </summary>
/// <returns>True if in "Headless Mode", false otherwise</returns>
public static bool IsHeadlessMode() {
return Environment.CommandLine.Contains("-batchmode") && Environment.CommandLine.Contains("-nographics");
}
/// <summary>
/// Get the value of a specific command-line argument passed when starting the executable
/// </summary>
/// <example>
/// Starting the binary with: "./my-game.exe -map street -type hide-and-seek"
/// and calling `var mapValue = HeadlessUtils.GetArg("-map", "-m")` will return the string "street"
/// </example>
/// <param name="keys">List of possible keys for the argument</param>
/// <returns>The string value of the argument if the at least 1 key was found, null otherwise</returns>
public static string GetArg(params string[] keys) {
var args = Environment.GetCommandLineArgs();
for (int i = 0; i < args.Length; i++) {
foreach (var name in keys) {
if (args[i] == name && args.Length > i + 1) {
return args[i + 1];
}
}
}
return null;
}
}