using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Logging;
using Microsoft.CodeAnalysis;
using Mirror;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: IgnoresAccessChecksTo("")]
[assembly: AssemblyCompany("ValhATLYSS")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+f7c5f081f7b5148cfa891a52698a5ac58702a1c1")]
[assembly: AssemblyProduct("ValhATLYSS")]
[assembly: AssemblyTitle("ValhATLYSS")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.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 ValhATLYSS
{
internal static class HoddKista
{
private struct VaultModel
{
public int Level;
public long Exp;
}
private static string _root;
private static readonly Regex RxLevel = new Regex("(?im)^\\s*level\\s*=\\s*(?<n>-?\\d+)\\s*$", RegexOptions.Compiled | RegexOptions.CultureInvariant);
private static readonly Regex RxExp = new Regex("(?im)^\\s*exp\\s*=\\s*(?<n>-?\\d+)\\s*$", RegexOptions.Compiled | RegexOptions.CultureInvariant);
internal static void EnsureReady(ManualLogSource log)
{
try
{
string profilesRoot = Plugin.GetProfilesRoot();
if (!string.IsNullOrEmpty(profilesRoot))
{
_root = Path.Combine(profilesRoot, "ValhATLYSS", "vault");
Directory.CreateDirectory(_root);
if (log != null)
{
log.LogInfo((object)("[ValhATLYSS] Vault ready at: " + _root));
}
}
}
catch (Exception ex)
{
if (log != null)
{
log.LogWarning((object)("[ValhATLYSS] Vault setup failed: " + ex.Message));
}
}
}
private static string VaultPathForProfile(string profileFullPath)
{
string text = Path.GetFileName(profileFullPath) ?? "unknown_profile";
return Path.Combine(_root ?? "", text + ".json");
}
private static bool LoadVault(string vp, out VaultModel model)
{
model = new VaultModel
{
Level = 0,
Exp = -1L
};
try
{
if (!File.Exists(vp))
{
return false;
}
string input;
using (FileStream stream = new FileStream(vp, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
using StreamReader streamReader = new StreamReader(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks: true);
input = streamReader.ReadToEnd();
}
Match match = RxLevel.Match(input);
Match match2 = RxExp.Match(input);
if (match.Success && int.TryParse(match.Groups["n"].Value, out var result))
{
model.Level = Math.Max(0, result);
}
if (match2.Success && long.TryParse(match2.Groups["n"].Value, out var result2))
{
model.Exp = ((result2 < 0) ? (-1) : result2);
}
return match.Success || match2.Success;
}
catch
{
return false;
}
}
private static void SaveVault(string vp, VaultModel model)
{
try
{
StringBuilder stringBuilder = new StringBuilder(64);
stringBuilder.Append("level=").Append(model.Level).Append('\n');
stringBuilder.Append("exp=").Append(model.Exp).Append('\n');
string text = vp + ".tmp";
File.WriteAllText(text, stringBuilder.ToString(), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
File.Copy(text, vp, overwrite: true);
File.Delete(text);
}
catch
{
}
}
internal static int Reconcile(string profilePath, KappaSlot.DiskStats disk, ManualLogSource log)
{
if (string.IsNullOrEmpty(_root))
{
return 0;
}
string vp = VaultPathForProfile(profilePath);
LoadVault(vp, out var model);
bool num = disk.HasLevel || disk.HasExp;
bool flag = model.Level > 0 || model.Exp >= 0;
if (!num && !flag)
{
if (log != null)
{
log.LogInfo((object)"[ValhATLYSS] Reconcile: nothing to do (no readable stats).");
}
return 0;
}
int num2 = (disk.HasLevel ? disk.Level : 0);
long num3 = (disk.HasExp ? disk.Exp : (-1));
int level = model.Level;
long exp = model.Exp;
if (level > num2 || (level == num2 && exp >= 0 && num3 >= 0 && exp > num3))
{
if (disk.HasLevel || disk.HasExp)
{
KappaSlot.DiskStats diskStats = default(KappaSlot.DiskStats);
diskStats.Level = ((level > 0) ? level : num2);
diskStats.Exp = ((exp >= 0) ? exp : num3);
diskStats.LevelPatternUsed = disk.LevelPatternUsed;
diskStats.ExpPatternUsed = disk.ExpPatternUsed;
KappaSlot.DiskStats target = diskStats;
if (KappaSlot.TryWriteProfileStats(profilePath, target, log))
{
if (log != null)
{
log.LogInfo((object)"[ValhATLYSS] Disk was restored from vault (anti-regression).");
}
SaveVault(vp, new VaultModel
{
Level = Math.Max(level, target.Level),
Exp = Math.Max(exp, target.Exp)
});
return -1;
}
if (log != null)
{
log.LogInfo((object)"[ValhATLYSS] Vault better, but safe patch failed (pattern not found).");
}
return 0;
}
if (log != null)
{
log.LogInfo((object)"[ValhATLYSS] Vault better but disk tokens not recognized; skip write.");
}
return 0;
}
VaultModel vaultModel = default(VaultModel);
vaultModel.Level = Math.Max(level, num2);
vaultModel.Exp = Math.Max(exp, num3);
VaultModel model2 = vaultModel;
SaveVault(vp, model2);
return 1;
}
}
internal static class KappaSlot
{
internal struct DiskStats
{
public int Level;
public long Exp;
public string LevelPatternUsed;
public string ExpPatternUsed;
public bool HasLevel
{
get
{
if (Level > 0)
{
return !string.IsNullOrEmpty(LevelPatternUsed);
}
return false;
}
}
public bool HasExp
{
get
{
if (Exp >= 0)
{
return !string.IsNullOrEmpty(ExpPatternUsed);
}
return false;
}
}
}
private static bool TryParseStatsFromText(string text, out DiskStats stats, ManualLogSource log)
{
stats = default(DiskStats);
if (string.IsNullOrWhiteSpace(text))
{
return false;
}
string[] array = new string[4] { "level", "mainLevel", "playerLevel", "lvl" };
string[] array2 = new string[5] { "exp", "experience", "mainExp", "xp", "totalExp" };
string[] array3 = array;
for (int i = 0; i < array3.Length; i++)
{
string text2 = MakeNumberPattern(array3[i]);
Match match = Regex.Match(text, text2, RegexOptions.CultureInvariant);
if (match.Success && int.TryParse(match.Groups["num"].Value, out var result) && result > 0)
{
stats.Level = result;
stats.LevelPatternUsed = text2;
break;
}
}
array3 = array2;
for (int i = 0; i < array3.Length; i++)
{
string text3 = MakeNumberPattern(array3[i]);
Match match2 = Regex.Match(text, text3, RegexOptions.CultureInvariant);
if (match2.Success && long.TryParse(match2.Groups["num"].Value, out var result2) && result2 >= 0)
{
stats.Exp = result2;
stats.ExpPatternUsed = text3;
break;
}
}
int num;
if (!stats.HasLevel)
{
num = (stats.HasExp ? 1 : 0);
if (num == 0 && log != null)
{
log.LogInfo((object)"[ValhATLYSS] Parser: no Level/Exp tokens matched. Profile format likely changed.");
}
}
else
{
num = 1;
}
return (byte)num != 0;
static string MakeNumberPattern(string key)
{
return "(?i)(?:[\"']?" + Regex.Escape(key) + "[\"']?\\s*[:=]\\s*)(?<num>-?\\d+)";
}
}
internal static bool TryReadProfileStats(string path, out DiskStats stats, ManualLogSource log)
{
stats = default(DiskStats);
if (string.IsNullOrWhiteSpace(path) || !File.Exists(path))
{
if (log != null)
{
log.LogWarning((object)("[ValhATLYSS] Parser: file not found: " + path));
}
return false;
}
string text;
try
{
using FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
using StreamReader streamReader = new StreamReader(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks: true);
text = streamReader.ReadToEnd();
}
catch (Exception ex)
{
if (log != null)
{
log.LogWarning((object)("[ValhATLYSS] Parser: read failed: " + ex.Message));
}
return false;
}
if (!TryParseStatsFromText(text, out stats, log))
{
return false;
}
if (stats.Level <= 0)
{
stats.Level = 0;
}
if (stats.Exp < 0)
{
stats.Exp = -1L;
}
if (!stats.HasLevel)
{
return stats.HasExp;
}
return true;
}
internal static bool TryWriteProfileStats(string path, DiskStats target, ManualLogSource log)
{
if (string.IsNullOrWhiteSpace(path) || !File.Exists(path))
{
return false;
}
string text;
try
{
using FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
using StreamReader streamReader = new StreamReader(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks: true);
text = streamReader.ReadToEnd();
}
catch (Exception ex)
{
if (log != null)
{
log.LogWarning((object)("[ValhATLYSS] Writer: read failed: " + ex.Message));
}
return false;
}
bool flag = false;
if (target.HasLevel)
{
Regex regex = new Regex(target.LevelPatternUsed, RegexOptions.CultureInvariant);
if (regex.Match(text).Success)
{
string text2 = regex.Replace(text, (Match match) => match.Value.Substring(0, match.Value.LastIndexOf(match.Groups["num"].Value, StringComparison.Ordinal)) + target.Level, 1);
if ((object)text2 != text)
{
text = text2;
flag = true;
}
}
else if (log != null)
{
log.LogInfo((object)"[ValhATLYSS] Writer: Level pattern no longer matches; skip safe write.");
}
}
if (target.HasExp)
{
Regex regex2 = new Regex(target.ExpPatternUsed, RegexOptions.CultureInvariant);
if (regex2.Match(text).Success)
{
string text3 = regex2.Replace(text, (Match match) => match.Value.Substring(0, match.Value.LastIndexOf(match.Groups["num"].Value, StringComparison.Ordinal)) + target.Exp, 1);
if ((object)text3 != text)
{
text = text3;
flag = true;
}
}
else if (log != null)
{
log.LogInfo((object)"[ValhATLYSS] Writer: Exp pattern no longer matches; skip safe write.");
}
}
if (!flag)
{
return false;
}
try
{
string text4 = path + ".valh_tmp";
File.WriteAllText(text4, text, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
File.Copy(text4, path, overwrite: true);
File.Delete(text4);
return true;
}
catch (Exception ex2)
{
if (log != null)
{
log.LogWarning((object)("[ValhATLYSS] Writer: write failed: " + ex2.Message));
}
return false;
}
}
}
[BepInPlugin("cconx.ValhATLYSS", "ValhATLYSS", "0.4.19")]
public sealed class Plugin : BaseUnityPlugin
{
private sealed class Seen
{
public DateTime LastWriteUtc;
public long LastSize;
public int ContentHash;
public int LastLevel;
public long LastExp;
public DateTime PendingUntilUtc;
}
[CompilerGenerated]
private sealed class <Bootstrap>d__11 : IEnumerator<object>, IEnumerator, IDisposable
{
private int <>1__state;
private object <>2__current;
public Plugin <>4__this;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <Bootstrap>d__11(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<>1__state = -2;
}
private bool MoveNext()
{
//IL_0024: Unknown result type (might be due to invalid IL or missing references)
//IL_002e: Expected O, but got Unknown
int num = <>1__state;
Plugin plugin = <>4__this;
switch (num)
{
default:
return false;
case 0:
<>1__state = -1;
<>2__current = (object)new WaitForSeconds(2f);
<>1__state = 1;
return true;
case 1:
{
<>1__state = -1;
plugin._bootstrapped = true;
HoddKista.EnsureReady(Log);
((MonoBehaviour)plugin).StartCoroutine(plugin.EnforceServerCapForever());
((MonoBehaviour)plugin).StartCoroutine(plugin.ProfilePoller());
if (TryGetMostRecentProfile(out var fullPath))
{
Log.LogInfo((object)("[ValhATLYSS] Initial reconcile of most recent profile: " + fullPath));
plugin.TryReconcileProfile(fullPath);
}
else
{
Log.LogInfo((object)"[ValhATLYSS] No recent non-bak profile found to reconcile at bootstrap.");
}
return false;
}
}
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
[CompilerGenerated]
private sealed class <CoalesceAndProcess>d__18 : IEnumerator<object>, IEnumerator, IDisposable
{
private int <>1__state;
private object <>2__current;
public Plugin <>4__this;
public string path;
private int <i>5__2;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <CoalesceAndProcess>d__18(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<>1__state = -2;
}
private bool MoveNext()
{
//IL_02a6: Unknown result type (might be due to invalid IL or missing references)
//IL_02b0: Expected O, but got Unknown
int num = <>1__state;
Plugin plugin = <>4__this;
Seen value;
ManualLogSource log;
switch (num)
{
default:
return false;
case 0:
<>1__state = -1;
goto IL_0029;
case 1:
<>1__state = -1;
goto IL_0029;
case 2:
{
<>1__state = -1;
<i>5__2++;
break;
}
IL_0029:
if (plugin._seen.TryGetValue(path, out value) && DateTime.UtcNow < value.PendingUntilUtc)
{
<>2__current = null;
<>1__state = 1;
return true;
}
log = Log;
if (log != null)
{
log.LogInfo((object)("[ValhATLYSS] Watcher: processing " + Path.GetFileName(path) + " after quiet window"));
}
<i>5__2 = 0;
break;
}
if (<i>5__2 < 10)
{
if (TryReadAllText(path, out var text, out var info))
{
if (!plugin._seen.TryGetValue(path, out var value2))
{
value2 = (plugin._seen[path] = new Seen());
}
int num2 = StableHash(text);
if (info.LastWriteTimeUtc == value2.LastWriteUtc && info.Length == value2.LastSize && num2 == value2.ContentHash)
{
ManualLogSource log2 = Log;
if (log2 != null)
{
log2.LogInfo((object)("[ValhATLYSS] Watcher: no FS/hash change for " + Path.GetFileName(path)));
}
return false;
}
if (!KappaSlot.TryReadProfileStats(path, out var stats, Log))
{
ManualLogSource log3 = Log;
if (log3 != null)
{
log3.LogInfo((object)("[ValhATLYSS] Watcher: could not read Level/Exp from " + Path.GetFileName(path)));
}
value2.LastWriteUtc = info.LastWriteTimeUtc;
value2.LastSize = info.Length;
value2.ContentHash = num2;
return false;
}
ManualLogSource log4 = Log;
if (log4 != null)
{
log4.LogInfo((object)$"[ValhATLYSS] Watcher: parsed Level={stats.Level} Exp={stats.Exp} (prev L={value2.LastLevel} E={value2.LastExp})");
}
switch (HoddKista.Reconcile(path, stats, Log))
{
case -1:
{
ManualLogSource log6 = Log;
if (log6 != null)
{
log6.LogInfo((object)"[ValhATLYSS] Anti-regression: disk restored from vault.");
}
break;
}
case 1:
{
ManualLogSource log7 = Log;
if (log7 != null)
{
log7.LogInfo((object)"[ValhATLYSS] Anti-regression: vault updated from disk.");
}
break;
}
default:
{
ManualLogSource log5 = Log;
if (log5 != null)
{
log5.LogInfo((object)"[ValhATLYSS] Anti-regression: no change needed.");
}
break;
}
}
value2.LastWriteUtc = info.LastWriteTimeUtc;
value2.LastSize = info.Length;
value2.ContentHash = num2;
value2.LastLevel = stats.Level;
value2.LastExp = stats.Exp;
return false;
}
<>2__current = (object)new WaitForSeconds(0.1f);
<>1__state = 2;
return true;
}
ManualLogSource log8 = Log;
if (log8 != null)
{
log8.LogDebug((object)("[ValhATLYSS] Skipped profile check (file busy): " + path));
}
return false;
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
[CompilerGenerated]
private sealed class <EnforceServerCapForever>d__12 : IEnumerator<object>, IEnumerator, IDisposable
{
private int <>1__state;
private object <>2__current;
public Plugin <>4__this;
private int <lastAnnounced>5__2;
private int <desired>5__3;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <EnforceServerCapForever>d__12(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<>1__state = -2;
}
private bool MoveNext()
{
//IL_003e: Unknown result type (might be due to invalid IL or missing references)
//IL_0048: Expected O, but got Unknown
//IL_0184: Unknown result type (might be due to invalid IL or missing references)
//IL_018e: Expected O, but got Unknown
int num = <>1__state;
Plugin plugin = <>4__this;
switch (num)
{
default:
return false;
case 0:
<>1__state = -1;
<lastAnnounced>5__2 = -1;
<desired>5__3 = 64;
<>2__current = (object)new WaitForSeconds(1f);
<>1__state = 1;
return true;
case 1:
<>1__state = -1;
break;
case 2:
<>1__state = -1;
break;
}
try
{
bool flag = false;
try
{
flag = NetworkServer.active;
}
catch
{
}
if (!flag && (Object)(object)Player._mainPlayer != (Object)null)
{
flag = ((NetworkBehaviour)Player._mainPlayer).isServer;
}
StatLogics val = (((Object)(object)GameManager._current != (Object)null) ? GameManager._current._statLogics : null);
if (flag && (Object)(object)val != (Object)null)
{
if (val._maxMainLevel != <desired>5__3)
{
int maxMainLevel = val._maxMainLevel;
val._maxMainLevel = <desired>5__3;
ManualLogSource logger = ((BaseUnityPlugin)plugin).Logger;
if (logger != null)
{
logger.LogInfo((object)$"[ValhATLYSS] Server level cap set: {maxMainLevel} → {val._maxMainLevel} (enforce)");
}
<lastAnnounced>5__2 = val._maxMainLevel;
}
else if (<lastAnnounced>5__2 != <desired>5__3)
{
ManualLogSource logger2 = ((BaseUnityPlugin)plugin).Logger;
if (logger2 != null)
{
logger2.LogInfo((object)$"[ValhATLYSS] Server level cap OK: {val._maxMainLevel} (>= {<desired>5__3})");
}
<lastAnnounced>5__2 = <desired>5__3;
}
}
}
catch (Exception ex)
{
ManualLogSource logger3 = ((BaseUnityPlugin)plugin).Logger;
if (logger3 != null)
{
logger3.LogDebug((object)("[ValhATLYSS] Cap enforcer tick failed: " + ex.Message));
}
}
<>2__current = (object)new WaitForSeconds(1f);
<>1__state = 2;
return true;
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
[CompilerGenerated]
private sealed class <ProfilePoller>d__19 : IEnumerator<object>, IEnumerator, IDisposable
{
private int <>1__state;
private object <>2__current;
public Plugin <>4__this;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <ProfilePoller>d__19(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<>1__state = -2;
}
private bool MoveNext()
{
//IL_002f: Unknown result type (might be due to invalid IL or missing references)
//IL_0039: Expected O, but got Unknown
//IL_0263: Unknown result type (might be due to invalid IL or missing references)
//IL_026d: Expected O, but got Unknown
int num = <>1__state;
Plugin plugin = <>4__this;
switch (num)
{
default:
return false;
case 0:
<>1__state = -1;
<>2__current = (object)new WaitForSeconds(3f);
<>1__state = 1;
return true;
case 1:
<>1__state = -1;
break;
case 2:
<>1__state = -1;
break;
}
if (!string.IsNullOrEmpty(plugin._profilesRoot))
{
try
{
if (TryGetMostRecentProfile(out var fullPath) && File.Exists(fullPath) && TryReadAllText(fullPath, out var text, out var info))
{
if (!plugin._seen.TryGetValue(fullPath, out var value))
{
value = (plugin._seen[fullPath] = new Seen());
}
int num2 = StableHash(text);
if (info.LastWriteTimeUtc != value.LastWriteUtc || info.Length != value.LastSize || num2 != value.ContentHash)
{
if (KappaSlot.TryReadProfileStats(fullPath, out var stats, Log))
{
ManualLogSource log = Log;
if (log != null)
{
log.LogInfo((object)$"[ValhATLYSS] Poller: parsed Level={stats.Level} Exp={stats.Exp} (prev L={value.LastLevel} E={value.LastExp}) from {Path.GetFileName(fullPath)}");
}
switch (HoddKista.Reconcile(fullPath, stats, Log))
{
case -1:
{
ManualLogSource log3 = Log;
if (log3 != null)
{
log3.LogInfo((object)"[ValhATLYSS] Anti-regression: disk restored from vault. (poller)");
}
break;
}
case 1:
{
ManualLogSource log4 = Log;
if (log4 != null)
{
log4.LogInfo((object)"[ValhATLYSS] Anti-regression: vault updated from disk. (poller)");
}
break;
}
default:
{
ManualLogSource log2 = Log;
if (log2 != null)
{
log2.LogInfo((object)"[ValhATLYSS] Anti-regression: no change needed. (poller)");
}
break;
}
}
value.LastLevel = stats.Level;
value.LastExp = stats.Exp;
}
else
{
ManualLogSource log5 = Log;
if (log5 != null)
{
log5.LogInfo((object)("[ValhATLYSS] Poller: could not read Level/Exp in " + Path.GetFileName(fullPath)));
}
}
value.LastWriteUtc = info.LastWriteTimeUtc;
value.LastSize = info.Length;
value.ContentHash = num2;
}
else
{
ManualLogSource log6 = Log;
if (log6 != null)
{
log6.LogDebug((object)("[ValhATLYSS] Poller: no FS/hash change for " + Path.GetFileName(fullPath)));
}
}
}
}
catch (Exception ex)
{
ManualLogSource log7 = Log;
if (log7 != null)
{
log7.LogDebug((object)("[ValhATLYSS] Poller error: " + ex.Message));
}
}
}
<>2__current = (object)new WaitForSeconds(5f);
<>1__state = 2;
return true;
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
private const int DesiredCap = 64;
private const int ReconcileDebounceMs = 2000;
private const float PollIntervalSeconds = 5f;
private const bool VerboseWatcherLogs = true;
internal static ManualLogSource Log;
private FileSystemWatcher _watcher;
private string _profilesRoot;
private bool _bootstrapped;
private readonly Dictionary<string, Seen> _seen = new Dictionary<string, Seen>(StringComparer.OrdinalIgnoreCase);
private void Awake()
{
Log = ((BaseUnityPlugin)this).Logger;
Log.LogInfo((object)"[ValhATLYSS] Awake");
_profilesRoot = GetProfilesRoot();
if (string.IsNullOrEmpty(_profilesRoot))
{
Log.LogWarning((object)"[ValhATLYSS] Could not locate profileCollections path. Mod will idle.");
}
else
{
Log.LogInfo((object)("[ValhATLYSS] Profiles root: " + _profilesRoot));
TryEnableWatcher(_profilesRoot);
try
{
string[] files = Directory.GetFiles(_profilesRoot, "atl_characterProfile_*", SearchOption.TopDirectoryOnly);
ManualLogSource log = Log;
if (log != null)
{
log.LogInfo((object)$"[ValhATLYSS] Profiles discovered: {files.Length}");
}
string[] array = files;
foreach (string text in array)
{
ManualLogSource log2 = Log;
if (log2 != null)
{
log2.LogInfo((object)("[ValhATLYSS] Profile: " + text));
}
}
}
catch (Exception ex)
{
ManualLogSource log3 = Log;
if (log3 != null)
{
log3.LogDebug((object)("[ValhATLYSS] Profile discovery failed: " + ex.Message));
}
}
}
((MonoBehaviour)this).StartCoroutine(Bootstrap());
}
[IteratorStateMachine(typeof(<Bootstrap>d__11))]
private IEnumerator Bootstrap()
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <Bootstrap>d__11(0)
{
<>4__this = this
};
}
[IteratorStateMachine(typeof(<EnforceServerCapForever>d__12))]
private IEnumerator EnforceServerCapForever()
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <EnforceServerCapForever>d__12(0)
{
<>4__this = this
};
}
private void TryEnableWatcher(string profilesRoot)
{
try
{
_watcher = new FileSystemWatcher(profilesRoot)
{
NotifyFilter = (NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite),
IncludeSubdirectories = false,
Filter = "atl_characterProfile_*",
EnableRaisingEvents = true
};
_watcher.Changed += OnProfileChanged;
_watcher.Created += OnProfileChanged;
_watcher.Renamed += OnProfileRenamed;
Log.LogInfo((object)"[ValhATLYSS] File watcher enabled.");
}
catch (Exception ex)
{
ManualLogSource log = Log;
if (log != null)
{
log.LogWarning((object)("[ValhATLYSS] Failed to enable watcher: " + ex.Message));
}
}
}
private void OnProfileChanged(object sender, FileSystemEventArgs e)
{
QueueProfileCheck(e.FullPath);
}
private void OnProfileRenamed(object sender, RenamedEventArgs e)
{
QueueProfileCheck(e.FullPath);
}
private static bool IsBackupPath(string path)
{
if (!path.EndsWith("_bak", StringComparison.OrdinalIgnoreCase))
{
return Path.GetFileName(path).EndsWith("_bak", StringComparison.OrdinalIgnoreCase);
}
return true;
}
private void QueueProfileCheck(string path)
{
if (!_bootstrapped || string.IsNullOrEmpty(path))
{
return;
}
if (IsBackupPath(path))
{
ManualLogSource log = Log;
if (log != null)
{
log.LogInfo((object)("[ValhATLYSS] Watcher: ignoring backup file " + Path.GetFileName(path)));
}
return;
}
if (!_seen.TryGetValue(path, out var value))
{
value = (_seen[path] = new Seen());
}
value.PendingUntilUtc = DateTime.UtcNow.AddMilliseconds(2000.0);
ManualLogSource log2 = Log;
if (log2 != null)
{
log2.LogInfo((object)$"[ValhATLYSS] Watcher: queued {Path.GetFileName(path)} until {value.PendingUntilUtc:HH:mm:ss.fff}Z");
}
((MonoBehaviour)this).StartCoroutine(CoalesceAndProcess(path));
}
[IteratorStateMachine(typeof(<CoalesceAndProcess>d__18))]
private IEnumerator CoalesceAndProcess(string path)
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <CoalesceAndProcess>d__18(0)
{
<>4__this = this,
path = path
};
}
[IteratorStateMachine(typeof(<ProfilePoller>d__19))]
private IEnumerator ProfilePoller()
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <ProfilePoller>d__19(0)
{
<>4__this = this
};
}
private static bool TryReadAllText(string path, out string text, out FileInfo info)
{
text = null;
info = null;
try
{
info = new FileInfo(path);
using FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
using StreamReader streamReader = new StreamReader(stream);
text = streamReader.ReadToEnd();
return true;
}
catch
{
return false;
}
}
private static int StableHash(string s)
{
int num = 23;
if (!string.IsNullOrEmpty(s))
{
for (int i = 0; i < s.Length; i++)
{
num = num * 31 + s[i];
}
}
return num;
}
private void TryReconcileProfile(string profilePath)
{
try
{
if (string.IsNullOrEmpty(profilePath) || IsBackupPath(profilePath))
{
ManualLogSource log = Log;
if (log != null)
{
log.LogInfo((object)"[ValhATLYSS] Reconcile skipped: path null or backup file.");
}
return;
}
if (!File.Exists(profilePath))
{
ManualLogSource log2 = Log;
if (log2 != null)
{
log2.LogWarning((object)("[ValhATLYSS] Reconcile skipped; path missing: " + profilePath));
}
return;
}
if (!KappaSlot.TryReadProfileStats(profilePath, out var stats, Log))
{
ManualLogSource log3 = Log;
if (log3 != null)
{
log3.LogWarning((object)("[ValhATLYSS] Could not read disk stats for: " + profilePath));
}
return;
}
switch (HoddKista.Reconcile(profilePath, stats, Log))
{
case -1:
{
ManualLogSource log5 = Log;
if (log5 != null)
{
log5.LogInfo((object)"[ValhATLYSS] Anti-regression: disk restored from vault.");
}
break;
}
case 1:
{
ManualLogSource log6 = Log;
if (log6 != null)
{
log6.LogInfo((object)"[ValhATLYSS] Anti-regression: vault updated from disk.");
}
break;
}
default:
{
ManualLogSource log4 = Log;
if (log4 != null)
{
log4.LogInfo((object)"[ValhATLYSS] No reconcile change necessary.");
}
break;
}
}
}
catch (Exception ex)
{
ManualLogSource log7 = Log;
if (log7 != null)
{
log7.LogWarning((object)("[ValhATLYSS] Reconcile failed: " + ex.Message));
}
}
}
internal static string GetProfilesRoot()
{
try
{
string text = Path.Combine(Paths.GameRootPath, "ATLYSS_Data", "profileCollections");
return Directory.Exists(text) ? text : null;
}
catch
{
return null;
}
}
internal static bool TryGetMostRecentProfile(out string fullPath)
{
fullPath = null;
try
{
string profilesRoot = GetProfilesRoot();
if (profilesRoot == null)
{
return false;
}
string[] files = Directory.GetFiles(profilesRoot, "atl_characterProfile_*", SearchOption.TopDirectoryOnly);
DateTime dateTime = DateTime.MinValue;
string[] array = files;
foreach (string text in array)
{
if (!Path.GetFileName(text).EndsWith("_bak", StringComparison.OrdinalIgnoreCase))
{
DateTime lastWriteTimeUtc = File.GetLastWriteTimeUtc(text);
if (lastWriteTimeUtc > dateTime)
{
dateTime = lastWriteTimeUtc;
fullPath = text;
}
}
}
return fullPath != null;
}
catch
{
return false;
}
}
}
internal static class Ristir
{
internal static void Print(ManualLogSource log, string message)
{
try
{
if (log != null)
{
log.LogInfo((object)("[ValhATLYSS] " + message));
}
}
catch
{
}
}
internal static int ClampLevel(int level, int min = 1, int max = 64)
{
if (level < min)
{
return min;
}
if (level > max)
{
return max;
}
return level;
}
internal static int CompareProgress(int level0, int exp0, int level1, int exp1)
{
if (level1 > level0)
{
return 1;
}
if (level1 < level0)
{
return -1;
}
if (exp1 > exp0)
{
return 1;
}
if (exp1 < exp0)
{
return -1;
}
return 0;
}
}
internal static class SeidrVegr
{
internal static bool Handshake()
{
return false;
}
}
}
namespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
internal sealed class IgnoresAccessChecksToAttribute : Attribute
{
public IgnoresAccessChecksToAttribute(string assemblyName)
{
}
}
}