Decompiled source of AchievementSync v1.2.0

plugins\LiveriaDevs.AchievementSync.dll

Decompiled 3 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
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: AssemblyCompany("LiveriaDevs.AchievementSync")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.2.0.0")]
[assembly: AssemblyInformationalVersion("1.2.0")]
[assembly: AssemblyProduct("LiveriaDevs.AchievementSync")]
[assembly: AssemblyTitle("LiveriaDevs.AchievementSync")]
[assembly: AssemblyVersion("1.2.0.0")]
namespace LiveriaDevs.AchievementSync;

[BepInPlugin("liveriadevs.valheim.achievementsync", "Achievement Sync", "1.2.0")]
public sealed class AchievementSyncPlugin : BaseUnityPlugin
{
	public const string PluginGuid = "liveriadevs.valheim.achievementsync";

	public const string PluginName = "Achievement Sync";

	public const string PluginVersion = "1.2.0";

	internal static ConfigEntry<bool> EnableAchievements;

	internal static ConfigEntry<bool> SyncPastStats;

	internal static ConfigEntry<bool> ClearItemCheatFlags;

	internal static ConfigEntry<bool> ClearContainerCheatFlags;

	internal static ConfigEntry<bool> ClearWorldDropCheatFlags;

	private ConfigEntry<KeyboardShortcut> resyncKey;

	private static bool syncedThisSession;

