Decompiled source of MeadhallChronicles MumbleContextRepair v0.2.0

BepInEx/plugins/MeadhallContextRepair.dll

Decompiled 2 weeks ago
using System;
using System.IO.MemoryMappedFiles;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using BepInEx;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
[BepInPlugin("com.meadhall.mumblecontextrepair", "Meadhall Mumble Context Repair (Test)", "0.2.0")]
public sealed class MeadhallContextRepair : BaseUnityPlugin
{
	private const long NameOffset = 44L;

	private const long ContextLengthOffset = 1104L;

	private const long ContextBytesOffset = 1108L;

	private const float SamplePeriod = 0.1f;

	private const float StableAfterRespawnSeconds = 6f;

	private const float StableAtInitialSpawnSeconds = 3f;

	private float elapsed;

	private float aliveSince = -1f;

	private bool everSawLivingPlayer;

	private bool recovering;

	private bool reportedFailure;

	private uint lastTick;

	private bool loggedRecovery;

	private void FixedUpdate()
	{
		elapsed += Time.fixedDeltaTime;
		if (elapsed < 0.1f)
		{
			return;
		}
		elapsed = 0f;
		bool flag = (Object)(object)ZNet.instance != (Object)null;
		Player localPlayer = Player.m_localPlayer;
		bool flag2 = flag && (Object)(object)localPlayer != (Object)null && !((Character)localPlayer).IsDead();
		float realtimeSinceStartup = Time.realtimeSinceStartup;
		if (!flag2)
		{
			aliveSince = -1f;
			if (everSawLivingPlayer && !recovering)
			{
				recovering = true;
				loggedRecovery = false;
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Player absent/dead: withholding positional context until stable respawn.");
			}
		}
		else
		{
			if (aliveSince < 0f)
			{
				aliveSince = realtimeSinceStartup;
			}
			everSawLivingPlayer = true;
		}
		try
		{
			using MemoryMappedFile memoryMappedFile = MemoryMappedFile.OpenExisting("MumbleLink", MemoryMappedFileRights.ReadWrite);
			using MemoryMappedViewAccessor memoryMappedViewAccessor = memoryMappedFile.CreateViewAccessor(0L, 0L, MemoryMappedFileAccess.ReadWrite);
			if (memoryMappedViewAccessor.ReadUInt32(0L) != 2)
			{
				return;
			}
			uint num = memoryMappedViewAccessor.ReadUInt32(4L);
			if (num == lastTick)
			{
				return;
			}
			lastTick = num;
			byte[] array = new byte[512];
			memoryMappedViewAccessor.ReadArray(44L, array, 0, array.Length);
			string text = Encoding.Unicode.GetString(array);
			char[] trimChars = new char[1];
			string text2 = text.TrimEnd(trimChars);
			if (text2 != "Valheim")
			{
				return;
			}
			uint num2 = memoryMappedViewAccessor.ReadUInt32(1104L);
			if (!flag2 || (recovering && realtimeSinceStartup - aliveSince < 6f))
			{
				if (num2 != 0)
				{
					memoryMappedViewAccessor.Write(1104L, 0u);
					memoryMappedViewAccessor.WriteArray(1108L, new byte[256], 0, 256);
				}
				return;
			}
			if (recovering)
			{
				recovering = false;
				if (!loggedRecovery)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)"Player stable after respawn; context repair allowed.");
					loggedRecovery = true;
				}
			}
			if (realtimeSinceStartup - aliveSince < 3f || num2 != 0)
			{
				return;
			}
			string worldName = ZNet.instance.GetWorldName();
			if (!string.IsNullOrEmpty(worldName))
			{
				byte[] bytes = Encoding.UTF8.GetBytes("Valheim\0" + worldName);
				if (bytes.Length <= 256)
				{
					byte[] array2 = new byte[256];
					Array.Copy(bytes, array2, bytes.Length);
					memoryMappedViewAccessor.WriteArray(1108L, array2, 0, array2.Length);
					memoryMappedViewAccessor.Write(1104L, (uint)bytes.Length);
					((BaseUnityPlugin)this).Logger.LogInfo((object)("Restored missing context after stable live-player check: " + worldName));
					reportedFailure = false;
				}
			}
		}
		catch (Exception ex)
		{
			if (!reportedFailure)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not inspect Mumble shared memory: " + ex.Message));
				reportedFailure = true;
			}
		}
	}
}