-
Notifications
You must be signed in to change notification settings - Fork 17
Expand file tree
/
Copy pathRealmLaunchSettings.cs
More file actions
276 lines (232 loc) · 12.9 KB
/
Copy pathRealmLaunchSettings.cs
File metadata and controls
276 lines (232 loc) · 12.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
using DCL.Browser;
using DCL.CommunicationData.URLHelpers;
using DCL.Diagnostics;
using ECS.SceneLifeCycle.Realm;
using Global.AppArgs;
using System;
using System.Collections.Generic;
using System.Linq;
using SceneRunner.Scene;
using System.Text.RegularExpressions;
using DCL.FeatureFlags;
using DCL.MapRenderer.MapLayers.HomeMarker;
using DCL.UserInAppInitializationFlow.StartupOperations;
using DCL.Utility;
using Unity.Mathematics;
using UnityEngine;
namespace Global.Dynamic
{
[Serializable]
public class RealmLaunchSettings : ILaunchMode
{
/// <summary>
/// Path under the local-scene-development realm where the preview server proxies its abgen sidecar
/// (decentraland/js-sdk-toolchain#1504) — the client derives the optimized-assets base from the realm
/// origin it already has instead of being told a second origin.
/// </summary>
public const string OPTIMIZED_ASSETS_PATH = "/optimized-assets";
[Serializable]
public struct PredefinedScenes
{
[SerializeField] public bool enabled;
[SerializeField] public Vector2Int[] parcels;
}
[SerializeField] internal InitialRealm initialRealm;
[SerializeField] internal Vector2Int targetScene;
[SerializeField] internal bool EditorSceneStartPosition = true;
[SerializeField] internal PredefinedScenes predefinedScenes;
[SerializeField] private string targetWorld = "MetadyneLabs.dcl.eth";
[SerializeField] internal string customRealm = IRealmNavigator.GOERLI_URL;
[SerializeField] internal string remoteHibridWorld = "MetadyneLabs.dcl.eth";
[SerializeField] internal HybridSceneContentServer remoteHybridSceneContentServer = HybridSceneContentServer.Goerli;
[SerializeField] internal bool useRemoteAssetsBundles;
[SerializeField] [Tooltip("Local scene development only: load the scene's asset bundles from the preview server instead of loading "
+ "raw GLTFs. The assets base is derived from the realm ({realm}" + OPTIMIZED_ASSETS_PATH + "); "
+ "an explicit --optimized-assets-url overrides it")] internal bool useLocalAssetBundles;
[SerializeField] [Tooltip("In Worlds there is one LiveKit room for all scenes so it's possible to communicate changes outside of the scene. "
+ "In Genesis City there are individual LiveKit rooms and only one connection at a time is maintained. "
+ "Toggle this flag to equalize this behavior")] internal bool isolateSceneCommunication;
[SerializeField] private string[] portableExperiencesEnsToLoadAtGameStart = Array.Empty<string>();
private bool isLocalSceneDevelopmentRealm;
internal string? spawnPointName;
public LaunchMode CurrentMode => isLocalSceneDevelopmentRealm
// This is for development purposes only,
// so we can easily start local development from the editor without application args
|| initialRealm == InitialRealm.Localhost
? LaunchMode.LocalSceneDevelopment
: LaunchMode.Play;
public string TargetWorld => targetWorld;
public IReadOnlyList<int2> GetPredefinedParcels()
{
if (predefinedScenes.enabled)
return predefinedScenes.parcels.Select(p => new int2(p.x, p.y)).ToList();
return CurrentMode switch
{
LaunchMode.Play => Array.Empty<int2>(),
LaunchMode.LocalSceneDevelopment => new List<int2>
{
new (targetScene.x, targetScene.y)
},
_ => throw new ArgumentOutOfRangeException()
};
}
public HybridSceneParams CreateHybridSceneParams()
{
if (initialRealm == InitialRealm.Localhost || isLocalSceneDevelopmentRealm)
{
return new HybridSceneParams
{
EnableHybridScene = useRemoteAssetsBundles && !useLocalAssetBundles,
HybridSceneContentServer = remoteHybridSceneContentServer,
World = remoteHybridSceneContentServer.Equals(HybridSceneContentServer.World)
? remoteHibridWorld
: "",
};
}
return new HybridSceneParams();
}
/// <summary>
/// Base URL for local asset bundles: the local-scene-development realm plus
/// <see cref="OPTIMIZED_ASSETS_PATH" />. No value other than the realm itself — which the deep-link
/// allowlist already gates on being loopback — feeds the result, so a deep link cannot point it
/// anywhere the realm doesn't already reach. Null outside local scene development.
/// </summary>
public string? LocalAssetBundlesBaseUrl()
{
if (isLocalSceneDevelopmentRealm)
return customRealm.TrimEnd('/') + OPTIMIZED_ASSETS_PATH;
if (initialRealm == InitialRealm.Localhost)
return IRealmNavigator.LOCALHOST + OPTIMIZED_ASSETS_PATH;
return null;
}
public void ApplyConfig(IAppArgs applicationParameters)
{
if (applicationParameters.TryGetValue(AppArgsFlags.REALM, out string? realm))
ParseRealmAppParameter(applicationParameters, realm!);
if (applicationParameters.TryGetValue(AppArgsFlags.POSITION, out string? parcelToTeleportOverride))
ParsePositionAppParameter(parcelToTeleportOverride!);
if (applicationParameters.TryGetValue(AppArgsFlags.SPAWN_POINT, out string? spawnPointOverride) && !string.IsNullOrEmpty(spawnPointOverride))
spawnPointName = spawnPointOverride;
}
private void ParseRealmAppParameter(IAppArgs appParameters, string realmParamValue)
{
if (string.IsNullOrEmpty(realmParamValue)) return;
bool isLocalSceneDevelopment = appParameters.TryGetValue(AppArgsFlags.LOCAL_SCENE, out string? localSceneParamValue)
&& ParseLocalSceneParameter(localSceneParamValue!)
&& ExternalUrlPolicy.IsWebScheme(realmParamValue);
if (isLocalSceneDevelopment)
{
// The serialized checkbox is an Editor convenience only: player builds opt in
// exclusively through the app arg, so a box ticked (and accidentally committed)
// in the scene asset cannot ship enabled.
bool useLocalAB = appParameters.HasFlag(AppArgsFlags.LOCAL_AB) || (Application.isEditor && useLocalAssetBundles);
bool useRemoteAB = appParameters.HasFlag(AppArgsFlags.LSD_USE_REMOTE_AB) || useRemoteAssetsBundles;
if (useLocalAB && useRemoteAB)
{
ReportHub.LogWarning(ReportCategory.ASSET_BUNDLES, $"Both '{AppArgsFlags.LOCAL_AB}' and '{AppArgsFlags.LSD_USE_REMOTE_AB}' were provided: local asset bundles take precedence");
useRemoteAB = false;
}
useLocalAssetBundles = useLocalAB;
SetLocalSceneDevelopmentRealm(realmParamValue, useRemoteAB);
if (appParameters.TryGetValue(AppArgsFlags.LSD_REMOTE_AB_SERVER, out string? serverValue))
ParseLsdRemoteABServer(serverValue!);
if (appParameters.TryGetValue(AppArgsFlags.LSD_REMOTE_AB_WORLD, out string? worldValue))
{
remoteHibridWorld = worldValue!;
remoteHybridSceneContentServer = HybridSceneContentServer.World;
}
}
else if (IsRealmAWorld(realmParamValue))
SetWorldRealm(realmParamValue);
else
SetCustomRealm(realmParamValue);
}
private void SetCustomRealm(string realm)
{
customRealm = realm;
initialRealm = InitialRealm.Custom;
}
private void SetWorldRealm(string world)
{
targetWorld = world;
initialRealm = InitialRealm.World;
}
private void SetLocalSceneDevelopmentRealm(string targetRealm, bool useRemoteAB)
{
customRealm = targetRealm;
initialRealm = InitialRealm.Custom;
useRemoteAssetsBundles = useRemoteAB;
isLocalSceneDevelopmentRealm = true;
}
private void ParseLsdRemoteABServer(string serverValue)
{
if (Enum.TryParse<HybridSceneContentServer>(serverValue, true, out var server))
remoteHybridSceneContentServer = server;
}
private void ParsePositionAppParameter(string targetPositionParam)
{
if (!RealmHelper.TryParseParcelFromString(targetPositionParam, out var targetPosition)) return;
targetScene = targetPosition;
}
private bool ParseLocalSceneParameter(string localSceneParameter)
{
if (string.IsNullOrEmpty(localSceneParameter)) return false;
var isLocalScene = false;
Match match = new Regex(@"true|false").Match(localSceneParameter);
if (match.Success)
bool.TryParse(match.Value, out isLocalScene);
return isLocalScene;
}
private bool IsRealmAWorld(string realmParam) =>
realmParam.IsEns();
public void CheckStartParcelOverride(IAppArgs appArgs, FeatureFlagsConfiguration featureFlagsConfigurationCache)
{
// Priority 1: App argument position (highest - from command line/Creator Hub)
if (HasAppArgPosition(appArgs))
return;
// Priority 2: Editor position override (for development convenience)
if (HasEditorPositionOverride())
return;
string? parcelToTeleportOverride = "0,0";
bool hasDefaultSpawnFlag = featureFlagsConfigurationCache.IsEnabled(FeatureFlagsStrings.GENESIS_STARTING_PARCEL)
&& featureFlagsConfigurationCache.TryGetTextPayload(FeatureFlagsStrings.GENESIS_STARTING_PARCEL,
FeatureFlagsStrings.STRING_VARIANT, out parcelToTeleportOverride)
&& parcelToTeleportOverride != null;
// Priority 3: Serialized home (used when no feature flag exists, or feature flag is set to "0,0")
if (HomeMarkerController.HasSerializedHome() && (!hasDefaultSpawnFlag || parcelToTeleportOverride == "0,0"))
{
if (HomeMarkerController.HasSerializedWorldName())
{
SetWorldRealm(HomeMarkerController.DeserializeWorldName()!);
return;
}
if (HomeMarkerController.HasSerializedPosition())
{
targetScene = HomeMarkerController.Deserialize()!.Value;
return;
}
}
// Priority 4: Feature flag override (used as fallback if home position not available or feature flag has specific value)
// Note: If you don't want the feature flag for localhost, remove it from the feature flag configuration
// (https://features.decentraland.systems/#/features/strategies/explorer-alfa-genesis-spawn-parcel)
if (hasDefaultSpawnFlag)
ParsePositionAppParameter(parcelToTeleportOverride!);
}
/// <summary>
/// Checks if the Editor position override is enabled.
/// If this is true in the Editor, the position will be the one that has been serialized.
/// This prevents the feature flag from overriding the developer's start position.
/// </summary>
/// <returns>True if running in Editor and EditorSceneStartPosition is enabled.</returns>
internal bool HasEditorPositionOverride() => Application.isEditor && EditorSceneStartPosition;
/// <summary>
/// Checks if the user has passed a position as an argument.
/// If a position is set through args, the feature flag should not be taken into consideration.
/// This is the case used on local scene development from Creator Hub/scene args.
/// See: https://github.com/decentraland/js-sdk-toolchain/blob/2c002ca9e6feb98a771337190db2945e013d7b93/packages/%40dcl/sdk-commands/src/commands/start/explorer-alpha.ts#L29
/// </summary>
/// <param name="appArgs">The application arguments to check.</param>
/// <returns>True if the POSITION flag is present in the arguments.</returns>
private static bool HasAppArgPosition(IAppArgs appArgs) => appArgs.HasFlag(AppArgsFlags.POSITION);
}
}