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 G3 Continents v1.0.0
G3Continents.dll
Decompiled a day agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using System.Threading.Tasks; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Jotunn.Utils; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("0.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace G3Continents { internal static class Commands { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static ConsoleEventFailable <>9__0_0; public static ConsoleEventFailable <>9__0_1; public static ConsoleEventFailable <>9__0_2; public static ConsoleEventFailable <>9__0_3; public static ConsoleEventFailable <>9__0_4; public static ConsoleEventFailable <>9__0_5; internal object <Register>b__0_0(ConsoleEventArgs args) { return Maps(args, batch: false); } internal object <Register>b__0_1(ConsoleEventArgs args) { return Maps(args, batch: true); } internal object <Register>b__0_2(ConsoleEventArgs args) { if (Preview.Busy) { return "a map is being drawn"; } int size = 2048; for (int i = 1; i < args.Length; i++) { TrySize(args[i], ref size); } ((MonoBehaviour)Plugin.Instance).StartCoroutine(Preview.ExportCurrent(args.Context, size, open: true)); return true; } internal object <Register>b__0_3(ConsoleEventArgs args) { int keyMissing; int shortCount; string text = LocationCheck.Report(out keyMissing, out shortCount); if (text.Length == 0) { return "locations are known only where the world runs (host or server)"; } string[] array = text.Split(new char[1] { '\n' }); foreach (string text2 in array) { if (!text2.StartsWith("ok ", StringComparison.Ordinal) && !text2.StartsWith(" ", StringComparison.Ordinal)) { Terminal context = args.Context; if (context != null) { context.AddString(text2.TrimEnd(new char[1] { '\r' })); } } } return true; } internal object <Register>b__0_4(ConsoleEventArgs args) { if ((Object)(object)Minimap.instance == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null) { return "join a world first"; } if ((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.LocalPlayerIsAdminOrHost()) { return "only the host or a server admin (adminlist.txt) can reveal the map"; } Minimap.instance.ExploreAll(); Terminal context = args.Context; if (context != null) { context.AddString("G3 Continents: the whole map is explored."); } return true; } internal object <Register>b__0_5(ConsoleEventArgs args) { Terminal context = args.Context; if (context != null) { context.AddString($"G3 Continents generator {1}: " + ShapeParams.ForVersion(1).Describe()); } Terminal context2 = args.Context; if (context2 != null) { context2.AddString("New worlds get continents: " + Plugin.NewWorlds.Value); } return true; } } internal static void Register() { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected O, but got Unknown //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Expected O, but got Unknown //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Expected O, but got Unknown //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Expected O, but got Unknown //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Expected O, but got Unknown //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Expected O, but got Unknown object obj = <>c.<>9__0_0; if (obj == null) { ConsoleEventFailable val = (ConsoleEventArgs args) => Maps(args, batch: false); <>c.<>9__0_0 = val; obj = (object)val; } new ConsoleCommand("g3c_map", "[G3 Continents] g3c_map [seed] [vanilla|both] [size=N] [noopen] [Key=value...]: draws a seed map", (ConsoleEventFailable)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); object obj2 = <>c.<>9__0_1; if (obj2 == null) { ConsoleEventFailable val2 = (ConsoleEventArgs args) => Maps(args, batch: true); <>c.<>9__0_1 = val2; obj2 = (object)val2; } new ConsoleCommand("g3c_maps", "[G3 Continents] g3c_maps <count> [vanilla|both] [size=N] [Key=value...]: draws maps of random seeds", (ConsoleEventFailable)obj2, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); object obj3 = <>c.<>9__0_2; if (obj3 == null) { ConsoleEventFailable val3 = delegate(ConsoleEventArgs args) { if (Preview.Busy) { return "a map is being drawn"; } int size = 2048; for (int i = 1; i < args.Length; i++) { TrySize(args[i], ref size); } ((MonoBehaviour)Plugin.Instance).StartCoroutine(Preview.ExportCurrent(args.Context, size, open: true)); return true; }; <>c.<>9__0_2 = val3; obj3 = (object)val3; } new ConsoleCommand("g3c_world", "[G3 Continents] g3c_world [size=N]: map of this world with its key locations", (ConsoleEventFailable)obj3, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); object obj4 = <>c.<>9__0_3; if (obj4 == null) { ConsoleEventFailable val4 = delegate(ConsoleEventArgs args) { int keyMissing; int shortCount; string text = LocationCheck.Report(out keyMissing, out shortCount); if (text.Length == 0) { return "locations are known only where the world runs (host or server)"; } string[] array = text.Split(new char[1] { '\n' }); foreach (string text2 in array) { if (!text2.StartsWith("ok ", StringComparison.Ordinal) && !text2.StartsWith(" ", StringComparison.Ordinal)) { Terminal context = args.Context; if (context != null) { context.AddString(text2.TrimEnd(new char[1] { '\r' })); } } } return true; }; <>c.<>9__0_3 = val4; obj4 = (object)val4; } new ConsoleCommand("g3c_check", "[G3 Continents] g3c_check: are this world's locations all placed", (ConsoleEventFailable)obj4, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); object obj5 = <>c.<>9__0_4; if (obj5 == null) { ConsoleEventFailable val5 = delegate(ConsoleEventArgs args) { if ((Object)(object)Minimap.instance == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null) { return "join a world first"; } if ((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.LocalPlayerIsAdminOrHost()) { return "only the host or a server admin (adminlist.txt) can reveal the map"; } Minimap.instance.ExploreAll(); Terminal context = args.Context; if (context != null) { context.AddString("G3 Continents: the whole map is explored."); } return true; }; <>c.<>9__0_4 = val5; obj5 = (object)val5; } new ConsoleCommand("g3c_explore", "[G3 Continents] g3c_explore: reveals the whole map (single player, the host or a server admin)", (ConsoleEventFailable)obj5, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); object obj6 = <>c.<>9__0_5; if (obj6 == null) { ConsoleEventFailable val6 = delegate(ConsoleEventArgs args) { Terminal context = args.Context; if (context != null) { context.AddString($"G3 Continents generator {1}: " + ShapeParams.ForVersion(1).Describe()); } Terminal context2 = args.Context; if (context2 != null) { context2.AddString("New worlds get continents: " + Plugin.NewWorlds.Value); } return true; }; <>c.<>9__0_5 = val6; obj6 = (object)val6; } new ConsoleCommand("g3c_params", "[G3 Continents] g3c_params: the continent generator numbers", (ConsoleEventFailable)obj6, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); } private static object Maps(ConsoleEventArgs args, bool batch) { if (Preview.Busy) { return "a map is being drawn"; } int num = 1; int size = (batch ? 512 : 1024); bool open = true; bool flag = false; bool flag2 = false; string text = null; ShapeParams shapeParams = null; ShapeParams shapeParams2 = ShapeParams.ForVersion(1); for (int i = 1; i < args.Length; i++) { string text2 = args[i]; switch (text2.ToLowerInvariant()) { case "vanilla": flag = true; continue; case "both": flag2 = true; continue; case "noopen": open = false; continue; } if (TrySize(text2, ref size)) { continue; } int result; if (text2.Contains("=")) { string[] array = text2.Split(new char[1] { '=' }, 2); shapeParams = shapeParams ?? shapeParams2.Clone(); if (!shapeParams.Set(array[0], array[1])) { return "unknown generator number '" + text2 + "', see g3c_params"; } } else if (batch && num == 1 && int.TryParse(text2, NumberStyles.Integer, CultureInfo.InvariantCulture, out result)) { num = Math.Max(1, Math.Min(result, 200)); } else { if (batch || text != null) { return "unknown word '" + text2 + "'"; } text = text2; } } List<Preview.Job> list = new List<Preview.Job>(); for (int j = 0; j < num; j++) { string seed = text ?? World.GenerateSeed(); if (!flag || flag2) { list.Add(new Preview.Job { Seed = seed, Custom = shapeParams, Size = size }); } if (flag || flag2) { list.Add(new Preview.Job { Seed = seed, Vanilla = true, Size = size }); } } ((MonoBehaviour)Plugin.Instance).StartCoroutine(Preview.Run(list, args.Context, open)); return true; } private static bool TrySize(string a, ref int size) { if (!a.StartsWith("size=", StringComparison.OrdinalIgnoreCase) || !int.TryParse(a.Substring(5), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { return false; } size = Math.Max(128, Math.Min(result, 4096)); return true; } } [HarmonyPatch] internal static class Compat { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static Func<MethodBase, bool> <>9__5_1; public static PopupButtonCallback <>9__8_0; internal bool <Check>b__5_1(MethodBase m) { return m != null; } internal void <Show>b__8_0() { UnifiedPopup.Pop(); } } private static readonly string[] s_knownMods = new string[7] { "bettercontinents", "better continents", "expand_world_data", "expand world data", "expand_world_size", "expand world size", "riverheim" }; internal static readonly List<string> Conflicts = new List<string>(); private static bool s_shown; internal static bool HasConflict => Conflicts.Count > 0; internal static void Check() { Conflicts.Clear(); foreach (PluginInfo value in Chainloader.PluginInfos.Values) { string guid = value.Metadata.GUID ?? string.Empty; string name = value.Metadata.Name ?? string.Empty; if (!(guid == "g3.continents") && s_knownMods.Any((string k) => guid.IndexOf(k, StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf(k, StringComparison.OrdinalIgnoreCase) >= 0)) { Add(name + " (" + guid + ")"); } } List<MethodBase> list = new List<MethodBase>(); list.Add(AccessTools.Method(typeof(WorldGenerator), "GetBaseHeight", (Type[])null, (Type[])null)); list.Add(AccessTools.Method(typeof(WorldGenerator), "GetBiome", new Type[4] { typeof(float), typeof(float), typeof(float), typeof(bool) }, (Type[])null)); list.Add(AccessTools.Method(typeof(WorldGenerator), "GetBiomeHeight", (Type[])null, (Type[])null)); foreach (MethodBase item in list.Where((MethodBase m) => m != null)) { Patches patchInfo = Harmony.GetPatchInfo(item); if (patchInfo == null) { continue; } foreach (string owner in patchInfo.Owners) { if (owner != "g3.continents") { Add(owner + " (patches WorldGenerator." + item.Name + ")"); } } } if (HasConflict) { Plugin.Log.LogWarning((object)("Other world generation mods found: " + string.Join(", ", Conflicts) + ". New worlds will NOT get continents.")); } } private static void Add(string what) { if (!Conflicts.Contains(what)) { Conflicts.Add(what); } } [HarmonyPatch(typeof(FejdStartup), "Start")] [HarmonyPostfix] private static void MainMenu(FejdStartup __instance) { if (HasConflict && !s_shown) { ((MonoBehaviour)__instance).StartCoroutine(Show()); } } private static IEnumerator Show() { yield return (object)new WaitForSeconds(1.5f); if (s_shown) { yield break; } s_shown = true; string text = string.Join("\n", Conflicts); string text2 = "Another world generation mod is installed, so new worlds will NOT get continents:\n" + text + "\n\nУстановлен другой мод генерации мира, поэтому новые миры НЕ получат континенты. Уберите один из модов."; object obj = <>c.<>9__8_0; if (obj == null) { PopupButtonCallback val = delegate { UnifiedPopup.Pop(); }; <>c.<>9__8_0 = val; obj = (object)val; } UnifiedPopup.Push((PopupBase)new WarningPopup("G3 Continents", text2, (PopupButtonCallback)obj, false)); } } internal sealed class ShapeParams { public float Warp = 700f; public float WarpScale = 2600f; public float PoleBand = 500f; public float EdgeOcean = 150f; public float Shelf = 220f; public float DeepDistance = 600f; public float LowlandSpan = 0.07f; public float Relief = 0.05f; public float BigLakes = 0.845f; public float BigLakeScale = 1400f; public float SmallLakes = 0.87f; public float SmallLakeScale = 450f; public float BiomeWarp = 600f; public float BiomeWarpScale = 1800f; public float RingWobble = 250f; public float MountainBoost = 0.4f; public ShapeParams Clone() { return (ShapeParams)MemberwiseClone(); } public static ShapeParams ForVersion(int gen) { if (gen == 1) { return new ShapeParams(); } return null; } public bool Set(string key, string value) { FieldInfo[] fields = typeof(ShapeParams).GetFields(BindingFlags.Instance | BindingFlags.Public); foreach (FieldInfo fieldInfo in fields) { if (string.Equals(fieldInfo.Name, key, StringComparison.OrdinalIgnoreCase)) { if (fieldInfo.FieldType == typeof(float) && float.TryParse(value.Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { fieldInfo.SetValue(this, result); return true; } return false; } } return false; } public string Describe(ShapeParams baseline = null) { StringBuilder stringBuilder = new StringBuilder(); FieldInfo[] fields = typeof(ShapeParams).GetFields(BindingFlags.Instance | BindingFlags.Public); foreach (FieldInfo fieldInfo in fields) { object value = fieldInfo.GetValue(this); if (baseline == null || !object.Equals(value, fieldInfo.GetValue(baseline))) { if (stringBuilder.Length > 0) { stringBuilder.Append(' '); } stringBuilder.Append(fieldInfo.Name).Append('=').Append(Convert.ToString(value, CultureInfo.InvariantCulture)); } } return stringBuilder.ToString(); } } internal sealed class ContinentShape { private struct Rng { private ulong _s; public Rng(int seed, int gen) { _s = ((ulong)(uint)seed << 16) ^ (ulong)((uint)gen * -7046029254386353131L) ^ 0xD1B54A32D192ED03uL; } private ulong Next() { ulong num = (_s += 11400714819323198485uL); long num2 = (long)(num ^ (num >> 30)) * -4658895280553007687L; long num3 = (num2 ^ (num2 >>> 27)) * -7723592293110705685L; return (ulong)(num3 ^ (num3 >>> 31)); } public float Float() { return (float)(Next() >> 40) / 16777216f; } public float Range(float a, float b) { return a + (b - a) * Float(); } } private const float WorldRadius = 10000f; private const float InlandFloor = 0.24f; private const float LakeBottom = 0.01f; private const float ShoreSea = 0.12f; private const float DeepSea = -0.14f; private readonly ShapeParams _p; private readonly Vector2[] _offsets = (Vector2[])(object)new Vector2[19]; public ShapeParams Params => _p; public ContinentShape(int seed, int gen, ShapeParams p) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) _p = p; Rng rng = new Rng(seed, gen); for (int i = 0; i < _offsets.Length; i++) { _offsets[i] = new Vector2(rng.Range(0f, 1000f), rng.Range(0f, 1000f)); } } private static float PoleDistance(float x, float y) { float num = WorldGenerator.WorldAngle(x, y) * 100f; float num2 = Len(x, y - 4000f) - (12000f + num); float num3 = Len(x, y + 4000f) - (12000f + num); return Mathf.Max(num2, num3); } public void BiomePoint(float x, float y, out float bx, out float by) { ShapeParams p = _p; float num = Mathf.Clamp01(Len(x, y) / 2000f); float biomeWarpScale = p.BiomeWarpScale; float scale = biomeWarpScale * 0.29f; float scale2 = biomeWarpScale * 0.09f; float scale3 = biomeWarpScale * 0.03f; float num2 = p.BiomeWarp * num; float num3 = num2 * 0.45f; float num4 = num2 * 0.2f; float num5 = num2 * 0.08f; bx = x + num2 * Signed(11, x, y, biomeWarpScale) + num3 * Signed(13, x, y, scale) + num4 * Signed(15, x, y, scale2) + num5 * Signed(17, x, y, scale3); by = y + num2 * Signed(12, x, y, biomeWarpScale) + num3 * Signed(14, x, y, scale) + num4 * Signed(16, x, y, scale2) + num5 * Signed(18, x, y, scale3); } public float RingWobble(float bx, float by) { return _p.RingWobble * Signed(6, bx, by, 1200f); } private float Signed(int k, float x, float y, float scale) { return Noise(k, x / scale, y / scale) * 2f - 1f; } public float Apply(float x, float y, float h) { float num = Len(x, y); if (num >= 10000f) { return h; } float num2 = PoleDistance(x, y); if (num2 >= 0f) { return h; } ShapeParams p = _p; float warpScale = p.WarpScale; float num3 = warpScale * 0.33f; float x2 = x + p.Warp * (Noise(0, x / warpScale, y / warpScale) * 2f - 1f) + p.Warp * 0.35f * (Noise(2, x / num3, y / num3) * 2f - 1f); float y2 = y + p.Warp * (Noise(1, x / warpScale, y / warpScale) * 2f - 1f) + p.Warp * 0.35f * (Noise(3, x / num3, y / num3) * 2f - 1f); float num4 = Noise(5, x / 1500f, y / 1500f) * 2f - 1f; float num5 = Mathf.Min(0f - (PoleDistance(x2, y2) + p.PoleBand + num4 * 300f), 0f - (num2 + 500f)); float num6 = Mathf.Min(10000f - p.EdgeOcean - (Len(x2, y2) + num4 * 150f), 9750f - num); float num7 = Mathf.Min(num5, num6); num7 = Mathf.Max(num7, 900f - num); float num8 = h; if (h < 0.24f) { float num9 = 1f - Mathf.Exp(0f - (0.24f - h) / p.LowlandSpan); num8 = 0.24f - p.LowlandSpan * num9 + (Noise(9, x / 900f, y / 900f) - 0.5f) * p.Relief * num9; } if (h > 0.3f) { num8 += (h - 0.3f) * p.MountainBoost; } float num10 = SmoothStep(p.BigLakes, p.BigLakes + 0.05f, Noise(7, x / p.BigLakeScale, y / p.BigLakeScale)); float num11 = SmoothStep(p.SmallLakes, p.SmallLakes + 0.05f, Noise(8, x / p.SmallLakeScale, y / p.SmallLakeScale)); float num12 = Mathf.Max(num10, num11) * SmoothStep(p.Shelf + 150f, p.Shelf + 500f, num7) * (1f - SmoothStep(0.3f, 0.38f, h)) * SmoothStep(900f, 1400f, num); num8 = Mathf.Lerp(num8, 0.01f, num12); float num13 = SmoothStep(0f, p.Shelf, num7); return Mathf.Lerp(Mathf.Lerp(0.12f, -0.14f, SmoothStep(0f, p.DeepDistance, 0f - num7)), num8, num13); } private float Noise(int k, float x, float y) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) Vector2 val = _offsets[k]; return Mathf.PerlinNoise(val.x + x, val.y + y); } private static float Len(float x, float y) { return Mathf.Sqrt(x * x + y * y); } private static float SmoothStep(float a, float b, float v) { float num = Mathf.Clamp01((v - a) / (b - a)); return num * num * (3f - 2f * num); } } [HarmonyPatch] internal static class LocationCheck { internal sealed class Row { public string Name; public int Placed; public int Wanted; public bool Key; public bool Short => Placed < Wanted; } private static readonly string[] s_key = new string[11] { "StartTemple", "Eikthyrnir", "GDKing", "Bonemass", "Dragonqueen", "GoblinKing", "Mistlands_DvergrBossEntrance1", "FaderLocation", "Vendor_BlackForest", "Hildir_camp", "BogWitch_Camp" }; private const int RareQuantity = 5; internal static bool IsKey(string name) { return s_key.Any((string k) => string.Equals(k, name, StringComparison.OrdinalIgnoreCase)); } internal static List<Row> Rows() { //IL_0050: Unknown result type (might be due to invalid IL or missing references) ZoneSystem instance = ZoneSystem.instance; if ((Object)(object)instance == (Object)null || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return null; } Dictionary<string, int> placed = new Dictionary<string, int>(); foreach (LocationInstance value4 in instance.m_locationInstances.Values) { string text = value4.m_location?.m_prefabName; if (!string.IsNullOrEmpty(text)) { placed.TryGetValue(text, out var value); placed[text] = value + 1; } } Dictionary<string, int> dictionary = new Dictionary<string, int>(); foreach (ZoneLocation location in instance.m_locations) { if (location != null && location.m_enable && location.m_quantity > 0 && !string.IsNullOrEmpty(location.m_prefabName)) { dictionary.TryGetValue(location.m_prefabName, out var value2); dictionary[location.m_prefabName] = value2 + location.m_quantity; } } int value3; return (from w in dictionary select new Row { Name = w.Key, Wanted = w.Value, Placed = (placed.TryGetValue(w.Key, out value3) ? value3 : 0), Key = IsKey(w.Key) } into r orderby r.Key && r.Short descending, r.Key descending, r.Short descending, r.Name select r).ToList(); } internal static List<Preview.Marker> Markers() { List<Row> list = Rows(); if (list == null) { return null; } HashSet<string> shown = new HashSet<string>(from r in list where r.Key || r.Wanted <= 5 select r.Name); return (from li in ZoneSystem.instance.m_locationInstances.Values where li.m_location != null && shown.Contains(li.m_location.m_prefabName) select new Preview.Marker { Name = li.m_location.m_prefabName, X = li.m_position.x, Z = li.m_position.z, Key = IsKey(li.m_location.m_prefabName) }).ToList(); } internal static string Report(out int keyMissing, out int shortCount) { keyMissing = 0; shortCount = 0; List<Row> list = Rows(); if (list == null) { return string.Empty; } StringBuilder stringBuilder = new StringBuilder(); foreach (Row item in list) { if (item.Key && item.Short) { keyMissing++; } if (item.Short) { shortCount++; } } World val = WorldGenerator.instance?.m_world; stringBuilder.AppendLine($"G3 Continents location check, world '{val?.m_name}', seed '{val?.m_seedName}', generator {WorldGenPatch.GenOf(val)}"); stringBuilder.AppendLine((keyMissing > 0) ? $"PROBLEM: {keyMissing} key location(s) not placed in full. Make the world again with another seed." : "Key locations: all placed."); stringBuilder.AppendLine($"Locations placed short of their quantity: {shortCount}."); stringBuilder.AppendLine(); foreach (Row item2 in list) { string text = (item2.Key ? (item2.Short ? "!! " : "ok ") : (item2.Short ? " - " : " ")); stringBuilder.AppendLine($"{text}{item2.Name}: {item2.Placed}/{item2.Wanted}"); } return stringBuilder.ToString(); } [HarmonyPatch(typeof(ZoneSystem), "Start")] [HarmonyPostfix] private static void Watch(ZoneSystem __instance) { if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) { __instance.GenerateLocationsCompleted += OnLocations; } } private static void OnLocations() { try { if (WorldGenPatch.GenOf(WorldGenerator.instance?.m_world) <= 0) { return; } int keyMissing; int shortCount; string text = Report(out keyMissing, out shortCount); if (text.Length != 0) { Directory.CreateDirectory(Preview.Folder); World world = WorldGenerator.instance.m_world; string text2 = Path.Combine(Preview.Folder, "locations_" + Preview.Safe(world.m_name) + ".txt"); File.WriteAllText(text2, text, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); if (keyMissing > 0) { Plugin.Log.LogWarning((object)$"{keyMissing} key location(s) missing in world '{world.m_name}', see {text2}"); } else { Plugin.Log.LogInfo((object)$"Locations checked: key ones all placed, {shortCount} other(s) short. Report: {text2}"); } } } catch (Exception ex) { Plugin.Log.LogError((object)("Location check failed: " + ex)); } } } [HarmonyPatch] internal static class NetGate { private sealed class Info { public string Version; public int Gen; } private const string Hello = "G3C_Hello"; private const int ErrorVersion = 3; private static readonly ConditionalWeakTable<ZRpc, Info> s_hello = new ConditionalWeakTable<ZRpc, Info>(); [HarmonyPatch(typeof(ZNet), "OnNewConnection")] [HarmonyPostfix] private static void Connected(ZNet __instance, ZNetPeer peer) { if (__instance.IsServer()) { peer.m_rpc.Register<string, int>("G3C_Hello", (Action<ZRpc, string, int>)delegate(ZRpc rpc, string version, int gen) { s_hello.Remove(rpc); s_hello.Add(rpc, new Info { Version = version, Gen = gen }); }); } else { peer.m_rpc.Invoke("G3C_Hello", new object[2] { "1.0.0", 1 }); } } [HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")] [HarmonyPrefix] private static bool PeerInfo(ZNet __instance, ZRpc rpc) { if (!__instance.IsServer()) { return true; } int num = WorldGenPatch.GenOf(WorldGenerator.instance?.m_world); if (num <= 0) { return true; } ISocket socket = rpc.GetSocket(); string text = ((socket != null) ? socket.GetEndPointString() : null) ?? "?"; if (!s_hello.TryGetValue(rpc, out var value)) { Plugin.Log.LogWarning((object)("Turned away " + text + ": this world has continents and the player has no G3 Continents.")); rpc.Invoke("Error", new object[1] { 3 }); return false; } if (value.Gen < num) { Plugin.Log.LogWarning((object)$"Turned away {text}: G3 Continents {value.Version} knows generators up to {value.Gen}, this world uses {num}."); rpc.Invoke("Error", new object[1] { 3 }); return false; } return true; } } [BepInPlugin("g3.continents", "G3 Continents", "1.0.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { public const string PluginGUID = "g3.continents"; public const string PluginName = "G3 Continents"; public const string PluginVersion = "1.0.0"; internal static Plugin Instance; internal static ManualLogSource Log; internal static ConfigEntry<bool> NewWorlds; private Harmony _harmony; private void Awake() { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; NewWorlds = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "NewWorldsWithContinents", true, "New worlds made while this is on get continents. The choice is saved in the world: existing worlds never change, either way."); _harmony = new Harmony("g3.continents"); _harmony.PatchAll(typeof(Plugin).Assembly); Commands.Register(); Log.LogInfo((object)string.Format("{0} {1} loaded, generator {2}.", "G3 Continents", "1.0.0", 1)); } private void Start() { Compat.Check(); } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } internal static class Preview { internal sealed class Job { public string Seed; public bool Vanilla; public ShapeParams Custom; public int Size = 1024; } internal sealed class Marker { public string Name; public float X; public float Z; public bool Key; } private sealed class MapData { public Color32[] Pixels; public string StatsJson; public string Summary; } public const string WorldName = "g3c_preview"; private const float Span = 21000f; private const float WaterLevel = 30f; private static readonly ConstructorInfo s_genCtor = AccessTools.Constructor(typeof(WorldGenerator), new Type[1] { typeof(World) }, false); private static readonly Dictionary<Biome, Color> s_colors = new Dictionary<Biome, Color> { { (Biome)1, new Color(0.56f, 0.71f, 0.33f) }, { (Biome)8, new Color(0.25f, 0.42f, 0.22f) }, { (Biome)2, new Color(0.48f, 0.4f, 0.3f) }, { (Biome)4, new Color(0.9f, 0.91f, 0.94f) }, { (Biome)16, new Color(0.86f, 0.78f, 0.42f) }, { (Biome)512, new Color(0.42f, 0.36f, 0.47f) }, { (Biome)32, new Color(0.6f, 0.22f, 0.16f) }, { (Biome)64, new Color(0.78f, 0.86f, 0.94f) }, { (Biome)256, new Color(0.8f, 0.75f, 0.55f) } }; private static readonly Biome[] s_biomeOrder; internal static string Folder => Path.Combine(Paths.BepInExRootPath, "G3Continents", "maps"); internal static string ViewerPath => Path.Combine(Folder, "viewer.html"); internal static bool Busy { get; private set; } internal static IEnumerator Run(List<Job> jobs, Terminal ctx, bool open) { Busy = true; string last = null; try { Directory.CreateDirectory(Folder); for (int i = 0; i < jobs.Count; i++) { Job job = jobs[i]; string text = (job.Vanilla ? "vanilla" : ((job.Custom != null) ? "custom" : ("continents " + 1))); Say(ctx, $"G3 Continents: map {i + 1}/{jobs.Count}, seed '{job.Seed}' ({text})..."); yield return null; World val = new World("g3c_preview", job.Seed); val.m_worldGenVersion = (job.Vanilla ? 2 : 1002); if (!job.Vanilla && job.Custom != null) { WorldGenPatch.PreviewParams.Add(val, job.Custom); } Stopwatch watch = Stopwatch.StartNew(); WorldGenerator gen = (WorldGenerator)s_genCtor.Invoke(new object[1] { val }); double pregen = watch.Elapsed.TotalSeconds; MapData data = null; Task task = Task.Run(() => data = Render(gen, job.Size, null)); while (!task.IsCompleted) { yield return null; } if (task.Exception != null) { Plugin.Log.LogError((object)("Map failed: " + task.Exception)); Say(ctx, "G3 Continents: the map failed, see the log."); continue; } double num = watch.Elapsed.TotalSeconds - pregen; string text2 = FileName(job.Seed, job.Vanilla ? "vanilla" : ("g" + 1), job.Custom); SavePng(data.Pixels, job.Size, Path.Combine(Folder, text2 + ".png")); ContinentShape shape = (job.Vanilla ? null : WorldGenPatch.ShapeOf(gen)); WriteJson(text2, job.Seed, (!job.Vanilla) ? 1 : 0, (job.Custom != null) ? "custom" : (job.Vanilla ? "vanilla" : "continents"), shape, job.Custom, job.Size, pregen, num, data.StatsJson, null, null, gen); last = text2; Say(ctx, $"G3 Continents: {text2}.png in {pregen + num:0.0} s. {data.Summary}"); } WriteIndex(); if (last != null) { Say(ctx, "G3 Continents: maps are in " + Folder); if (open) { Application.OpenURL(new Uri(ViewerPath).AbsoluteUri + "#" + last); } } } finally { Busy = false; } } internal static IEnumerator ExportCurrent(Terminal ctx, int size, bool open) { WorldGenerator gen = WorldGenerator.instance; World world = gen?.m_world; if (world == null) { Say(ctx, "G3 Continents: no world is loaded."); yield break; } Busy = true; try { Directory.CreateDirectory(Folder); List<LocationCheck.Row> rows = LocationCheck.Rows(); List<Marker> markers = LocationCheck.Markers(); Say(ctx, "G3 Continents: drawing world '" + world.m_name + "'..."); yield return null; Stopwatch watch = Stopwatch.StartNew(); MapData data = null; Task task = Task.Run(() => data = Render(gen, size, markers)); while (!task.IsCompleted) { yield return null; } if (task.Exception != null) { Plugin.Log.LogError((object)("Map failed: " + task.Exception)); Say(ctx, "G3 Continents: the map failed, see the log."); yield break; } int gen2 = WorldGenPatch.GenOf(world); string text = FileName(world.m_seedName, "world_" + Safe(world.m_name), null); SavePng(data.Pixels, size, Path.Combine(Folder, text + ".png")); WriteJson(text, world.m_seedName, gen2, "world " + world.m_name, WorldGenPatch.ShapeOf(gen), null, size, 0.0, watch.Elapsed.TotalSeconds, data.StatsJson, markers, rows, gen); WriteIndex(); Say(ctx, "G3 Continents: " + text + ".png. " + data.Summary); if (rows == null) { Say(ctx, "G3 Continents: locations are known only where the world runs (host or server), so the map has none."); } if (open) { Application.OpenURL(new Uri(ViewerPath).AbsoluteUri + "#" + text); } } finally { Busy = false; } } private static void Say(Terminal ctx, string text) { Plugin.Log.LogInfo((object)text); if (ctx != null) { ctx.AddString(text); } } internal static string Safe(string s) { StringBuilder stringBuilder = new StringBuilder(); string text = s ?? string.Empty; foreach (char c in text) { stringBuilder.Append((c < '\u0080' && char.IsLetterOrDigit(c)) ? c : '_'); } if (stringBuilder.Length != 0) { return stringBuilder.ToString(); } return "_"; } private static string FileName(string seed, string mode, ShapeParams custom) { string text = $"{Safe(seed)}_{StringExtensionMethods.GetStableHashCode(seed) & 0xFFFF:x4}_{mode}"; if (custom != null) { text += $"_c{StringExtensionMethods.GetStableHashCode(custom.Describe()) & 0xFFFF:x4}"; } return text; } private static void SavePng(Color32[] pixels, int size, string path) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Expected O, but got Unknown Texture2D val = new Texture2D(size, size, (TextureFormat)3, false); val.SetPixels32(pixels); val.Apply(false); File.WriteAllBytes(path, ImageConversion.EncodeToPNG(val)); Object.Destroy((Object)(object)val); } private static MapData Render(WorldGenerator gen, int size, List<Marker> markers) { //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) float m = 21000f / (float)size; float[] heights = new float[size * size]; Biome[] biomes = (Biome[])(object)new Biome[size * size]; Parallel.For(0, size, delegate(int row) { //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Expected I4, but got Unknown //IL_0090: Unknown result type (might be due to invalid IL or missing references) float num = ((float)row + 0.5f) * m - 10500f; Color val2 = default(Color); for (int i = 0; i < size; i++) { float num2 = ((float)i + 0.5f) * m - 10500f; int num3 = row * size + i; if (num2 * num2 + num * num > 110250000f) { biomes[num3] = (Biome)0; heights[num3] = -400f; } else { Biome biome = gen.GetBiome(num2, num, 0.02f, false); biomes[num3] = (Biome)(int)biome; heights[num3] = gen.GetBiomeHeight(biome, num2, num, ref val2, false, true); } } }); Color32[] pixels = (Color32[])(object)new Color32[size * size]; Vector3 val = new Vector3(-1f, 1f, 1.3f); Vector3 light = ((Vector3)(ref val)).normalized; float flat = light.z; Color shallow = new Color(0.33f, 0.58f, 0.72f); Color deep = new Color(0.07f, 0.19f, 0.38f); Color outside = new Color(0.07f, 0.09f, 0.12f); Parallel.For(0, size, delegate(int row) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_0170: Unknown result type (might be due to invalid IL or missing references) //IL_01b8: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_01be: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < size; i++) { int num = row * size + i; if ((int)biomes[num] == 0) { pixels[num] = Color32.op_Implicit(outside); } else { float num2 = heights[num]; Color val2; if (num2 < 30f) { val2 = Color.Lerp(shallow, deep, Mathf.Clamp01((30f - num2) / 45f)); } else { float num3 = heights[row * size + Mathf.Max(i - 1, 0)]; float num4 = heights[row * size + Mathf.Min(i + 1, size - 1)]; float num5 = heights[Mathf.Max(row - 1, 0) * size + i]; float num6 = heights[Mathf.Min(row + 1, size - 1) * size + i]; Vector3 val3 = new Vector3((0f - (num4 - num3)) / (2f * m), (0f - (num6 - num5)) / (2f * m), 1f); Vector3 normalized = ((Vector3)(ref val3)).normalized; float num7 = Mathf.Clamp(1f + (Vector3.Dot(normalized, light) / flat - 1f) * 1.8f, 0.55f, 1.3f); val2 = (s_colors.TryGetValue(biomes[num], out var value) ? value : Color.magenta) * num7; val2 = Color.Lerp(val2, Color.white, Mathf.Clamp01((num2 - 60f) / 400f) * 0.25f); } val2.a = 1f; pixels[num] = Color32.op_Implicit(val2); } } }); return Stats(gen, size, m, heights, biomes, pixels); } private static MapData Stats(WorldGenerator gen, int size, float m, float[] heights, Biome[] biomes, Color32[] pixels) { //IL_01f1: Unknown result type (might be due to invalid IL or missing references) //IL_01f6: Unknown result type (might be due to invalid IL or missing references) //IL_01f9: Unknown result type (might be due to invalid IL or missing references) //IL_02df: Unknown result type (might be due to invalid IL or missing references) //IL_02ed: Unknown result type (might be due to invalid IL or missing references) //IL_02f0: Invalid comparison between Unknown and I4 //IL_0333: Unknown result type (might be due to invalid IL or missing references) //IL_0337: Unknown result type (might be due to invalid IL or missing references) //IL_03d4: Unknown result type (might be due to invalid IL or missing references) //IL_035d: Unknown result type (might be due to invalid IL or missing references) float num = 10500f; int num2 = size * size; bool[] array = new bool[num2]; bool[] array2 = new bool[num2]; int num3 = 0; int num4 = 0; Dictionary<Biome, int> dictionary = new Dictionary<Biome, int>(); for (int i = 0; i < size; i++) { float num5 = ((float)i + 0.5f) * m - num; for (int j = 0; j < size; j++) { float num6 = ((float)j + 0.5f) * m - num; int num7 = i * size + j; if (!(num6 * num6 + num5 * num5 > 100000000f)) { array[num7] = true; num3++; if (heights[num7] >= 30f) { array2[num7] = true; num4++; dictionary.TryGetValue(biomes[num7], out var value); dictionary[biomes[num7]] = value + 1; } } } } float km2 = m * m / 1000000f; List<int> list = Components(size, array2, null); list.Sort((int a, int b) => b.CompareTo(a)); int num8 = list.Count((int a) => (float)a * km2 >= 25f); int num9 = list.Count((int a) => (float)a * km2 >= 0.05f && (float)a * km2 < 25f); float v = ((list.Count > 0) ? ((float)list[0] * km2) : 0f); bool[] array3 = new bool[num2]; for (int num10 = 0; num10 < num2; num10++) { array3[num10] = array[num10] && !array2[num10]; } bool[] array4 = new bool[num2]; FloodFromEdge(size, m, array3, array4); bool[] array5 = new bool[num2]; for (int num11 = 0; num11 < num2; num11++) { array5[num11] = array3[num11] && !array4[num11]; } int num12 = Components(size, array5, null).Count((int a) => (float)a * km2 >= 0.1f); Biome biome = gen.GetBiome(0f, 0f, 0.02f, false); Color val = default(Color); float biomeHeight = gen.GetBiomeHeight(biome, 0f, 0f, ref val, false, true); int num13 = gen.GetRivers()?.Count ?? 0; int num14 = gen.GetLakes()?.Count ?? 0; StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append('{'); stringBuilder.Append("\"land\":" + Pct(num4, num3) + ","); stringBuilder.Append($"\"continents\":{num8},\"islands\":{num9},\"largestKm2\":{F(v)},\"landKm2\":{F((float)num4 * km2)},"); stringBuilder.Append($"\"inlandWaters\":{num12},\"rivers\":{num13},\"lakePoints\":{num14},"); stringBuilder.Append(string.Format("\"center\":{{\"biome\":\"{0}\",\"height\":{1},\"ok\":{2}}},", biome, F(biomeHeight), ((int)biome == 1 && biomeHeight >= 30f) ? "true" : "false")); stringBuilder.Append("\"biomes\":{"); bool flag = true; Biome[] array6 = s_biomeOrder; foreach (Biome val2 in array6) { dictionary.TryGetValue(val2, out var value2); if (value2 != 0) { if (!flag) { stringBuilder.Append(','); } flag = false; stringBuilder.Append($"\"{val2}\":{Pct(value2, num4)}"); } } stringBuilder.Append("}}"); string summary = $"Land {Pct(num4, num3)}%, continents {num8}, islets {num9}, inland waters {num12}, rivers {num13}, center {biome}."; return new MapData { Pixels = pixels, StatsJson = stringBuilder.ToString(), Summary = summary }; } private static List<int> Components(int size, bool[] mask, bool[] seen) { seen = seen ?? new bool[mask.Length]; List<int> list = new List<int>(); Stack<int> stack = new Stack<int>(); for (int i = 0; i < mask.Length; i++) { if (mask[i] && !seen[i]) { int num = 0; seen[i] = true; stack.Push(i); while (stack.Count > 0) { int num2 = stack.Pop(); num++; int num3 = num2 / size; int num4 = num2 % size; Visit((num4 > 0) ? (num2 - 1) : (-1)); Visit((num4 < size - 1) ? (num2 + 1) : (-1)); Visit((num3 > 0) ? (num2 - size) : (-1)); Visit((num3 < size - 1) ? (num2 + size) : (-1)); } list.Add(num); } } return list; void Visit(int j) { if (j >= 0 && mask[j] && !seen[j]) { seen[j] = true; stack.Push(j); } } } private static void FloodFromEdge(int size, float m, bool[] water, bool[] reached) { float num = 10500f; Stack<int> stack = new Stack<int>(); for (int i = 0; i < size; i++) { float num2 = ((float)i + 0.5f) * m - num; for (int j = 0; j < size; j++) { float num3 = ((float)j + 0.5f) * m - num; int num4 = i * size + j; float num5 = Mathf.Sqrt(num3 * num3 + num2 * num2); if (water[num4] && num5 > 10000f - 2f * m && !reached[num4]) { reached[num4] = true; stack.Push(num4); } } } while (stack.Count > 0) { int num6 = stack.Pop(); int num7 = num6 / size; int num8 = num6 % size; Push((num8 > 0) ? (num6 - 1) : (-1)); Push((num8 < size - 1) ? (num6 + 1) : (-1)); Push((num7 > 0) ? (num6 - size) : (-1)); Push((num7 < size - 1) ? (num6 + size) : (-1)); } void Push(int num9) { if (num9 >= 0 && water[num9] && !reached[num9]) { reached[num9] = true; stack.Push(num9); } } } private static string Pct(int part, int whole) { return F((whole > 0) ? (100f * (float)part / (float)whole) : 0f); } private static string F(float v) { return v.ToString("0.##", CultureInfo.InvariantCulture); } private static string Str(string s) { StringBuilder stringBuilder = new StringBuilder("\""); string text = s ?? string.Empty; foreach (char c in text) { if (c == '"' || c == '\\') { stringBuilder.Append('\\').Append(c); } else if (c < ' ') { stringBuilder.Append($"\\u{(int)c:x4}"); } else { stringBuilder.Append(c); } } return stringBuilder.Append('"').ToString(); } private static void WriteJson(string name, string seed, int gen, string mode, ContinentShape shape, ShapeParams custom, int size, double pregen, double render, string stats, List<Marker> markers, List<LocationCheck.Row> rows, WorldGenerator wg) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append('{'); stringBuilder.Append(string.Format("\"file\":{0},\"name\":{1},\"seed\":{2},\"gen\":{3},\"mode\":{4},", Str(name + ".png"), Str(name), Str(seed), gen, Str(mode))); stringBuilder.Append("\"made\":" + Str(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture)) + ",\"plugin\":" + Str("1.0.0") + ","); stringBuilder.Append($"\"size\":{size},\"span\":{F(21000f)},\"seconds\":{{\"layout\":{F((float)pregen)},\"draw\":{F((float)render)}}},"); stringBuilder.Append("\"params\":" + Str((shape == null) ? string.Empty : ((custom != null) ? custom.Describe(ShapeParams.ForVersion(1)) : string.Empty)) + ","); stringBuilder.Append("\"stats\":").Append(stats).Append(','); stringBuilder.Append("\"rivers\":["); List<River> rivers = wg.GetRivers(); if (rivers != null) { for (int i = 0; i < rivers.Count; i++) { River val = rivers[i]; stringBuilder.Append((i > 0) ? "," : string.Empty).Append("[" + F(val.p0.x) + "," + F(val.p0.y) + "," + F(val.p1.x) + "," + F(val.p1.y) + "]"); } } stringBuilder.Append("],\"markers\":["); if (markers != null) { for (int j = 0; j < markers.Count; j++) { Marker marker = markers[j]; stringBuilder.Append((j > 0) ? "," : string.Empty).Append("{\"n\":" + Str(marker.Name) + ",\"x\":" + F(marker.X) + ",\"z\":" + F(marker.Z) + ",\"key\":" + (marker.Key ? "true" : "false") + "}"); } } stringBuilder.Append("],\"locations\":"); if (rows == null) { stringBuilder.Append("null"); } else { stringBuilder.Append('['); for (int k = 0; k < rows.Count; k++) { LocationCheck.Row row = rows[k]; stringBuilder.Append((k > 0) ? "," : string.Empty).Append(string.Format("{{\"n\":{0},\"placed\":{1},\"wanted\":{2},\"key\":{3}}}", Str(row.Name), row.Placed, row.Wanted, row.Key ? "true" : "false")); } stringBuilder.Append(']'); } stringBuilder.Append('}'); File.WriteAllText(Path.Combine(Folder, name + ".json"), stringBuilder.ToString(), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); } internal static void WriteIndex() { Directory.CreateDirectory(Folder); List<FileInfo> list = (from f in new DirectoryInfo(Folder).GetFiles("*.json") orderby f.LastWriteTimeUtc descending select f).ToList(); StringBuilder stringBuilder = new StringBuilder("window.G3C_MAPS = [\n"); for (int num = 0; num < list.Count; num++) { stringBuilder.Append(File.ReadAllText(list[num].FullName, Encoding.UTF8)).Append((num < list.Count - 1) ? ",\n" : "\n"); } stringBuilder.Append("];\n"); File.WriteAllText(Path.Combine(Folder, "maps.js"), stringBuilder.ToString(), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); using Stream stream = typeof(Preview).Assembly.GetManifestResourceStream("G3Continents.viewer.html"); using StreamReader streamReader = new StreamReader(stream, Encoding.UTF8); File.WriteAllText(ViewerPath, streamReader.ReadToEnd(), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); } static Preview() { //IL_003e: 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_0074: Unknown result type (might be due to invalid IL or missing references) //IL_008f: 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_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) Biome[] array = new Biome[9]; RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/); s_biomeOrder = (Biome[])(object)array; } } [HarmonyPatch] internal static class WorldGenPatch { private sealed class Holder { public ContinentShape Shape; } private sealed class Last { public WorldGenerator Gen; public ContinentShape Shape; } public const int GenStep = 1000; public const int CurrentGen = 1; private static readonly ConditionalWeakTable<WorldGenerator, Holder> s_shapes = new ConditionalWeakTable<WorldGenerator, Holder>(); private static Last s_last = new Last(); internal static readonly ConditionalWeakTable<World, ShapeParams> PreviewParams = new ConditionalWeakTable<World, ShapeParams>(); [ThreadStatic] private static WorldGenerator t_lastGen; [ThreadStatic] private static float t_lastX; [ThreadStatic] private static float t_lastY; [ThreadStatic] private static float t_lastH; private static readonly FieldRef<WorldGenerator, float> s_offset0 = AccessTools.FieldRefAccess<WorldGenerator, float>("m_offset0"); private static readonly FieldRef<WorldGenerator, float> s_offset1 = AccessTools.FieldRefAccess<WorldGenerator, float>("m_offset1"); private static readonly FieldRef<WorldGenerator, float> s_offset2 = AccessTools.FieldRefAccess<WorldGenerator, float>("m_offset2"); private static readonly FieldRef<WorldGenerator, float> s_offset4 = AccessTools.FieldRefAccess<WorldGenerator, float>("m_offset4"); private static readonly FieldRef<WorldGenerator, float> s_maxMarsh = AccessTools.FieldRefAccess<WorldGenerator, float>("maxMarshDistance"); private static readonly FieldRef<WorldGenerator, float> s_minDarkland = AccessTools.FieldRefAccess<WorldGenerator, float>("minDarklandNoise"); private static readonly Func<WorldGenerator, float, float, bool, float> s_baseHeight = AccessTools.MethodDelegate<Func<WorldGenerator, float, float, bool, float>>(AccessTools.Method(typeof(WorldGenerator), "GetBaseHeight", (Type[])null, (Type[])null), (object)null, true); internal static int GenOf(World world) { if (world != null && !world.m_menu) { return world.m_worldGenVersion / 1000; } return 0; } internal static ContinentShape ShapeOf(WorldGenerator gen) { Last last = s_last; if (last.Gen == gen) { return last.Shape; } ContinentShape shape = s_shapes.GetValue(gen, Create).Shape; s_last = new Last { Gen = gen, Shape = shape }; return shape; } private static Holder Create(WorldGenerator gen) { Holder holder = new Holder(); World world = gen.m_world; int num = GenOf(world); if (num <= 0) { return holder; } ShapeParams value; ShapeParams shapeParams = (PreviewParams.TryGetValue(world, out value) ? value : ShapeParams.ForVersion(num)); if (shapeParams == null) { Plugin.Log.LogError((object)$"World '{world.m_name}' was made by G3 Continents generator {num}, which this build does not know: update G3 Continents. The terrain will not match the world."); return holder; } if (Compat.HasConflict && world.m_name != "g3c_preview") { Plugin.Log.LogError((object)("World '" + world.m_name + "' has continents, but another world generation mod is installed (" + string.Join(", ", Compat.Conflicts) + "): the terrain may come out wrong. Remove that mod to play this world.")); } holder.Shape = new ContinentShape(world.m_seed, num, shapeParams); return holder; } [HarmonyPatch(typeof(WorldGenerator), "GetBaseHeight")] [HarmonyPrefix] private static bool BaseHeightCached(WorldGenerator __instance, float wx, float wy, bool menuTerrain, ref float __result, out bool __state) { __state = !menuTerrain && t_lastGen == __instance && t_lastX == wx && t_lastY == wy && t_lastGen != null; if (__state) { __result = t_lastH; return false; } return true; } [HarmonyPatch(typeof(WorldGenerator), "GetBaseHeight")] [HarmonyPostfix] private static void BaseHeight(WorldGenerator __instance, float wx, float wy, bool menuTerrain, ref float __result, bool __state) { if (!(menuTerrain || __state)) { ContinentShape continentShape = ShapeOf(__instance); if (continentShape != null) { __result = continentShape.Apply(wx, wy, __result); t_lastGen = __instance; t_lastX = wx; t_lastY = wy; t_lastH = __result; } } } [HarmonyPatch(typeof(WorldGenerator), "GetBiome", new Type[] { typeof(float), typeof(float), typeof(float), typeof(bool) })] [HarmonyPrefix] private static bool Biome(WorldGenerator __instance, float wx, float wy, float oceanLevel, bool waterAlwaysOcean, ref Biome __result) { if (__instance.m_world.m_menu) { return true; } ContinentShape continentShape = ShapeOf(__instance); if (continentShape == null) { return true; } float num = s_baseHeight(__instance, wx, wy, arg4: false); if (waterAlwaysOcean && __instance.GetHeight(wx, wy) <= oceanLevel) { __result = (Biome)256; return false; } if (WorldGenerator.IsAshlands(wx, wy)) { __result = (Biome)32; return false; } if (!waterAlwaysOcean && num <= oceanLevel) { __result = (Biome)256; return false; } if (WorldGenerator.IsDeepnorth(wx, wy)) { __result = (Biome)64; return false; } if (num > 0.4f) { __result = (Biome)4; return false; } continentShape.BiomePoint(wx, wy, out var bx, out var by); float num2 = DUtils.Length(bx, by); float num3 = continentShape.RingWobble(bx, by); float num4 = s_offset0.Invoke(__instance); float num5 = s_offset1.Invoke(__instance); float num6 = s_offset2.Invoke(__instance); float num7 = s_offset4.Invoke(__instance); if (DUtils.PerlinNoise((double)(float)((double)num4 + (double)bx) * 0.0010000000474974513, (double)(float)((double)num4 + (double)by) * 0.0010000000474974513) > 0.6f && num2 > 2000f && num2 < s_maxMarsh.Invoke(__instance) && num > 0.05f && num < 0.25f) { __result = (Biome)2; } else if (DUtils.PerlinNoise((double)(float)((double)num7 + (double)bx) * 0.0010000000474974513, (double)(float)((double)num7 + (double)by) * 0.0010000000474974513) > s_minDarkland.Invoke(__instance) && num2 > 6000f + num3 && num2 < 10000f) { __result = (Biome)512; } else if (DUtils.PerlinNoise((double)(float)((double)num5 + (double)bx) * 0.0010000000474974513, (double)(float)((double)num5 + (double)by) * 0.0010000000474974513) > 0.4f && num2 > 3000f + num3 && num2 < 8000f) { __result = (Biome)16; } else if (DUtils.PerlinNoise((double)(float)((double)num6 + (double)bx) * 0.0010000000474974513, (double)(float)((double)num6 + (double)by) * 0.0010000000474974513) > 0.4f && num2 > 600f + num3 && num2 < 6000f) { __result = (Biome)8; } else { __result = (Biome)((!(num2 > 5000f + num3)) ? 1 : 8); } return false; } [HarmonyPatch(typeof(WorldGenerator), "VersionSetup")] [HarmonyPrefix] private static void VanillaVersion(ref int version) { if (version >= 1000) { version %= 1000; } } [HarmonyPatch(/*Could not decode attribute arguments.*/)] [HarmonyPostfix] private static void NewWorld(World __instance, string name) { if (Plugin.NewWorlds == null || !Plugin.NewWorlds.Value) { return; } switch (name) { case "editor": return; case "g3c_preview": return; } if (Compat.HasConflict) { Plugin.Log.LogWarning((object)("New world '" + name + "' stays vanilla: another world generation mod is installed (" + string.Join(", ", Compat.Conflicts) + ").")); } else { __instance.m_worldGenVersion = 1000 + __instance.m_worldGenVersion % 1000; Plugin.Log.LogInfo((object)$"New world '{name}' (seed '{__instance.m_seedName}') gets continents, generator {1}."); } } } }