Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of World Gen Accelerator v1.0.0
worldGenAccelerator.dll
Decompiled 3 weeks agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.Networking; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("worldGenAccelerator")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("worldGenAccelerator")] [assembly: AssemblyCopyright("Copyright © 2023")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("FBB901EA-0092-4689-8EE8-776E5CBFBC26")] [assembly: AssemblyFileVersion("0.1.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = "")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.1.0.0")] [module: UnverifiableCode] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } } namespace worldGenAccelerator { public static class Analytics { private const string PingUrl = "https://mod-analytics.vercel.app/api/ping"; private static bool _hasSentPing; public static void Init(ConfigFile config, string modId, string modVersion) { if (!_hasSentPing) { ConfigEntry<bool> val = config.Bind<bool>("Analytics", "Enabled", true, "Send a single anonymous ping when the game starts. No gameplay data is collected."); ConfigEntry<string> val2 = config.Bind<string>("Analytics", "InstanceID", Guid.NewGuid().ToString(), "Random anonymous ID. Change or delete to reset."); if (!val.Value) { worldGenAcceleratorPlugin.TemplateLogger.LogDebug((object)"Analytics disabled by config"); return; } _hasSentPing = true; ((MonoBehaviour)ThreadingHelper.Instance).StartCoroutine(SendPing(modId, modVersion, val2.Value)); } } private static IEnumerator SendPing(string modId, string modVersion, string instanceId) { string json = "{\"mod_id\":\"" + Escape(modId) + "\",\"mod_version\":\"" + Escape(modVersion) + "\",\"instance_id\":\"" + Escape(instanceId) + "\"}"; byte[] body = Encoding.UTF8.GetBytes(json); UnityWebRequest req = new UnityWebRequest("https://mod-analytics.vercel.app/api/ping", "POST"); try { req.uploadHandler = (UploadHandler)new UploadHandlerRaw(body); req.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer(); req.SetRequestHeader("Content-Type", "application/json"); req.timeout = 10; yield return req.SendWebRequest(); if ((int)req.result == 1) { worldGenAcceleratorPlugin.TemplateLogger.LogDebug((object)"Analytics ping sent"); } else { worldGenAcceleratorPlugin.TemplateLogger.LogDebug((object)("Analytics ping failed: " + req.error)); } } finally { ((IDisposable)req)?.Dispose(); } } private static string Escape(string s) { return s.Replace("\\", "\\\\").Replace("\"", "\\\""); } } public class BiomeZoneCache { private Dictionary<Biome, List<Vector2i>> m_zonesByBiome = new Dictionary<Biome, List<Vector2i>>(); private Dictionary<Vector2i, BiomeArea> m_zoneBiomeArea = new Dictionary<Vector2i, BiomeArea>(); private int m_cachedZoneCount; private bool m_built; public static BiomeZoneCache Instance { get; private set; } = new BiomeZoneCache(); public void Build() { //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) if (m_built) { return; } Stopwatch stopwatch = Stopwatch.StartNew(); m_zonesByBiome.Clear(); m_zoneBiomeArea.Clear(); m_cachedZoneCount = 0; WorldGenerator instance = WorldGenerator.instance; float zoneSize = ZoneSystem.instance.m_zoneSize; float worldRadius = ExpandWorldSizeBridge.GetWorldRadius(); int num = Mathf.CeilToInt(worldRadius / zoneSize); double num2 = (double)worldRadius * (double)worldRadius; Vector2i val = default(Vector2i); for (int i = -num; i <= num; i++) { for (int j = -num; j <= num; j++) { ((Vector2i)(ref val))..ctor(i, j); Vector3 zonePos = ZoneSystem.GetZonePos(val); if (!((double)((Vector3)(ref zonePos)).sqrMagnitude >= num2)) { Biome biome = instance.GetBiome(zonePos); BiomeArea biomeArea = instance.GetBiomeArea(zonePos); if (!m_zonesByBiome.TryGetValue(biome, out List<Vector2i> value)) { value = new List<Vector2i>(); m_zonesByBiome[biome] = value; } value.Add(val); m_zoneBiomeArea[val] = biomeArea; m_cachedZoneCount++; } } } stopwatch.Stop(); m_built = true; worldGenAcceleratorPlugin.TemplateLogger.LogInfo((object)($"BiomeZoneCache built in {stopwatch.ElapsedMilliseconds}ms " + $"({m_cachedZoneCount} zones cached, worldRadius={worldRadius}m, gridRadius={num})")); } public List<Vector2i> GetCandidateZones(Biome biomeMask, BiomeArea biomeAreaMask) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Invalid comparison between Unknown and I4 //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Invalid comparison between Unknown and I4 //IL_0066: Unknown result type (might be due to invalid IL or missing references) List<Vector2i> list = new List<Vector2i>(); foreach (KeyValuePair<Biome, List<Vector2i>> item in m_zonesByBiome) { if ((item.Key & biomeMask) == 0) { continue; } foreach (Vector2i item2 in item.Value) { if ((m_zoneBiomeArea[item2] & biomeAreaMask) > 0) { list.Add(item2); } } } return list; } public void Reset() { m_zonesByBiome.Clear(); m_zoneBiomeArea.Clear(); m_cachedZoneCount = 0; m_built = false; } } public static class ExpandWorldSizeBridge { private const string PluginGuid = "expand_world_size"; private const string ConfigTypeName = "ExpandWorldSize.Configuration"; private const string RadiusPropertyName = "WorldRadius"; public const float VanillaWorldRadius = 10000f; private static bool s_resolved; private static PropertyInfo? s_radiusProperty; public static float GetWorldRadius() { PropertyInfo radiusProperty = GetRadiusProperty(); if (radiusProperty == null) { return 10000f; } try { object value = radiusProperty.GetValue(null); if (value is float) { float num = (float)value; if (num > 0f) { return num; } } } catch (Exception ex) { worldGenAcceleratorPlugin.TemplateLogger.LogWarning((object)("Failed to read ExpandWorldSize.Configuration.WorldRadius from ExpandWorldSize: " + ex.Message + ". Falling back to vanilla radius.")); } return 10000f; } private static PropertyInfo? GetRadiusProperty() { if (s_resolved) { return s_radiusProperty; } s_resolved = true; if (!Chainloader.PluginInfos.TryGetValue("expand_world_size", out var value)) { return null; } Assembly assembly = ((object)value.Instance).GetType().Assembly; Type type = assembly.GetType("ExpandWorldSize.Configuration"); if (type == null) { worldGenAcceleratorPlugin.TemplateLogger.LogWarning((object)"ExpandWorldSize is loaded but type ExpandWorldSize.Configuration was not found. Falling back to vanilla radius."); return null; } PropertyInfo property = type.GetProperty("WorldRadius", BindingFlags.Static | BindingFlags.Public); if (property == null) { worldGenAcceleratorPlugin.TemplateLogger.LogWarning((object)"ExpandWorldSize is loaded but ExpandWorldSize.Configuration.WorldRadius was not found. Falling back to vanilla radius."); return null; } s_radiusProperty = property; return property; } } [BepInPlugin("warpalicious.worldGenAccelerator", "worldGenAccelerator", "1.0.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class worldGenAcceleratorPlugin : BaseUnityPlugin { private const string ModName = "worldGenAccelerator"; private const string ModVersion = "1.0.0"; private const string Author = "warpalicious"; private const string ModGUID = "warpalicious.worldGenAccelerator"; private static string ConfigFileName = "warpalicious.worldGenAccelerator.cfg"; private static string ConfigFileFullPath; private readonly Harmony HarmonyInstance = new Harmony("warpalicious.worldGenAccelerator"); public static readonly ManualLogSource TemplateLogger; private static ConfigEntry<bool> _enableOptimization; private static ConfigEntry<bool> _enableTimingLogs; private DateTime _lastReloadTime; private const long RELOAD_DELAY = 10000000L; public static bool OptimizationEnabled => _enableOptimization.Value; public static bool TimingLogsEnabled => _enableTimingLogs.Value; public void Awake() { Analytics.Init(((BaseUnityPlugin)this).Config, "warpalicious.worldGenAccelerator", "1.0.0"); _enableOptimization = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableOptimization", true, "Enable the biome zone cache optimization for faster world generation. Disabling this uses vanilla generation logic. Note: Optimized worlds will have different layouts than vanilla for the same seed."); _enableTimingLogs = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableTimingLogs", true, "Log detailed timing information for each location placement and total generation time."); Assembly executingAssembly = Assembly.GetExecutingAssembly(); HarmonyInstance.PatchAll(executingAssembly); SetupWatcher(); } private void OnDestroy() { ((BaseUnityPlugin)this).Config.Save(); } private void SetupWatcher() { _lastReloadTime = DateTime.Now; FileSystemWatcher fileSystemWatcher = new FileSystemWatcher(Paths.ConfigPath, ConfigFileName); fileSystemWatcher.Changed += ReadConfigValues; fileSystemWatcher.Created += ReadConfigValues; fileSystemWatcher.Renamed += ReadConfigValues; fileSystemWatcher.IncludeSubdirectories = true; fileSystemWatcher.EnableRaisingEvents = true; } private void ReadConfigValues(object sender, FileSystemEventArgs e) { DateTime now = DateTime.Now; long num = now.Ticks - _lastReloadTime.Ticks; if (File.Exists(ConfigFileFullPath) && num >= 10000000) { try { TemplateLogger.LogInfo((object)"Attempting to reload configuration..."); ((BaseUnityPlugin)this).Config.Reload(); TemplateLogger.LogInfo((object)"Configuration reloaded successfully!"); } catch { TemplateLogger.LogError((object)("There was an issue loading " + ConfigFileName)); return; } _lastReloadTime = now; if ((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsDedicated()) { TemplateLogger.LogInfo((object)"Updating runtime configurations..."); } } } static worldGenAcceleratorPlugin() { string configPath = Paths.ConfigPath; char directorySeparatorChar = Path.DirectorySeparatorChar; ConfigFileFullPath = configPath + directorySeparatorChar + ConfigFileName; TemplateLogger = Logger.CreateLogSource("worldGenAccelerator"); _enableOptimization = null; _enableTimingLogs = null; } } [HarmonyPatch] public static class ZoneSystemPatch { private static Stopwatch s_totalGenerationTimer = new Stopwatch(); [HarmonyPatch(typeof(ZoneSystem), "GenerateLocations")] [HarmonyPrefix] private static void GenerateLocations_Prefix() { if (worldGenAcceleratorPlugin.TimingLogsEnabled) { s_totalGenerationTimer.Restart(); } if (worldGenAcceleratorPlugin.OptimizationEnabled) { BiomeZoneCache.Instance.Reset(); BiomeZoneCache.Instance.Build(); } } [HarmonyPatch(typeof(ZoneSystem), "set_LocationsGenerated")] [HarmonyPostfix] private static void LocationsGenerated_Postfix(bool value) { if (value && worldGenAcceleratorPlugin.TimingLogsEnabled && s_totalGenerationTimer.IsRunning) { s_totalGenerationTimer.Stop(); worldGenAcceleratorPlugin.TemplateLogger.LogInfo((object)$"Total location generation completed in {s_totalGenerationTimer.ElapsedMilliseconds}ms"); } } [HarmonyPatch(typeof(ZoneSystem), "GenerateLocationsTimeSliced", new Type[] { typeof(ZoneLocation), typeof(Stopwatch), typeof(ZPackage) })] [HarmonyPrefix] private static bool GenerateLocationsTimeSliced_Prefix(ZoneSystem __instance, ZoneLocation location, Stopwatch timeSliceStopwatch, ZPackage iterationsPkg, ref IEnumerator __result) { if (!worldGenAcceleratorPlugin.OptimizationEnabled) { return true; } __result = OptimizedGenerateLocationsTimeSliced(__instance, location, timeSliceStopwatch, iterationsPkg); return false; } private static IEnumerator OptimizedGenerateLocationsTimeSliced(ZoneSystem zoneSystem, ZoneLocation location, Stopwatch timeSliceStopwatch, ZPackage iterationsPkg) { ZoneSystem zoneSystem2 = zoneSystem; Stopwatch locationTimer = null; if (worldGenAcceleratorPlugin.TimingLogsEnabled) { locationTimer = Stopwatch.StartNew(); } int seed = WorldGenerator.instance.GetSeed() + StringExtensionMethods.GetStableHashCode(location.m_prefab.Name); State state = Random.state; Random.InitState(seed); float maxRadius = Mathf.Max(location.m_exteriorRadius, location.m_interiorRadius); int iterations = 0; int placed = zoneSystem2.CountNrOfLocation(location); if (!location.m_unique || placed <= 0) { zoneSystem2.s_tempVeg.Clear(); List<Vector2i> candidates = BiomeZoneCache.Instance.GetCandidateZones(location.m_biome, location.m_biomeArea); candidates.RemoveAll((Vector2i z) => zoneSystem2.m_locationInstances.ContainsKey(z) || zoneSystem2.m_generatedZones.Contains(z)); int candidateCount = candidates.Count; if (location.m_centerFirst) { candidates.Sort(delegate(Vector2i a, Vector2i b) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) Vector3 zonePos = ZoneSystem.GetZonePos(a); float magnitude2 = ((Vector3)(ref zonePos)).magnitude; zonePos = ZoneSystem.GetZonePos(b); float magnitude3 = ((Vector3)(ref zonePos)).magnitude; return magnitude2.CompareTo(magnitude3); }); } else { for (int i = candidates.Count - 1; i > 0; i--) { int k = Random.Range(0, i + 1); Vector2i tmp = candidates[i]; candidates[i] = candidates[k]; candidates[k] = tmp; } } Color mask1 = default(Color); float delta = default(float); Vector3 slopeDir = default(Vector3); Color mask2 = default(Color); for (int j = 0; j < candidates.Count; j++) { if (placed >= location.m_quantity) { break; } Vector2i zoneID = candidates[j]; if (timeSliceStopwatch.Elapsed.TotalSeconds >= (double)zoneSystem2.m_timeSlicedGenerationTimeBudget) { State insideState = Random.state; Random.state = state; yield return null; timeSliceStopwatch.Restart(); state = Random.state; Random.state = insideState; } if (zoneSystem2.m_locationInstances.ContainsKey(zoneID)) { continue; } for (int l = 0; l < 20; l++) { if (timeSliceStopwatch.Elapsed.TotalSeconds >= (double)zoneSystem2.m_timeSlicedGenerationTimeBudget) { State insideState = Random.state; Random.state = state; yield return null; timeSliceStopwatch.Restart(); state = Random.state; Random.state = insideState; } iterations++; Vector3 randomPointInZone = ZoneSystem.GetRandomPointInZone(zoneID, maxRadius); float magnitude = ((Vector3)(ref randomPointInZone)).magnitude; if (((double)location.m_minDistance != 0.0 && (double)magnitude < (double)location.m_minDistance) || ((double)location.m_maxDistance != 0.0 && (double)magnitude > (double)location.m_maxDistance) || (location.m_biome & WorldGenerator.instance.GetBiome(randomPointInZone)) == 0) { continue; } randomPointInZone.y = WorldGenerator.instance.GetHeight(randomPointInZone.x, randomPointInZone.z, ref mask1); float altitude = randomPointInZone.y - 30f; if ((double)altitude < (double)location.m_minAltitude || (double)altitude > (double)location.m_maxAltitude) { continue; } if (location.m_inForest) { float forestFactor = WorldGenerator.GetForestFactor(randomPointInZone); if ((double)forestFactor < (double)location.m_forestTresholdMin || (double)forestFactor > (double)location.m_forestTresholdMax) { continue; } } if ((double)location.m_minDistanceFromCenter > 0.0 || (double)location.m_maxDistanceFromCenter > 0.0) { float lengthXZ = Utils.LengthXZ(randomPointInZone); if (((double)location.m_minDistanceFromCenter > 0.0 && (double)lengthXZ < (double)location.m_minDistanceFromCenter) || ((double)location.m_maxDistanceFromCenter > 0.0 && (double)lengthXZ > (double)location.m_maxDistanceFromCenter)) { continue; } } WorldGenerator.instance.GetTerrainDelta(randomPointInZone, location.m_exteriorRadius, ref delta, ref slopeDir); if ((double)delta > (double)location.m_maxTerrainDelta || (double)delta < (double)location.m_minTerrainDelta || ((double)location.m_minDistanceFromSimilar > 0.0 && zoneSystem2.HaveLocationInRange(location.m_prefab.Name, location.m_group, randomPointInZone, location.m_minDistanceFromSimilar, false)) || ((double)location.m_maxDistanceFromSimilar > 0.0 && !zoneSystem2.HaveLocationInRange(location.m_prefabName, location.m_groupMax, randomPointInZone, location.m_maxDistanceFromSimilar, true))) { continue; } float vegMask = mask1.a; if (((double)location.m_minimumVegetation > 0.0 && (double)vegMask <= (double)location.m_minimumVegetation) || ((double)location.m_maximumVegetation < 1.0 && (double)vegMask >= (double)location.m_maximumVegetation)) { continue; } if (location.m_surroundCheckVegetation) { float num3 = 0f; for (int index1 = 0; index1 < location.m_surroundCheckLayers; index1++) { float num4 = (float)(index1 + 1) / (float)location.m_surroundCheckLayers * location.m_surroundCheckDistance; for (int index2 = 0; index2 < 6; index2++) { float f = (float)((double)index2 / 6.0 * 3.1415927410125732 * 2.0); Vector3 samplePos = randomPointInZone + new Vector3(Mathf.Sin(f) * num4, 0f, Mathf.Cos(f) * num4); WorldGenerator.instance.GetHeight(samplePos.x, samplePos.z, ref mask2); float num5 = (float)(((double)location.m_surroundCheckDistance - (double)num4) / ((double)location.m_surroundCheckDistance * 2.0)); num3 += mask2.a * num5; mask2 = default(Color); } } zoneSystem2.s_tempVeg.Add(num3); if (zoneSystem2.s_tempVeg.Count < 10) { continue; } float maxVeg = zoneSystem2.s_tempVeg.Max(); float avgVeg = zoneSystem2.s_tempVeg.Average(); float threshold = avgVeg + (maxVeg - avgVeg) * location.m_surroundBetterThanAverage; if ((double)num3 < (double)threshold) { continue; } } zoneSystem2.RegisterLocation(location, randomPointInZone, false); placed++; break; } } if (placed < location.m_quantity) { worldGenAcceleratorPlugin.TemplateLogger.LogWarning((object)$" {location.m_prefab.Name}: placed {placed}/{location.m_quantity} (incomplete)"); } if (worldGenAcceleratorPlugin.TimingLogsEnabled && locationTimer != null) { worldGenAcceleratorPlugin.TemplateLogger.LogInfo((object)($" {location.m_prefab.Name}: placed {placed}/{location.m_quantity} " + $"({candidateCount} candidate zones, {iterations} point iterations) " + $"in {locationTimer.ElapsedMilliseconds}ms")); } } Random.state = state; iterationsPkg.Write(iterations); iterationsPkg.SetPos(0); } } }