public static string SafeGetAssetDesc(CustomAssetMetaData metaData, Package package) { string localeID; if (metaData.type == CustomAssetMetaData.Type.Building) { localeID = "BUILDING_DESC"; } else if (metaData.type == CustomAssetMetaData.Type.Prop) { localeID = "PROPS_DESC"; } else if (metaData.type == CustomAssetMetaData.Type.Tree) { localeID = "TREE_DESC"; } else { if (metaData.type != CustomAssetMetaData.Type.Road) { return(metaData.name); } localeID = "NET_DESC"; } return(SafeGetAssetString(localeID, metaData.name, package)); }
void AddToQueue(List <Package.Asset>[] queues, CustomAssetMetaData meta, int offset, bool dontSpawn) { Package.Asset assetRef = meta.assetRef; if (assetRef == null) { Util.DebugPrint(meta.name, " Warning : NULL asset"); return; } CustomAssetMetaData.Type type = meta.type; Package package = assetRef.package; string fullName = type < CustomAssetMetaData.Type.RoadElevation ? assetRef.fullName : PillarOrElevationName(package.packageName, assetRef.name); if (!IsDuplicate(fullName, allLoads[(int)type], package)) { queues[loadQueueIndex[(int)type] + offset].Add(assetRef); metaTypes[assetRef.fullName] = type; if (dontSpawn) { dontSpawnNormally.Add(fullName); } if (type == CustomAssetMetaData.Type.Citizen) { citizenMetaDatas[fullName] = meta; } } }
bool PropTreeTrailer(Package.Asset asset) { CustomAssetMetaData assetMetaData = null; try { bool wantBecauseEnabled = loadEnabled && IsEnabled(asset); if (!wantBecauseEnabled && !(loadUsed && refs.GotPropTreeTrailerPackage(asset.package.packageName))) { return(false); } assetMetaData = asset.Instantiate <CustomAssetMetaData>(); if (assetMetaData.type == CustomAssetMetaData.Type.Building || assetMetaData.type == CustomAssetMetaData.Type.Vehicle || assetMetaData.type == CustomAssetMetaData.Type.Unknown || (AssetImporterAssetTemplate.GetAssetDLCMask(assetMetaData) & notMask) != 0) { return(false); } if (wantBecauseEnabled || loadUsed && refs.GotPropTreeAsset(assetMetaData.assetRef.fullName) || loadUsed && assetMetaData.type == CustomAssetMetaData.Type.Trailer && refs.GotTrailerAsset(assetMetaData.assetRef.fullName)) { PropTreeTrailerImpl(asset.package.packageName, assetMetaData.assetRef); } } catch (Exception ex) { Failed(assetMetaData?.assetRef, ex); // CODebugBase<LogChannel>.Warn(LogChannel.Modding, string.Concat(new object[] { ex.GetType(), ": Loading custom asset failed[", asset, "]\n", ex.Message })); } return(true); }
internal CustomAssetMetaData.Type GetMetaType(Package.Asset assetRef) { if (metaTypes.TryGetValue(assetRef.fullName, out CustomAssetMetaData.Type type)) { return(type); } try { foreach (Package.Asset asset in assetRef.package.FilterAssets(UserAssetType.CustomAssetMetaData)) { CustomAssetMetaData meta = AssetDeserializer.Instantiate(asset) as CustomAssetMetaData; if (meta?.assetRef != null) { metaTypes[meta.assetRef.fullName] = meta.type; } } if (metaTypes.TryGetValue(assetRef.fullName, out type)) { return(type); } } catch (Exception) { } Util.DebugPrint("Cannot resolve metatype:", assetRef.fullName); return(CustomAssetMetaData.Type.Unknown); }
void AddToQueue(List <Package.Asset>[] queues, CustomAssetMetaData meta, CustomAssetMetaData.Type type, int offset, bool dontSpawn) { Package.Asset assetRef = meta.assetRef; if (assetRef == null) { Util.DebugPrint(meta.name, " Error : NULL asset"); return; } if (assetRef.fullName == null) { Util.DebugPrint(meta.name, " Warning : NULL asset name"); } Package package = assetRef.package; string fullName = type < CustomAssetMetaData.Type.RoadElevation ? assetRef.fullName : PillarOrElevationName(package.packageName, assetRef.name); if (!IsDuplicate(assetRef, type, queues)) { int index = Math.Max(loadQueueIndex[(int)type] + offset, 0); queues[index].Add(assetRef); metaDatas[assetRef.fullName] = new SomeMetaData(meta.userDataRef, meta.name, type); if (dontSpawn) { dontSpawnNormally.Add(fullName); } if (type == CustomAssetMetaData.Type.Citizen) { citizenMetaDatas[fullName] = meta; } } }
public CustomAssetMetaData InstantiateAssetMetaData(Package.Asset asset) { //Profiler.Trace( "Instantiate asset start" ); CustomAssetMetaData assetMetaData = asset.Instantiate <CustomAssetMetaData>(); //Profiler.Trace( "Instantiate asset end" ); return(assetMetaData); }
public static bool SaveTemplateAsset(TemplateAsset templateAsset) { SingletonMod <Mod> .Logger.Debug($"Start save template asset {templateAsset}"); try { var meta = new CustomAssetMetaData() { name = $"{templateAsset.Template.Name}_{Guid.NewGuid().Unique()}", timeStamp = DateTime.Now, type = CustomAssetMetaData.Type.Unknown, dlcMask = SteamHelper.DLC_BitMask.None, steamTags = new string[] { "Marking" }, guid = templateAsset.Template.Id.ToString(), }; var package = new Package(templateAsset.IsWorkshop ? templateAsset.WorkshopId.ToString() : meta.name) { packageMainAsset = meta.name, packageAuthor = $"steamid:{TemplateManager.UserId}", }; var gameObject = new GameObject(typeof(MarkingInfo).Name); var markingInfo = gameObject.AddComponent <MarkingInfo>(); markingInfo.data = GetString(templateAsset.Template.ToXml()); meta.assetRef = package.AddAsset($"{meta.name}_Data", markingInfo.gameObject); if (templateAsset.Preview is Image image) { meta.imageRef = package.AddAsset(templateAsset.MetaPreview, image, false, Image.BufferFileFormat.PNG, false, false); } if (templateAsset.SeparatePreview && templateAsset.SteamPreview is Image steamImage) { meta.steamPreviewRef = package.AddAsset(templateAsset.MetaSteamPreview, steamImage, false, Image.BufferFileFormat.PNG, false, false); } else { meta.steamPreviewRef = meta.imageRef; } package.AddAsset(meta.name, meta, UserAssetType.CustomAssetMetaData); var path = Path.Combine(DataLocation.assetsPath, PathUtils.AddExtension(PathEscaper.Escape(templateAsset.FileName), PackageManager.packageExtension)); package.Save(path); SingletonMod <Mod> .Logger.Debug($"Template asset saved to {path}"); return(true); } catch (Exception error) { SingletonMod <Mod> .Logger.Error($"Could not save template asset", error); return(false); } }
internal void AddPackage(Package p, CustomAssetMetaData lastmeta, bool enabled, bool useddir) { Package.Asset mainAssetRef = lastmeta.assetRef ?? AssetLoader.FindMainAssetRef(p); string fullName = mainAssetRef?.fullName; if (!string.IsNullOrEmpty(fullName)) { assets[fullName] = new Available(mainAssetRef, lastmeta.type, enabled, useddir); } }
public GameObject InstantiateAssetGameObject(CustomAssetMetaData assetMetaData) { //Profiler.Trace( "Instantiate asset game object start {0}", notFound.Count() ); var go = assetMetaData.assetRef.Instantiate <GameObject>(); //Profiler.Trace( "Instantiate asset game object {0} end {1}", go, notFound.Count() ); go.name = assetMetaData.assetRef.package.packageName + "." + assetMetaData.assetRef.name; go.SetActive(false); return(go); }
void ReadMetaData(Package package) { foreach (Package.Asset asset in package.FilterAssets(UserAssetType.CustomAssetMetaData)) { try { CustomAssetMetaData meta = AssetDeserializer.Instantiate(asset) as CustomAssetMetaData; metaDatas[meta.assetRef.fullName] = new SomeMetaData(meta.userDataRef, meta.name, meta.type); } catch (Exception) { Util.DebugPrint("!Cannot read metadata from", package.packageName); } } }
static NetInfo GetOriginalNetInfo(CustomAssetMetaData listingMetaData) { LogCalled(); var roadName = listingMetaData.name; if (!roadName.EndsWith("_Data")) { roadName = roadName + "_Data"; } var packageName = listingMetaData.assetRef.package.packageName; var originalName = packageName + "." + PackageHelper.StripName(roadName); Log.Debug("searching for raod:" + originalName); var ret = PrefabCollection <NetInfo> .FindLoaded(originalName); Log.Debug("GetOriginalNetInfo() returns " + ret.ToSTR()); return(ret); }
public static string[] Tags(this CustomAssetMetaData metadata) { var tags = new List <string>(); foreach (var tag in metadata.steamTags) { if (Tag2Category.TryGetValue(tag, out var cat)) { tags.Add(cat); } else { tags.Add(tag); } } return(tags.ToArray()); }
void Load(Package.Asset asset) { string fullName = null; try { CustomAssetMetaData assetMetaData = asset.Instantiate <CustomAssetMetaData>(); // Always remember: assetRef may point to another package because the deserialization method accepts any asset with a matching checksum. // There is a bug in the 1.6.0 game update in this. fullName = asset.package.packageName + "." + assetMetaData.assetRef.name; CustomAssetMetaData.Type type = assetMetaData.type; bool spawnNormally = (type == CustomAssetMetaData.Type.Building || type == CustomAssetMetaData.Type.SubBuilding) ? loadEnabled && IsEnabled(asset) || loadUsed && UsedAssets.instance.GotBuilding(fullName) : true; stack.Clear(); LoadImpl(fullName, assetMetaData.assetRef, spawnNormally); } catch (Exception e) { AssetFailed(fullName ?? asset.fullName, e); } }
bool BuildingVehicle(Package.Asset asset, bool includedInStyle) { CustomAssetMetaData assetMetaData = null; try { bool wantBecauseEnabled = loadEnabled && IsEnabled(asset); if (!includedInStyle && !wantBecauseEnabled && !(loadUsed && refs.GotBuildingVehiclePackage(asset.package.packageName))) { return(false); } assetMetaData = asset.Instantiate <CustomAssetMetaData>(); if (assetMetaData.type != CustomAssetMetaData.Type.Building && assetMetaData.type != CustomAssetMetaData.Type.Vehicle && assetMetaData.type != CustomAssetMetaData.Type.Unknown || (AssetImporterAssetTemplate.GetAssetDLCMask(assetMetaData) & notMask) != 0) { return(false); } bool wanted = wantBecauseEnabled || loadUsed && refs.GotBuildingVehicleAsset(assetMetaData.assetRef.fullName); if (includedInStyle || wanted) { BuildingVehicleImpl(asset.package.packageName, assetMetaData.assetRef, wanted); } } catch (Exception ex) { Failed(assetMetaData?.assetRef, ex); // CODebugBase<LogChannel>.Warn(LogChannel.Modding, string.Concat(new object[] { ex.GetType(), ": Loading custom asset failed:[", asset, "]\n", ex.Message })); } return(true); }
bool IsCommonBuilding(String fullName, Package.Asset asset, CustomAssetMetaData assetMetaData) { CustomAssetMetaData.Type type = assetMetaData.type; return(type == CustomAssetMetaData.Type.Building || type == CustomAssetMetaData.Type.SubBuilding); }
public bool LoadAsset(string fullName, Package.Asset asset, bool mainAsset = false) { if (asset == null) { return(StoreNotFoundName(fullName)); } Profiler.Info("Loading asset {0}", asset.fullName); try { Profiler.Trace("Clearing stack"); stack.Clear(); if (mainAsset) { Profiler.Info("Pushing {0} on stack", fullName); stack.Push(fullName); } Profiler.Info("Stack count {0}, current {1}", stack.Count, Current); CustomAssetMetaData assetMetaData = InstantiateAssetMetaData(asset); // Always remember: assetRef may point to another package because the deserialization method accepts any asset with a matching checksum. // There is a bug in the 1.6.0 game update in this. fullName = asset.package.packageName + "." + assetMetaData.assetRef.name; //impl GameObject assetGameObject = InstantiateAssetGameObject(assetMetaData); assetGameObject.name = fullName; PrefabInfo assetPrefabInfo = InstantiateAssetPrefab(assetGameObject); if (mainAsset && Settings.instance.installDependencies && notFoundIndirect.Count > 0) { return(HotloadManager.DeferLoad(fullName, notFoundIndirect.Keys.ToList())); } RegisterPrefab(fullName, asset, assetMetaData, assetPrefabInfo, assetGameObject); //impl if (mainAsset) { Profiler.Trace("Popping {0} from stack", stack.Peek()); stack.Pop(); } Profiler.Info("Stack count {0}", stack.Count); return(StorePrefabInfo(assetPrefabInfo)); } catch (Exception e) { if (mainAsset) { Profiler.Trace("Popping {0} from stack due to error {1}", stack.Peek(), e.Message); stack.Pop(); } Profiler.Error("Cannot load " + fullName, e); StoreFailedAssetName(fullName ?? asset.fullName, e); return(false); } }
Package.Asset[] GetLoadQueue(HashSet <string> styleBuildings) { Package[] packages = PackageManager.allPackages.ToArray(); Array.Sort(packages, (a, b) => string.Compare(a.packageName, b.packageName)); List <Package.Asset> assets = new List <Package.Asset>(8); List <CustomAssetMetaData> metas = new List <CustomAssetMetaData>(8); // Why this load order? By having related and identical assets close to each other, we get more loader cache hits (of meshes and textures) // in Sharing. We also get faster disk reads. // [0] propvar and prop, citizen [1] prop, tree [2] pillar and elevation and road [3] road // [4] sub-building and building [5] building [6] trailer and vehicle [7] vehicle List <Package.Asset>[] queues = { new List <Package.Asset>(4), new List <Package.Asset>(64), new List <Package.Asset>(4), new List <Package.Asset>(4), new List <Package.Asset>(4), new List <Package.Asset>(64), new List <Package.Asset>(32), new List <Package.Asset>(32) }; SteamHelper.DLC_BitMask notMask = ~SteamHelper.GetOwnedDLCMask(); bool loadEnabled = Settings.settings.loadEnabled, loadUsed = Settings.settings.loadUsed, report = loadUsed && Settings.settings.reportAssets; //PrintPackages(packages); foreach (Package p in packages) { CustomAssetMetaData meta = null; try { assets.Clear(); assets.AddRange(p.FilterAssets(UserAssetType.CustomAssetMetaData)); if (assets.Count == 0) { continue; } if (report) { AssetReport.instance.AddPackage(p); } bool enabled = loadEnabled && IsEnabled(p); if (assets.Count == 1) // the common case { bool want = enabled || styleBuildings.Contains(assets[0].fullName); // Fast exit. if (!want && !(loadUsed && UsedAssets.instance.GotPackage(p.packageName))) { continue; } meta = AssetDeserializer.Instantiate(assets[0]) as CustomAssetMetaData; bool used = loadUsed && UsedAssets.instance.IsUsed(meta); if (used || want && (AssetImporterAssetTemplate.GetAssetDLCMask(meta) & notMask) == 0) { CustomAssetMetaData.Type type = meta.type; int offset = type == CustomAssetMetaData.Type.Trailer || type == CustomAssetMetaData.Type.SubBuilding || type == CustomAssetMetaData.Type.PropVariation || type >= CustomAssetMetaData.Type.RoadElevation ? -1 : 0; AddToQueue(queues, meta, offset, !(enabled | used)); } } else { bool want = enabled; // Fast exit. if (!want) { for (int i = 0; i < assets.Count; i++) { want = want || styleBuildings.Contains(assets[i].fullName); } if (!want && !(loadUsed && UsedAssets.instance.GotPackage(p.packageName))) { continue; } } metas.Clear(); bool used = false; for (int i = 0; i < assets.Count; i++) { meta = AssetDeserializer.Instantiate(assets[i]) as CustomAssetMetaData; metas.Add(meta); want = want && (AssetImporterAssetTemplate.GetAssetDLCMask(meta) & notMask) == 0; used = used || loadUsed && UsedAssets.instance.IsUsed(meta); } if (want | used) { metas.Sort((a, b) => b.type - a.type); // prop variation, sub-building, trailer, elevation, pillar before main asset CustomAssetMetaData.Type type = metas[0].type; int offset = type == CustomAssetMetaData.Type.Trailer || type == CustomAssetMetaData.Type.SubBuilding || type == CustomAssetMetaData.Type.PropVariation || type >= CustomAssetMetaData.Type.RoadElevation ? -1 : 0; bool dontSpawn = !(enabled | used); for (int i = 0; i < metas.Count; i++) { AddToQueue(queues, metas[i], offset, dontSpawn); } } } } catch (Exception e) { AssetFailed(meta?.assetRef?.fullName ?? p.packageName + "." + p.packageMainAsset, e); } } Package.Asset[] queue = new Package.Asset[queues.Select(lst => lst.Count).Sum()]; for (int i = 0, k = 0; i < queues.Length; k += queues[i].Count, i++) { queues[i].CopyTo(queue, k); } return(queue); }
/// <summary> /// Is the asset used in the city? /// </summary> internal bool IsUsed(CustomAssetMetaData meta) { Package.Asset assetRef = meta.assetRef; return(assetRef != null ? allAssets[(int)meta.type].Contains(assetRef.fullName) : false); }
bool RegisterPrefab(String fullName, Package.Asset asset, CustomAssetMetaData metaData, PrefabInfo info, GameObject go) { PropInfo pi = go.GetComponent <PropInfo>(); if (pi != null) { if (pi.m_lodObject != null) { pi.m_lodObject.SetActive(false); } if (StorePropName(fullName)) { PrefabCollection <PropInfo> .InitializePrefabs("Custom Assets", pi, null); return(true); } } TreeInfo ti = go.GetComponent <TreeInfo>(); if (ti != null && StoreTreeName(fullName)) { PrefabCollection <TreeInfo> .InitializePrefabs("Custom Assets", ti, null); return(true); } BuildingInfo bi = go.GetComponent <BuildingInfo>(); if (bi != null) { if (bi.m_lodObject != null) { bi.m_lodObject.SetActive(false); } if (StoreBuildingName(fullName)) { PrefabCollection <BuildingInfo> .InitializePrefabs("Custom Assets", bi, null); bi.m_dontSpawnNormally = !IsCommonBuilding(fullName, asset, metaData); if (bi.GetAI() is IntersectionAI) { loadedIntersections.Add(fullName); } return(true); } } VehicleInfo vi = go.GetComponent <VehicleInfo>(); if (vi != null) { if (vi.m_lodObject != null) { vi.m_lodObject.SetActive(false); } if (StoreVehicleName(fullName)) { PrefabCollection <VehicleInfo> .InitializePrefabs("Custom Assets", vi, null); return(true); } } return(false); }
List <Package.Asset>[] GetLoadQueues(IEnumerable <Package.Asset> assets) { Profiler.Info("Create Load queue for {0} assets", assets.Count()); List <Package.Asset>[] queues = { new List <Package.Asset>(4), new List <Package.Asset>(64), new List <Package.Asset>(4), new List <Package.Asset>(64) }; SteamHelper.DLC_BitMask notMask = ~SteamHelper.GetOwnedDLCMask(); foreach (Package.Asset asset in assets) { string fullName = null; // Profiler.Trace( "Q {0} - {1} - {2} - {3}", asset, loadEnabled, IsEnabled( asset ), styleBuildings.Contains( asset.fullName ) ); //Profiler.Trace( "Q {0} - {1} - {2}", asset, IsEnabled( asset ), styleBuildings); try { //triggers calls to CustomSerialize CustomAssetMetaData assetMetaData = asset.Instantiate <CustomAssetMetaData>(); //Profiler.Trace( "meta {0}", assetMetaData ); // Always remember: assetRef may point to another package because the deserialization method accepts any asset with the same checksum. // Think of identical vehicle trailers in different crp's. // There is a bug in the 1.6.0 game update in this. fullName = asset.package.packageName + "." + assetMetaData.assetRef.name; if ((AssetImporterAssetTemplate.GetAssetDLCMask(assetMetaData) & notMask) == 0) { switch (assetMetaData.type) { case CustomAssetMetaData.Type.Building: if (!IsDuplicate(fullName, loadedBuildings, asset.package)) { queues[3].Add(asset); } break; case CustomAssetMetaData.Type.Prop: if (!IsDuplicate(fullName, loadedProps, asset.package)) { queues[1].Add(asset); } break; case CustomAssetMetaData.Type.Tree: if (!IsDuplicate(fullName, loadedTrees, asset.package)) { queues[1].Add(asset); } break; case CustomAssetMetaData.Type.Vehicle: if (!IsDuplicate(fullName, loadedVehicles, asset.package)) { queues[3].Add(asset); } break; case CustomAssetMetaData.Type.Trailer: if (!IsDuplicate(fullName, loadedVehicles, asset.package)) { queues[1].Add(asset); } break; case CustomAssetMetaData.Type.Unknown: queues[3].Add(asset); break; case CustomAssetMetaData.Type.SubBuilding: if (!IsDuplicate(fullName, loadedBuildings, asset.package)) { queues[2].Add(asset); } break; case CustomAssetMetaData.Type.PropVariation: if (!IsDuplicate(fullName, loadedProps, asset.package)) { queues[0].Add(asset); } break; } } } catch (Exception e) { Profiler.Error("GetLoadQueues", e); StoreFailedAssetName(fullName ?? asset.fullName, e); } } return(queues); }
public override void OnLevelLoaded(LoadMode mode) { Debug.Log("IconModifier: Patching icons"); int patchCount = 0; // Load the selection filter LUT texture var stream = (UnmanagedMemoryStream)Assembly.GetAssembly(typeof(IconModifier)).GetManifestResourceStream("AssetIcons.Assets.SelectFilter.png"); var data = new BinaryReader(stream).ReadBytes((int)stream.Length); focusedFilterTexture = new Texture2D(1024, 32); focusedFilterTexture.LoadImage(data); // Build references to the assets for thumbnails and tooltips. var thumbnails = new Dictionary <string, Package.Asset>(); var tooltips = new Dictionary <string, Package.Asset>(); foreach (Package package in PackageManager.allPackages) { foreach (Package.Asset asset in package) { if (asset.type == UserAssetType.CustomAssetMetaData) { CustomAssetMetaData md = asset.Instantiate <CustomAssetMetaData>(); if (md.imageRef != null && !thumbnails.ContainsKey(package.packageName)) { md.imageRef.Cache(); thumbnails.Add(package.packageName, md.imageRef); } if (md.steamPreviewRef != null && !tooltips.ContainsKey(package.packageName)) { md.steamPreviewRef.Cache(); tooltips.Add(package.packageName, md.steamPreviewRef); } } } } // Now go through all BuildingInfos and patch their icons. int infoCount = PrefabCollection <BuildingInfo> .LoadedCount(); for (uint infoIndex = 0; infoIndex < infoCount; ++infoIndex) { BuildingInfo info = PrefabCollection <BuildingInfo> .GetLoaded(infoIndex); Texture2D thumbnailTexture = null; Texture2D tooltipTexture = null; string packageName = info.name.Split('.')[0]; if (!string.IsNullOrEmpty(info.m_Thumbnail)) { // This item has a thumbnail, check if it's a generated one. GameObject gameObject = GameObject.Find(info.name); if (gameObject != null) { UIButton uiButton = gameObject.GetComponent <UIButton>(); if (uiButton != null && uiButton.atlas != null) { var focusedSpriteInfo = uiButton.atlas[uiButton.focusedFgSprite]; if (focusedSpriteInfo != null && focusedSpriteInfo.texture != null) { long nonBlueCount = 0; try { Color32[] focusedPixels = focusedSpriteInfo.texture.GetPixels32(); // The default atlas generator creates ugly dark blue focused icons // by removing everything from the red and green channel. Detect these // by adding up the amount of red and green in the image. foreach (Color32 pixel in focusedPixels) { if (pixel.a > 32) { nonBlueCount += pixel.r + pixel.g; } } } catch (Exception) { Debug.Log(String.Format("Failed to patch texture for {0}, skipping.", info.name)); continue; } if (nonBlueCount < 10000) { // This is a generated atlas. Replace the focused icon by generating // a new atlas. Include the tooltip if there is one. var thumbnailSpriteInfo = uiButton.atlas[uiButton.normalFgSprite]; if (thumbnailSpriteInfo != null && thumbnailSpriteInfo.texture != null) { thumbnailTexture = thumbnailSpriteInfo.texture; thumbnailTexture.name = info.name + "Icon"; } var tooltipSpriteInfo = uiButton.atlas["tooltip"]; if (tooltipSpriteInfo != null && thumbnailSpriteInfo.texture != null) { tooltipTexture = tooltipSpriteInfo.texture; tooltipTexture.name = "tooltip"; } } } } } } // Create a thumbnail based on the steam workshop image. if (string.IsNullOrEmpty(info.m_Thumbnail) && thumbnails.ContainsKey(packageName)) { thumbnailTexture = thumbnails[packageName].Instantiate <Texture2D>(); if (thumbnailTexture != null) { thumbnailTexture.wrapMode = TextureWrapMode.Clamp; thumbnailTexture.name = info.name + "Icon"; if (thumbnailTexture.width > thumbnailTexture.height) { ScaleTexture(thumbnailTexture, THUMBNAIL_SIZE, (THUMBNAIL_SIZE * thumbnailTexture.height) / thumbnailTexture.width); } else { ScaleTexture(thumbnailTexture, (THUMBNAIL_SIZE * thumbnailTexture.width) / thumbnailTexture.height, THUMBNAIL_SIZE); } } } // Create a tooltip image based on the steam workshop image. if (string.IsNullOrEmpty(info.m_InfoTooltipThumbnail) && tooltips.ContainsKey(packageName)) { tooltipTexture = tooltips[packageName].Instantiate <Texture2D>(); if (tooltipTexture != null) { // The tooltip texture name must match the info name, as that's the key value that's used // (stored on UIButton.m_Tooltip, not by us). tooltipTexture.name = info.name; // Crop and scale the tooltip to TOOLTIP_WIDTH x TOOLTIP_HEIGHT if (((float)tooltipTexture.width / (float)tooltipTexture.height) > (float)TOOLTIP_WIDTH / (float)TOOLTIP_HEIGHT) { // Picture is too wide, scale to TOOLTIP_HEIGHT pixels tall and then crop out the middle ScaleTexture(tooltipTexture, (TOOLTIP_HEIGHT * tooltipTexture.width) / tooltipTexture.height, TOOLTIP_HEIGHT); CropTexture(tooltipTexture, (tooltipTexture.width - TOOLTIP_WIDTH) / 2, 0, TOOLTIP_WIDTH, TOOLTIP_HEIGHT); } else if (((float)tooltipTexture.width / (float)tooltipTexture.height) < (float)TOOLTIP_WIDTH / (float)TOOLTIP_HEIGHT) { // Picture is too tall, scale to TOOLTIP_WIDTH pixels wide and then crop out the middle ScaleTexture(tooltipTexture, TOOLTIP_WIDTH, (TOOLTIP_WIDTH * tooltipTexture.height) / tooltipTexture.width); CropTexture(tooltipTexture, 0, (tooltipTexture.height - TOOLTIP_HEIGHT) / 2, TOOLTIP_WIDTH, TOOLTIP_HEIGHT); } else { // Picture is the right aspect ratio, just scale it ScaleTexture(tooltipTexture, TOOLTIP_WIDTH, TOOLTIP_HEIGHT); } } } // Build these textures into an atlas. Texture2D[] textures; if (thumbnailTexture != null) { if (tooltipTexture != null) { textures = new Texture2D[] { thumbnailTexture, null, null, null, null, tooltipTexture }; } else { textures = new Texture2D[] { thumbnailTexture, null, null, null, null }; } GenerateMissingThumbnailVariants(ref textures); } else if (tooltipTexture != null) { textures = new Texture2D[] { tooltipTexture }; } else { // Hmm, we've failed to make any textures. Move on to the next BuildingInfo. continue; } UITextureAtlas atlas = AssetImporterThumbnails.CreateThumbnailAtlas(textures, info.name + "Atlas"); if (thumbnailTexture != null) { // Store the thumbnail atlas and icon names on the uiButton for this building. GameObject gameObject = GameObject.Find(info.name); if (gameObject != null) { UIButton uiButton = gameObject.GetComponent <UIButton>(); if (uiButton != null) { uiButton.atlas = atlas; string baseIconName = info.name + "Icon"; uiButton.normalFgSprite = baseIconName; uiButton.focusedFgSprite = baseIconName + "Focused"; uiButton.hoveredFgSprite = baseIconName + "Hovered"; uiButton.pressedFgSprite = baseIconName + "Pressed"; uiButton.disabledFgSprite = baseIconName + "Disabled"; } } } if (tooltipTexture != null) { // Store the tooltip atlas for this building. info.m_InfoTooltipAtlas = atlas; // This is actually the transition thumbnail, we set it to the default to blank // out the tooltip during transitions, which matches the behaviour of the // existing icons. info.m_InfoTooltipThumbnail = "ThumbnailBuildingDefault"; } ++patchCount; } Debug.Log(String.Format("IconModifier: Complete. Patched {0} icons.", patchCount)); }
public override void OnLevelLoaded(LoadMode mode) { Debug.Log("IconModifier: Patching icons"); loadCount = 0; patchCount = 0; var stopwatch = System.Diagnostics.Stopwatch.StartNew(); // Load the selection filter LUT texture var stream = (UnmanagedMemoryStream)Assembly.GetAssembly(typeof(IconModifier)).GetManifestResourceStream("AssetIcons.Assets.SelectFilter.png"); var data = new BinaryReader(stream).ReadBytes((int)stream.Length); focusedFilterTexture = new Texture2D(1024, 32); focusedFilterTexture.LoadImage(data); // Build references to the assets for thumbnails and tooltips. var thumbnails = new Dictionary <string, Package.Asset>(); var tooltips = new Dictionary <string, Package.Asset>(); var timestamps = new Dictionary <string, DateTime>(); foreach (Package package in PackageManager.allPackages) { foreach (Package.Asset asset in package) { if (asset.type == UserAssetType.CustomAssetMetaData) { try { CustomAssetMetaData md = asset.Instantiate <CustomAssetMetaData>(); if (md.imageRef != null && !thumbnails.ContainsKey(package.packageName)) { thumbnails.Add(package.packageName, md.imageRef); } if (md.steamPreviewRef != null && !tooltips.ContainsKey(package.packageName)) { tooltips.Add(package.packageName, md.steamPreviewRef); } if (!timestamps.ContainsKey(package.packageName)) { timestamps.Add(package.packageName, md.timeStamp); } } catch (Exception e) { Debug.LogException(e); } } } } // Now go through all BuildingInfos and patch their icons. int infoCount = PrefabCollection <BuildingInfo> .LoadedCount(); for (uint infoIndex = 0; infoIndex < infoCount; ++infoIndex) { try { BuildingInfo info = PrefabCollection <BuildingInfo> .GetLoaded(infoIndex); PatchIcons(info, thumbnails, tooltips, timestamps); } catch (Exception e) { Debug.LogException(e); } } // Now go through all TreeInfos and patch their icons. infoCount = PrefabCollection <TreeInfo> .LoadedCount(); for (uint infoIndex = 0; infoIndex < infoCount; ++infoIndex) { try { TreeInfo info = PrefabCollection <TreeInfo> .GetLoaded(infoIndex); PatchIcons(info, thumbnails, tooltips, timestamps); } catch (Exception e) { Debug.LogException(e); } } stopwatch.Stop(); Debug.Log(String.Format( "IconModifier: Complete. Patched {0} icons, loading {1} from cache in {2}.", patchCount, loadCount, stopwatch.Elapsed)); }
List <Package.Asset>[] GetLoadQueues(HashSet <string> styleBuildings) { Package.Asset[] assets = FilterAssets(UserAssetType.CustomAssetMetaData); List <Package.Asset>[] queues = { new List <Package.Asset>(4), new List <Package.Asset>(64), new List <Package.Asset>(4), new List <Package.Asset>(64) }; SteamHelper.DLC_BitMask notMask = ~SteamHelper.GetOwnedDLCMask(); for (int i = 0; i < assets.Length; i++) { Package.Asset asset = assets[i]; string fullName = null; try { bool wantThis = loadEnabled && IsEnabled(asset) || styleBuildings.Contains(asset.fullName); // Make the first check fast. if (wantThis || loadUsed && UsedAssets.instance.GotPackage(asset.package.packageName)) { CustomAssetMetaData assetMetaData = asset.Instantiate <CustomAssetMetaData>(); // Always remember: assetRef may point to another package because the deserialization method accepts any asset with the same checksum. // Think of identical vehicle trailers in different crp's. // There is a bug in the 1.6.0 game update in this. fullName = asset.package.packageName + "." + assetMetaData.assetRef.name; if ((AssetImporterAssetTemplate.GetAssetDLCMask(assetMetaData) & notMask) == 0) { switch (assetMetaData.type) { case CustomAssetMetaData.Type.Building: if ((wantThis || loadUsed && UsedAssets.instance.GotBuilding(fullName)) && !IsDuplicate(fullName, loadedBuildings, asset.package)) { queues[3].Add(asset); } break; case CustomAssetMetaData.Type.Prop: if ((wantThis || loadUsed && UsedAssets.instance.GotProp(fullName)) && !IsDuplicate(fullName, loadedProps, asset.package)) { queues[1].Add(asset); } break; case CustomAssetMetaData.Type.Tree: if ((wantThis || loadUsed && UsedAssets.instance.GotTree(fullName)) && !IsDuplicate(fullName, loadedTrees, asset.package)) { queues[1].Add(asset); } break; case CustomAssetMetaData.Type.Vehicle: if ((wantThis || loadUsed && UsedAssets.instance.GotVehicle(fullName)) && !IsDuplicate(fullName, loadedVehicles, asset.package)) { queues[3].Add(asset); } break; case CustomAssetMetaData.Type.Trailer: if ((wantThis || loadUsed && UsedAssets.instance.GotVehicle(fullName)) && !IsDuplicate(fullName, loadedVehicles, asset.package)) { queues[1].Add(asset); } break; case CustomAssetMetaData.Type.Unknown: if (wantThis) { queues[3].Add(asset); } break; case CustomAssetMetaData.Type.SubBuilding: if ((wantThis || loadUsed && UsedAssets.instance.GotBuilding(fullName)) && !IsDuplicate(fullName, loadedBuildings, asset.package)) { queues[2].Add(asset); } break; case CustomAssetMetaData.Type.PropVariation: if ((wantThis || loadUsed && UsedAssets.instance.GotProp(fullName)) && !IsDuplicate(fullName, loadedProps, asset.package)) { queues[0].Add(asset); } break; } } } } catch (Exception e) { AssetFailed(fullName ?? asset.fullName, e); } } return(queues); }
Package.Asset[] GetLoadQueue(HashSet <string> styleBuildings) { Package.AssetType[] customAssets = { UserAssetType.CustomAssetMetaData }; Package[] packages = { }; try { packages = PackageManager.allPackages.Where(p => p.FilterAssets(customAssets).Any()).ToArray(); Array.Sort(packages, PackageComparison); } catch (Exception e) { UnityEngine.Debug.LogException(e); } List <Package.Asset> assets = new List <Package.Asset>(8); List <CustomAssetMetaData> metas = new List <CustomAssetMetaData>(8); // Why this load order? By having related and identical assets close to each other, we get more loader cache hits (of meshes and textures) // in Sharing. We also get faster disk reads. // [0] propvar and prop, tree, citizen [1] prop [2] pillar and elevation and road [3] road // [4] sub-building and building [5] building [6] trailer and vehicle [7] vehicle List <Package.Asset>[] queues = { new List <Package.Asset>(32), new List <Package.Asset>(64), new List <Package.Asset>(4), new List <Package.Asset>(4), new List <Package.Asset>(16), new List <Package.Asset>(64), new List <Package.Asset>(32), new List <Package.Asset>(32) }; SteamHelper.DLC_BitMask notMask = ~SteamHelper.GetOwnedDLCMask(); bool loadEnabled = Settings.settings.loadEnabled, loadUsed = Settings.settings.loadUsed; //PrintPackages(packages); foreach (Package p in packages) { CustomAssetMetaData meta = null; try { assets.Clear(); assets.AddRange(p.FilterAssets(customAssets)); int count = assets.Count; CustomDeserializer.instance.AddPackage(p); bool enabled = loadEnabled && IsEnabled(p); if (count == 1) // the common case { bool want = enabled || styleBuildings.Contains(assets[0].fullName); // Fast exit. if (!want && !(loadUsed && UsedAssets.instance.GotPackage(p.packageName))) { continue; } meta = AssetDeserializer.Instantiate(assets[0]) as CustomAssetMetaData; bool used = loadUsed && UsedAssets.instance.IsUsed(meta); if (used || want && (AssetImporterAssetTemplate.GetAssetDLCMask(meta) & notMask) == 0) { if (reportAssets) { AssetReport.instance.AddPackage(p, meta, want, used); } CustomAssetMetaData.Type type = meta.type; int offset = type == CustomAssetMetaData.Type.Trailer || type == CustomAssetMetaData.Type.SubBuilding || type == CustomAssetMetaData.Type.PropVariation || type >= CustomAssetMetaData.Type.RoadElevation ? -1 : 0; AddToQueue(queues, meta, type, offset, !(enabled | used)); } } else { bool want = enabled; // Fast exit. if (!want) { for (int i = 0; i < count; i++) { want = want || styleBuildings.Contains(assets[i].fullName); } if (!want && !(loadUsed && UsedAssets.instance.GotPackage(p.packageName))) { continue; } } metas.Clear(); bool used = false; for (int i = 0; i < count; i++) { meta = AssetDeserializer.Instantiate(assets[i]) as CustomAssetMetaData; metas.Add(meta); want = want && (AssetImporterAssetTemplate.GetAssetDLCMask(meta) & notMask) == 0; used = used || loadUsed && UsedAssets.instance.IsUsed(meta); } if (want | used) { metas.Sort((a, b) => b.type - a.type); // prop variation, sub-building, trailer, elevation, pillar before main asset CustomAssetMetaData lastmeta = metas[count - 1]; if (reportAssets) { AssetReport.instance.AddPackage(p, lastmeta, want, used); } CustomAssetMetaData.Type type = metas[0].type; int offset = type == CustomAssetMetaData.Type.Trailer || type == CustomAssetMetaData.Type.SubBuilding || type == CustomAssetMetaData.Type.PropVariation || type >= CustomAssetMetaData.Type.RoadElevation ? -1 : 0; bool treeVariations = lastmeta.type == CustomAssetMetaData.Type.Tree, dontSpawn = !(enabled | used); for (int i = 0; i < count; i++) { CustomAssetMetaData m = metas[i]; CustomAssetMetaData.Type t = m.type; if (treeVariations && t == CustomAssetMetaData.Type.PropVariation) { t = CustomAssetMetaData.Type.Tree; } AddToQueue(queues, m, t, offset, dontSpawn); } } } } catch (Exception e) { AssetFailed(meta?.assetRef, p, e); } } CheckSuspects(); for (int i = 0; i < queuedLoads.Length; i++) { queuedLoads[i].Clear(); queuedLoads[i] = null; } queuedLoads = null; Package.Asset[] queue = new Package.Asset[queues.Sum(lst => lst.Count)]; for (int i = 0, k = 0; i < queues.Length; i++) { queues[i].CopyTo(queue, k); k += queues[i].Count; queues[i].Clear(); queues[i] = null; } return(queue); }