To start, I’m building a multiplayer game hosted with Unity’s Game Server Hosting, and I keep encountering the same error. I’m happy to provide more details about the code and setup.
Long story short: Multiplay.Instance is never being set, causing an InvalidOperationException to be thrown when I try to access it. This error is not thrown in the editor, and only in a build for the server. Everything else seems to work, but I’m unable to call MultiplayService.Instance.ReadyServerForPlayersAsync() because of it, so it’s entirely blocking connection until that can be invoked (as you’d assume).
The flow of the program has been thoroughly tested, and there are no exceptions thrown before that; I’m calling UnityServices.InitializeAsync(), checking I get a true value returned to validate success, and only attempting to call MultiplayService.Instance.ReadyServerForPlayersAsync() after that has been checked and UnityServices.State == ServicesInitializationState.Initialized evaluates to true. This leads me to believe that Multiplay is not being registered as a service (since the property in question is just grabbing MultiplayServiceSdk.Instance and throwing the aforementioned exception if that reference is null), so I can only assume it’s never initialized when UnityServices.InitializeAsync() is called.
Yes, the project compiles without error; yes, the Build is uploaded directly; yes, the Build Configuration is set up properly; and, yes, all code is running flawlessly before that single call. The project is linked to UGS, I have debug logs to verify the success of everything I’ve explained above, and I’m thoroughly convinced I’ve followed every example and tutorial available on the entire internet.
Below is the pertinent error received when trying to call UnityServices.InitializeAsync(), including my leading debug logs; please, for the sake of my sanity, somebody save me.
StartServer() call succeeded.
Waiting to start server
UnityServices initialized with InitializeAsync().
Server started.
UnityServices.State: Initialized
Readying up and waiting for players
InvalidOperationException: Unable to get IMultiplayService because Multiplay API is not initialized. Make sure you call UnityServices.InitializeAsync();
at Unity.Services.Multiplay.MultiplayService.InitializeWrappedMultiplayService () [0x0001d] in <7098dc2e4f424a4e9f85f11159e6e437>:0
at Unity.Services.Multiplay.MultiplayService.get_Instance () [0x00007] in <7098dc2e4f424a4e9f85f11159e6e437>:0
at GameServer.ReadyServerAndWait () [0x0000a] in <1f3a14fdb43e46a08e2e31cb437d6382>:0
at GameServer.Initialize () [0x00038] in <1f3a14fdb43e46a08e2e31cb437d6382>:0
at MonoBehaviourExtensions+d__6.MoveNext () [0x0003d] in <1f3a14fdb43e46a08e2e31cb437d6382>:0
at UnityEngine.SetupCoroutine.InvokeMoveNext (System.Collections.IEnumerator enumerator, System.IntPtr returnValueAddress) [0x00026] in <2cd8a4611dae40c2b95bf6c7f9c16416>:0
Could you provide more details on how you’re getting UnityServices.InitializeAsync() initialized? A code fragment would be useful.
Also, it would be helpful if you can provide some details about projectID/EnvironmentID to see if there’s any issues on the server end.
Thanks,
Hello,
I am having the exact same problem and I have been running in circles for two days.
I reduced my code to the bare minimum to isolate the issue. It happens 100% of the time if I play in the Editor.
Edit : Unity 6000.0.27f1, com.unity.services.multiplayer 1.0.2, build target : Linux Dedicated Server
private async void Start()
{
await UnityServices.InitializeAsync();
Debug.Log(UnityServices.State); // Logs state "Initialized"
IMultiplayService multiplay = MultiplayService.Instance; // Throws exception
// "Unable to get IMultiplayService because Multiplay API is not initialized.
// Make sure you call UnityServices.InitializeAsync();"
}
Hi @Karim-Largelabs ,
Thank you for the feedback ! Multiplay is not supported locally so you will have to build your project and deploy it to Multiplay Hosting for it to work. Here is the documentation: Multiplay Hosting
However we acknowledge that the error is pretty cryptic, we will add a task for us to reword it.
1 Like
Hi, I get this exception as well despite my server being allocated on Matchmaker, not in Editor.
The error is:
InvalidOperationException: Unable to get IMultiplayService because Multiplay API is not initialized. Make sure you call UnityServices.InitializeAsync();
And that’s how I initialize my server:
The strangest thing is that it works from time to time, right now I deployed 2 builds consecutively, and once I was able to connect to the server, and just after that I got this exception again.
The UnityServices.State prints Initialized.
Hi @dreamcoregamestudio,
Thank you for the report ! We will investigate and come back to you
Hi @dreamcoregamestudio
I’ve started to look at this issue, and I’d like to ask you for testing something on your side if you don’t mind.
It would help us narrow down the root cause and potentially give you a workaround at the same time.
Could you modify your code along the lines of:
- await UnityServices.InitializeAsync();
+ var serviceRegistry = UnityServices.CreateServices();
+ await serviceRegistry.InitializeAsync();
Debug.Log(UnityServices.State);
MultiplayEventCallbacks multiplayCallback = new();
multiplayCallback.Allocate += OnAllocate;
multiplayCallback.Deallocate += OnDeallocate;
multiplayCallback.Error += OnError;
multiplayCallback.SubscriptionStateChanged += OnSubscriptionStateChanged;
- IServerEvents serverEvents = await MultiplayService.Instance.SubscribeToServerEventsAsync(multiplayCallback);
+ var multiplayServiceInstance = serviceRegistry.GetService<IMultiplayService>();
+ await multiplayServiceInstance.SubscribeToServerEventsAsync(multiplayCallback);
Thanks in advance!
Hi Kalman, I’ve tried changing the code the way you suggested, but now when trying to use serviceRegistry.GetService() it returns null and I still cannot access Multiplay.
Here is the log file from the dedicated server:
[UnityMemory] Configuration Parameters - Can be set up in boot.config
“memorysetup-bucket-allocator-granularity=16”
“memorysetup-bucket-allocator-bucket-count=8”
“memorysetup-bucket-allocator-block-size=4194304”
“memorysetup-bucket-allocator-block-count=1”
“memorysetup-main-allocator-block-size=16777216”
“memorysetup-thread-allocator-block-size=16777216”
“memorysetup-gfx-main-allocator-block-size=16777216”
“memorysetup-gfx-thread-allocator-block-size=16777216”
“memorysetup-cache-allocator-block-size=4194304”
“memorysetup-typetree-allocator-block-size=2097152”
“memorysetup-profiler-bucket-allocator-granularity=16”
“memorysetup-profiler-bucket-allocator-bucket-count=8”
“memorysetup-profiler-bucket-allocator-block-size=4194304”
“memorysetup-profiler-bucket-allocator-block-count=1”
“memorysetup-profiler-allocator-block-size=16777216”
“memorysetup-profiler-editor-allocator-block-size=1048576”
“memorysetup-temp-allocator-size-main=4194304”
“memorysetup-job-temp-allocator-block-size=2097152”
“memorysetup-job-temp-allocator-block-size-background=1048576”
“memorysetup-job-temp-allocator-reduction-small-platforms=262144”
“memorysetup-allocator-temp-initial-block-size-main=262144”
“memorysetup-allocator-temp-initial-block-size-worker=262144”
“memorysetup-temp-allocator-size-background-worker=32768”
“memorysetup-temp-allocator-size-job-worker=262144”
“memorysetup-temp-allocator-size-preload-manager=262144”
“memorysetup-temp-allocator-size-nav-mesh-worker=65536”
“memorysetup-temp-allocator-size-audio-worker=65536”
“memorysetup-temp-allocator-size-cloud-worker=32768”
“memorysetup-temp-allocator-size-gfx=262144”
Mono path[0] = ‘/game/AutoKnightsServer_0.0.3_Data/Managed’
Mono config path = ‘/game/AutoKnightsServer_0.0.3_Data/MonoBleedingEdge/etc’
Preloaded ‘lib_burst_generated.so’
[PhysX] Initialized MultithreadedTaskDispatcher with 2 workers.
Initialize engine version: 2022.3.52f1 (1120fcb54228)
[Subsystems] Discovering subsystems at path /game/AutoKnightsServer_0.0.3_Data/UnitySubsystems
Forcing GfxDevice: Null
GfxDevice: creating device client; threaded=0; jobified=0
NullGfxDevice:
Version: NULL 1.0 [1.0]
Renderer: Null Device
Vendor: Unity Technologies
Begin MonoManager ReloadAssembly
- Loaded All Assemblies, in 0.103 seconds
- Finished resetting the current domain, in 0.002 seconds
There is no texture data available to upload.
[PhysX] Initialized MultithreadedTaskDispatcher with 2 workers.
UnloadTime: 1.120734 ms
True
Server signed in successfully
Multiplay Service:
NullReferenceException: Object reference not set to an instance of an object
at Dreamcore.Infrastructure.DedicatedServer.DedicatedServerProcessor.Initialize (Unity.Services.Core.IUnityServices services, System.String accessToken) [0x0009b] in :0
at Cysharp.Threading.Tasks.UniTask+ExceptionResultSource.GetResult (System.Int16 token) [0x00015] in :0
at Dreamcore.Infrastructure.GameFsm.States.InitializeDedicatedServerState.InitializeServices () [0x00262] in :0
UnityEngine.DebugLogHandler:Internal_LogException(Exception, Object)
UnityEngine.DebugLogHandler:LogException(Exception, Object)
UnityEngine.Logger:LogException(Exception, Object)
UnityEngine.Debug:LogException(Exception)
Cysharp.Threading.Tasks.UniTaskScheduler:PublishUnobservedTaskException(Exception)
Dreamcore.Infrastructure.GameFsm.States.d__11:MoveNext()
Cysharp.Threading.Tasks.CompilerServices.AsyncUniTaskVoid1:Run() System.Threading.Tasks.AwaitTaskContinuation:InvokeAction(Object) System.Threading.Tasks.AwaitTaskContinuation:RunCallback(ContextCallback, Object, Task&) System.Threading.Tasks.SynchronizationContextAwaitTaskContinuation:Run(Task, Boolean) System.Threading.Tasks.Task:FinishContinuations() System.Threading.Tasks.Task:FinishStageThree() System.Threading.Tasks.Task1:TrySetResult(VoidTaskResult)
System.Runtime.CompilerServices.AsyncTaskMethodBuilder1:SetResult(VoidTaskResult) System.Runtime.CompilerServices.AsyncTaskMethodBuilder1:SetResult(Task1) System.Runtime.CompilerServices.AsyncTaskMethodBuilder:SetResult() Unity.Services.Authentication.Server.<SignInFromServerAsync>d__52:MoveNext() System.Runtime.CompilerServices.MoveNextRunner:InvokeMoveNext(Object) System.Threading.ExecutionContext:RunInternal(ExecutionContext, ContextCallback, Object, Boolean) System.Threading.ExecutionContext:Run(ExecutionContext, ContextCallback, Object, Boolean) System.Runtime.CompilerServices.MoveNextRunner:Run() System.Threading.Tasks.AwaitTaskContinuation:InvokeAction(Object) System.Threading.Tasks.AwaitTaskContinuation:RunCallback(ContextCallback, Object, Task&) System.Threading.Tasks.SynchronizationContextAwaitTaskContinuation:Run(Task, Boolean) System.Threading.Tasks.Task:FinishContinuations() System.Threading.Tasks.Task:FinishStageThree() System.Threading.Tasks.Task1:TrySetResult(ApiResponse1) System.Runtime.CompilerServices.AsyncTaskMethodBuilder1:SetResult(ApiResponse1) Unity.Services.Authentication.Server.Proxy.Generated.<GetTokenAsync>d__9:MoveNext() System.Runtime.CompilerServices.MoveNextRunner:InvokeMoveNext(Object) System.Threading.ExecutionContext:RunInternal(ExecutionContext, ContextCallback, Object, Boolean) System.Threading.ExecutionContext:Run(ExecutionContext, ContextCallback, Object, Boolean) System.Runtime.CompilerServices.MoveNextRunner:Run() System.Threading.Tasks.AwaitTaskContinuation:InvokeAction(Object) System.Threading.Tasks.AwaitTaskContinuation:RunCallback(ContextCallback, Object, Task&) System.Threading.Tasks.SynchronizationContextAwaitTaskContinuation:Run(Task, Boolean) System.Threading.Tasks.Task:FinishContinuations() System.Threading.Tasks.Task:FinishStageThree() System.Threading.Tasks.Task1:TrySetResult(ApiResponse1) System.Runtime.CompilerServices.AsyncTaskMethodBuilder1:SetResult(ApiResponse1) Unity.Services.Authentication.Server.<SendAsync>d__261:MoveNext()
System.Runtime.CompilerServices.MoveNextRunner:InvokeMoveNext(Object)
System.Threading.ExecutionContext:RunInternal(ExecutionContext, ContextCallback, Object, Boolean)
System.Threading.ExecutionContext:Run(ExecutionContext, ContextCallback, Object, Boolean)
System.Runtime.CompilerServices.MoveNextRunner:Run()
System.Threading.Tasks.AwaitTaskContinuation:InvokeAction(Object)
System.Threading.Tasks.AwaitTaskContinuation:RunCallback(ContextCallback, Object, Task&)
System.Threading.Tasks.SynchronizationContextAwaitTaskContinuation:Run(Task, Boolean)
System.Threading.Tasks.Task:FinishContinuations()
System.Threading.Tasks.Task:FinishStageThree()
System.Threading.Tasks.Task1:TrySetResult(ApiResponse1)
System.Threading.Tasks.TaskCompletionSource1:TrySetResult(ApiResponse1)
System.Threading.Tasks.TaskCompletionSource1:SetResult(ApiResponse1)
Unity.Services.Authentication.Server.WebRequestUtils:ProcessResponse(TaskCompletionSource1, UnityWebRequest) Unity.Services.Authentication.Server.<>c__DisplayClass1_01:b__1(AsyncOperation)
UnityEngine.AsyncOperation:InvokeCompletionEvent()
And the code:
private async UniTaskVoid InitializeServices()
{
UnityServices.InitializeFailed += OnInitializeFailed;
UnityServices.ExternalUserId = "Server";
(bool initializationResult, IUnityServices services) = await TryInitServicesAsync("production");
Debug.Log("Initialization result: " + initializationResult + ", UnityServices.State: " + services != null + ", " + services.State);
string accessToken = string.Empty;
try
{
IServerAuthenticationService authenticationService = services.GetService<IServerAuthenticationService>();
authenticationService.Authorized += OnSignedIn;
authenticationService.AuthorizationFailed += OnSignInFailed;
await authenticationService.SignInFromServerAsync();
accessToken = authenticationService.AccessToken;
}
catch (Exception ex)
{
Debug.LogError($"Could not sign in: {ex.Message}");
// initializationResult = await TryInitServicesAsync("production");
// Debug.Log("Connecting again: " + initializationResult);
//
// D.Log("[InitializeDedicatedServerState].Again", UnityServices.State);
}
#if DEDICATED_SERVER
await _dedicatedServerProcessor.Initialize(services, accessToken);
#endif
_currentSceneProvider.SceneToLoad = SceneNames.Battle;
_currentSceneProvider.IsGlobalScene = true;
RequestNextState.Invoke(GameStateType.LoadLevel);
}
private void OnSignedIn()
{
// Debug.Log("Server signed in successfully with token: " + ServerAuthenticationService.Instance.AccessToken);
Debug.Log("Server signed in successfully");
}
private void OnSignInFailed(ServerAuthenticationException failedException)
{
Debug.Log("Server sign in failed: " + failedException);
}
private void OnInitializeFailed(Exception exception)
{
Debug.LogException(exception);
}
public static async UniTask<(bool, IUnityServices)> TryInitServicesAsync(string environment)
{
if (UnityServices.State == ServicesInitializationState.Initialized)
{
return (true, null);
}
//Another Service is mid-initialization:
if (UnityServices.State == ServicesInitializationState.Initializing)
{
await WaitForInitialized();
return (UnityServices.State == ServicesInitializationState.Initialized, UnityServices.Instance);
}
IUnityServices services = null;
try
{
var initializationOptions = new InitializationOptions();
initializationOptions.SetEnvironmentName(environment);
services = UnityServices.CreateServices();
await services.InitializeAsync(initializationOptions);
UnityServices.Instance = services;
}
catch (Exception e)
{
Debug.Log(e);
}
return (services?.State == ServicesInitializationState.Initialized, services);
async UniTask WaitForInitialized()
{
int msPassed = 0;
while (UnityServices.State != ServicesInitializationState.Initialized && msPassed < 3000)
{
await UniTask.Delay(100);
msPassed += 100;
}
}
}
using Cysharp.Threading.Tasks;
using FishNet.Managing;
using Unity.Services.Core;
using UnityEngine;
using VContainer.Unity;
#if DEDICATED_SERVER || UNITY_EDITOR
using Dreamcore.Integrations.CloudSave;
using Unity.Services.Multiplay;
#endif
namespace Dreamcore.Infrastructure.DedicatedServer
{
#if DEDICATED_SERVER || UNITY_EDITOR
public class DedicatedServerProcessor : ITickable
{
private readonly NetworkManager _networkManager;
private readonly ICloudSaveWriter _cloudSaveWriter;
private bool _isAlreadyAllocated;
private IServerQueryHandler _serverQueryHandler;
private IMultiplayService _multiplayService;
public DedicatedServerProcessor(NetworkManager networkManager, ICloudSaveWriter cloudSaveWriter)
{
_networkManager = networkManager;
_cloudSaveWriter = cloudSaveWriter;
}
public async UniTask Initialize(IUnityServices services, string accessToken)
{
MultiplayEventCallbacks multiplayCallbacks = new();
multiplayCallbacks.Allocate += OnAllocate;
multiplayCallbacks.Deallocate += OnDeallocate;
multiplayCallbacks.Error += OnError;
multiplayCallbacks.SubscriptionStateChanged += OnSubscriptionStateChanged;
_multiplayService = services.GetService<IMultiplayService>();
Debug.Log("Multiplay Service: " + _multiplayService);
await _multiplayService.SubscribeToServerEventsAsync(multiplayCallbacks);
_cloudSaveWriter.Initialize(accessToken);
_serverQueryHandler =
await _multiplayService.StartServerQueryHandlerAsync(8, "SomeName", "Public", "Id", "Battle");
var serverConfig = _multiplayService.ServerConfig;
if (!serverConfig.AllocationId.IsNullOrEmpty())
{
OnAllocate(new MultiplayAllocation("", serverConfig.ServerId, serverConfig.AllocationId));
}
}
public void Tick()
{
if (!_isAlreadyAllocated) return;
if (_serverQueryHandler == null) return;
_serverQueryHandler.CurrentPlayers = (ushort)_networkManager.ServerManager.Clients.Count;
_serverQueryHandler.UpdateServerCheck();
}
private void OnAllocate(MultiplayAllocation allocation)
{
if (_isAlreadyAllocated)
{
D.Log("[DedicatedServerProcessor]", "Already allocated!");
return;
}
_isAlreadyAllocated = true;
var serverConfig = _multiplayService.ServerConfig;
Debug.Log($"Server ID: {serverConfig.ServerId}");
Debug.Log($"Allocation ID: {serverConfig.AllocationId}");
Debug.Log($"Port: {serverConfig.Port}");
Debug.Log($"QueryPort: {serverConfig.QueryPort}");
_networkManager.ServerManager.StartConnection(serverConfig.Port);
}
private void OnDeallocate(MultiplayDeallocation deallocation)
{
D.Log("[DedicatedServerProcessor]", "Deallocate");
}
private void OnError(MultiplayError error)
{
D.Log("[DedicatedServerProcessor]", error.Reason, error.Detail);
}
private void OnSubscriptionStateChanged(MultiplayServerSubscriptionState state)
{
}
}
#else
public class DedicatedServerProcessor { }
#endif
}
1 Like
Thanks for the update!
Upon closer inspection of the logs it seems you are using 2022 with the standalone Multiplay sdk.
Sadly that package does not provide access to the instances via the API I mentioned previously, sorry for the unintended wild goose chase.
Could you please revert to using UnityServices.InitalizeAsync() but move the call in the Start() method of a Monobehaviour? I believe you might be using a dependency injection framework and initialization might happen in an unexpected order due to that.
Moving the call into a Monobehaviour should allow all required services to have time to be initialized before you try to access them through the static Instances.
You can disregard the below (hidden )section, as it is most likely not relevant for you, but will keep it here for the off chance if would be useful for others.
Unity 6 and MPSSDK related
I was looking at reproducing this in one of our test projects, but I couldn't yet.
I’ve made a few modifications to your sample:
- replacing UniTask with System.Threading.Task
- commenting out some unrelated code snippets
- changing the DEDICATED_SERVER to UNITY_SERVER defs
I seem to get your modified sample code (see below) working just fine on a Multiplay hosted server.
Note: Our test project is set up in a way that it has the below monobehavior script attached to a cube in an empty scene, and will attempt to access the MultiplayService with a slight delay.
Editor version used: 6000.0.31f
com.unity.services.multiplayer: 1.0.2
Expand for the Engine log extracted from the test project used
Mono path[0] = ‘/game/server_Data/Managed’
Mono config path = ‘/game/server_Data/MonoBleedingEdge/etc’
[Physics::Module] Initialized fallback backend.
[Physics::Module] Id: 0xdecafbad
PlayerPrefs - Creating folder: /mnt/unity/.config/unity3d/DefaultCompany
PlayerPrefs - Creating folder: /mnt/unity/.config/unity3d/DefaultCompany/MultiplayTestProject
Unable to load player prefs
Initialize engine version: 6000.0.31f1 (a206c360e2a8)
[Subsystems] Discovering subsystems at path /game/server_Data/UnitySubsystems
Forcing GfxDevice: Null
GfxDevice: creating device client; kGfxThreadingModeNonThreaded
NullGfxDevice:
Version: NULL 1.0 [1.0]
Renderer: Null Device
Vendor: Unity Technologies
Begin MonoManager ReloadAssembly
- Loaded All Assemblies, in 0.099 seconds
- Finished resetting the current domain, in 0.002 seconds
[Physics::Module] Selected backend.
[Physics::Module] Name: PhysX
[Physics::Module] Id: 0xf2b8ea05
[Physics::Module] SDK Version: 4.1.2
[Physics::Module] Integration Version: 1.0.0
[Physics::Module] Threading Mode: Multi-Threaded
UnloadTime: 0.578208 ms
True
Server signed in successfully
DedicatedServerProcessor is enabled.
DedicatedServerProcessor: Multiplay Service available? : True
DedicatedServerProcessor: OnSubscriptionStateChanged Subscribing
[Wire]: Attempting connection on: ws://127.0.0.1:8086/v1/connection/websocket
[Wire]: Websocket connected to : ws://127.0.0.1:8086/v1/connection/websocket. Initiating Wire handshake.
DedicatedServerProcessor: OnSubscriptionStateChanged Synced
SQP server: SQP server started on 0.0.0.0:9010
Server ID: 80501475
Allocation ID: 10213a84-d808-4abc-833d-da7dc412e327
Port: 9000
QueryPort: 9010
DedicatedServerProcessor: Already allocated!
DedicatedServerProcessor: Deallocate
[Physics::Module] Cleanup current backned.
[Physics::Module] Id: 0xf2b8ea05e
Expand for the modified code snippet used for testing in our empty project
using System;
using System.Collections;
using System.Threading.Tasks;
using Unity.Services.Authentication.Server;
using Unity.Services.Core;
using Unity.Services.Core.Environments;
using Unity.Services.Multiplay;
using UnityEngine;
namespace DreamCore
{
public class DreamcoreConnectToMultiplay : MonoBehaviour
{
private DedicatedServerProcessor _dedicatedServerProcessor = new DedicatedServerProcessor();
private IEnumerator Start()
{
yield return new WaitForSeconds(2f);
_ = InitializeServices();
}
private async Task InitializeServices()
{
UnityServices.InitializeFailed += OnInitializeFailed;
UnityServices.ExternalUserId = "Server";
(bool initializationResult, IUnityServices services) = await TryInitServicesAsync("production");
Debug.Log("Initialization result: " + initializationResult + ", UnityServices.State: " + services != null + ", " + services.State);
string accessToken = string.Empty;
try
{
IServerAuthenticationService authenticationService = services.GetService<IServerAuthenticationService>();
authenticationService.Authorized += OnSignedIn;
authenticationService.AuthorizationFailed += OnSignInFailed;
await authenticationService.SignInFromServerAsync();
accessToken = authenticationService.AccessToken;
}
catch (Exception ex)
{
Debug.LogError($"Could not sign in: {ex.Message}");
// initializationResult = await TryInitServicesAsync("production");
// Debug.Log("Connecting again: " + initializationResult);
//
// D.Log("[InitializeDedicatedServerState].Again", UnityServices.State);
}
#if UNITY_SERVER || UNITY_EDITOR
Debug.Log(nameof(DedicatedServerProcessor) + " is enabled.");
await _dedicatedServerProcessor.Initialize(services, accessToken);
#endif
/*
_currentSceneProvider.SceneToLoad = SceneNames.Battle;
_currentSceneProvider.IsGlobalScene = true;
RequestNextState.Invoke(GameStateType.LoadLevel);
*/
}
private void OnSignedIn()
{
// Debug.Log("Server signed in successfully with token: " + ServerAuthenticationService.Instance.AccessToken);
Debug.Log("Server signed in successfully");
}
private void OnSignInFailed(ServerAuthenticationException failedException)
{
Debug.Log("Server sign in failed: " + failedException);
}
private void OnInitializeFailed(Exception exception)
{
Debug.LogException(exception);
}
public static async Task<(bool, IUnityServices)> TryInitServicesAsync(string environment)
{
if (UnityServices.State == ServicesInitializationState.Initialized)
{
return (true, null);
}
//Another Service is mid-initialization:
if (UnityServices.State == ServicesInitializationState.Initializing)
{
await WaitForInitialized();
return (UnityServices.State == ServicesInitializationState.Initialized, UnityServices.Instance);
}
IUnityServices services = null;
try
{
var initializationOptions = new InitializationOptions();
initializationOptions.SetEnvironmentName(environment);
services = UnityServices.CreateServices();
await services.InitializeAsync(initializationOptions);
UnityServices.Instance = services;
}
catch (Exception e)
{
Debug.Log(e);
}
return (services?.State == ServicesInitializationState.Initialized, services);
async Task WaitForInitialized()
{
int msPassed = 0;
while (UnityServices.State != ServicesInitializationState.Initialized && msPassed < 3000)
{
await Task.Delay(100);
msPassed += 100;
}
}
}
}
#if UNITY_SERVER || UNITY_EDITOR
public class DedicatedServerProcessor : ITickable
{
private const string k_LOGTag = nameof(DedicatedServerProcessor);
private bool _isAlreadyAllocated;
private IServerQueryHandler _serverQueryHandler;
private IMultiplayService _multiplayService;
public DedicatedServerProcessor( /*NetworkManager networkManager, ICloudSaveWriter cloudSaveWriter*/)
{
//_networkManager = networkManager;
//_cloudSaveWriter = cloudSaveWriter;
}
public async Task Initialize(IUnityServices services, string accessToken)
{
try
{
_multiplayService = services.GetService<IMultiplayService>();
Debug.unityLogger.Log(k_LOGTag, $"{nameof(MultiplayService)} Service available? : {_multiplayService != null}", null);
if (_multiplayService == null)
{
Debug.LogError($"Could not retrieve any {nameof(MultiplayService)} instances. Ensure that you are running on a Multiplay Hosted server for accessing this API!");
return;
}
MultiplayEventCallbacks multiplayCallbacks = new();
multiplayCallbacks.Allocate += OnAllocate;
multiplayCallbacks.Deallocate += OnDeallocate;
multiplayCallbacks.Error += OnError;
multiplayCallbacks.SubscriptionStateChanged += OnSubscriptionStateChanged;
await _multiplayService.SubscribeToServerEventsAsync(multiplayCallbacks);
//_cloudSaveWriter.Initialize(accessToken);
_serverQueryHandler =
await _multiplayService.StartServerQueryHandlerAsync(8, "SomeName", "Public", "Id", "Battle");
var serverConfig = _multiplayService.ServerConfig;
if (!string.IsNullOrEmpty(serverConfig.AllocationId))
{
OnAllocate(new MultiplayAllocation("", serverConfig.ServerId, serverConfig.AllocationId));
}
}
catch (Exception ex)
{
Debug.LogException(ex);
}
}
public void Tick()
{
if (!_isAlreadyAllocated)
return;
if (_serverQueryHandler == null)
return;
//_serverQueryHandler.CurrentPlayers = (ushort)_networkManager.ServerManager.Clients.Count;
_serverQueryHandler.UpdateServerCheck();
}
private void OnAllocate(MultiplayAllocation allocation)
{
if (_isAlreadyAllocated)
{
Debug.unityLogger.Log(k_LOGTag, "Already allocated!", null);
return;
}
_isAlreadyAllocated = true;
var serverConfig = _multiplayService.ServerConfig;
Debug.Log($"Server ID: {serverConfig.ServerId}");
Debug.Log($"Allocation ID: {serverConfig.AllocationId}");
Debug.Log($"Port: {serverConfig.Port}");
Debug.Log($"QueryPort: {serverConfig.QueryPort}");
//_networkManager.ServerManager.StartConnection(serverConfig.Port);
}
private void OnDeallocate(MultiplayDeallocation deallocation)
{
Debug.unityLogger.Log(k_LOGTag, "Deallocate", null);
}
private void OnError(MultiplayError error)
{
Debug.unityLogger.LogError(k_LOGTag, error);
}
private void OnSubscriptionStateChanged(MultiplayServerSubscriptionState state)
{
Debug.unityLogger.LogWarning(k_LOGTag, nameof(OnSubscriptionStateChanged) + $" {state:F}");
}
}
// From Zenject?
public interface ITickable
{
void Tick();
}
#endif
}
Going to let you know as soon as I have an update, meanwhile could you confirm if you tried to access the MultiplayService on a Multiplay hosted server or in the Editor?
Hi Kalman,
Thanks for the suggestion!
Yeah, I’ve come to a thought that I may be getting different results from time to time because the scripting order changes (not sure about that, correct me if I am wrong) or some async initialization happens and my initialization tries to access Multiplay which may not be initialized yet.
I’ll try moving the initialization to Start() and retrying to get Multiplay in a few frames in try-catch if I get an exception.
However, I can’t reproduce this issue 100% times so I won’t be able to confirm that it helped soon.