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 Continue v1.0.0
Continue.dll
Decompiled 14 hours agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security.Cryptography; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Splatform; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")] [assembly: AssemblyCompany("Continue")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+29a4a21da5d0ec62dd38d6a5e37d0d84a004bd1d")] [assembly: AssemblyProduct("Continue")] [assembly: AssemblyTitle("Continue")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace Continue { internal static class ContinueButton { [HarmonyPatch(typeof(FejdStartup), "Start")] private static class StartPatch { private static void Postfix() { try { Resume.OnMenu(); Refresh(); } catch (Exception error) { Log.Fail("ContinueButton", error); } } } private static Button _button; private static LastGame _game; internal static void Refresh() { FejdStartup instance = FejdStartup.instance; if (!Log.SafeMode && Object.op_Implicit((Object)(object)instance)) { if (!Settings.Enabled.Value) { Remove(instance); } else if (Object.op_Implicit((Object)(object)_button)) { SetText(_button, Label(_game)); } else { Add(instance); } } } private static void Add(FejdStartup startup) { //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Expected O, but got Unknown LastGame game = LastGame.Load(); if (game == null || !Resume.CanResume(game)) { return; } ref Button[] reference = ref GameAccess.MenuButtons.Invoke(startup); if (reference != null && reference.Length != 0) { Button val = Create(reference[0], Label(game)); ((UnityEvent)val.onClick).AddListener((UnityAction)delegate { OnClick(startup, game); }); Button[] array = (Button[])(object)new Button[reference.Length + 1]; array[0] = val; Array.Copy(reference, 0, array, 1, reference.Length); reference = array; _button = val; _game = game; } } private static void Remove(FejdStartup startup) { if (Object.op_Implicit((Object)(object)_button)) { Button removed = _button; ref Button[] reference = ref GameAccess.MenuButtons.Invoke(startup); reference = Array.FindAll(reference, (Button button) => (Object)(object)button != (Object)(object)removed); Object.Destroy((Object)(object)((Component)removed).gameObject); _button = null; _game = null; } } private static Button Create(Button template, string label) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown GameObject obj = Object.Instantiate<GameObject>(((Component)template).gameObject, ((Component)template).transform.parent); ((Object)obj).name = "Continue"; obj.transform.SetSiblingIndex(((Component)template).transform.GetSiblingIndex()); Button component = obj.GetComponent<Button>(); component.onClick = new ButtonClickedEvent(); SetText(component, label); return component; } private static void SetText(Button button, string label) { TMP_Text componentInChildren = ((Component)button).GetComponentInChildren<TMP_Text>(true); if (Object.op_Implicit((Object)(object)componentInChildren)) { componentInChildren.text = label; return; } Text componentInChildren2 = ((Component)button).GetComponentInChildren<Text>(true); if (Object.op_Implicit((Object)(object)componentInChildren2)) { componentInChildren2.text = label; } } private static string Label(LastGame game) { string text = Settings.ButtonLabel.Value; if (string.IsNullOrWhiteSpace(text)) { text = GameLabel(); } string displayName = game.DisplayName; if (!Settings.ShowGameName.Value || string.IsNullOrEmpty(displayName)) { return text; } return text + " (" + displayName + ")"; } private static string GameLabel() { string text = Localization.instance.Localize("$menu_continue"); if (!string.IsNullOrEmpty(text) && text[0] != '[') { return text; } return "Continue"; } private static void OnClick(FejdStartup startup, LastGame game) { try { Resume.Start(startup, game); } catch (Exception error) { Log.Fail("Resume", error); } } } [BepInPlugin("grogui.Continue", "Continue", "1.0.0")] [BepInProcess("valheim.exe")] internal sealed class ContinuePlugin : BaseUnityPlugin { internal const string PluginGuid = "grogui.Continue"; internal const string PluginName = "Continue"; internal const string PluginVersion = "1.0.0"; private Harmony _harmony; private void Awake() { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown Log.Init(((BaseUnityPlugin)this).Logger); Settings.Bind(((BaseUnityPlugin)this).Config); GameAccess.Resolve(); _harmony = new Harmony("grogui.Continue"); Type[] typesFromAssembly = AccessTools.GetTypesFromAssembly(typeof(ContinuePlugin).Assembly); foreach (Type type in typesFromAssembly) { try { if (type.GetCustomAttributes(typeof(HarmonyPatch), inherit: false).Length != 0) { _harmony.CreateClassProcessor(type).Patch(); } } catch (Exception error) { Log.Fail("patch " + type.FullName, error); } } WatchSettings(); Log.Info("Continue 1.0.0 loaded."); } private static void WatchSettings() { Settings.Enabled.SettingChanged += OnButtonSettingChanged; Settings.ButtonLabel.SettingChanged += OnButtonSettingChanged; Settings.ShowGameName.SettingChanged += OnButtonSettingChanged; Settings.RememberPassword.SettingChanged += delegate { ForgetPasswordIfDisabled(); }; ForgetPasswordIfDisabled(); } private static void OnButtonSettingChanged(object sender, EventArgs args) { try { ContinueButton.Refresh(); } catch (Exception error) { Log.Fail("ContinueButton.Refresh", error); } } private static void ForgetPasswordIfDisabled() { try { if (!Settings.RememberPassword.Value && LastGame.ForgetPassword()) { Log.Info("RememberPassword is disabled: the remembered password is erased."); } } catch (Exception error) { Log.Fail("ForgetPassword", error); } } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } internal static class GameAccess { internal static Action<FejdStartup, string, FileSource> SelectCharacter { get; private set; } internal static Action<FejdStartup, string> SetSelectedProfile { get; private set; } internal static Action<string> SetServerPassword { get; private set; } internal static FieldRef<FejdStartup, World> SelectedWorld { get; private set; } internal static Func<FejdStartup, string, World, bool> IsPublicPasswordValid { get; private set; } internal static Action<FejdStartup> ShowStartGame { get; private set; } internal static FieldRef<FejdStartup, Button[]> MenuButtons { get; private set; } internal static bool Ready { get { if (SelectCharacter != null && SetSelectedProfile != null && SetServerPassword != null && SelectedWorld != null && IsPublicPasswordValid != null && ShowStartGame != null) { return MenuButtons != null; } return false; } } internal static void Resolve() { SelectCharacter = Get("FejdStartup.SelectCharacter", () => AccessTools.MethodDelegate<Action<FejdStartup, string, FileSource>>(AccessTools.Method(typeof(FejdStartup), "SelectCharacter", new Type[2] { typeof(string), typeof(FileSource) }, (Type[])null), (object)null, true)); SetSelectedProfile = Get("FejdStartup.SetSelectedProfile", () => AccessTools.MethodDelegate<Action<FejdStartup, string>>(AccessTools.Method(typeof(FejdStartup), "SetSelectedProfile", new Type[1] { typeof(string) }, (Type[])null), (object)null, true)); SetServerPassword = Get("FejdStartup.ServerPassword", () => AccessTools.MethodDelegate<Action<string>>(AccessTools.PropertySetter(typeof(FejdStartup), "ServerPassword"), (object)null, true)); SelectedWorld = Get("FejdStartup.m_world", () => AccessTools.FieldRefAccess<FejdStartup, World>("m_world")); IsPublicPasswordValid = Get("FejdStartup.IsPublicPasswordValid", () => AccessTools.MethodDelegate<Func<FejdStartup, string, World, bool>>(AccessTools.Method(typeof(FejdStartup), "IsPublicPasswordValid", new Type[2] { typeof(string), typeof(World) }, (Type[])null), (object)null, true)); ShowStartGame = Get("FejdStartup.ShowStartGame", () => AccessTools.MethodDelegate<Action<FejdStartup>>(AccessTools.Method(typeof(FejdStartup), "ShowStartGame", Type.EmptyTypes, (Type[])null), (object)null, true)); MenuButtons = Get("FejdStartup.m_menuButtons", () => AccessTools.FieldRefAccess<FejdStartup, Button[]>("m_menuButtons")); } private static T Get<T>(string what, Func<T> resolve) where T : class { try { T val = resolve(); if (val == null) { Log.WarnOnce(what, what + " not found in this version of the game, Continue button disabled."); } return val; } catch (Exception ex) { Log.WarnOnce(what, what + " unusable (" + ex.GetType().Name + ": " + ex.Message + "), Continue button disabled."); return null; } } } internal static class GameRecorder { [HarmonyPatch(typeof(Game), "SetProfile")] private static class SetProfilePatch { private static void Postfix(string filename, FileSource fileSource) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) _profile = filename; _profileSource = fileSource; } } [HarmonyPatch(typeof(FejdStartup), "OnWorldStart")] private static class OnWorldStartPatch { private static void Prefix(bool ___m_startingWorld, out bool __state) { __state = ___m_startingWorld; } private static void Postfix(FejdStartup __instance, bool ___m_startingWorld, World ___m_world, bool __state) { //IL_0062: Unknown result type (might be due to invalid IL or missing references) try { if (!__state && ___m_startingWorld && ___m_world != null && Settings.Enabled.Value && !Log.SafeMode && !string.IsNullOrEmpty(_profile)) { _pending = null; _enteredPassword = null; bool isOn = __instance.m_openServerToggle.isOn; LastGame lastGame = LastGame.FromWorld(___m_world, isOn, __instance.m_publicServerToggle.isOn, __instance.m_crossplayServerToggle.isOn, _profile, _profileSource); string text = ((TMP_InputField)__instance.m_serverPassword).text; lastGame.Password = ((isOn && Settings.RememberPassword.Value && !string.IsNullOrEmpty(text)) ? text : null); lastGame.Save(); Log.Info("Last game saved: " + lastGame.Summary + ((lastGame.Password != null) ? ", with its password." : ", without a password.")); } } catch (Exception error) { Log.Fail("GameRecorder.OnWorldStart", error); } } } [HarmonyPatch(typeof(ServerListGui), "AddToRecentServersList")] private static class AddToRecentServersListPatch { private static void Postfix(ServerJoinData data) { //IL_0034: 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) try { _pending = null; _enteredPassword = null; if (Settings.Enabled.Value && !Log.SafeMode && !string.IsNullOrEmpty(_profile) && ((ServerJoinData)(ref data)).IsValid) { _pending = LastGame.FromServer(data, _profile, _profileSource); } } catch (Exception error) { Log.Fail("GameRecorder.AddToRecentServersList", error); } } } [HarmonyPatch(typeof(ZNet), "OnPasswordEntered")] private static class OnPasswordEnteredPatch { private static void Prefix(string pwd) { if (!string.IsNullOrEmpty(pwd)) { _enteredPassword = pwd; } } private static void Postfix() { try { Resume.ClearPassword(); } catch (Exception error) { Log.Fail("GameRecorder.OnPasswordEntered", error); } } } [HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")] private static class RpcPeerInfoPatch { private static void Postfix(ZNet __instance) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Invalid comparison between Unknown and I4 try { if (_pending != null && !__instance.IsServer() && (int)ZNet.GetConnectionStatus() == 2) { LastGame pending = _pending; _pending = null; pending.Password = (Settings.RememberPassword.Value ? _enteredPassword : null); _enteredPassword = null; pending.Save(); Resume.Connected(); Log.Info("Last game saved: " + pending.Summary + ((pending.Password != null) ? ", with its password." : ", without a password.")); } } catch (Exception error) { Log.Fail("GameRecorder.RPC_PeerInfo", error); } } } [HarmonyPatch(typeof(ZNet), "RPC_Error")] private static class RpcErrorPatch { private static void Postfix(int error) { try { if (error == 6) { Resume.PasswordRefused(); } } catch (Exception error2) { Log.Fail("GameRecorder.RPC_Error", error2); } } } private static string _profile; private static FileSource _profileSource; private static LastGame _pending; private static string _enteredPassword; } internal sealed class LastGame { private static readonly byte[] Entropy = Encoding.UTF8.GetBytes("grogui.Continue"); private const string WorldType = "World"; internal bool IsWorld; internal ServerJoinDataType Type; internal string Host; internal ushort Port; internal ulong SteamId; internal string PlayFabId; internal string Owner; internal string WorldFile; internal FileSource WorldSource; internal bool OpenServer; internal bool PublicServer; internal bool Crossplay; internal string Name; internal string Profile; internal FileSource ProfileSource; internal string Password; private static string FilePath => Path.Combine(Paths.ConfigPath, "grogui.Continue.last.txt"); internal string DisplayName { get { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Invalid comparison between Unknown and I4 if (!string.IsNullOrEmpty(Name)) { return Name; } if (IsWorld) { return WorldFile ?? ""; } if ((int)Type != 3) { return ""; } return $"{Host}:{Port}"; } } internal string Summary { get { //IL_0014: Unknown result type (might be due to invalid IL or missing references) if (!IsWorld) { return $"{DisplayName} ({Type}), character {Profile}"; } string text = ((!OpenServer) ? "solo" : ("open" + (PublicServer ? ", public" : "") + (Crossplay ? ", crossplay" : ""))); return "world " + DisplayName + " (" + text + "), character " + Profile; } } internal unsafe static LastGame FromServer(ServerJoinData data, string profile, FileSource profileSource) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002a: 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_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: 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) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Expected I4, but got Unknown //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) string name = default(string); MultiBackendMatchmaking.TryGetServerName(data, ref name); LastGame lastGame = new LastGame { Type = data.m_type, Name = name, Profile = profile, ProfileSource = profileSource }; ServerJoinDataType type = data.m_type; switch (type - 1) { case 2: { ServerJoinDataDedicated dedicated = ((ServerJoinData)(ref data)).Dedicated; lastGame.Host = ((ServerJoinDataDedicated)(ref dedicated)).m_host; lastGame.Port = ((ServerJoinData)(ref data)).Dedicated.m_port; if (!string.IsNullOrEmpty(lastGame.Host)) { return lastGame; } return null; } case 0: lastGame.SteamId = ((ServerJoinData)(ref data)).SteamUser.m_joinUserID.m_SteamID; return lastGame; case 1: lastGame.PlayFabId = ((ServerJoinData)(ref data)).PlayFabUser.m_remotePlayerId; lastGame.Owner = ((object)(*(PlatformUserID*)(&data.m_owner))/*cast due to .constrained prefix*/).ToString(); return lastGame; default: return null; } } internal static LastGame FromWorld(World world, bool openServer, bool publicServer, bool crossplay, string profile, FileSource profileSource) { //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_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) return new LastGame { IsWorld = true, WorldFile = world.m_worldName, WorldSource = world.m_fileSource, Name = world.m_name, OpenServer = openServer, PublicServer = publicServer, Crossplay = crossplay, Profile = profile, ProfileSource = profileSource }; } internal bool TryGetJoinData(out ServerJoinData data) { //IL_0001: 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_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected I4, but got Unknown //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: 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_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006e: 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) //IL_0093: 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) ServerJoinDataType type = Type; switch (type - 1) { case 2: data = new ServerJoinData(new ServerJoinDataDedicated(Host, Port)); break; case 0: data = new ServerJoinData(new ServerJoinDataSteamUser(SteamId)); break; case 1: { PlatformUserID val = default(PlatformUserID); data = (PlatformUserID.TryParse(Owner, ref val) ? new ServerJoinData(new ServerJoinDataPlayFabUser(PlayFabId), val) : new ServerJoinData(new ServerJoinDataPlayFabUser(PlayFabId))); break; } default: data = ServerJoinData.None; break; } return ((ServerJoinData)(ref data)).IsValid; } internal bool TryFindWorld(out World world) { //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) foreach (World world2 in SaveSystem.GetWorldList()) { if (world2.m_fileSource == WorldSource && string.Equals(world2.m_worldName, WorldFile, StringComparison.Ordinal)) { world = world2; return true; } } world = null; return false; } internal static LastGame Load() { //IL_0081: 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_00df: Unknown result type (might be due to invalid IL or missing references) if (!File.Exists(FilePath)) { return null; } Dictionary<string, string> dictionary = new Dictionary<string, string>(); string[] array = File.ReadAllLines(FilePath, Encoding.UTF8); foreach (string text in array) { int num = text.IndexOf('='); if (num > 0) { dictionary[text.Substring(0, num)] = text.Substring(num + 1); } } bool flag = Value(dictionary, "type") == "World"; ServerJoinDataType result = (ServerJoinDataType)0; if ((flag ? (Value(dictionary, "world") == null) : (!TryEnum<ServerJoinDataType>(dictionary, "type", out result))) || !dictionary.TryGetValue("profile", out var value)) { Log.WarnOnce("load", FilePath + " is unreadable, ignored."); return null; } LastGame lastGame = new LastGame { IsWorld = flag, Type = result, Host = Value(dictionary, "host"), PlayFabId = Value(dictionary, "playFabId"), Owner = Value(dictionary, "owner"), WorldFile = Value(dictionary, "world"), OpenServer = (Value(dictionary, "open") == "1"), PublicServer = (Value(dictionary, "public") == "1"), Crossplay = (Value(dictionary, "crossplay") == "1"), Name = Value(dictionary, "name"), Profile = value }; ushort.TryParse(Value(dictionary, "port"), out lastGame.Port); ulong.TryParse(Value(dictionary, "steamId"), out lastGame.SteamId); TryEnum<FileSource>(dictionary, "worldSource", out lastGame.WorldSource); TryEnum<FileSource>(dictionary, "profileSource", out lastGame.ProfileSource); lastGame.Password = Unprotect(Value(dictionary, "password")); return lastGame; } internal static bool ForgetPassword() { LastGame lastGame = Load(); if (lastGame == null || lastGame.Password == null) { return false; } lastGame.Password = null; lastGame.Save(); return true; } internal void Save() { StringBuilder stringBuilder = new StringBuilder(); Line(stringBuilder, "type", IsWorld ? "World" : ((object)Unsafe.As<ServerJoinDataType, ServerJoinDataType>(ref Type)/*cast due to .constrained prefix*/).ToString()); Line(stringBuilder, "host", Host); Line(stringBuilder, "port", (Port == 0) ? null : Port.ToString()); Line(stringBuilder, "steamId", (SteamId == 0L) ? null : SteamId.ToString()); Line(stringBuilder, "playFabId", PlayFabId); Line(stringBuilder, "owner", Owner); Line(stringBuilder, "world", WorldFile); Line(stringBuilder, "worldSource", IsWorld ? ((object)Unsafe.As<FileSource, FileSource>(ref WorldSource)/*cast due to .constrained prefix*/).ToString() : null); Line(stringBuilder, "open", Flag(OpenServer)); Line(stringBuilder, "public", Flag(PublicServer)); Line(stringBuilder, "crossplay", Flag(Crossplay)); Line(stringBuilder, "name", Name); Line(stringBuilder, "profile", Profile); Line(stringBuilder, "profileSource", ((object)Unsafe.As<FileSource, FileSource>(ref ProfileSource)/*cast due to .constrained prefix*/).ToString()); Line(stringBuilder, "password", Protect(Password)); string text = FilePath + ".tmp"; File.WriteAllText(text, stringBuilder.ToString(), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); if (File.Exists(FilePath)) { File.Delete(FilePath); } File.Move(text, FilePath); } private static void Line(StringBuilder text, string key, string value) { if (!string.IsNullOrEmpty(value)) { text.Append(key).Append('=').Append(value.Replace('\r', ' ').Replace('\n', ' ')) .Append('\n'); } } private static string Flag(bool value) { if (!value) { return null; } return "1"; } private static string Value(Dictionary<string, string> values, string key) { if (!values.TryGetValue(key, out var value)) { return null; } return value; } private static bool TryEnum<T>(Dictionary<string, string> values, string key, out T result) where T : struct { result = default(T); string text = Value(values, key); if (text == null || !Enum.IsDefined(typeof(T), text)) { return false; } result = (T)Enum.Parse(typeof(T), text); return true; } private static string Protect(string password) { if (string.IsNullOrEmpty(password)) { return null; } try { return Convert.ToBase64String(ProtectedData.Protect(Encoding.UTF8.GetBytes(password), Entropy, (DataProtectionScope)0)); } catch (Exception ex) { Log.WarnOnce("protect", "Cannot encrypt the password (" + ex.GetType().Name + ": " + ex.Message + "): it will not be remembered."); return null; } } private static string Unprotect(string stored) { if (string.IsNullOrEmpty(stored)) { return null; } try { return Encoding.UTF8.GetString(ProtectedData.Unprotect(Convert.FromBase64String(stored), Entropy, (DataProtectionScope)0)); } catch (Exception ex) { Log.WarnOnce("unprotect", "Cannot decrypt the remembered password (" + ex.GetType().Name + ": " + ex.Message + "): the game will ask for it again."); return null; } } } internal static class Log { private static ManualLogSource _source; private static readonly HashSet<string> Reported = new HashSet<string>(); internal static bool SafeMode { get; private set; } internal static void Init(ManualLogSource source) { _source = source; } internal static void Info(string message) { ManualLogSource source = _source; if (source != null) { source.LogInfo((object)message); } } internal static void WarnOnce(string key, string message) { if (Reported.Add("w:" + key)) { ManualLogSource source = _source; if (source != null) { source.LogWarning((object)message); } } } internal static void Fail(string key, Exception error) { if (Reported.Add("e:" + key)) { ManualLogSource source = _source; if (source != null) { source.LogError((object)$"[{key}] {error}"); } } if (!SafeMode) { SafeMode = true; ManualLogSource source2 = _source; if (source2 != null) { source2.LogError((object)"Continue is disabled for this session: no Continue button until the game restarts."); } } } } internal static class Resume { private static bool _passwordSet; private static bool _usedStoredPassword; internal static bool CanResume(LastGame game) { if (!GameAccess.Ready || !ProfileExists(game)) { return false; } ServerJoinData data; if (!game.IsWorld) { return game.TryGetJoinData(out data); } World world; return game.TryFindWorld(out world); } internal static void Start(FejdStartup startup, LastGame game) { if (game.IsWorld) { StartWorld(startup, game); } else { JoinServer(startup, game); } } private static void JoinServer(FejdStartup startup, LastGame server) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) if (!server.TryGetJoinData(out var data) || !ProfileExists(server)) { Log.Info("Cannot resume: server or character " + server.Profile + " not found."); return; } GameAccess.SelectCharacter(startup, server.Profile, server.ProfileSource); _usedStoredPassword = false; string text = StoredPassword(server); if (text != null && FejdStartup.ServerPassword == null) { GameAccess.SetServerPassword(text); _passwordSet = true; _usedStoredPassword = true; } Log.Info("Resuming: " + server.Summary + "."); startup.SetServerToJoin(data); startup.JoinServer(); } private static void StartWorld(FejdStartup startup, LastGame game) { //IL_005a: Unknown result type (might be due to invalid IL or missing references) if (!game.TryFindWorld(out var world) || !ProfileExists(game)) { Log.Info("Cannot resume: world " + game.DisplayName + " or character " + game.Profile + " not found."); return; } GameAccess.SelectCharacter(startup, game.Profile, game.ProfileSource); GameAccess.SetSelectedProfile(startup, game.Profile); GameAccess.SelectedWorld.Invoke(startup) = world; string text = StoredPassword(game) ?? ""; ((TMP_InputField)startup.m_serverPassword).text = text; startup.m_publicServerToggle.SetIsOnWithoutNotify(game.PublicServer); startup.m_crossplayServerToggle.SetIsOnWithoutNotify(game.Crossplay); bool flag = PlatformPrefs.GetInt("SameOnlinePlatformOnly", 1) == 1; IPrivilegeProvider privilegeProvider = PlatformManager.DistributionPlatform.PrivilegeProvider; if (privilegeProvider != null) { privilegeProvider.SetCrossplayPrivilege((PrivilegeResult)(flag ? 8 : 0)); } bool flag2 = game.OpenServer && (game.PublicServer || game.Crossplay); bool num = !game.OpenServer || (PlatformPrefs.GetBool("EulaAccepted", false) && (!flag2 || GameAccess.IsPublicPasswordValid(startup, text, world))); startup.m_openServerToggle.SetIsOnWithoutNotify(false); if (!num) { Log.Info("Resuming " + game.Summary + ": password missing or EULA not accepted, showing the world screen."); GameAccess.ShowStartGame(startup); startup.m_openServerToggle.isOn = game.OpenServer; } else { startup.m_openServerToggle.isOn = game.OpenServer; Log.Info("Resuming: " + game.Summary + "."); startup.OnWorldStart(); } } internal static void OnMenu() { ClearPassword(); _usedStoredPassword = false; } internal static void ClearPassword() { if (_passwordSet) { _passwordSet = false; GameAccess.SetServerPassword(null); } } internal static void Connected() { _usedStoredPassword = false; } internal static void PasswordRefused() { if (_usedStoredPassword) { _usedStoredPassword = false; if (LastGame.ForgetPassword()) { Log.Info("The server refused the remembered password: forgotten, the game will ask for it again."); } } } private static string StoredPassword(LastGame game) { if (!Settings.RememberPassword.Value) { return null; } return game.Password; } private static bool ProfileExists(LastGame game) { //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) foreach (PlayerProfile allPlayerProfile in SaveSystem.GetAllPlayerProfiles()) { if (allPlayerProfile.m_fileSource == game.ProfileSource && string.Equals(allPlayerProfile.GetFilename(), game.Profile, StringComparison.Ordinal)) { return true; } } return false; } } internal static class Settings { internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<bool> RememberPassword; internal static ConfigEntry<string> ButtonLabel; internal static ConfigEntry<bool> ShowGameName; internal static void Bind(ConfigFile config) { Enabled = config.Bind<bool>("1. General", "Enabled", true, "Shows the Continue button in the main menu and remembers the last game: a server joined or a world started."); RememberPassword = config.Bind<bool>("1. General", "RememberPassword", true, "Remembers the password of the last server, or of the last world you opened to other players, encrypted for your Windows account (DPAPI), and gives it without asking. When disabled, the remembered password is erased."); ButtonLabel = config.Bind<string>("2. Button", "ButtonLabel", "", "Button text. Empty: the game's own \"Continue\", in the game's language."); ShowGameName = config.Bind<bool>("2. Button", "ShowGameName", false, "Adds the server's or world's name to the button, in parentheses."); } } }