using 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 val = config.Bind("Analytics", "Enabled", true, "Send a single anonymous ping when the game starts. No gameplay data is collected."); ConfigEntry val2 = config.Bind("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> m_zonesByBiome = new Dictionary>(); private Dictionary m_zoneBiomeArea = new Dictionary(); 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 value)) { value = new List(); 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 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 list = new List(); foreach (KeyValuePair> 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 _enableOptimization; private static ConfigEntry _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("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("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 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); } } }