	private void Awake()
	{
		//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
		EnableAchievements = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableAchievements", true, "Allow Steam achievements even with mods, cheats, cheated items or cheated world modifiers.");
		SyncPastStats = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "SyncPastStats", true, "On character load, copy previously recorded stats into the achievement track and re-check unlocks. Only restores progress Valheim already stored (builds, kills, crafts, etc.). One-off events with no saved marker cannot be reconstructed.");
		ClearItemCheatFlags = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ClearItemCheatFlags", true, "Clear the 'obtained through cheats' flag from player inventory items, and whenever any inventory receives an item.");
		ClearContainerCheatFlags = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ClearContainerCheatFlags", true, "On sync, clear cheated marks from all currently loaded containers (chests, ship storage, tombstones, etc.). Helps CraftFromContainers-style mods.");
		ClearWorldDropCheatFlags = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ClearWorldDropCheatFlags", true, "On sync, clear cheated marks from world ItemDrops currently loaded in the scene.");
		resyncKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("General", "ResyncKey", new KeyboardShortcut((KeyCode)288, Array.Empty<KeyCode>()), "Force a re-sync of past stats into the achievement track and re-check unlocks.");
		new Harmony("liveriadevs.valheim.achievementsync").PatchAll();
		((BaseUnityPlugin)this).Logger.LogInfo((object)("Achievement Sync 1.2.0 loaded. Press " + ((object)resyncKey.Value/*cast due to .constrained prefix*/).ToString() + " to force a stats re-sync."));
	}

	private void Update()
	{
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		if (EnableAchievements.Value)
		{
			KeyboardShortcut value = resyncKey.Value;
			if (((KeyboardShortcut)(ref value)).IsDown())
			{
				syncedThisSession = false;
				TrySyncAndUnlock("manual");
			}
			if (!syncedThisSession)
			{
				TrySyncAndUnlock("autoload");
			}
		}
	}

	private void TrySyncAndUnlock(string reason)
	{
		if (!Object.op_Implicit((Object)(object)Game.instance) || !Object.op_Implicit((Object)(object)Player.m_localPlayer) || (Object)(object)Achievements.m_instance == (Object)null)
		{
			return;
		}
		PlayerProfile playerProfile = Game.instance.GetPlayerProfile();
		if (playerProfile == null)
		{
			return;
		}
		syncedThisSession = true;
		if (ClearItemCheatFlags.Value)
		{
			ClearInventoryCheatFlags(((Humanoid)Player.m_localPlayer).GetInventory());
		}
		int num = 0;
		int num2 = 0;
		if (ClearContainerCheatFlags.Value)
		{
			num = ClearLoadedContainerCheatFlags();
		}
		if (ClearWorldDropCheatFlags.Value)
		{
			num2 = ClearLoadedWorldDropCheatFlags();
		}
		if (num > 0 || num2 > 0)
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Cleared cheated marks in " + num + " containers and " + num2 + " world drops (" + reason + ")."));
		}
		if (SyncPastStats.Value)
		{
			int num3 = SyncRawStatsIntoAchievementTracks(playerProfile);
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Synced " + num3 + " past stats into achievement tracks (" + reason + ")."));
		}
		try
		{
			MethodInfo methodInfo = AccessTools.Method(typeof(Achievements), "SetPlatformInitialized", (Type[])null, (Type[])null);
			if (methodInfo != null)
			{
				methodInfo.Invoke(null, null);
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Re-checked all achievements after sync.");
			}
			else
			{
				RecheckAllAchievementsFallback();
			}
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Achievement re-check failed: " + ex.Message));
			RecheckAllAchievementsFallback();
		}
	}

	internal static void ClearInventoryCheatFlags(Inventory inventory)
	{
		if (inventory == null)
		{
			return;
		}
		foreach (ItemData allItem in inventory.GetAllItems())
		{
			if (allItem != null)
			{
				allItem.m_cheated = false;
			}
		}
	}

	private static int ClearLoadedContainerCheatFlags()
	{
		int num = 0;
		Container[] array = Object.FindObjectsOfType<Container>();
		foreach (Container val in array)
		{
			if (!((Object)(object)val == (Object)null))
			{
				Inventory inventory = val.GetInventory();
				if (inventory != null)
				{
					ClearInventoryCheatFlags(inventory);
					num++;
				}
			}
		}
		return num;
	}

	private static int ClearLoadedWorldDropCheatFlags()
	{
		int num = 0;
		ItemDrop[] array = Object.FindObjectsOfType<ItemDrop>();
		foreach (ItemDrop val in array)
		{
			if (!((Object)(object)val == (Object)null) && val.m_itemData != null && val.m_itemData.m_cheated)
			{
				val.m_itemData.m_cheated = false;
				num++;
			}
		}
		return num;
	}

	private static int SyncRawStatsIntoAchievementTracks(PlayerProfile profile)
	{
		PlayerStats val = profile.m_playerStats[0];
		int currentAchievementDifficultyIndex = Achievements.GetCurrentAchievementDifficultyIndex();
		int num = 0;
		num += MergeStats(val.m_stats, profile.m_playerStats[1].m_stats);
		num += MergeStats(val.m_stats, profile.m_playerStats[currentAchievementDifficultyIndex].m_stats);
		num += MergeNamed(val.m_piecesPlacedStats, profile.m_playerStats[1].m_piecesPlacedStats);
		num += MergeNamed(val.m_piecesPlacedStats, profile.m_playerStats[currentAchievementDifficultyIndex].m_piecesPlacedStats);
		num += MergeNamed(val.m_itemPickupStats, profile.m_playerStats[1].m_itemPickupStats);
		num += MergeNamed(val.m_itemPickupStats, profile.m_playerStats[currentAchievementDifficultyIndex].m_itemPickupStats);
		num += MergeNamed(val.m_itemCraftStats, profile.m_playerStats[1].m_itemCraftStats);
		num += MergeNamed(val.m_itemCraftStats, profile.m_playerStats[currentAchievementDifficultyIndex].m_itemCraftStats);
		num += MergeNamed(val.m_pickableStats, profile.m_playerStats[1].m_pickableStats);
		num += MergeNamed(val.m_pickableStats, profile.m_playerStats[currentAchievementDifficultyIndex].m_pickableStats);
		num += MergeNamed(val.m_foodEatenStats, profile.m_playerStats[1].m_foodEatenStats);
		num += MergeNamed(val.m_foodEatenStats, profile.m_playerStats[currentAchievementDifficultyIndex].m_foodEatenStats);
		for (int i = 0; i < val.m_enemyStats.Length; i++)
		{
			num += MergeNamed(val.m_enemyStats[i], profile.m_playerStats[1].m_enemyStats[i]);
			num += MergeNamed(val.m_enemyStats[i], profile.m_playerStats[currentAchievementDifficultyIndex].m_enemyStats[i]);
		}
		PushSteamStat((PlayerStatType)2);
		PushSteamStat((PlayerStatType)168);
		PushSteamStat((PlayerStatType)169);
		PushSteamStat((PlayerStatType)170);
		PushSteamStat((PlayerStatType)1);
		PushSteamStat((PlayerStatType)6);
		PushSteamStat((PlayerStatType)85);
		PushSteamStat((PlayerStatType)12);
		return num;
	}

	private static void PushSteamStat(PlayerStatType stat)
	{
		//IL_000a: 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)
		float stat2 = Game.instance.GetPlayerProfile().GetStat(stat);
		if (stat2 > 0f)
		{
			Achievements.AchievementStatSetEvent(stat, (uint)stat2);
		}
	}

	private static int MergeStats(Dictionary<PlayerStatType, float> from, Dictionary<PlayerStatType, float> to)
	{
		//IL_0016: 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)
		int num = 0;
		foreach (KeyValuePair<PlayerStatType, float> item in from)
		{
			if (!to.TryGetValue(item.Key, out var value) || item.Value > value)
			{
				to[item.Key] = item.Value;
				num++;
			}
		}
		return num;
	}

	private static int MergeNamed(Dictionary<string, float> from, Dictionary<string, float> to)
	{
		if (from == null || to == null)
		{
			return 0;
		}
		int num = 0;
		foreach (KeyValuePair<string, float> item in from)
		{
			if (!to.TryGetValue(item.Key, out var value) || item.Value > value)
			{
				to[item.Key] = item.Value;
				num++;
			}
		}
		return num;
	}

	private static void RecheckAllAchievementsFallback()
	{
		if ((Object)(object)Achievements.m_instance == (Object)null)
		{
			return;
		}
		foreach (AchievementList achievementList in Achievements.m_instance.m_achievementLists)
		{
			foreach (Achievement achievement in achievementList.m_achievements)
			{
				if (!achievement.m_unlocked && achievement.CheckUnlocked())
				{
					Achievements.AchievementEvent(achievement, false, true);
				}
			}
		}
		Achievements.AchievementStatSetEvent((PlayerStatType)2, (uint)Game.instance.GetPlayerProfile().GetStat((PlayerStatType)2));
	}
}
[HarmonyPatch(typeof(PlayerProfile), "get_s_bypassCheatChecks")]
internal static class BypassCheatChecksPatch
{
	private static void Postfix(ref bool __result)
	{
		if (AchievementSyncPlugin.EnableAchievements.Value)
		{
			__result = true;
		}
	}
}
[HarmonyPatch(typeof(Achievements), "CanGetAchievements")]
internal static class CanGetAchievementsPatch
{
	private static void Postfix(ref bool __result)
	{
		if (AchievementSyncPlugin.EnableAchievements.Value)
		{
			__result = true;
		}
	}
}
[HarmonyPatch(typeof(Inventory), "ItemCheated", new Type[]
{
	typeof(Requirement[]),
	typeof(int),
	typeof(bool)
})]
internal static class ItemCheatedRequirementsPatch
{
	private static void Postfix(ref bool __result)
	{
		if (AchievementSyncPlugin.EnableAchievements.Value)
		{
			__result = false;
		}
	}
}
[HarmonyPatch(typeof(Inventory), "ItemCheated", new Type[]
{
	typeof(string),
	typeof(int),
	typeof(bool)
})]
internal static class ItemCheatedNamePatch
{
	private static void Postfix(ref bool __result)
	{
		if (AchievementSyncPlugin.EnableAchievements.Value)
		{
			__result = false;
		}
	}
}
[HarmonyPatch(typeof(Inventory), "AddItem", new Type[] { typeof(ItemData) })]
internal static class AddItemPatch
{
	private static void Postfix(ItemData item, bool __result)
	{
		if (__result && item != null && AchievementSyncPlugin.ClearItemCheatFlags.Value)
		{
			item.m_cheated = false;
		}
	}
}
[HarmonyPatch(typeof(Inventory), "AddItem", new Type[]
{
	typeof(ItemData),
	typeof(int),
	typeof(int),
	typeof(int),
	typeof(bool)
})]
internal static class AddItemGridPatch
{
	private static void Postfix(ItemData item, bool __result)
	{
		if (__result && item != null && AchievementSyncPlugin.ClearItemCheatFlags.Value)
		{
			item.m_cheated = false;
		}
	}
}
[HarmonyPatch(typeof(PlayerProfile), "IncrementStat")]
internal static class IncrementStatPatch
{
	private static void Prefix(ref bool cheated)
	{
		if (AchievementSyncPlugin.EnableAchievements.Value)
		{
			cheated = false;
		}
	}
}
[HarmonyPatch(typeof(PlayerProfile), "IncrementStatBuildPiecePlaced")]
internal static class IncrementBuildPiecePatch
{
	private static void Prefix(ref bool cheated)
	{
		if (AchievementSyncPlugin.EnableAchievements.Value)
		{
			cheated = false;
		}
	}
}
[HarmonyPatch(typeof(PlayerProfile), "IncrementStatItemPickup")]
internal static class IncrementPickupPatch
{
	private static void Prefix(ref bool cheated)
	{
		if (AchievementSyncPlugin.EnableAchievements.Value)
		{
			cheated = false;
		}
	}
}
[HarmonyPatch(typeof(PlayerProfile), "IncrementStatItemCraft")]
internal static class IncrementCraftPatch
{
	private static void Prefix(ref bool cheated)
	{
		if (AchievementSyncPlugin.EnableAchievements.Value)
		{
			cheated = false;
		}
	}
}
[HarmonyPatch(typeof(PlayerProfile), "IncrementStatPickable")]
internal static class IncrementPickablePatch
{
	private static void Prefix(ref bool cheated)
	{
		if (AchievementSyncPlugin.EnableAchievements.Value)
		{
			cheated = false;
		}
	}
}
[HarmonyPatch(typeof(PlayerProfile), "IncrementStatFoodEaten")]
internal static class IncrementFoodPatch
{
	private static void Prefix(ref bool cheated)
	{
		if (AchievementSyncPlugin.EnableAchievements.Value)
		{
			cheated = false;
		}
	}
}
[HarmonyPatch(typeof(PlayerProfile), "IncrementStatEnemy")]
internal static class IncrementEnemyPatch
{
	private static void Prefix(ref bool cheated)
	{
		if (AchievementSyncPlugin.EnableAchievements.Value)
		{
			cheated = false;
		}
	}
}
[HarmonyPatch(typeof(PlayerProfile), "SetStat")]
internal static class SetStatPatch
{
	private static void Prefix(ref bool cheated)
	{
		if (AchievementSyncPlugin.EnableAchievements.Value)
		{
			cheated = false;
		}
	}
}
[HarmonyPatch(typeof(ItemData), "GetTooltip", new Type[]
{
	typeof(ItemData),
	typeof(int),
	typeof(bool),
	typeof(float),
	typeof(int),
	typeof(bool)
})]
internal static class TooltipCleanPatch
{
	private static void Prefix(ItemData item)
	{
		if (AchievementSyncPlugin.ClearItemCheatFlags.Value && item != null)
		{
			item.m_cheated = false;
		}
	}
}