| | 1 | | using System; |
| | 2 | | using System.Collections.Generic; |
| | 3 | | using System.Linq; |
| | 4 | | using System.Text.RegularExpressions; |
| | 5 | | using DCL.Builder.Manifest; |
| | 6 | | using DCL.Components; |
| | 7 | | using DCL.Configuration; |
| | 8 | | using DCL.Controllers; |
| | 9 | | using DCL.Models; |
| | 10 | | using Newtonsoft.Json; |
| | 11 | | using UnityEngine; |
| | 12 | |
|
| | 13 | | namespace DCL.Builder |
| | 14 | | { |
| | 15 | | public static class ManifestTranslator |
| | 16 | | { |
| | 17 | | private const string NFT_ETHEREUM_PROTOCOL = "ethereum://"; |
| | 18 | |
|
| 0 | 19 | | private static readonly Dictionary<string, int> idToHumanReadableDictionary = new Dictionary<string, int>() |
| | 20 | | { |
| | 21 | |
|
| | 22 | | { "Transform", (int) CLASS_ID_COMPONENT.TRANSFORM }, |
| | 23 | |
|
| | 24 | | { "GLTFShape", (int) CLASS_ID.GLTF_SHAPE }, |
| | 25 | |
|
| | 26 | | { "NFTShape", (int) CLASS_ID.NFT_SHAPE }, |
| | 27 | |
|
| | 28 | | { "Name", (int)CLASS_ID.NAME }, |
| | 29 | |
|
| | 30 | | { "LockedOnEdit", (int)CLASS_ID.LOCKED_ON_EDIT }, |
| | 31 | |
|
| | 32 | | { "VisibleOnEdit", (int)CLASS_ID.VISIBLE_ON_EDIT }, |
| | 33 | |
|
| | 34 | | { "Script", (int) CLASS_ID_COMPONENT.SMART_ITEM } |
| | 35 | | }; |
| | 36 | |
|
| | 37 | | public static StatelessManifest SceneToStatelessManifest(ParcelScene scene) |
| | 38 | | { |
| 0 | 39 | | StatelessManifest manifest = new StatelessManifest(); |
| 0 | 40 | | manifest.schemaVersion = 1; |
| | 41 | |
|
| 0 | 42 | | foreach (var entity in scene.entities.Values) |
| | 43 | | { |
| 0 | 44 | | Entity statlesEntity = new Entity(); |
| 0 | 45 | | statlesEntity.id = entity.entityId; |
| | 46 | |
|
| 0 | 47 | | foreach (KeyValuePair<CLASS_ID_COMPONENT, IEntityComponent> entityComponent in entity.components) |
| | 48 | | { |
| 0 | 49 | | Component statelesComponent = new Component(); |
| 0 | 50 | | statelesComponent.type = idToHumanReadableDictionary.FirstOrDefault( x => x.Value == (int)entityComp |
| 0 | 51 | | statelesComponent.value = entityComponent.Value.GetModel(); |
| 0 | 52 | | statlesEntity.components.Add(statelesComponent); |
| | 53 | | } |
| | 54 | |
|
| 0 | 55 | | foreach (KeyValuePair<Type, ISharedComponent> entitySharedComponent in entity.sharedComponents) |
| | 56 | | { |
| 0 | 57 | | Component statelesComponent = new Component(); |
| 0 | 58 | | statelesComponent.type = idToHumanReadableDictionary.FirstOrDefault( x => x.Value == (int)entityShar |
| 0 | 59 | | statelesComponent.value = entitySharedComponent.Value.GetModel(); |
| 0 | 60 | | statlesEntity.components.Add(statelesComponent); |
| | 61 | | } |
| | 62 | |
|
| 0 | 63 | | manifest.entities.Add(statlesEntity); |
| | 64 | | } |
| | 65 | |
|
| 0 | 66 | | return manifest; |
| | 67 | | } |
| | 68 | |
|
| | 69 | | public static WebBuilderScene TranslateSceneToManifest(ParcelScene scene) |
| | 70 | | { |
| 0 | 71 | | WebBuilderScene builderScene = new WebBuilderScene(); |
| 0 | 72 | | builderScene.id = Guid.NewGuid().ToString(); |
| | 73 | |
|
| 0 | 74 | | BuilderGround ground = new BuilderGround(); |
| 0 | 75 | | List<string> namesList = new List<string>(); |
| | 76 | |
|
| | 77 | | //We iterate all the entities to create its counterpart in the builder manifest |
| 0 | 78 | | foreach (IDCLEntity entity in scene.entities.Values) |
| | 79 | | { |
| 0 | 80 | | BuilderEntity builderEntity = new BuilderEntity(); |
| 0 | 81 | | builderEntity.id = entity.entityId; |
| 0 | 82 | | string componentType = ""; |
| 0 | 83 | | string entityName = ""; |
| | 84 | |
|
| | 85 | | // Iterate the entity components to transform them to the builder format |
| 0 | 86 | | foreach (KeyValuePair<CLASS_ID_COMPONENT, IEntityComponent> entityComponent in entity.components) |
| | 87 | | { |
| 0 | 88 | | BuilderComponent builderComponent = new BuilderComponent(); |
| 0 | 89 | | switch (entityComponent.Key) |
| | 90 | | { |
| | 91 | | case CLASS_ID_COMPONENT.TRANSFORM: |
| 0 | 92 | | componentType = "Transform"; |
| | 93 | |
|
| | 94 | | // We can't serialize the quaternions from Unity since newton serializes have recursive prob |
| 0 | 95 | | ProtocolV2.TransformComponent entityTransformComponentModel = new ProtocolV2.TransformCompon |
| 0 | 96 | | entityTransformComponentModel.position = WorldStateUtils.ConvertUnityToScenePosition(entity. |
| 0 | 97 | | entityTransformComponentModel.rotation = new ProtocolV2.QuaternionRepresentation(entity.game |
| 0 | 98 | | entityTransformComponentModel.scale = entity.gameObject.transform.lossyScale; |
| | 99 | |
|
| 0 | 100 | | builderComponent.data = entityTransformComponentModel; |
| | 101 | |
|
| 0 | 102 | | break; |
| | 103 | | case CLASS_ID_COMPONENT.SMART_ITEM: |
| 0 | 104 | | componentType = "Script"; |
| | 105 | | break; |
| | 106 | | } |
| | 107 | |
|
| | 108 | | // We generate a new uuid for the component since there is no uuid for components in the stateful sc |
| 0 | 109 | | builderComponent.id = Guid.NewGuid().ToString(); |
| 0 | 110 | | builderComponent.type = componentType; |
| | 111 | |
|
| | 112 | | // Since the transform model data is different from the others, we set it in the switch instead of h |
| 0 | 113 | | if (builderComponent.type != "Transform") |
| 0 | 114 | | builderComponent.data = JsonConvert.SerializeObject(entityComponent.Value.GetModel()); |
| | 115 | |
|
| 0 | 116 | | builderEntity.components.Add(builderComponent.id); |
| | 117 | |
|
| 0 | 118 | | if (!builderScene.components.ContainsKey(builderComponent.id)) |
| 0 | 119 | | builderScene.components.Add(builderComponent.id, builderComponent); |
| | 120 | | } |
| | 121 | |
|
| | 122 | | // Iterate the entity shared components to transform them to the builder format |
| 0 | 123 | | foreach (KeyValuePair<System.Type, ISharedComponent> sharedEntityComponent in entity.sharedComponents) |
| | 124 | | { |
| 0 | 125 | | BuilderComponent builderComponent = new BuilderComponent(); |
| | 126 | | // We generate a new uuid for the component since there is no uuid for components in the stateful sc |
| 0 | 127 | | builderComponent.id = Guid.NewGuid().ToString(); |
| 0 | 128 | | builderComponent.data = JsonConvert.SerializeObject(sharedEntityComponent.Value.GetModel()); |
| | 129 | |
|
| 0 | 130 | | if (sharedEntityComponent.Value is GLTFShape) |
| | 131 | | { |
| 0 | 132 | | componentType = "GLTFShape"; |
| | 133 | |
|
| 0 | 134 | | var gltfModel = JsonConvert.DeserializeObject<GLTFShape.Model>(builderComponent.data.ToString()) |
| | 135 | |
|
| | 136 | | //We get the associated asset to the GLFTShape and add it to the scene |
| 0 | 137 | | var asset = AssetCatalogBridge.i.sceneObjectCatalog.Get(gltfModel.assetId); |
| 0 | 138 | | if (!builderScene.assets.ContainsKey(asset.id)) |
| 0 | 139 | | builderScene.assets.Add(asset.id, asset); |
| | 140 | |
|
| | 141 | | // This is a special case. The builder needs the ground separated from the rest of the component |
| | 142 | | // Since all the grounds have the same asset, we assign it and disable the gizmos in the builder |
| 0 | 143 | | if (asset.category == BIWSettings.FLOOR_CATEGORY) |
| | 144 | | { |
| 0 | 145 | | ground.assetId = asset.id; |
| 0 | 146 | | ground.componentId = builderComponent.id; |
| 0 | 147 | | builderEntity.disableGizmos = true; |
| | 148 | | } |
| | 149 | |
|
| 0 | 150 | | entityName = asset.name; |
| 0 | 151 | | } |
| 0 | 152 | | else if (sharedEntityComponent.Value is NFTShape) |
| | 153 | | { |
| 0 | 154 | | componentType = "NFTShape"; |
| | 155 | |
|
| | 156 | | // This is a special case where we are assigning the builder url field for NFTs because builder |
| | 157 | | NFTShapeBuilderRepresentantion representantion; |
| 0 | 158 | | representantion.url = JsonConvert.DeserializeObject<NFTShape.Model>(builderComponent.data.ToStri |
| 0 | 159 | | builderComponent.data = JsonConvert.SerializeObject(representantion); |
| | 160 | |
|
| | 161 | | //This is the name format that is used by builder, we will have a different name in unity due to |
| 0 | 162 | | entityName = "nft"; |
| 0 | 163 | | } |
| 0 | 164 | | else if (sharedEntityComponent.Key == typeof(DCLName)) |
| | 165 | | { |
| 0 | 166 | | componentType = "Name"; |
| 0 | 167 | | entityName = ((DCLName.Model) sharedEntityComponent.Value.GetModel()).value; |
| 0 | 168 | | } |
| 0 | 169 | | else if (sharedEntityComponent.Key == typeof(DCLLockedOnEdit)) |
| | 170 | | { |
| 0 | 171 | | componentType = "LockedOnEdit"; |
| | 172 | | } |
| | 173 | |
|
| 0 | 174 | | builderComponent.type = componentType; |
| | 175 | |
|
| 0 | 176 | | builderEntity.components.Add(builderComponent.id); |
| 0 | 177 | | if (!builderScene.components.ContainsKey(builderComponent.id)) |
| 0 | 178 | | builderScene.components.Add(builderComponent.id, builderComponent); |
| | 179 | | } |
| | 180 | |
|
| | 181 | | // We need to give to each entity a unique name so we search for a unique name there |
| | 182 | | // Also, since the name of the entity will be used in the code, we need to ensure that the it doesn't ha |
| 0 | 183 | | builderEntity.name = GetCleanUniqueName(namesList, entityName); |
| | 184 | |
|
| 0 | 185 | | if (!builderScene.entities.ContainsKey(builderEntity.id)) |
| 0 | 186 | | builderScene.entities.Add(builderEntity.id, builderEntity); |
| | 187 | | } |
| | 188 | |
|
| | 189 | | //We add the limits to the scene, the current metrics are calculated in the builder |
| 0 | 190 | | builderScene.limits = BIWUtils.GetSceneMetricsLimits(scene.parcels.Count); |
| 0 | 191 | | builderScene.ground = ground; |
| | 192 | |
|
| 0 | 193 | | return builderScene; |
| | 194 | | } |
| | 195 | |
|
| | 196 | | private static string GetCleanUniqueName(List<string> namesList, string currentName) |
| | 197 | | { |
| | 198 | | //We clean the name to don't include special characters |
| 0 | 199 | | Regex r = new Regex("(?:[^a-z]|(?<=['\"])s)", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | Rege |
| | 200 | |
|
| 0 | 201 | | string newName = r.Replace(currentName, String.Empty); |
| 0 | 202 | | newName = newName.ToLower(); |
| | 203 | |
|
| | 204 | | //We get a unique name |
| 0 | 205 | | bool uniqueName = false; |
| 0 | 206 | | int cont = 2; |
| 0 | 207 | | while (!uniqueName) |
| | 208 | | { |
| 0 | 209 | | if (!namesList.Contains(newName)) |
| 0 | 210 | | uniqueName = true; |
| | 211 | | else |
| 0 | 212 | | newName = newName + cont; |
| | 213 | |
|
| 0 | 214 | | cont++; |
| | 215 | | } |
| | 216 | |
|
| 0 | 217 | | namesList.Add(newName); |
| 0 | 218 | | return newName; |
| | 219 | | } |
| | 220 | |
|
| | 221 | | public static ParcelScene TranslateManifestToScene(Manifest.Manifest manifest) |
| | 222 | | { |
| 0 | 223 | | GameObject parcelGameObject = new GameObject("Builder Scene SceneId: " + manifest.scene.id); |
| 0 | 224 | | ParcelScene scene = parcelGameObject.AddComponent<ParcelScene>(); |
| | 225 | |
|
| | 226 | | //We remove the old assets to they don't collide with the new ones |
| 0 | 227 | | BIWUtils.RemoveAssetsFromCurrentScene(); |
| | 228 | |
|
| | 229 | | //We add the assets from the scene to the catalog |
| 0 | 230 | | var assets = manifest.scene.assets.Values.ToArray(); |
| 0 | 231 | | AssetCatalogBridge.i.AddScenesObjectToSceneCatalog(assets); |
| | 232 | |
|
| | 233 | | //We create and assign the data of the scene |
| 0 | 234 | | LoadParcelScenesMessage.UnityParcelScene parcelData = new LoadParcelScenesMessage.UnityParcelScene(); |
| 0 | 235 | | parcelData.id = manifest.scene.id; |
| | 236 | |
|
| | 237 | | //We set the current scene in the 0,0 |
| 0 | 238 | | int x = 0; |
| 0 | 239 | | int y = 0; |
| | 240 | |
|
| 0 | 241 | | parcelData.basePosition = new Vector2Int(x, y); |
| | 242 | |
|
| | 243 | | //We set the parcels as the first one is in the base position, the first one will be in the bottom-left corn |
| 0 | 244 | | parcelData.parcels = new Vector2Int[manifest.project.rows * manifest.project.cols]; |
| | 245 | |
|
| | 246 | | //We assign the parcels position |
| 0 | 247 | | for (int index = 0; index == parcelData.parcels.Length; index++) |
| | 248 | | { |
| 0 | 249 | | parcelData.parcels[index] = new Vector2Int(x, y); |
| 0 | 250 | | y++; |
| 0 | 251 | | if (y == manifest.project.rows) |
| | 252 | | { |
| 0 | 253 | | x++; |
| 0 | 254 | | y = CommonScriptableObjects.playerCoords.Get().y; |
| | 255 | | } |
| | 256 | | } |
| | 257 | |
|
| | 258 | | //We prepare the mappings to the scenes |
| 0 | 259 | | Dictionary<string, string> contentDictionary = new Dictionary<string, string>(); |
| | 260 | |
|
| 0 | 261 | | foreach (var sceneObject in assets) |
| | 262 | | { |
| 0 | 263 | | foreach (var content in sceneObject.contents) |
| | 264 | | { |
| 0 | 265 | | if (!contentDictionary.ContainsKey(content.Key)) |
| 0 | 266 | | contentDictionary.Add(content.Key, content.Value); |
| | 267 | | } |
| | 268 | | } |
| | 269 | |
|
| | 270 | | //We add the mappings to the scene |
| 0 | 271 | | BIWUtils.AddSceneMappings(contentDictionary, BIWUrlUtils.GetUrlSceneObjectContent(), parcelData); |
| | 272 | |
|
| | 273 | | //The data is built so we set the data of the scene |
| 0 | 274 | | scene.SetData(parcelData); |
| | 275 | |
|
| | 276 | | // We iterate all the entities to create the entity in the scene |
| 0 | 277 | | foreach (BuilderEntity builderEntity in manifest.scene.entities.Values) |
| | 278 | | { |
| 0 | 279 | | var entity = scene.CreateEntity(builderEntity.id); |
| | 280 | |
|
| 0 | 281 | | bool nameComponentFound = false; |
| | 282 | | // We iterate all the id of components in the entity, to add the component |
| 0 | 283 | | foreach (string idComponent in builderEntity.components) |
| | 284 | | { |
| | 285 | | //This shouldn't happen, the component should be always in the scene, but just in case |
| 0 | 286 | | if (!manifest.scene.components.ContainsKey(idComponent)) |
| | 287 | | continue; |
| | 288 | |
|
| | 289 | | // We get the component from the scene and create it in the entity |
| 0 | 290 | | BuilderComponent component = manifest.scene.components[idComponent]; |
| | 291 | |
|
| 0 | 292 | | switch (component.type) |
| | 293 | | { |
| | 294 | | case "Transform": |
| 0 | 295 | | DCLTransform.Model model = JsonConvert.DeserializeObject<DCLTransform.Model>(component.data. |
| 0 | 296 | | EntityComponentsUtils.AddTransformComponent(scene, entity, model); |
| 0 | 297 | | break; |
| | 298 | |
|
| | 299 | | case "GLTFShape": |
| 0 | 300 | | LoadableShape.Model gltfModel = JsonConvert.DeserializeObject<LoadableShape.Model>(component |
| 0 | 301 | | EntityComponentsUtils.AddGLTFComponent(scene, entity, gltfModel, component.id); |
| 0 | 302 | | break; |
| | 303 | |
|
| | 304 | | case "NFTShape": |
| | 305 | | //Builder use a different way to load the NFT so we convert it to our system |
| 0 | 306 | | string url = JsonConvert.DeserializeObject<string>(component.data.ToString()); |
| 0 | 307 | | string assedId = url.Replace(NFT_ETHEREUM_PROTOCOL, ""); |
| 0 | 308 | | int index = assedId.IndexOf("/", StringComparison.Ordinal); |
| 0 | 309 | | string partToremove = assedId.Substring(index); |
| 0 | 310 | | assedId = assedId.Replace(partToremove, ""); |
| | 311 | |
|
| 0 | 312 | | NFTShape.Model nftModel = new NFTShape.Model(); |
| 0 | 313 | | nftModel.color = new Color(0.6404918f, 0.611472f, 0.8584906f); |
| 0 | 314 | | nftModel.src = url; |
| 0 | 315 | | nftModel.assetId = assedId; |
| | 316 | |
|
| 0 | 317 | | EntityComponentsUtils.AddNFTShapeComponent(scene, entity, nftModel, component.id); |
| 0 | 318 | | break; |
| | 319 | |
|
| | 320 | | case "Name": |
| 0 | 321 | | nameComponentFound = true; |
| 0 | 322 | | DCLName.Model nameModel = JsonConvert.DeserializeObject<DCLName.Model>(component.data.ToStri |
| 0 | 323 | | nameModel.builderValue = builderEntity.name; |
| 0 | 324 | | EntityComponentsUtils.AddNameComponent(scene , entity, nameModel, Guid.NewGuid().ToString()) |
| 0 | 325 | | break; |
| | 326 | |
|
| | 327 | | case "LockedOnEdit": |
| 0 | 328 | | DCLLockedOnEdit.Model lockedModel = JsonConvert.DeserializeObject<DCLLockedOnEdit.Model>(com |
| 0 | 329 | | EntityComponentsUtils.AddLockedOnEditComponent(scene , entity, lockedModel, Guid.NewGuid().T |
| | 330 | | break; |
| | 331 | | } |
| | 332 | | } |
| | 333 | |
|
| | 334 | | // We need to mantain the builder name of the entity, so we create the equivalent part in biw. We do thi |
| 0 | 335 | | if (!nameComponentFound) |
| | 336 | | { |
| 0 | 337 | | DCLName.Model nameModel = new DCLName.Model(); |
| 0 | 338 | | nameModel.value = builderEntity.name; |
| 0 | 339 | | nameModel.builderValue = builderEntity.name; |
| 0 | 340 | | EntityComponentsUtils.AddNameComponent(scene , entity, nameModel, Guid.NewGuid().ToString()); |
| | 341 | | } |
| | 342 | | } |
| | 343 | |
|
| | 344 | | //We already have made all the necessary steps to configure the parcel, so we init the scene |
| 0 | 345 | | scene.sceneLifecycleHandler.SetInitMessagesDone(); |
| | 346 | |
|
| 0 | 347 | | return scene; |
| | 348 | | } |
| | 349 | |
|
| | 350 | | } |
| | 351 | | } |