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 PortalPreview v1.5.6
PortalPreview.dll
Decompiled 2 days ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.Compression; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.ExceptionServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security.Cryptography; using System.Text; using System.Threading; using System.Threading.Tasks; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using SoftReferenceableAssets; using Unity.Collections; using UnityEngine; using UnityEngine.Experimental.Rendering; using UnityEngine.PostProcessing; using UnityEngine.Rendering; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: InternalsVisibleTo("PortalPreviewProbe")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("PortalPreview")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.5.6.0")] [assembly: AssemblyInformationalVersion("1.5.6+3e51051fb4da1e6202f7bb5ba1ab7ef6b608a2b5")] [assembly: AssemblyProduct("PortalPreview")] [assembly: AssemblyTitle("PortalPreview")] [assembly: AssemblyVersion("1.5.6.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 PortalPreview { internal sealed class ArchiveBlobPrefetch : IDisposable { private const long MemoryBudget = 100663296L; private const int ScanLimit = 1048576; private readonly object _gate = new object(); private readonly Func<string, Stream> _read; private readonly Queue<string> _ahead = new Queue<string>(); private readonly Queue<string> _demand = new Queue<string>(); private readonly HashSet<string> _discovered = new HashSet<string>(); private readonly HashSet<string> _queued = new HashSet<string>(); private readonly HashSet<string> _demanded = new HashSet<string>(); private readonly HashSet<string> _working = new HashSet<string>(); private readonly HashSet<string> _discarded = new HashSet<string>(); private readonly Dictionary<string, Stream> _ready = new Dictionary<string, Stream>(); private readonly Dictionary<string, Exception> _errors = new Dictionary<string, Exception>(); private long _buffered; private bool _stopped; internal long RetainedBytes { get { lock (_gate) { return _buffered; } } } private bool Full => _buffered >= 100663296; internal bool Idle { get { lock (_gate) { return _stopped || (_queued.Count == 0 && _working.Count == 0) || Full; } } } internal ArchiveBlobPrefetch(byte[] manifest, Func<string, Stream> read, int workers = 0) { _read = read; EnqueueReferences(manifest, manifest.Length); workers = ((workers > 0) ? Math.Min(6, workers) : Math.Max(1, Math.Min(6, Environment.ProcessorCount - 2))); for (int i = 0; i < workers; i++) { Thread thread = new Thread(Run); thread.IsBackground = true; thread.Name = "PortalPreview blob prefetch " + i; thread.Start(); } } internal bool TryTake(string hash, out Stream result) { lock (_gate) { result = null; if (_errors.TryGetValue(hash, out var value)) { throw value; } if (_ready.TryGetValue(hash, out result)) { _ready.Remove(hash); _buffered -= Retained(result); Monitor.PulseAll(_gate); return true; } if (_stopped) { throw new ObjectDisposedException("ArchiveBlobPrefetch"); } if (_working.Contains(hash)) { return false; } while (Full && _ready.Count > 0) { string key = null; using (Dictionary<string, Stream>.KeyCollection.Enumerator enumerator = _ready.Keys.GetEnumerator()) { if (enumerator.MoveNext()) { key = enumerator.Current; } } Stream stream = _ready[key]; _ready.Remove(key); _buffered -= Retained(stream); stream.Dispose(); } _discarded.Remove(hash); _queued.Add(hash); if (_demanded.Add(hash)) { _demand.Enqueue(hash); } Monitor.PulseAll(_gate); return false; } } internal void Discard(string hash) { lock (_gate) { _discarded.Add(hash); _queued.Remove(hash); _errors.Remove(hash); if (_ready.TryGetValue(hash, out var value)) { _ready.Remove(hash); _buffered -= Retained(value); value.Dispose(); } Monitor.PulseAll(_gate); } } private static long Retained(Stream stream) { return stream.Length; } private void Run() { while (true) { string text; lock (_gate) { while (!_stopped && (_queued.Count == 0 || Full)) { Monitor.Wait(_gate); } if (_stopped) { break; } Queue<string> queue = ((_demand.Count > 0) ? _demand : _ahead); if (queue.Count != 0) { text = queue.Dequeue(); _demanded.Remove(text); if (_queued.Remove(text) && !_discarded.Contains(text)) { _working.Add(text); goto IL_00c3; } } } continue; IL_00c3: Stream stream = null; Exception ex = null; try { stream = _read(text); } catch (Exception ex2) { stream?.Dispose(); stream = null; ex = ex2; } lock (_gate) { _working.Remove(text); if (_stopped || _discarded.Contains(text)) { stream?.Dispose(); } else if (ex != null) { _errors[text] = ex; } else { _ready.Add(text, stream); _buffered += Retained(stream); if (stream.Length <= 1048576) { if (stream is MemoryStream memoryStream && memoryStream.TryGetBuffer(out var buffer) && buffer.Offset == 0) { EnqueueReferences(buffer.Array, (int)stream.Length); } else { byte[] array = new byte[(int)stream.Length]; int i; int num; for (i = 0; i < array.Length; i += num) { if ((num = stream.Read(array, i, array.Length - i)) <= 0) { break; } } stream.Position = 0L; EnqueueReferences(array, i); } } } Monitor.PulseAll(_gate); } } } private void EnqueueReferences(byte[] data, int length) { for (int i = 0; i + 64 < length; i++) { if (data[i] != 64) { continue; } int j; for (j = 1; j <= 64; j++) { byte b = data[i + j]; if ((b < 48 || b > 57) && (b < 97 || b > 102)) { break; } } if (j <= 64) { continue; } if (!IsAssetFallback(data, i)) { string item = Encoding.ASCII.GetString(data, i + 1, 64); if (_discovered.Add(item) && _queued.Add(item)) { _ahead.Enqueue(item); } } i += 64; } } private static bool IsAssetFallback(byte[] data, int prefix) { int num; for (num = prefix - 1; num >= 0 && prefix - num < 64; num--) { byte b = data[num]; switch (b) { default: if ((b >= 97 && b <= 122) || (b >= 48 && b <= 57) || b == 95) { continue; } return false; case 65: case 66: case 67: case 68: case 69: case 70: case 71: case 72: case 73: case 74: case 75: case 76: case 77: case 78: case 79: case 80: case 81: case 82: case 83: case 84: case 85: case 86: case 87: case 88: case 89: case 90: continue; case 124: break; } break; } if (num < "|Tex2D".Length || data[num] != 124) { return false; } for (int i = 0; i < "|Tex2D".Length; i++) { if (data[num - "|Tex2D".Length + i] != "|Tex2D"[i]) { return false; } } return true; } public void Dispose() { lock (_gate) { _stopped = true; foreach (Stream value in _ready.Values) { value.Dispose(); } _ready.Clear(); _ahead.Clear(); _demand.Clear(); _queued.Clear(); _errors.Clear(); _buffered = 0L; Monitor.PulseAll(_gate); } } } internal static class BlobCollection { internal struct Result { public int Blobs; public long Bytes; public bool Aborted; public bool Skipped; } internal const string StateFile = "blob-cleanup.state"; private const string StateVersion = "2"; internal static string Key(string hash) { if (hash.Length <= 32) { return hash; } return hash.Substring(0, 32); } internal static string PathOf(string blobs, string hash) { string text = Path.Combine(blobs, Key(hash) + ".bin"); if (hash.Length > 32 && !File.Exists(text)) { string text2 = Path.Combine(blobs, hash + ".bin"); if (File.Exists(text2)) { return text2; } } return text; } internal static Result Collect(string directory, DateTime nowUtc, TimeSpan grace, object gate) { Result result = default(Result); string text = Path.Combine(directory, "blobs"); if (!Directory.Exists(text)) { return result; } string path = Path.Combine(directory, "blob-cleanup.state"); string text2 = ManifestFingerprint(directory); if (TryReadState(path, out var manifests, out var blobs, out var rescanAfter) && manifests == text2 && blobs == CountBlobs(text) && nowUtc < rescanAfter) { result.Skipped = true; return result; } BlobReferences blobReferences = new BlobReferences(); string[] array = Manifests(directory); foreach (string path2 in array) { try { using FileStream stream = OpenShared(path2); blobReferences.Scan(stream); } catch (FileNotFoundException) { } } string hash; while (blobReferences.TryNext(out hash)) { string text3 = PathOf(text, hash); try { using FileStream stream2 = OpenShared(text3); using DeflateStream stream3 = new DeflateStream(stream2, CompressionMode.Decompress); if (!blobReferences.ScanVerified(stream3, hash)) { throw new InvalidDataException("Portal blob checksum mismatch."); } } catch (Exception ex2) when (ex2 is IOException || ex2 is InvalidDataException || ex2 is UnauthorizedAccessException) { throw new IOException("Cannot complete portal blob cleanup: " + text3, ex2); } } rescanAfter = DateTime.MaxValue; List<FileInfo> list = new List<FileInfo>(); array = Directory.GetFiles(text, "*.bin"); foreach (string text4 in array) { if (blobReferences.Reaches(Path.GetFileNameWithoutExtension(text4))) { continue; } FileInfo fileInfo = new FileInfo(text4); if (nowUtc - fileInfo.LastWriteTimeUtc < grace) { DateTime dateTime = fileInfo.LastWriteTimeUtc + grace; if (dateTime < rescanAfter) { rescanAfter = dateTime; } } else { list.Add(fileInfo); } } for (int j = 0; j < list.Count; j += 100) { lock (gate) { if (SavedSince(directory, nowUtc)) { result.Aborted = true; return result; } for (int k = j; k < Math.Min(j + 100, list.Count); k++) { result.Bytes += list[k].Length; list[k].Delete(); result.Blobs++; } } } if (list.Count > 0) { PruneAssetIndex(text, gate); } lock (gate) { if (!SavedSince(directory, nowUtc) && ManifestFingerprint(directory) == text2) { try { WriteState(path, text2, CountBlobs(text), rescanAfter); } catch (Exception ex3) when (ex3 is IOException || ex3 is UnauthorizedAccessException) { } } } return result; } private static string[] Manifests(string directory) { return Array.FindAll(Directory.GetFiles(directory, "*.portal*"), (string p) => !p.EndsWith(".tmp", StringComparison.OrdinalIgnoreCase)); } private static FileStream OpenShared(string path) { return new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete); } private static bool SavedSince(string directory, DateTime started) { string[] array = Manifests(directory); for (int i = 0; i < array.Length; i++) { if (File.GetLastWriteTimeUtc(array[i]) > started) { return true; } } return false; } private static void PruneAssetIndex(string blobs, object gate) { string path = Path.Combine(blobs, "asset-textures.index"); lock (gate) { if (!File.Exists(path)) { return; } List<string> list = new List<string>(); string[] array = File.ReadAllLines(path); foreach (string text in array) { int num = text.LastIndexOf('\t'); if (num > 0 && File.Exists(PathOf(blobs, text.Substring(num + 1)))) { list.Add(text); } } File.WriteAllLines(path, list); } } private static int CountBlobs(string blobs) { return Directory.GetFiles(blobs, "*.bin").Length; } internal static string ManifestFingerprint(string directory) { string[] array = Manifests(directory); Array.Sort(array, (IComparer<string>?)StringComparer.Ordinal); StringBuilder stringBuilder = new StringBuilder(); string[] array2 = array; for (int i = 0; i < array2.Length; i++) { FileInfo fileInfo = new FileInfo(array2[i]); stringBuilder.Append(fileInfo.Name).Append('|').Append(fileInfo.Length) .Append('|') .Append(fileInfo.LastWriteTimeUtc.Ticks) .Append('\n'); } return stringBuilder.ToString(); } private static bool TryReadState(string path, out string manifests, out int blobs, out DateTime rescanAfter) { manifests = null; blobs = -1; rescanAfter = DateTime.MinValue; try { if (!File.Exists(path)) { return false; } string[] array = File.ReadAllText(path).Split(new char[1] { '\n' }, 4); if (array.Length < 4 || array[0] != "2") { return false; } blobs = int.Parse(array[1], CultureInfo.InvariantCulture); rescanAfter = new DateTime(long.Parse(array[2], CultureInfo.InvariantCulture), DateTimeKind.Utc); manifests = array[3]; return true; } catch (Exception ex) when (ex is IOException || ex is FormatException || ex is OverflowException || ex is ArgumentException || ex is UnauthorizedAccessException) { return false; } } private static void WriteState(string path, string manifests, int blobs, DateTime rescanAfter) { string contents = "2\n" + blobs.ToString(CultureInfo.InvariantCulture) + "\n" + rescanAfter.Ticks.ToString(CultureInfo.InvariantCulture) + "\n" + manifests; string text = path + ".tmp"; File.WriteAllText(text, contents); if (File.Exists(path)) { File.Delete(path); } File.Move(text, path); } } internal sealed class BlobReferences { private readonly struct Key128 : IEquatable<Key128> { private readonly ulong _high; private readonly ulong _low; private Key128(ulong high, ulong low) { _high = high; _low = low; } internal static Key128 From(byte[] data, int offset) { ulong num = 0uL; ulong num2 = 0uL; for (int i = 0; i < 16; i++) { num = (num << 4) | Nibble(data[offset + i]); } for (int j = 16; j < 32; j++) { num2 = (num2 << 4) | Nibble(data[offset + j]); } return new Key128(num, num2); } internal static bool TryParse(string name, out Key128 key) { ulong num = 0uL; ulong num2 = 0uL; for (int i = 0; i < 32; i++) { char c = name[i]; if ((c < '0' || c > '9') && (c < 'a' || c > 'f')) { key = default(Key128); return false; } if (i < 16) { num = (num << 4) | Nibble((byte)c); } else { num2 = (num2 << 4) | Nibble((byte)c); } } key = new Key128(num, num2); return true; } private static ulong Nibble(byte c) { return (ulong)((c <= 57) ? (c - 48) : (c - 97 + 10)); } public bool Equals(Key128 other) { if (_high == other._high) { return _low == other._low; } return false; } public override bool Equals(object obj) { if (obj is Key128 other) { return Equals(other); } return false; } public override int GetHashCode() { return (_high ^ (ulong)((long)_low * -7046029254386353131L)).GetHashCode(); } } private const int HashLength = 64; private readonly byte[] _buffer = new byte[65600]; private readonly HashSet<Key128> _reached = new HashSet<Key128>(); private readonly Queue<string> _pending = new Queue<string>(); internal int Count => _reached.Count; internal bool TryNext(out string hash) { if (_pending.Count == 0) { hash = null; return false; } hash = _pending.Dequeue(); return true; } internal bool Reaches(string name) { if (name.Length >= 32 && Key128.TryParse(name, out var key)) { return _reached.Contains(key); } return false; } internal void Scan(Stream stream) { Scan(stream, null); } internal bool ScanVerified(Stream stream, string expectedHash) { using Sha256.Hasher hasher = new Sha256.Hasher(); Scan(stream, hasher); return Sha256.Hex(hasher.Finish()) == expectedHash; } private void Scan(Stream stream, Sha256.Hasher checksum) { int num = 0; int num2; while ((num2 = stream.Read(_buffer, num, _buffer.Length - num)) > 0) { checksum?.Append(_buffer, num, num2); num += num2; int num3 = ScanBuffer(num); int num4 = num - num3; Buffer.BlockCopy(_buffer, num3, _buffer, 0, num4); num = num4; } } private int ScanBuffer(int length) { byte[] buffer = _buffer; int i; for (i = 0; i + 64 < length; i++) { if (buffer[i] != 64) { continue; } int j; for (j = 1; j <= 64; j++) { byte b = buffer[i + j]; if ((b < 48 || b > 57) && (b < 97 || b > 102)) { break; } } if (j > 64) { if (_reached.Add(Key128.From(buffer, i + 1))) { _pending.Enqueue(Encoding.ASCII.GetString(buffer, i + 1, 64)); } i += 64; } } return i; } } internal sealed class BoundedArchiveWriter : IDisposable { private sealed class Job { internal MemoryStream Payload; internal int Capacity; internal Task<bool> Task; } internal const long DefaultCapacityLimit = 33554432L; internal const int DefaultJobLimit = 8; internal const int DefaultWorkers = 4; private readonly long _capacityLimit; private readonly int _jobLimit; private readonly SemaphoreSlim _workers; private readonly List<Job> _jobs = new List<Job>(); private readonly HashSet<string> _scheduled = new HashSet<string>(StringComparer.Ordinal); private long _retainedCapacity; private ExceptionDispatchInfo _failure; private bool _disposed; private int _activeWorkers; private int _peakWorkers; internal int WrittenCount { get; private set; } internal long PeakRetainedCapacity { get; private set; } internal int PeakJobs { get; private set; } internal int PeakWorkers => Volatile.Read(in _peakWorkers); internal long RetainedCapacity => _retainedCapacity; internal int PendingJobs => _jobs.Count; internal bool Full { get { ReapCompleted(); if (_jobs.Count < _jobLimit) { return _retainedCapacity >= _capacityLimit * 3 / 4; } return true; } } internal bool Idle { get { ReapCompleted(); return _jobs.Count == 0; } } internal BoundedArchiveWriter(long capacityLimit = 33554432L, int jobLimit = 8, int workers = 4) { if (capacityLimit <= 0 || jobLimit <= 0 || workers <= 0) { throw new ArgumentOutOfRangeException(); } _capacityLimit = capacityLimit; _jobLimit = jobLimit; _workers = new SemaphoreSlim(workers, workers); } internal void Enqueue(string hash, MemoryStream payload, Func<Stream, bool> write) { bool flag = false; try { if (_disposed) { throw new ObjectDisposedException("BoundedArchiveWriter"); } ReapCompleted(); if (_failure != null) { Drain(); } if (_scheduled.Contains(hash)) { return; } int capacity = payload.Capacity; if (capacity > _capacityLimit) { Drain(); _scheduled.Add(hash); payload.Position = 0L; if (write(payload)) { WrittenCount++; } return; } while (_retainedCapacity + capacity > _capacityLimit || _jobs.Count >= _jobLimit) { WaitForAny(); if (_failure != null) { Drain(); } } _scheduled.Add(hash); Job job = new Job { Payload = payload, Capacity = capacity }; job.Task = Task.Run(delegate { _workers.Wait(); int num = Interlocked.Increment(ref _activeWorkers); int num2; do { num2 = Volatile.Read(in _peakWorkers); } while (num > num2 && Interlocked.CompareExchange(ref _peakWorkers, num, num2) != num2); try { job.Payload.Position = 0L; return write(job.Payload); } finally { job.Payload.Dispose(); job.Payload = null; Interlocked.Decrement(ref _activeWorkers); _workers.Release(); } }); flag = true; _jobs.Add(job); _retainedCapacity += capacity; PeakRetainedCapacity = Math.Max(PeakRetainedCapacity, _retainedCapacity); PeakJobs = Math.Max(PeakJobs, _jobs.Count); } finally { if (!flag) { payload.Dispose(); } } } private void ReapCompleted() { for (int num = _jobs.Count - 1; num >= 0; num--) { Job job = _jobs[num]; if (job.Task.IsCompleted) { try { if (job.Task.GetAwaiter().GetResult()) { WrittenCount++; } } catch (Exception source) { if (_failure == null) { _failure = ExceptionDispatchInfo.Capture(source); } } _retainedCapacity -= job.Capacity; _jobs.RemoveAt(num); } } } private void WaitForAny() { Task[] array = new Task[_jobs.Count]; for (int i = 0; i < array.Length; i++) { array[i] = _jobs[i].Task; } Task.WaitAny(array); ReapCompleted(); } internal void Drain() { while (_jobs.Count > 0) { WaitForAny(); } _failure?.Throw(); } public void Dispose() { if (_disposed) { return; } try { Drain(); } finally { _disposed = true; _workers.Dispose(); } } } internal static class BuildGuides { private static readonly HashSet<CircleProjector> Circles = new HashSet<CircleProjector>(); private static readonly HashSet<StationExtension> Lines = new HashSet<StationExtension>(); private static readonly FieldInfo PlacementMarker = AccessTools.Field(typeof(Player), "m_placementMarkerInstance"); private static readonly FieldInfo ConnectionLine = AccessTools.Field(typeof(StationExtension), "m_connection"); internal static void Noted(CircleProjector circle) { if ((Object)(object)circle != (Object)null) { Circles.Add(circle); } } internal static void Noted(StationExtension extension) { if ((Object)(object)extension != (Object)null) { Lines.Add(extension); } } internal static void Collect(HashSet<Renderer> excluded) { Circles.RemoveWhere((CircleProjector circle) => (Object)(object)circle == (Object)null); foreach (CircleProjector circle in Circles) { Add(((Component)circle).gameObject, excluded); } Lines.RemoveWhere((StationExtension extension) => (Object)(object)extension == (Object)null); if (ConnectionLine != null) { foreach (StationExtension line in Lines) { if (!line.m_continousConnection) { object? value = ConnectionLine.GetValue(line); GameObject val = (GameObject)((value is GameObject) ? value : null); if (val != null && (Object)(object)val != (Object)null) { Add(val, excluded); } } } } if (!(PlacementMarker != null)) { return; } foreach (Player allPlayer in Player.GetAllPlayers()) { if ((Object)(object)allPlayer != (Object)null) { object? value2 = PlacementMarker.GetValue(allPlayer); GameObject val2 = (GameObject)((value2 is GameObject) ? value2 : null); if (val2 != null && (Object)(object)val2 != (Object)null) { Add(val2, excluded); } } } } private static void Add(GameObject root, HashSet<Renderer> excluded) { Renderer[] componentsInChildren = root.GetComponentsInChildren<Renderer>(true); foreach (Renderer val in componentsInChildren) { if ((Object)(object)val != (Object)null) { excluded.Add(val); } } } } [HarmonyPatch] internal static class Patch_CircleProjector_Noted { private static MethodBase TargetMethod() { return AccessTools.Method(typeof(CircleProjector), "Start", (Type[])null, (Type[])null); } private static bool Prepare() { return TargetMethod() != null; } private static void Postfix(CircleProjector __instance) { BuildGuides.Noted(__instance); } } [HarmonyPatch] internal static class Patch_StationExtension_Noted { private static MethodBase TargetMethod() { return AccessTools.Method(typeof(StationExtension), "StartConnectionEffect", new Type[2] { typeof(Vector3), typeof(float) }, (Type[])null); } private static bool Prepare() { return TargetMethod() != null; } private static void Postfix(StationExtension __instance) { BuildGuides.Noted(__instance); } } internal sealed class CaptureBudget { private readonly long _ticks; private long _deadline; internal static CaptureBudget Unlimited => new CaptureBudget(double.PositiveInfinity); internal bool IsUnlimited => _ticks == long.MaxValue; internal bool Spent { get { if (!IsUnlimited) { return Stopwatch.GetTimestamp() >= _deadline; } return false; } } internal CaptureBudget(double milliseconds) { _ticks = (double.IsPositiveInfinity(milliseconds) ? long.MaxValue : ((long)(milliseconds * (double)Stopwatch.Frequency / 1000.0))); Restart(); } internal void Restart() { _deadline = (IsUnlimited ? long.MaxValue : (Stopwatch.GetTimestamp() + _ticks)); } } internal sealed class CaptureCostSamples { internal const int Keep = 7; internal const int MinSamples = 3; internal const float MinSecondsBetween = 10f; private readonly List<float> _samples = new List<float>(); private readonly List<float> _sorted = new List<float>(); internal int Count => _samples.Count; internal bool Valid => _samples.Count >= 3; internal float MedianMs { get { if (_samples.Count == 0) { return 0f; } _sorted.Clear(); _sorted.AddRange(_samples); _sorted.Sort(); int num = _sorted.Count / 2; if (_sorted.Count % 2 != 1) { return (_sorted[num - 1] + _sorted[num]) / 2f; } return _sorted[num]; } } internal void Add(float ms) { if (ms > 0f && !float.IsInfinity(ms)) { _samples.Add(ms); if (_samples.Count > 7) { _samples.RemoveAt(0); } } } internal static bool WantsSample(int capturesOfScene, bool sceneTimed, float secondsSinceLast) { if (capturesOfScene >= 2 && !sceneTimed) { return secondsSinceLast >= 10f; } return false; } } internal enum MeasuredLoad { Normal, Reduced, Minimal } internal static class CaptureBudgetPolicy { internal const float Share = 0.25f; internal const int MaxFramesBetween = 30; internal const float ReducedAbove = 12f; internal const float MinimalAbove = 24f; internal const float Hysteresis = 0.8f; private static readonly int[] Sizes = new int[5] { 1024, 1536, 2048, 3072, 4096 }; private static readonly float[] Factors = new float[5] { 0.78f, 0.94f, 1f, 1.2f, 1.4f }; internal static int MinFramesBetween(bool valid, float costMs, float baseMs, float share = 0.25f) { if (!valid || !(costMs > 0f) || !(baseMs > 0f)) { return 1; } double val = Math.Ceiling((double)((1f - share) * costMs / (share * baseMs)) - 1E-06); return (int)Math.Max(1.0, Math.Min(30.0, val)); } internal static MeasuredLoad Level(MeasuredLoad current, bool valid, float costAtReference) { if (!valid) { return current; } MeasuredLoad measuredLoad = ((costAtReference > 24f) ? MeasuredLoad.Minimal : ((costAtReference > 12f) ? MeasuredLoad.Reduced : MeasuredLoad.Normal)); if (measuredLoad >= current) { return measuredLoad; } if (current == MeasuredLoad.Minimal && costAtReference >= 19.2f) { return MeasuredLoad.Minimal; } if (costAtReference >= 9.6f) { return MeasuredLoad.Reduced; } return MeasuredLoad.Normal; } internal static float CostAtReference(float costMs, int eyeCaptureLimit) { return costMs / Factor(eyeCaptureLimit); } internal static float CostAt(float costAtReference, int eyeCaptureLimit) { return costAtReference * Factor(eyeCaptureLimit); } private static float Factor(int eyeCaptureLimit) { float result = Factors[Factors.Length - 1]; if (eyeCaptureLimit <= Sizes[0]) { result = Factors[0]; } else { for (int i = 1; i < Sizes.Length; i++) { if (eyeCaptureLimit <= Sizes[i]) { float num = (float)(eyeCaptureLimit - Sizes[i - 1]) / (float)(Sizes[i] - Sizes[i - 1]); result = Factors[i - 1] + (Factors[i] - Factors[i - 1]) * num; break; } } } return result; } } internal static class CaptureFilterChecks { internal static void Verify(int layer, Action<bool, string> require) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Expected O, but got Unknown //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Expected O, but got Unknown //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_018d: Unknown result type (might be due to invalid IL or missing references) int num = LayerMask.NameToLayer("ghost"); require(num >= 0, "Valheim's placement-ghost layer is available"); List<Object> owned = new List<Object>(); try { Vector3 val = default(Vector3); ((Vector3)(ref val))..ctor(0f, 14000f, 0f); Mesh mesh = new Mesh(); owned.Add((Object)(object)mesh); mesh.vertices = (Vector3[])(object)new Vector3[3] { Vector3.zero, Vector3.right, Vector3.up }; mesh.triangles = new int[3] { 0, 1, 2 }; mesh.RecalculateBounds(); mesh.RecalculateNormals(); Material material = new Material(PortalProjection.FindShader("Standard")); owned.Add((Object)(object)material); Func<string, Vector3, int, Renderer> obj = delegate(string name, Vector3 position, int sourceLayer) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown //IL_0020: Unknown result type (might be due to invalid IL or missing references) GameObject val8 = new GameObject(name); owned.Add((Object)(object)val8); val8.layer = sourceLayer; val8.transform.position = position; val8.AddComponent<MeshFilter>().sharedMesh = mesh; MeshRenderer obj2 = val8.AddComponent<MeshRenderer>(); ((Renderer)obj2).sharedMaterial = material; return (Renderer)(object)obj2; }; Renderer val2 = obj("Capture filter ordinary piece", val, 0); Renderer val3 = obj("Capture filter nearby ghost", val + Vector3.right * 2f, num); Renderer val4 = obj("Capture filter distant ghost", val + Vector3.forward * 50f, num); Light val5 = ((Component)val2).gameObject.AddComponent<Light>(); val5.type = (LightType)2; val5.range = 5f; Light val6 = ((Component)val3).gameObject.AddComponent<Light>(); val6.type = (LightType)2; val6.range = 5f; using PortalSnapshot portalSnapshot = PortalSnapshot.Capture(val, Quaternion.identity, 8f, layer, (Renderer[])(object)new Renderer[3] { val2, val3, val4 }, (Light[])(object)new Light[2] { val5, val6 }); require(portalSnapshot.MeshCount == 1, "placement ghost does not become frozen scene geometry"); require(portalSnapshot.LightCount == 1, "placement ghost light is excluded while ordinary light is kept"); HashSet<Renderer> hashSet = (HashSet<Renderer>)AccessTools.Field(typeof(PortalSnapshot), "_backdropExcludedSources").GetValue(portalSnapshot); require(hashSet.Contains(val3) && hashSet.Contains(val4), "near and beyond-radius placement ghosts are both hidden from the panorama"); require(!val3.forceRenderingOff && !val4.forceRenderingOff && ((Behaviour)val6).enabled && ((Behaviour)val5).enabled, "capture restores the live ghost renderers and lights"); require(!portalSnapshot.ShouldExcludeCaptureSource(((Component)val2).transform), "ordinary world geometry remains eligible"); foreach (Player allPlayer in Player.GetAllPlayers()) { require(portalSnapshot.ShouldExcludeCaptureSource(((Component)allPlayer).transform), "every player's root is excluded independently of capture distance"); Light[] componentsInChildren = ((Component)allPlayer).GetComponentsInChildren<Light>(true); foreach (Light val7 in componentsInChildren) { require(portalSnapshot.ShouldExcludeCaptureSource(((Component)val7).transform), "players' attached lights are excluded"); } } } finally { foreach (Object item in owned) { if (item != (Object)null) { Object.Destroy(item); } } } } } internal static class CaptureRetry { internal const float MinimumRadius = 32f; internal const float MaximumDelay = 600f; internal static float Delay(int failures) { return (float)Math.Min(600.0, 30.0 * Math.Pow(2.0, Math.Max(0, failures - 1))); } internal static float SmallerRadius(float radius) { return Math.Max(32f, radius * 0.75f); } } internal sealed class SnapshotLimitException : InvalidOperationException { internal SnapshotLimitException(string message) : base(message) { } } internal struct Lighting { public string Environment; public Texture AuroraGradient; public Quaternion SunRotation; public Vector3 SkyboxSunDir; public Color SunColor; public Color FogColor; public Color SunFogColor; public Color Ambient; public float SunIntensity; public float FogDensity; public float Wet; public float Aurora; internal bool Differs(in Lighting other) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_004c: 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_005f: 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_0072: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) if (Environment != other.Environment) { return true; } if (Quaternion.Angle(SunRotation, other.SunRotation) >= 10f) { return true; } if (Far(SunColor * SunIntensity, other.SunColor * other.SunIntensity) || Far(Ambient, other.Ambient) || Far(FogColor, other.FogColor) || Far(SunFogColor, other.SunFogColor)) { return true; } if (Mathf.Abs(FogDensity - other.FogDensity) > 0.15f * Mathf.Max(new float[3] { FogDensity, other.FogDensity, 0.0001f })) { return true; } if (!(Mathf.Abs(Aurora - other.Aurora) > 0.05f)) { return Wet != other.Wet; } return true; } private static bool Far(Color a, Color b) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) if (!(Mathf.Abs(a.r - b.r) > 0.03f) && !(Mathf.Abs(a.g - b.g) > 0.03f)) { return Mathf.Abs(a.b - b.b) > 0.03f; } return true; } } internal static class DestinationLighting { private static readonly List<EnvEntry> Available = new List<EnvEntry>(); private const float EyeHeight = 1.6f; internal static bool TryCompute(Vector3 position, out Lighting lighting) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) lighting = default(Lighting); if (!TryEnvironment(position, out var setup)) { return false; } EnvMan instance = EnvMan.instance; float dayFraction = instance.GetDayFraction(); lighting = At(instance, setup, dayFraction); lighting.FogDensity = OtherAir.FogDensity(setup, lighting.FogDensity, dayFraction, position.y + 1.6f); return true; } internal static bool TryEnvironment(Vector3 position, out EnvSetup setup) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) setup = null; EnvMan instance = EnvMan.instance; if ((Object)(object)instance == (Object)null || (Object)(object)ZNet.instance == (Object)null || WorldGenerator.instance == null || Character.InInterior(position)) { return false; } setup = Weather(instance, position); return setup != null; } internal static string CompareWithLive(Vector3 position) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0084: 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_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00de: 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_010c: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) EnvMan instance = EnvMan.instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance.m_dirLight == (Object)null) { return "no EnvMan"; } if (!TryCompute(position, out var lighting)) { return "no lighting computed here (interior or no weather)"; } Light dirLight = instance.m_dirLight; EnvSetup currentEnvironment = instance.GetCurrentEnvironment(); return "weather computed " + lighting.Environment + ", live " + ((currentEnvironment != null) ? currentEnvironment.m_name : "none") + "; " + $"sun angle {Quaternion.Angle(lighting.SunRotation, ((Component)dirLight).transform.rotation):F1} deg, " + $"intensity {lighting.SunIntensity:F3}/{dirLight.intensity:F3}, colour {lighting.SunColor}/{dirLight.color}; " + $"ambient {lighting.Ambient}/{RenderSettings.ambientLight}; fog {lighting.FogColor}/{RenderSettings.fogColor}, " + string.Format("density {0:F5}/{1:F5}; aurora {2:F2}/{3:F2}; ", lighting.FogDensity, RenderSettings.fogDensity, lighting.Aurora, Shader.GetGlobalFloat("_AuroraStrength")) + $"probe difference {ProbeDifference(PortalSnapshot.FlatProbe(RenderSettings.ambientLight), RenderSettings.ambientProbe):F4}"; } internal static float ProbeDifference(SphericalHarmonicsL2 a, SphericalHarmonicsL2 b) { float num = 0f; for (int i = 0; i < 3; i++) { for (int j = 0; j < 9; j++) { num = Mathf.Max(num, Mathf.Abs(((SphericalHarmonicsL2)(ref a))[i, j] - ((SphericalHarmonicsL2)(ref b))[i, j])); } } return num; } internal static Lighting At(EnvMan env, EnvSetup setup, float fraction) { //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_0187: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_01a6: Unknown result type (might be due to invalid IL or missing references) //IL_01ea: Unknown result type (might be due to invalid IL or missing references) //IL_01ec: Unknown result type (might be due to invalid IL or missing references) //IL_01f2: Unknown result type (might be due to invalid IL or missing references) //IL_01f8: Unknown result type (might be due to invalid IL or missing references) //IL_01fd: Unknown result type (might be due to invalid IL or missing references) //IL_01cb: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Unknown result type (might be due to invalid IL or missing references) //IL_01e1: Unknown result type (might be due to invalid IL or missing references) //IL_01e6: Unknown result type (might be due to invalid IL or missing references) //IL_0242: Unknown result type (might be due to invalid IL or missing references) //IL_0244: Unknown result type (might be due to invalid IL or missing references) //IL_024c: Unknown result type (might be due to invalid IL or missing references) //IL_0252: Unknown result type (might be due to invalid IL or missing references) //IL_0258: Unknown result type (might be due to invalid IL or missing references) //IL_025e: Unknown result type (might be due to invalid IL or missing references) //IL_0263: Unknown result type (might be due to invalid IL or missing references) //IL_0269: Unknown result type (might be due to invalid IL or missing references) //IL_0270: Unknown result type (might be due to invalid IL or missing references) //IL_0275: Unknown result type (might be due to invalid IL or missing references) //IL_027b: Unknown result type (might be due to invalid IL or missing references) //IL_0282: Unknown result type (might be due to invalid IL or missing references) //IL_0287: Unknown result type (might be due to invalid IL or missing references) //IL_028c: Unknown result type (might be due to invalid IL or missing references) //IL_0292: Unknown result type (might be due to invalid IL or missing references) //IL_0298: Unknown result type (might be due to invalid IL or missing references) //IL_029d: Unknown result type (might be due to invalid IL or missing references) //IL_0207: Unknown result type (might be due to invalid IL or missing references) //IL_020a: Unknown result type (might be due to invalid IL or missing references) //IL_0210: Unknown result type (might be due to invalid IL or missing references) //IL_0216: Unknown result type (might be due to invalid IL or missing references) //IL_021d: Unknown result type (might be due to invalid IL or missing references) //IL_0222: Unknown result type (might be due to invalid IL or missing references) //IL_0228: Unknown result type (might be due to invalid IL or missing references) //IL_022f: Unknown result type (might be due to invalid IL or missing references) //IL_0234: Unknown result type (might be due to invalid IL or missing references) //IL_0239: Unknown result type (might be due to invalid IL or missing references) //IL_023e: Unknown result type (might be due to invalid IL or missing references) //IL_02e4: Unknown result type (might be due to invalid IL or missing references) //IL_02e9: Unknown result type (might be due to invalid IL or missing references) //IL_02fd: Unknown result type (might be due to invalid IL or missing references) //IL_0302: Unknown result type (might be due to invalid IL or missing references) //IL_0336: Unknown result type (might be due to invalid IL or missing references) //IL_033c: Unknown result type (might be due to invalid IL or missing references) //IL_0342: Unknown result type (might be due to invalid IL or missing references) //IL_0347: Unknown result type (might be due to invalid IL or missing references) //IL_0350: Unknown result type (might be due to invalid IL or missing references) //IL_0356: Unknown result type (might be due to invalid IL or missing references) //IL_035c: Unknown result type (might be due to invalid IL or missing references) //IL_0361: Unknown result type (might be due to invalid IL or missing references) //IL_02a7: Unknown result type (might be due to invalid IL or missing references) //IL_02aa: Unknown result type (might be due to invalid IL or missing references) //IL_02b0: Unknown result type (might be due to invalid IL or missing references) //IL_02b6: Unknown result type (might be due to invalid IL or missing references) //IL_02bd: Unknown result type (might be due to invalid IL or missing references) //IL_02c2: Unknown result type (might be due to invalid IL or missing references) //IL_02c8: Unknown result type (might be due to invalid IL or missing references) //IL_02cf: Unknown result type (might be due to invalid IL or missing references) //IL_02d4: Unknown result type (might be due to invalid IL or missing references) //IL_02d9: Unknown result type (might be due to invalid IL or missing references) //IL_02de: Unknown result type (might be due to invalid IL or missing references) float num = (((Object)(object)env != (Object)null) ? env.m_sunHorizonTransitionL : 0.02f); float num2 = (((Object)(object)env != (Object)null) ? env.m_sunHorizonTransitionH : 0.08f); float num3 = Mathf.Pow(Mathf.Max(1f - Mathf.Clamp01(fraction / 0.25f), Mathf.Clamp01((fraction - 0.75f) / 0.25f)), 0.5f); float num4 = Mathf.Pow(Mathf.Clamp01(1f - Mathf.Abs(fraction - 0.5f) / 0.25f), 0.5f); float num5 = Mathf.Min(Mathf.Clamp01(1f - (fraction - 0.26f) / (0f - num)), Mathf.Clamp01(1f - (fraction - 0.26f) / num2)); float num6 = Mathf.Min(Mathf.Clamp01(1f - (fraction - 0.74f) / (0f - num2)), Mathf.Clamp01(1f - (fraction - 0.74f) / num)); float num7 = num4 + num3 + num5 + num6; if (num7 > 0f) { num4 /= num7; num3 /= num7; num5 /= num7; num6 /= num7; } Lighting result = new Lighting { Environment = setup.m_name, AuroraGradient = (Texture)(object)setup.m_auroraGradientTexture }; Quaternion val = Quaternion.Euler(-90f + setup.m_sunAngle, 0f, 0f) * Quaternion.Euler(0f, -90f, 0f) * Quaternion.Euler(-90f + 360f * fraction, 0f, 0f); result.SkyboxSunDir = -(val * Vector3.forward); result.SunIntensity = setup.m_lightIntensityDay * num4 + setup.m_lightIntensityNight * num3; if (num3 > 0f) { val *= Quaternion.Euler(180f, 0f, 0f); } result.SunRotation = val; Color val2 = setup.m_sunColorNight * num3; if (num4 > 0f) { val2 += setup.m_sunColorDay * num4 + setup.m_sunColorMorning * num5 + setup.m_sunColorEvening * num6; } result.SunColor = val2; result.FogColor = setup.m_fogColorNight * num3 + setup.m_fogColorDay * num4 + setup.m_fogColorMorning * num5 + setup.m_fogColorEvening * num6; Color val3 = setup.m_fogColorSunNight * num3; if (num4 > 0f) { val3 += setup.m_fogColorSunDay * num4 + setup.m_fogColorSunMorning * num5 + setup.m_fogColorSunEvening * num6; } result.SunFogColor = Color.Lerp(result.FogColor, val3, Mathf.Clamp01(Mathf.Max(num3, num4) * 3f)); result.FogDensity = setup.m_fogDensityNight * num3 + setup.m_fogDensityDay * num4 + setup.m_fogDensityMorning * num5 + setup.m_fogDensityEvening * num6; result.Ambient = Color.Lerp(setup.m_ambColorNight, setup.m_ambColorDay, num4); result.Ambient = WeatherOverhaulBridge.Ambient(result.Ambient, setup.m_ambColorDay, num3); result.Wet = (setup.m_isWet ? 1f : 0f); result.Aurora = setup.m_auroraIntensityDay * num4 + setup.m_auroraIntensityEvening * num6 + setup.m_auroraIntensityMorning * num5 + setup.m_auroraIntensityNight * num3; return result; } private static EnvSetup Weather(EnvMan env, Vector3 position) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_018d: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0106: 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) BiomeSector biomeSector = WorldGenerator.instance.GetBiomeSector(position, false); string text = null; foreach (AltBiome altBiome in biomeSector.AltBiomes) { if (!string.IsNullOrEmpty(altBiome.m_forceEnvironment)) { text = altBiome.m_forceEnvironment; break; } } if (text == null) { text = PersistentEventEnvironment(position); } if (text != null) { foreach (EnvSetup environment in env.m_environments) { if (environment.m_name == text) { return environment; } } return null; } long num = (long)ZNet.instance.GetTimeSeconds() / env.m_environmentDuration; List<EnvEntry> availableEnvironments = env.GetAvailableEnvironments(biomeSector); if (availableEnvironments == null || availableEnvironments.Count == 0) { return null; } Available.Clear(); Available.AddRange(availableEnvironments); bool flag = WorldGenerator.IsAshlands(position.x, position.z); bool flag2 = WorldGenerator.IsDeepnorth(position.x, position.y); State state = Random.state; try { Random.InitState((int)num); EnvSetup result = SelectWeighted(Available); foreach (EnvEntry item in Available) { if (item.m_ashlandsOverride && flag) { result = item.m_env; } if (item.m_deepnorthOverride && flag2) { result = item.m_env; } } return result; } finally { Random.state = state; } } private static string PersistentEventEnvironment(Vector3 position) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) PersistentEventSystem instance = PersistentEventSystem.instance; if ((Object)(object)instance == (Object)null || instance.m_activePersistentEvents == null) { return null; } foreach (ActivePersistentEvent item in instance.m_activePersistentEvents.list) { Vector3 val = item.position - position; if (((Vector3)(ref val)).sqrMagnitude < item.radius * item.radius) { string environmentOverride = item.Source.GetEnvironmentOverride(position); return string.IsNullOrEmpty(environmentOverride) ? null : environmentOverride; } } return null; } private static EnvSetup SelectWeighted(List<EnvEntry> environments) { float num = 0f; foreach (EnvEntry environment in environments) { if (!environment.m_ashlandsOverride && !environment.m_deepnorthOverride) { num += environment.m_weight; } } float num2 = Random.Range(0f, num); float num3 = 0f; foreach (EnvEntry environment2 in environments) { if (!environment2.m_ashlandsOverride && !environment2.m_deepnorthOverride) { num3 += environment2.m_weight; if (num3 >= num2) { return environment2.m_env; } } } EnvEntry val = environments[environments.Count - 1]; if (!val.m_ashlandsOverride && !val.m_deepnorthOverride) { return val.m_env; } return null; } } internal static class FarLandsBridge { private static bool _probed; private static Func<bool> _ownsFog; private static Func<EnvSetup, float, float, float, float> _fogDensity; internal static bool OwnsFog { get { Probe(); if (_ownsFog == null) { return false; } try { return _ownsFog(); } catch (Exception ex) { _ownsFog = null; ManualLogSource log = PortalPreviewPlugin.Log; if (log != null) { log.LogWarning((object)("FarLands' air could not be read (" + ex.GetType().Name + ": " + ex.Message + "); portals follow the game's fog switch.")); } return false; } } } internal static float FogDensity(EnvSetup setup, float density, float dayFraction, float height) { Probe(); if (_fogDensity == null) { return density; } try { float num = _fogDensity(setup, density, dayFraction, height); return (float.IsNaN(num) || float.IsInfinity(num) || num < 0f) ? density : num; } catch (Exception ex) { _fogDensity = null; ManualLogSource log = PortalPreviewPlugin.Log; if (log != null) { log.LogWarning((object)("FarLands' air could not be read (" + ex.GetType().Name + ": " + ex.Message + "); portals keep the game's fog density.")); } return density; } } private static void Probe() { if (_probed) { return; } _probed = true; Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); for (int i = 0; i < assemblies.Length; i++) { Type type = assemblies[i].GetType("FarLands.FarLandsAir"); if (!(type == null)) { MethodInfo methodInfo = type.GetProperty("OwnsFog", BindingFlags.Static | BindingFlags.Public)?.GetGetMethod(); if (methodInfo != null && methodInfo.ReturnType == typeof(bool)) { _ownsFog = (Func<bool>)Delegate.CreateDelegate(typeof(Func<bool>), methodInfo); } MethodInfo method = type.GetMethod("Density", BindingFlags.Static | BindingFlags.Public, null, new Type[4] { typeof(EnvSetup), typeof(float), typeof(float), typeof(float) }, null); if (method != null && method.ReturnType == typeof(float)) { _fogDensity = (Func<EnvSetup, float, float, float, float>)Delegate.CreateDelegate(typeof(Func<EnvSetup, float, float, float, float>), method); } if (_ownsFog != null) { PortalPreviewPlugin.Diag("FarLands found: portal views take its air's fog while it runs."); } break; } } } } internal static class OtherAir { internal static bool OwnsFog { get { if (!WeatherOverhaulBridge.OwnsFog) { return FarLandsBridge.OwnsFog; } return true; } } internal static float FogDensity(EnvSetup setup, float density, float dayFraction, float height) { return FarLandsBridge.FogDensity(setup, WeatherOverhaulBridge.FogDensity(setup, density, dayFraction, height), dayFraction, height); } } internal static class FrozenPortals { private sealed class SceneCaptureJob { public Endpoint Capture; public IEnumerator<bool> Steps; public CaptureBudget Budget; public long Started; public int Frames; public double LongestStepMs; public bool SceneTaken; public string LongestStepDetail; } private sealed class Endpoint { public readonly PortalViewBudget.Entry<Endpoint> ViewState = new PortalViewBudget.Entry<Endpoint>(); public ZDOID Id; public ZDOID Target; public TeleportWorld Portal; public PortalSnapshot Snapshot; public bool SnapshotVerified; public float RetryAt; public float LoadRetryAt; public float LastUsed; public bool Reported; private string _lastError; public string WaitReason; public bool Refresh; public bool FirstLinkCapture; public bool SkipArchiveLoad; public float StableAt; public float NextZdoRequestAt; public int Failures; public float CaptureRadius; public float CapturedAt = float.NegativeInfinity; public CaptureWhen When; public bool Observed; public float NoArrivalHoldUntil; public ulong? Fingerprint; public bool FingerprintKnown; public bool CopiesPending; public string LastError { get { return _lastError; } set { _lastError = value; DiskLoadFailed = false; } } public bool DiskLoadFailed { get; private set; } public void FailDiskLoad(string message) { LastError = "disk load failed: " + message; DiskLoadFailed = true; } } private enum CaptureWhen { Now, WhenShown, AtTeleport } private sealed class ArchivedScene { public string Id; public string Path; public Vector3 Position; public Quaternion Rotation; public int Version; public DateTime WrittenUtc; } private sealed class AtmosphereState { internal readonly PortalAtmosphere Effect = new PortalAtmosphere(); internal EnvSetup Environment; internal float CheckAt; internal ZDOID Target; } private struct ViewCandidate { public Endpoint Endpoint; public PortalSnapshot Snapshot; public PortalView View; } [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static ConsoleEvent <>9__15_0; public static Comparison<Endpoint> <>9__154_0; internal void <Initialize>b__15_0(ConsoleEventArgs args) { //IL_04c3: Unknown result type (might be due to invalid IL or missing references) string text = ((args.Length > 1) ? args[1].ToLowerInvariant() : "status"); switch (text) { case "on": Clear(); PortalPreviewPlugin.Cfg.Enabled.Value = true; _status = "enabled; waiting for a loaded wooden or stone portal"; break; case "off": PortalPreviewPlugin.Cfg.Enabled.Value = false; Clear(); break; case "clear": Clear(); _status = "RAM cache cleared; saved snapshots retained on disk"; break; case "backdropcheck": { if (RefreshNearby() == 0) { args.Context.AddString("No connected portal within 24 m to capture."); return; } string text3 = Path.Combine(Paths.BepInExRootPath, "cache", "PortalPreview", "diagnostics", "backdrop-" + DateTime.Now.ToString("yyyyMMdd-HHmmss")); Directory.CreateDirectory(text3); PortalSnapshot.BackdropDiagnostics = text3; _viewDiagnostics = text3; _diagnosticsUntil = Time.realtimeSinceStartup + 60f; args.Context.AddString("The next capture of a nearby portal writes a panorama and view comparison to " + text3); break; } case "refresh": RefreshNearby(); break; case "log": if (args.Length != 3 || (args[2] != "on" && args[2] != "off")) { args.Context.AddString("Usage: frozenportal log on|off"); return; } PortalPreviewPlugin.Verbose = args[2] == "on"; args.Context.AddString("Portal diagnostics " + (PortalPreviewPlugin.Verbose ? "enabled" : "disabled") + " for this session."); return; case "lighting": { if ((Object)(object)Player.m_localPlayer == (Object)null) { args.Context.AddString("Enter a world first."); return; } string text2 = "Lighting computed/live here: " + DestinationLighting.CompareWithLive(((Component)Player.m_localPlayer).transform.position); args.Context.AddString(text2); PortalPreviewPlugin.Log.LogInfo((object)("[portal preview] " + text2)); return; } case "cleanup": if ((Object)(object)ZNet.instance == (Object)null) { args.Context.AddString("Enter a world first."); return; } PortalCleanup.Start(ArchiveDirectory); _status = "cleanup of stale saved scenes started; 'frozenportal status' shows the result"; break; case "mode": if (args.Length != 3 || (args[2] != "always" && args[2] != "approach")) { args.Context.AddString("Usage: frozenportal mode always|approach"); return; } PortalPreviewPlugin.Cfg.FrozenPortalAlwaysVisible.Value = args[2] == "always"; break; case "profile": { if (args.Length != 3 || !Enum.TryParse<HardwareProfileSetting>(args[2], ignoreCase: true, out var result)) { args.Context.AddString("Usage: frozenportal profile auto|low|medium|high|ultra|custom"); return; } PortalPreviewPlugin.Cfg.HardwareProfile.Value = result; PreviewConfig cfg = PortalPreviewPlugin.Cfg; if (cfg.FrozenPortalResolution.Value > 0 || cfg.FrozenPortalMaxVisible.Value > 0 || cfg.FrozenPortalMaxResident.Value > 0) { cfg.FrozenPortalResolution.Value = 0; cfg.FrozenPortalMaxVisible.Value = 0; cfg.FrozenPortalMaxResident.Value = 0; args.Context.AddString("Resolution, Max visible portals and Max resident snapshots follow the profile again (set to 0)."); } break; } case "count": case "spiral": case "radius": case "memory": { if (args.Length != 3 || !float.TryParse(args[2].Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out var result2) || float.IsNaN(result2) || float.IsInfinity(result2)) { args.Context.AddString("Usage: frozenportal " + text + " <number>"); return; } if (text == "count") { PortalPreviewPlugin.Cfg.FrozenPortalMaxVisible.Value = ((!(result2 <= 0f)) ? Mathf.Clamp(Mathf.RoundToInt(result2), 1, 16) : 0); } if (text == "memory") { PortalPreviewPlugin.Cfg.FrozenPortalMaxResident.Value = ((!(result2 <= 0f)) ? Mathf.Clamp(Mathf.RoundToInt(result2), 2, 128) : 0); } if (text == "radius") { PortalPreviewPlugin.Cfg.FrozenPortalApproachRadius.Value = Mathf.Clamp(result2, 1f, 100f); } if (text == "spiral") { PortalPreviewPlugin.Cfg.FrozenPortalRevealSeconds.Value = Mathf.Clamp(result2, 0f, 5f); } break; } case "recapture": { if (args.Length != 3 || !float.TryParse(args[2].Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out var result4) || result4 < 0.02f || result4 > 1f) { args.Context.AddString("Usage: frozenportal recapture <metres, 0.02-1; 0.3 by default>"); return; } PortalView.EyeRecaptureDistance = result4; _status = $"eye recapture every {result4:F2} m (diagnostic, not saved)"; break; } case "pair": if (args.Length != 3 || (args[2] != "on" && args[2] != "off")) { args.Context.AddString("Usage: frozenportal pair on|off"); return; } PortalView.EyePrediction = args[2] == "on"; _status = "eye capture pair " + args[2] + " (diagnostic, not saved)"; break; case "mainview": if (args.Length != 3 || (args[2] != "on" && args[2] != "off")) { args.Context.AddString("Usage: frozenportal mainview on|off"); return; } PortalView.MainViewOnly = args[2] == "on"; _status = "main view only " + args[2] + " (diagnostic, not saved)"; break; case "load": { if (args.Length != 3 || (!Enum.TryParse<MeasuredLoad>(args[2], ignoreCase: true, out var result3) && args[2] != "auto")) { args.Context.AddString("Usage: frozenportal load normal|reduced|minimal|auto"); return; } HardwareProfile.ForcedLoad = ((args[2] == "auto") ? ((MeasuredLoad?)null) : new MeasuredLoad?(result3)); _status = "load " + args[2] + " (diagnostic, not saved)"; break; } case "buffers": if (args.Length != 3 || (args[2] != "on" && args[2] != "off")) { args.Context.AddString("Usage: frozenportal buffers on|off"); return; } PortalView.WriteVirtualBuffers = args[2] == "on"; _status = "portal depth and motion vectors " + (PortalView.WriteVirtualBuffers ? "written" : "not written (diagnostic)"); break; default: args.Context.AddString("Usage: frozenportal on|off|status|clear|refresh|backdropcheck|cleanup|lighting|buffers on|off|pair on|off|mainview on|off|load normal|reduced|minimal|auto|recapture <m>; profile auto|low|medium|high|ultra|custom; mode always|approach; count|radius|spiral|memory <number> (count and memory 0: the profile's)"); return; case "status": break; } args.Context.AddString($"Frozen portals (scene-v20): {_status}; {CountSnapshots()}/{MaxSnapshots} resident snapshots; " + string.Format("up to {0} views; {1}. ", MaxVisible, PortalPreviewPlugin.Cfg.FrozenPortalAlwaysVisible.Value ? "always" : ("approach " + PortalPreviewPlugin.Cfg.FrozenPortalApproachRadius.Value + " m")) + $"Snapshot updates: {PortalPreviewPlugin.Cfg.SnapshotUpdates.Value}; " + "Scene, grass, particles, clouds and world tree frozen; sun and fog " + (PortalPreviewPlugin.Cfg.FollowTimeOfDay.Value ? "follow the time of day" : "frozen") + "; disk snapshots retained."); args.Context.AddString(HardwareProfile.Describe()); if (PortalCleanup.LastReport != null) { args.Context.AddString("Last cleanup: " + PortalCleanup.LastReport); } string text4 = DescribeNearest(Utils.GetMainCamera()); args.Context.AddString(text4); PortalPreviewPlugin.Diag("[portal preview] status: " + text4); } internal int <CaptureOne>b__154_0(Endpoint a, Endpoint b) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0039: 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_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) Vector3 val = ((Component)Player.m_localPlayer).transform.position - ((Component)a.Portal).transform.position; float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude; val = ((Component)Player.m_localPlayer).transform.position - ((Component)b.Portal).transform.position; return sqrMagnitude.CompareTo(((Vector3)(ref val)).sqrMagnitude); } internal int <.cctor>b__193_0(Endpoint a, Endpoint b) { return DistanceSquaredToPlayer(a).CompareTo(DistanceSquaredToPlayer(b)); } } internal const double PlayCaptureBudgetMs = 4.0; private static SceneCaptureJob _sceneCapture; private static readonly Dictionary<ZDOID, Endpoint> Endpoints = new Dictionary<ZDOID, Endpoint>(); private static readonly List<ZDOID> Stale = new List<ZDOID>(); private static readonly Dictionary<ZDOID, PortalView> Views = new Dictionary<ZDOID, PortalView>(); private static readonly HashSet<ZDOID> VisibleTargets = new HashSet<ZDOID>(); private static readonly List<ArchivedScene> ArchivedScenes = new List<ArchivedScene>(); private static string _indexedArchiveDirectory; private static ZNet _session; private static int _layer = -1; private static bool _failed; private static float _maintenanceAt; private static string _status = "off"; private static string _lastViewDiagnostic; private static float _viewDiagnosticAt; private static float _statusAt; private static bool _renderAllowed; private static int _renderedFrame = -1; private const float ViewRetentionSeconds = 20f; private static readonly List<Endpoint> CandidateBuffer = new List<Endpoint>(); private static readonly List<ViewCandidate> Renderable = new List<ViewCandidate>(); private static readonly Plane[] FrustumPlanes = (Plane[])(object)new Plane[6]; private static readonly FieldInfo LoadedHeightmaps = AccessTools.Field(typeof(Heightmap), "s_heightmaps"); private static float _resumePreviewAt; private static IEnumerator<PortalSnapshot> _loadSteps; private static Endpoint _loading; private static long _loadStarted; private static double _loadWorkMs; private static ZDOID _arrival; private static ZDOID _teleportCapture; private static readonly TeleportCaptureGate TeleportGate = new TeleportCaptureGate(); private static Vector3 _departurePlayerPosition; private static readonly Dictionary<string, string> ArchiveDirectories = new Dictionary<string, string>(); private static bool _wasTeleporting; private static bool _screenlessTeleports; private static bool _screenSeenThisTeleport; private static double _frameCostMaxMs; private static readonly CaptureCostSamples CaptureCost = new CaptureCostSamples(); private static int _lastEyeCaptureFrame = -1000; private static readonly float[] FrameRing = new float[120]; private static readonly float[] FrameSorted = new float[120]; private static int _frameRingCount; private static int _frameRingNext; private static float _frameMedianMs; private static readonly Dictionary<string, float> NextStallReportAt = new Dictionary<string, float>(); private static float _nextSlowFrameLogAt; private static int _frameCostSlow; private static int _frameCostCaptures; private static double _frameCostMs; private static int _frameCostFrames; private static float _frameCostAt; private static readonly Comparison<Endpoint> NearestFirst = (Endpoint a, Endpoint b) => DistanceSquaredToPlayer(a).CompareTo(DistanceSquaredToPlayer(b)); private static readonly List<PortalRefreshScheduler.Request> RefreshRequests = new List<PortalRefreshScheduler.Request>(); private static readonly List<float> ScreenHeights = new List<float>(); private static ZDOID _mainView; private const float EyeCaptureRetentionSeconds = 2f; private static readonly Dictionary<ZDOID, AtmosphereState> Atmospheres = new Dictionary<ZDOID, AtmosphereState>(); private static readonly List<ZDOID> EndedAtmospheres = new List<ZDOID>(); private static readonly List<PortalSnapshot> RenderedScenes = new List<PortalSnapshot>(); private static readonly List<PortalViewBudget.Entry<Endpoint>> ViewBudget = new List<PortalViewBudget.Entry<Endpoint>>(); private static readonly RaycastHit[] OcclusionHits = (RaycastHit[])(object)new RaycastHit[8]; private static int _occlusionMask = -1; private static IEnumerator<PortalCapture> _captureSteps; private static PortalSnapshot _capturing; private const float RelightSpacing = 10f; private static bool _lightingFailed; private const string BuildModeWait = "waiting for build mode to end (put the hammer, hoe or cultivator away)"; private static float _stayStartedAt = float.NegativeInfinity; internal const float RecentCaptureSeconds = 120f; private static bool _missedDepartureReported; private static (bool Refresh, CaptureWhen When, bool FirstLink) _departurePending; internal const float ArrivalHoldSeconds = 4f; private const float ArrivalSettleSeconds = 0.5f; private static float _arrivalHoldUntil; private static bool _arrivalHoldDone; private static int _arrivalInstances = -1; private static float _arrivalInstancesSince; private static string _viewDiagnostics; private static float _diagnosticsUntil; private static ZDOID _instantArrival; private static ZDOID _arrivalPreparationFailed; private static float _instantArrivalUntil; private const float InstantArrivalSeconds = 60f; private const float InstantArrivalGrace = 3f; private static string ArchiveDirectory => ArchiveDirectoryFor(SnapshotRoot, ZNet.instance.GetWorldUID().ToString()); private static int MaxVisible => Mathf.Clamp(HardwareProfile.MaxVisiblePortals, 1, 16); private static int MaxSnapshots => Mathf.Max(MaxVisible + 1, Mathf.Clamp(HardwareProfile.MaxResidentSnapshots, 2, 128)); internal static string SnapshotRoot => Path.Combine(Paths.BepInExRootPath, "cache", "PortalPreview", "PortalSnapshots"); internal static string LegacySnapshotRoot => Path.Combine(Paths.BepInExRootPath, "cache", "FastLoading", "PortalSnapshots"); public static bool Enabled { get { if (PortalPreviewPlugin.Cfg != null && PortalPreviewPlugin.Cfg.Enabled.Value) { return !_failed; } return false; } } private static int CaptureInterval => CaptureBudgetPolicy.MinFramesBetween(HardwareProfile.MeasuredValid, HardwareProfile.MeasuredCostMs, HardwareProfile.MeasuredBaseMs); private static float CaptureIdleSeconds => HardwareProfile.CaptureIdleSeconds; private static float FingerprintRadius => Mathf.Clamp(PortalPreviewPlugin.Cfg.FrozenPortalRadius.Value, 32f, 128f); private static bool ArrivalOnly { get { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) if (_instantArrival != ZDOID.None) { return Time.realtimeSinceStartup < _resumePreviewAt; } return false; } } private static bool Ready(Endpoint endpoint) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0062: 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_0108: 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_00fa: 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_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)endpoint.Portal == (Object)null || (Object)(object)ZNetScene.instance == (Object)null) { endpoint.WaitReason = "portal object is not loaded"; return false; } if (!ZNetScene.instance.IsAreaReady(((Component)endpoint.Portal).transform.position)) { endpoint.WaitReason = "waiting for the zone to finish loading"; return false; } float realtimeSinceStartup = Time.realtimeSinceStartup; Vector3 position = ((Component)endpoint.Portal).transform.position; float num = Mathf.Clamp(PortalPreviewPlugin.Cfg.FrozenPortalRadius.Value, 32f, 128f); IEnumerable<Heightmap> enumerable = LoadedHeightmaps?.GetValue(null) as List<Heightmap>; foreach (Heightmap item in enumerable ?? Object.FindObjectsByType<Heightmap>((FindObjectsSortMode)0)) { if (!((Object)(object)item == (Object)null) && !item.IsDistantLod && item.m_doLateUpdate != 0) { Renderer component = (Renderer)(object)((Component)item).GetComponent<MeshRenderer>(); Bounds val = (Bounds)(((Object)(object)component != (Object)null) ? component.bounds : new Bounds(((Component)item).transform.position, Vector3.zero)); if (((Bounds)(ref val)).SqrDistance(position) <= num * num) { endpoint.StableAt = Mathf.Max(endpoint.StableAt, realtimeSinceStartup + 2f); endpoint.WaitReason = "waiting for modified terrain to finish rebuilding"; return false; } } } if (realtimeSinceStartup < endpoint.StableAt) { endpoint.WaitReason = $"settling loaded scene ({Mathf.CeilToInt(endpoint.StableAt - realtimeSinceStartup)} s)"; return false; } endpoint.WaitReason = null; return true; } private static void RecordFingerprint(Endpoint capture, ulong? measured) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) capture.FingerprintKnown = true; capture.Fingerprint = measured; if (!capture.Fingerprint.HasValue) { return; } try { PortalSceneChanges.Store(PortalSnapshot.ArchivePath(ArchiveDirectory, ((object)Unsafe.As<ZDOID, ZDOID>(ref capture.Id)/*cast due to .constrained prefix*/).ToString()), capture.Fingerprint.Value); } catch (Exception ex) { PortalPreviewPlugin.Log.LogWarning((object)($"[portal preview] What stands around {capture.Id} could not be saved ({ex.Message}); " + "it is captured again on the next trip.")); } } private static float CaptureRadiusFor(Endpoint capture) { float num = Mathf.Clamp(PortalPreviewPlugin.Cfg.FrozenPortalRadius.Value, 32f, 128f); if (!(capture.CaptureRadius > 0f)) { return num; } return Mathf.Min(num, capture.CaptureRadius); } private static void CaptureEndpoint(Endpoint capture) { SceneCaptureJob job = NewJob(capture, CaptureBudget.Unlimited); while (!Advance(job)) { } } private static void StartSceneCapture(Endpoint capture) { AbortSceneCapture(null); _sceneCapture = NewJob(capture, new CaptureBudget(4.0)); StepSceneCapture(); } private static SceneCaptureJob NewJob(Endpoint capture, CaptureBudget budget) { SceneCaptureJob obj = new SceneCaptureJob { Capture = capture, Budget = budget, Started = Stopwatch.GetTimestamp() }; obj.Steps = CaptureSteps(obj); return obj; } private static void StepSceneCapture() { //IL_0015: Unknown result type (might be due to invalid IL or missing references) SceneCaptureJob sceneCapture = _sceneCapture; if (sceneCapture != null) { if (!Endpoints.TryGetValue(sceneCapture.Capture.Id, out var value) || value != sceneCapture.Capture || (!sceneCapture.SceneTaken && (Object)(object)sceneCapture.Capture.Portal == (Object)null)) { AbortSceneCapture("its portal was unloaded"); } else if (!sceneCapture.SceneTaken && InBuildMode()) { sceneCapture.Capture.WaitReason = "waiting for build mode to end (put the hammer, hoe or cultivator away)"; AbortSceneCapture("build mode began"); } else if (Advance(sceneCapture)) { _sceneCapture = null; } } } private static void AbortSceneCapture(string reason) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) SceneCaptureJob sceneCapture = _sceneCapture; if (sceneCapture != null && (reason == null || !sceneCapture.SceneTaken)) { _sceneCapture = null; sceneCapture.Steps.Dispose(); if (reason != null) { PortalPreviewPlugin.Diag($"[portal preview] Capture of {sceneCapture.Capture.Id} stopped after {sceneCapture.Frames} frames ({reason}); it is captured later."); } } } private static bool Advance(SceneCaptureJob job) { job.Budget.Restart(); long timestamp = Stopwatch.GetTimestamp(); bool flag; try { flag = job.Steps.MoveNext(); } catch (Exception exception) { job.Steps.Dispose(); CaptureFailed(job, exception); return true; } finally { double num = (double)(Stopwatch.GetTimestamp() - timestamp) * 1000.0 / (double)Stopwatch.Frequency; if (num > job.LongestStepMs) { job.LongestStepMs = num; if (!job.Budget.IsUnlimited) { job.LongestStepDetail = StallProfile.Describe(); } } } if (!flag) { job.Steps.Dispose(); return true; } job.Frames++; return false; } private static double SinceMs(long started) { return (double)(Stopwatch.GetTimestamp() - started) * 1000.0 / (double)Stopwatch.Frequency; } private static IEnumerator<bool> CaptureSteps(SceneCaptureJob job) { Endpoint capture = job.Capture; CaptureBudget budget = job.Budget; EnsureLayer(); float radius = CaptureRadiusFor(capture); ulong? fingerprint = PortalSceneChanges.Measure(((Component)capture.Portal).transform.position, FingerprintRadius); PortalSnapshot fresh = null; try { using (IEnumerator<PortalSnapshot> steps = PortalSnapshot.CaptureSteps(((Component)capture.Portal).transform.position, ((Component)capture.Portal).transform.rotation, radius, _layer, budget, null, null, PortalShape.For(((Component)capture.Portal).transform))) { while (steps.MoveNext()) { if (steps.Current != null) { fresh = steps.Current; break; } yield return true; } } if (fresh == null) { throw new InvalidOperationException("Portal capture ended without a scene."); } job.SceneTaken = true; double captureMs = SinceMs(job.Started); string text = PortalSnapshot.ArchivePath(ArchiveDirectory, ((object)Unsafe.As<ZDOID, ZDOID>(ref capture.Id)/*cast due to .constrained prefix*/).ToString()); if (File.Exists(text)) { Vector3 position; Quaternion rotation; int version; bool flag = PortalSnapshot.TryReadArchiveHeader(text, ((object)Unsafe.As<ZDOID, ZDOID>(ref capture.Id)/*cast due to .constrained prefix*/).ToString(), out position, out rotation, out version); int num; if (flag) { Vector3 val = position - fresh.Position; num = ((((Vector3)(ref val)).sqrMagnitude > 0.25f || Quaternion.Angle(rotation, fresh.Rotation) > 1f) ? 1 : 0); } else { num = 0; } if (num != 0) { string text2 = text + ".reassigned-" + Guid.NewGuid().ToString("N"); File.Copy(text, text2); if (_indexedArchiveDirectory == ArchiveDirectory) { IndexArchivedScene(text2); } } else if (!flag || version < 24) { string text3 = text + ".legacy"; if (!File.Exists(text3)) { File.Copy(text, text3); } if (_indexedArchiveDirectory == ArchiveDirectory) { IndexArchivedScene(text3); } } } StallProfile.Mark("capture: archive bookkeeping"); if (budget.Spent) { yield return true; } string saveError = null; using (IEnumerator<bool> save = fresh.SaveSteps(ArchiveDirectory, ((object)Unsafe.As<ZDOID, ZDOID>(ref capture.Id)/*cast due to .constrained prefix*/).ToString(), ((object)Unsafe.As<ZDOID, ZDOID>(ref capture.Target)/*cast due to .constrained prefix*/).ToString(), budget)) { while (true) { bool flag2; try { flag2 = save.MoveNext(); StallProfile.Mark("save"); } catch (ArchiveWriteException ex) { saveError = ex.Message; break; } if (!flag2) { break; } yield return true; } } if (saveError == null && _indexedArchiveDirectory == ArchiveDirectory) { IndexArchivedScene(PortalSnapshot.ArchivePath(ArchiveDirectory, ((object)Unsafe.As<ZDOID, ZDOID>(ref capture.Id)/*cast due to .constrained prefix*/).ToString())); } capture.CopiesPending = saveError == null && fresh.DeferredTextureCopies > 0; if (saveError != null) { capture.FingerprintKnown = true; capture.Fingerprint = null; PortalSceneChanges.Clear(PortalSnapshot.ArchivePath(ArchiveDirectory, ((object)Unsafe.As<ZDOID, ZDOID>(ref capture.Id)/*cast due to .constrained prefix*/).ToString())); if (capture.LastError != saveError) { PortalPreviewPlugin.Log.LogWarning((object)($"[portal preview] Portal {capture.Id} is shown but its scene was not saved " + "(" + saveError + "); it is captured again on the next trip.")); } } else if (capture.CopiesPending) { capture.FingerprintKnown = true; capture.Fingerprint = null; PortalSceneChanges.Clear(PortalSnapshot.ArchivePath(ArchiveDirectory, ((object)Unsafe.As<ZDOID, ZDOID>(ref capture.Id)/*cast due to .constrained prefix*/).ToString())); PortalPreviewPlugin.Log.LogInfo((object)($"[portal preview] {capture.Id}: {fresh.DeferredTextureCopies} texture copies are " + "saved on its next captures (large texture pack); it is captured again on the next trip.")); } else { RecordFingerprint(capture, fingerprint); } double savedMs = SinceMs(job.Started); StallProfile.Mark("save: fingerprint, index"); if (budget.Spent) { yield return true; } using (IEnumerator<bool> save = fresh.PrepareSteps(budget)) { while (save.MoveNext()) { yield return true; } } StallProfile.Mark("prepare for render"); double num2 = SinceMs(job.Started); if (_viewDiagnostics != null) { string viewDiagnostics = _viewDiagnostics; ForgetDiagnostics(); try { if ((Object)(object)capture.Portal != (Object)null) { PortalDiagnostics.CompareView(viewDiagnostics, fresh, ((Component)capture.Portal).transform, _layer); } } catch (Exception ex2) { PortalPreviewPlugin.Log.LogWarning((object)("[portal preview] View comparison failed: " + ex2.Message)); } } if (_loading == capture) { StopLoad(); } MakeRoom(capture); capture.Snapshot?.Dispose(); capture.Snapshot = fresh; capture.SnapshotVerified = true; fresh = null; FollowTimeOfDay(capture, fresh: true); capture.LastUsed = Time.realtimeSinceStartup; capture.Refresh = false; capture.Reported = false; capture.CapturedAt = Time.realtimeSinceStartup; capture.When = CaptureWhen.Now; capture.SkipArchiveLoad = false; if (saveError == null) { capture.LoadRetryAt = 0f; } capture.FirstLinkCapture = false; capture.LastError = saveError; capture.WaitReason = null; capture.Failures = 0; string text4 = ((job.Frames > 0) ? $" over {job.Frames + 1} frames (longest step {job.LongestStepMs:F0} ms: {job.LongestStepDetail})" : ""); PortalPreviewPlugin.Diag($"[portal preview] Captured loaded exit {capture.Id} -> {capture.Target}: " + $"{capture.Snapshot.MeshCount} meshes, {capture.Snapshot.UniqueVertices} unique / {capture.Snapshot.Vertices} placed vertices, " + $"{capture.Snapshot.GrassCount} grass, {capture.Snapshot.TerrainCount} terrain tiles, " + $"{capture.Snapshot.ParticleCount} frozen particle systems, " + $"{capture.Snapshot.WeatherParticleCount} camera-centred weather systems, " + $"{capture.Snapshot.SkyBackdropCount} cloud/world-tree renderers in the backdrop, " + $"{capture.Snapshot.WaterBackdropCount} water renderers in the backdrop, " + $"{capture.Snapshot.SkippedVisuals} invalid visuals skipped. Saved with frozen lighting. " + $"Capture/save/prepare {captureMs:F0}/{savedMs - captureMs:F0}/{num2 - savedMs:F0} ms{text4}; save {capture.Snapshot.LastSaveTimings}."); StallProfile.Note($"captured {capture.Id} ({capture.Snapshot.MeshCount} meshes, {capture.Snapshot.UniqueVertices} unique vertices): " + $"capture/save/prepare {captureMs:F0}/{savedMs - captureMs:F0}/{num2 - savedMs:F0} ms{text4}, save {capture.Snapshot.LastSaveTimings}"); } finally { fresh?.Dispose(); } } private static void CaptureFailed(SceneCaptureJob job, Exception exception) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) Endpoint capture = job.Capture; float num = CaptureRadiusFor(capture); StallProfile.Note($"capture of {capture.Id} failed after {SinceMs(job.Started):F0} ms" + ((job.Frames > 0) ? $" over {job.Frames + 1} frames" : "")); capture.Failures++; float num2 = CaptureRetry.SmallerRadius(num); bool flag = exception is SnapshotLimitException && num2 < num; if (flag) { capture.CaptureRadius = num2; } float num3 = (flag ? 2f : CaptureRetry.Delay(capture.Failures)); capture.RetryAt = Time.realtimeSinceStartup + num3; if (capture.LastError != exception.Message) { PortalPreviewPlugin.Log.LogError((object)($"[portal preview] Portal {capture.Id} could not be captured: {exception.Message} " + (flag ? $"Trying again with a {num2:F0} m radius." : $"Trying again in {num3:F0} s.") + ((exception is SnapshotLimitException || exception.Message.StartsWith("There is no", StringComparison.Ordinal)) ? "" : (Environment.NewLine + exception.StackTrace)))); } capture.LastError = exception.Message; capture.Reported = true; } public static void Initialize() { //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Expected O, but got Unknown PortalPreviewPlugin.Diag(HardwareProfile.Describe()); PortalView.BindWaterReflection(null); PortalPreviewPlugin.Cfg.FrozenPortalRadius.SettingChanged += ResetCaptureRadii; PortalPreviewPlugin.Cfg.FrozenPortalMaxRenderers.SettingChanged += ResetCaptureRadii; PortalPreviewPlugin.Cfg.FrozenPortalMaxUniqueVertices.SettingChanged += ResetCaptureRadii; object obj = <>c.<>9__15_0; if (obj == null) { ConsoleEvent val = delegate(ConsoleEventArgs args) { //IL_04c3: Unknown result type (might be due to invalid IL or missing references) string text = ((args.Length > 1) ? args[1].ToLowerInvariant() : "status"); switch (text) { case "on": Clear(); PortalPreviewPlugin.Cfg.Enabled.Value = true; _status = "enabled; waiting for a loaded wooden or stone portal"; break; case "off": PortalPreviewPlugin.Cfg.Enabled.Value = false; Clear(); break; case "clear": Clear(); _status = "RAM cache cleared; saved snapshots retained on disk"; break; case "backdropcheck": { if (RefreshNearby() == 0) { args.Context.AddString("No connected portal within 24 m to capture."); return; } string text3 = Path.Combine(Paths.BepInExRootPath, "cache", "PortalPreview", "diagnostics", "backdrop-" + DateTime.Now.ToString("yyyyMMdd-HHmmss")); Directory.CreateDirectory(text3); PortalSnapshot.BackdropDiagnostics = text3; _viewDiagnostics = text3; _diagnosticsUntil = Time.realtimeSinceStartup + 60f; args.Context.AddString("The next capture of a nearby portal writes a panorama and view comparison to " + text3); break; } case "refresh": RefreshNearby(); break; case "log": if (args.Length != 3 || (args[2] != "on" && args[2] != "off")) { args.Context.AddString("Usage: frozenportal log on|off"); } else { PortalPreviewPlugin.Verbose = args[2] == "on"; args.Context.AddString("Portal diagnostics " + (PortalPreviewPlugin.Verbose ? "enabled" : "disabled") + " for this session."); } return; case "lighting": if ((Object)(object)Player.m_localPlayer == (Object)null) { args.Context.AddString("Enter a world first."); } else { string text2 = "Lighting computed/live here: " + DestinationLighting.CompareWithLive(((Component)Player.m_localPlayer).transform.position); args.Context.AddString(text2); PortalPreviewPlugin.Log.LogInfo((object)("[portal preview] " + text2)); } return; case "cleanup": if ((Object)(object)ZNet.instance == (Object)null) { args.Context.AddString("Enter a world first."); return; } PortalCleanup.Start(ArchiveDirectory); _status = "cleanup of stale saved scenes started; 'frozenportal status' shows the result"; break; case "mode": if (args.Length != 3 || (args[2] != "always" && args[2] != "approach")) { args.Context.AddString("Usage: frozenportal mode always|approach"); return; } PortalPreviewPlugin.Cfg.FrozenPortalAlwaysVisible.Value = args[2] == "always"; break; case "profile": { if (args.Length != 3 || !Enum.TryParse<HardwareProfileSetting>(args[2], ignoreCase: true, out var result)) { args.Context.AddString("Usage: frozenportal profile auto|low|medium|high|ultra|custom"); return; } PortalPreviewPlugin.Cfg.HardwareProfile.Value = result; PreviewConfig cfg = PortalPreviewPlugin.Cfg; if (cfg.FrozenPortalResolution.Value > 0 || cfg.FrozenPortalMaxVisible.Value > 0 || cfg.FrozenPortalMaxResident.Value > 0) { cfg.FrozenPortalResolution.Value = 0; cfg.FrozenPortalMaxVisible.Value = 0; cfg.FrozenPortalMaxResident.Value = 0; args.Context.AddString("Resolution, Max visible portals and Max resident snapshots follow the profile again (set to 0)."); } break; } case "count": case "spiral": case "radius": case "memory": { if (args.Length != 3 || !float.TryParse(args[2].Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out var result2) || float.IsNaN(result2) || float.IsInfinity(result2)) { args.Context.AddString("Usage: frozenportal " + text + " <number>"); return; } if (text == "count") { PortalPreviewPlugin.Cfg.FrozenPortalMaxVisible.Value = ((!(result2 <= 0f)) ? Mathf.Clamp(Mathf.RoundToInt(result2), 1, 16) : 0); } if (text == "memory") { PortalPreviewPlugin.Cfg.FrozenPortalMaxResident.Value = ((!(result2 <= 0f)) ? Mathf.Clamp(Mathf.RoundToInt(result2), 2, 128) : 0); } if (text == "radius") { PortalPreviewPlugin.Cfg.FrozenPortalApproachRadius.Value = Mathf.Clamp(result2, 1f, 100f); } if (text == "spiral") { PortalPreviewPlugin.Cfg.FrozenPortalRevealSeconds.Value = Mathf.Clamp(result2, 0f, 5f); } break; } case "recapture": { if (args.Length != 3 || !float.TryParse(args[2].Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out var result4) || result4 < 0.02f || result4 > 1f) { args.Context.AddString("Usage: frozenportal recapture <metres, 0.02-1; 0.3 by default>"); return; } PortalView.EyeRecaptureDistance = result4; _status = $"eye recapture every {result4:F2} m (diagnostic, not saved)"; break; } case "pair": if (args.Length != 3 || (args[2] != "on" && args[2] != "off")) { args.Context.AddString("Usage: frozenportal pair on|off"); return; } PortalView.EyePrediction = args[2] == "on"; _status = "eye capture pair " + args[2] + " (diagnostic, not saved)"; break; case "mainview": if (args.Length != 3 || (args[2] != "on" && args[2] != "off")) { args.Context.AddString("Usage: frozenportal mainview on|off"); return; } PortalView.MainViewOnly = args[2] == "on"; _status = "main view only " + args[2] + " (diagnostic, not saved)"; break; case "load": { if (args.Length != 3 || (!Enum.TryParse<MeasuredLoad>(args[2], ignoreCase: true, out var result3) && args[2] != "auto")) { args.Context.AddString("Usage: frozenportal load normal|reduced|minimal|auto"); return; } HardwareProfile.ForcedLoad = ((args[2] == "auto") ? ((MeasuredLoad?)null) : new MeasuredLoad?(result3)); _status = "load " + args[2] + " (diagnostic, not saved)"; break; } case "buffers": if (args.Length != 3 || (args[2] != "on" && args[2] != "off")) { args.Context.AddString("Usage: frozenportal buffers on|off"); return; } PortalView.WriteVirtualBuffers = args[2] == "on"; _status = "portal depth and motion vectors " + (PortalView.WriteVirtualBuffers ? "written" : "not written (diagnostic)"); break; default: args.Context.AddString("Usage: frozenportal on|off|status|clear|refresh|backdropcheck|cleanup|lighting|buffers on|off|pair on|off|mainview on|off|load normal|reduced|minimal|auto|recapture <m>; profile auto|low|medium|high|ultra|custom; mode always|approach; count|radius|spiral|memory <number> (count and memory 0: the profile's)"); return; case "status": break; } args.Context.AddString($"Frozen portals (scene-v20): {_status}; {CountSnapshots()}/{MaxSnapshots} resident snapshots; " + string.Format("up to {0} views; {1}. ", MaxVisible, PortalPreviewPlugin.Cfg.FrozenPortalAlwaysVisible.Value ? "always" : ("approach " + PortalPreviewPlugin.Cfg.FrozenPortalApproachRadius.Value + " m")) + $"Snapshot updates: {PortalPreviewPlugin.Cfg.SnapshotUpdates.Value}; " + "Scene, grass, particles, clouds and world tree frozen; sun and fog " + (PortalPreviewPlugin.Cfg.FollowTimeOfDay.Value ? "follow the time of day" : "frozen") + "; disk snapshots retained."); args.Context.AddString(HardwareProfile.Describe()); if (PortalCleanup.LastReport != null) { args.Context.AddString("Last cleanup: " + PortalCleanup.LastReport); } string text4 = DescribeNearest(Utils.GetMainCamera()); args.Context.AddString(text4); PortalPreviewPlugin.Diag("[portal preview] status: " + text4); }; <>c.<>9__15_0 = val; obj = (object)val; } new ConsoleCommand("frozenportal", "Frozen 3D portals: on, off, status, clear (RAM), refresh (nearby)", (ConsoleEvent)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); } internal static string ArchiveDirectoryFor(string root, string world) { string key = root + "|" + world; if (ArchiveDirectories.TryGetValue(key, out var value)) { return value; } string text = Path.Combine(root, world, PortalSnapshot.RevisionFolder); string text2 = Path.Combine(root, world, PortalSnapshot.GameRevision); if (!Directory.Exists(text) && Directory.Exists(text2)) { try { Directory.Move(text2, text); } catch (Exception ex) { PortalPreviewPlugin.Log.LogError((object)("[portal preview] Could not shorten the snapshot folder " + text2 + " (" + ex.Message + "); it stays in use.")); text = text2; } } ArchiveDirectories[key] = text; return text; } internal static void AdoptLegacySnapshots() { try { if (Directory.Exists(LegacySnapshotRoot) && !Directory.Exists(SnapshotRoot)) { Directory.CreateDirectory(Path.GetDirectoryName(SnapshotRoot)); Directory.Move(LegacySnapshotRoot, SnapshotRoot); PortalPreviewPlugin.Diag("[portal preview] Snapshots saved by FastLoading moved to " + SnapshotRoot); } } catch (Exception ex) { PortalPreviewPlugin.Log.LogError((object)("[portal preview] Could not move FastLoading's snapshots (" + ex.Message + "); portals are captured again.")); } } private static string DescribeNearest(Camera viewer) { //IL_0062: 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_007f: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //I