initial commit

This commit is contained in:
Jo 2025-01-07 02:06:59 +01:00
parent 6715289efe
commit 788c3389af
37645 changed files with 2526849 additions and 80 deletions

View file

@ -0,0 +1,22 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Unity.Services.Core.Registration")]
[assembly: InternalsVisibleTo("Unity.Services.Core.TestUtils")]
// Required for access to Networking API
[assembly: InternalsVisibleTo("Unity.Services.Core.Editor")]
[assembly: InternalsVisibleTo("Unity.Services.Core.Networking")]
[assembly: InternalsVisibleTo("Unity.Services.Core.Internal")]
[assembly: InternalsVisibleTo("Unity.Services.Core.Configuration")]
// Required for access to UnityThreadUtils
[assembly: InternalsVisibleTo("Unity.Services.Core.Threading")]
// Test assemblies
#if UNITY_INCLUDE_TESTS
[assembly: InternalsVisibleTo("Unity.Services.Core.Tests")]
[assembly: InternalsVisibleTo("Unity.Services.Core.EditorTests")]
[assembly: InternalsVisibleTo("Unity.Services.Core.TestUtils.Tests")]
[assembly: InternalsVisibleTo("Unity.Services.Core.TestUtils.EditorTests")]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
#endif

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a1ab09d0b3276c44dadbef4b90e85ccf
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,68 @@
namespace Unity.Services.Core
{
/// <summary>
/// Common error codes.
/// </summary>
public static class CommonErrorCodes
{
/// <summary>
/// Unable to determine what the error is.
/// </summary>
public const int Unknown = 0;
/// <summary>
/// DNS, TLS, and other transport errors that result in no valid HTTP response.
/// </summary>
public const int TransportError = 1;
/// <summary>
/// The request timed out because no response was received in the alotted time.
/// </summary>
public const int Timeout = 2;
/// <summary>
/// Service is unavailable. Typically returned when the service is overloaded.
/// </summary>
public const int ServiceUnavailable = 3;
/// <summary>
/// The API does not exist.
/// </summary>
public const int ApiMissing = 4;
/// <summary>
/// The request was rejected. Typically returned when the request was rejected before any reaching the API. See title/details for more information.
/// </summary>
public const int RequestRejected = 5;
/// <summary>
/// Request was rate limited. The client is making requests too frequently.
/// </summary>
public const int TooManyRequests = 50;
/// <summary>
/// The authentication token is malformed or invalid.
/// </summary>
public const int InvalidToken = 51;
/// <summary>
/// The authentication token is expired.
/// </summary>
public const int TokenExpired = 52;
/// <summary>
/// User does not have permission to perform the requested operation.
/// </summary>
public const int Forbidden = 53;
/// <summary>
/// The requested resource was not found.
/// </summary>
public const int NotFound = 54;
/// <summary>
/// The request was understood but the API refused to process it because something about it was invalid.
/// </summary>
public const int InvalidRequest = 55;
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8d4cfc84a4b414bb998fc192e3c3efff
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d602f3312a2f948d19abaffd36ab9e17
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,49 @@
//
// PreserveDependencyAttribute.cs
//
// Authors:
// Marek Safar <marek.safar@gmail.com>
//
// Copyright (C) 2018 Microsoft Corporation
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// Important: Keep namespace and class name as is, since the linker expects it
namespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Field, AllowMultiple = true)]
internal sealed class PreserveDependencyAttribute : Attribute
{
public PreserveDependencyAttribute(string memberSignature)
{
}
public PreserveDependencyAttribute(string memberSignature, string typeName)
{
}
public PreserveDependencyAttribute(string memberSignature, string typeName, string assembly)
{
}
public string Condition { get; set; }
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c6603f554e19f4ef6ac932f67674ca3d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,22 @@
using System;
using UnityEngine;
namespace Unity.Services.Core
{
class ExternalUserIdProperty
{
public event Action<string> UserIdChanged;
string m_UserId;
public string UserId
{
get => m_UserId;
set
{
m_UserId = value;
UserIdChanged?.Invoke(m_UserId);
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 92620b818fb3f4bf795270bbc57e9f11
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,13 @@
using System.Threading.Tasks;
namespace Unity.Services.Core
{
interface IUnityServices
{
ServicesInitializationState State { get; }
InitializationOptions Options { get; }
Task InitializeAsync(InitializationOptions options);
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2996bad1f88c4691aab8f33059bc86bc
timeCreated: 1622142195

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7096662908e5fed409f1e3d1a25a2eee
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,185 @@
using System.Collections.Generic;
namespace Unity.Services.Core
{
/// <summary>
/// Contain all options to customize services initialization when
/// calling <see cref="UnityServices.InitializeAsync(InitializationOptions)"/>.
/// </summary>
public class InitializationOptions
{
internal IDictionary<string, object> Values { get; }
/// <summary>
/// Create a new instance of the <see cref="InitializationOptions"/> class.
/// </summary>
public InitializationOptions()
: this(new Dictionary<string, object>()) {}
internal InitializationOptions(IDictionary<string, object> values)
{
Values = values;
}
internal InitializationOptions(InitializationOptions source)
: this(new Dictionary<string, object>(source.Values)) {}
/// <summary>
/// Get the option for the given <paramref name="key"/> if any.
/// </summary>
/// <param name="key">
/// The key of the option to retrieve.
/// </param>
/// <param name="option">
/// The stored option if any.
/// </param>
/// <returns>
/// Return true if there is a bool for the given <paramref name="key"/>;
/// return false otherwise.
/// </returns>
public bool TryGetOption(string key, out bool option)
{
return TryGetOption<bool>(key, out option);
}
/// <summary>
/// Get the option for the given <paramref name="key"/> if any.
/// </summary>
/// <param name="key">
/// The key of the option to retrieve.
/// </param>
/// <param name="option">
/// The stored option if any.
/// </param>
/// <returns>
/// Return true if there is a int for the given <paramref name="key"/>;
/// return false otherwise.
/// </returns>
public bool TryGetOption(string key, out int option)
{
return TryGetOption<int>(key, out option);
}
/// <summary>
/// Get the option for the given <paramref name="key"/> if any.
/// </summary>
/// <param name="key">
/// The key of the option to retrieve.
/// </param>
/// <param name="option">
/// The stored option if any.
/// </param>
/// <returns>
/// Return true if there is a float for the given <paramref name="key"/>;
/// return false otherwise.
/// </returns>
public bool TryGetOption(string key, out float option)
{
return TryGetOption<float>(key, out option);
}
/// <summary>
/// Get the option for the given <paramref name="key"/> if any.
/// </summary>
/// <param name="key">
/// The key of the option to retrieve.
/// </param>
/// <param name="option">
/// The stored option if any.
/// </param>
/// <returns>
/// Return true if there is a string for the given <paramref name="key"/>;
/// return false otherwise.
/// </returns>
public bool TryGetOption(string key, out string option)
{
return TryGetOption<string>(key, out option);
}
bool TryGetOption<T>(string key, out T option)
{
option = default;
if (Values.TryGetValue(key, out var rawValue)
&& rawValue is T value)
{
option = value;
return true;
}
return false;
}
/// <summary>
/// Stores the given <paramref name="value"/> for the given <paramref name="key"/>.
/// </summary>
/// <param name="key">
/// The identifier of the configuration entry.
/// </param>
/// <param name="value">
/// The value to store.
/// </param>
/// <returns>
/// Return this instance.
/// </returns>
public InitializationOptions SetOption(string key, bool value)
{
Values[key] = value;
return this;
}
/// <summary>
/// Stores the given <paramref name="value"/> for the given <paramref name="key"/>.
/// </summary>
/// <param name="key">
/// The identifier of the configuration entry.
/// </param>
/// <param name="value">
/// The value to store.
/// </param>
/// <returns>
/// Return this instance.
/// </returns>
public InitializationOptions SetOption(string key, int value)
{
Values[key] = value;
return this;
}
/// <summary>
/// Stores the given <paramref name="value"/> for the given <paramref name="key"/>.
/// </summary>
/// <param name="key">
/// The identifier of the configuration entry.
/// </param>
/// <param name="value">
/// The value to store.
/// </param>
/// <returns>
/// Return this instance.
/// </returns>
public InitializationOptions SetOption(string key, float value)
{
Values[key] = value;
return this;
}
/// <summary>
/// Stores the given <paramref name="value"/> for the given <paramref name="key"/>.
/// </summary>
/// <param name="key">
/// The identifier of the configuration entry.
/// </param>
/// <param name="value">
/// The value to store.
/// </param>
/// <returns>
/// Return this instance.
/// </returns>
public InitializationOptions SetOption(string key, string value)
{
Values[key] = value;
return this;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ed095cc016bfe354b86dbc64b34667e6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,29 @@
using System;
namespace Unity.Services.Core
{
/// <summary>
/// Represents an error during services initialization
/// </summary>
public class ServicesInitializationException : Exception
{
/// <inheritdoc cref="ServicesInitializationException(string, Exception)"/>
public ServicesInitializationException() {}
/// <inheritdoc cref="ServicesInitializationException(string, Exception)"/>
public ServicesInitializationException(string message)
: base(message) {}
/// <summary>
/// Initializes a new instance of the <see cref="ServicesInitializationException" /> class.
/// </summary>
/// <param name="message">
/// The error message that explains the reason for the exception.
/// </param>
/// <param name="innerException">
/// The exception that is the cause of the current exception, if any.
/// </param>
public ServicesInitializationException(string message, Exception innerException)
: base(message, innerException) {}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8aebbb7ccff5845b18681b4819457d83
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,21 @@
namespace Unity.Services.Core
{
/// <summary>
/// Initialization state of Unity Services
/// </summary>
public enum ServicesInitializationState
{
/// <summary>
/// Initialization has not been started
/// </summary>
Uninitialized,
/// <summary>
/// Initialization in progress
/// </summary>
Initializing,
/// <summary>
/// Initialization has been successfully completed
/// </summary>
Initialized,
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d13e2a11322224f628ff4c3821929eab
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,27 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Unity.Services.Core
{
/// <summary>
/// Exception when the project is not linked to a cloud project id
/// </summary>
class UnityProjectNotLinkedException : ServicesInitializationException
{
/// <summary>
/// Initialize a new instance of the <see cref="ServicesInitializationException" /> class.
/// </summary>
public UnityProjectNotLinkedException() {}
/// <summary>
/// Initialize a new instance of the <see cref="ServicesInitializationException" />
/// class with a specified error message.
/// </summary>
/// <param name="message">
/// The error message that explains the reason for the exception.
/// </param>
public UnityProjectNotLinkedException(string message)
: base(message) {}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dbecf6d1d3b2c469ab1a5a173a48ae06
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,61 @@
using System;
namespace Unity.Services.Core
{
/// <summary>
/// A base exception type for failed requests.
/// </summary>
public class RequestFailedException : Exception
{
/// <summary>
/// Gets the error code for the failure.
/// </summary>
/// <remarks>
/// See <see cref="CommonErrorCodes"/> for common error codes. Consult the
/// service documentation for specific error codes various APIs can return.
/// </remarks>
public int ErrorCode { get; }
/// <summary>
/// Creates an exception.
/// </summary>
/// <remarks>
/// <para>
/// The exception message is typically the "detail" field from the error
/// response returned by the service when it is available.
/// </para>
/// <para>
/// The error code is the "code" field from the error response returned
/// by the service when it is available. See <see cref="CommonErrorCodes"/>
/// for common error codes.
/// </para>
/// </remarks>
/// <param name="errorCode">The error code returned by the service.</param>
/// <param name="message">A message describing the error.</param>
public RequestFailedException(int errorCode, string message) : this(errorCode, message, null)
{
}
/// <summary>
/// Creates an exception.
/// </summary>
/// <remarks>
/// <para>
/// The exception message is typically the "detail" field from the error
/// response returned by the service when it is available.
/// </para>
/// <para>
/// The error code is the "code" field from the error response returned
/// by the service when it is available. See <see cref="CommonErrorCodes"/>
/// for common error codes.
/// </para>
/// </remarks>
/// <param name="errorCode">The error code returned by the service.</param>
/// <param name="message">A message describing the error.</param>
/// <param name="innerException">The inner exception reference.</param>
public RequestFailedException(int errorCode, string message, Exception innerException) : base(message, innerException)
{
this.ErrorCode = errorCode;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0b321f1156cb24f0e8d4c8fa0d32f5c7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,63 @@
// This file is generated. Do not modify by hand.
// XML documentation file not found. To check if public methods have XML comments,
// make sure the XML doc file is present and located next to the scraped dll
namespace Unity.Services.Core
{
public static class CommonErrorCodes
{
public const int ApiMissing = 4;
public const int Forbidden = 53;
public const int InvalidRequest = 55;
public const int InvalidToken = 51;
public const int NotFound = 54;
public const int RequestRejected = 5;
public const int ServiceUnavailable = 3;
public const int Timeout = 2;
public const int TokenExpired = 52;
public const int TooManyRequests = 50;
public const int TransportError = 1;
public const int Unknown = 0;
}
public class InitializationOptions
{
public InitializationOptions() {}
public InitializationOptions SetOption(string key, bool value);
public InitializationOptions SetOption(string key, int value);
public InitializationOptions SetOption(string key, float value);
public InitializationOptions SetOption(string key, string value);
public bool TryGetOption(string key, out bool option);
public bool TryGetOption(string key, out int option);
public bool TryGetOption(string key, out float option);
public bool TryGetOption(string key, out string option);
}
public class RequestFailedException : System.Exception
{
public int ErrorCode { get; }
public RequestFailedException(int errorCode, string message) {}
public RequestFailedException(int errorCode, string message, System.Exception innerException) {}
}
public class ServicesInitializationException : System.Exception
{
public ServicesInitializationException() {}
public ServicesInitializationException(string message) {}
public ServicesInitializationException(string message, System.Exception innerException) {}
}
public enum ServicesInitializationState
{
Initialized = 2,
Initializing = 1,
Uninitialized = 0,
}
public static class UnityServices
{
public static string ExternalUserId { get; set; }
public static ServicesInitializationState State { get; }
public static System.Threading.Tasks.Task InitializeAsync();
public static System.Threading.Tasks.Task InitializeAsync(InitializationOptions options);
}
}

View file

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 75ba8cc18b7164eb6aa7a8c4e64fbd25
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,13 @@
{
"name": "Unity.Services.Core",
"references": [],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View file

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: fe25561d224ed4743af4c60938a59d0b
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,113 @@
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using UnityEngine;
namespace Unity.Services.Core
{
/// <summary>
/// Utility to initialize all Unity services from a single endpoint.
/// </summary>
public static class UnityServices
{
internal static IUnityServices Instance { get; set; }
internal static TaskCompletionSource<object> InstantiationCompletion { get; set; }
internal static ExternalUserIdProperty ExternalUserIdProperty = new ExternalUserIdProperty();
/// <summary>
/// Initialization state.
/// </summary>
public static ServicesInitializationState State
{
get
{
if (!UnityThreadUtils.IsRunningOnUnityThread)
{
throw new ServicesInitializationException("You are attempting to access UnityServices.State from a non-Unity Thread." +
" UnityServices.State can only be accessed from Unity Thread");
}
if (Instance != null)
{
return Instance.State;
}
if (InstantiationCompletion?.Task.Status == TaskStatus.WaitingForActivation)
{
return ServicesInitializationState.Initializing;
}
return ServicesInitializationState.Uninitialized;
}
}
/// <summary>
/// ExternalUserId.
/// Use this property to pass a user ID from a 3rd party identity provider to Unity Gaming Services
/// </summary>
public static string ExternalUserId
{
get => ExternalUserIdProperty.UserId;
set => ExternalUserIdProperty.UserId = value;
}
/// <inheritdoc cref="InitializeAsync(InitializationOptions)"/>
public static Task InitializeAsync()
{
return InitializeAsync(new InitializationOptions());
}
/// <summary>
/// Single entry point to initialize all used services.
/// </summary>
/// <param name="options">
/// The options to customize services initialization.
/// </param>
/// <exception cref="ServicesInitializationException">
/// Exception when there's an error during services initialization
/// </exception>
/// <exception cref="UnityProjectNotLinkedException">
/// Exception when the project is not linked to a cloud project id
/// </exception>
/// <exception cref="CircularDependencyException">
/// Exception when two registered <see cref="IInitializablePackage"/> depend on the other.
/// </exception>
/// <returns>
/// Return a handle to the initialization operation.
/// </returns>
[PreserveDependency("Register()", "Unity.Services.Core.Registration.CorePackageInitializer", "Unity.Services.Core.Registration")]
[PreserveDependency("CreateStaticInstance()", "Unity.Services.Core.Internal.UnityServicesInitializer", "Unity.Services.Core.Internal")]
[PreserveDependency("EnableServicesInitializationAsync()", "Unity.Services.Core.Internal.UnityServicesInitializer", "Unity.Services.Core.Internal")]
[PreserveDependency("CaptureUnityThreadInfo()", "Unity.Services.Core.UnityThreadUtils", "Unity.Services.Core")]
public static async Task InitializeAsync(InitializationOptions options)
{
if (!UnityThreadUtils.IsRunningOnUnityThread)
{
throw new ServicesInitializationException("You are attempting to initialize Unity Services from a non-Unity Thread." +
" Unity Services can only be initialized from Unity Thread");
}
if (!Application.isPlaying)
{
throw new ServicesInitializationException("You are attempting to initialize Unity Services in Edit Mode." +
" Unity Services can only be initialized in Play Mode");
}
if (Instance == null)
{
if (InstantiationCompletion == null)
{
InstantiationCompletion = new TaskCompletionSource<object>();
}
await InstantiationCompletion.Task;
}
if (options is null)
{
options = new InitializationOptions();
}
await Instance.InitializeAsync(options);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: de65c849485d346e68c1186154a222ee
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 8ec5ff42a19804f12b6f36016197b5e8
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,22 @@
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;
namespace Unity.Services.Core
{
static class UnityThreadUtils
{
static int s_UnityThreadId;
internal static TaskScheduler UnityThreadScheduler { get; private set; }
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
static void CaptureUnityThreadInfo()
{
s_UnityThreadId = Thread.CurrentThread.ManagedThreadId;
UnityThreadScheduler = TaskScheduler.FromCurrentSynchronizationContext();
}
public static bool IsRunningOnUnityThread => Thread.CurrentThread.ManagedThreadId == s_UnityThreadId;
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 872d11033da0d4ce8aa948384ccc3df0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: