Decompiled source of co op Mod v0.0.2

skul_co_op_1.dll

Decompiled 2 months ago
using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using Characters;
using Data;
using HarmonyLib;
using Steamworks;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")]
[assembly: AssemblyCompany("skul_co_op_1")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("skul_co_op_1")]
[assembly: AssemblyTitle("skul_co_op_1")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace SkulMultiplayer;

public static class MultiplayerLogic
{
	public static void OnDataReceived(string json)
	{
		if (!string.IsNullOrEmpty(json) && !(json == "CONNECT_REQ") && json.StartsWith("{") && (Object)(object)Plugin.Instance != (Object)null)
		{
			Plugin.Instance.OnReceiveP2PPacket(json);
		}
	}
}
public enum PacketType
{
	PlayerUpdate,
	SyncSeed
}
[Serializable]
public class NetworkPacket
{
	public PacketType type;

	public int seedValue;

	public PlayerDataPacket playerData;
}
[Serializable]
public class PlayerDataPacket
{
	public string PlayerId;

	public float PositionX;

	public float PositionY;

	public float ScaleX;

	public string AnimationState;

	public int animatorStateHash;

	public float normalizedTime;

	public float animatorSpeed;
}
[HarmonyPatch(typeof(Character), "Update")]
public static class PlayerUpdatePatch
{
	public static PlayerDataPacket LocalPlayerData;

	public static void Postfix(Character __instance)
	{
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		//IL_001a: Invalid comparison between Unknown and I4
		//IL_004a: Unknown result type (might be due to invalid IL or missing references)
		//IL_004f: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
		//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)
		if (!((Object)(object)__instance == (Object)null) && ((int)__instance.type == 6 || ((Object)((Component)__instance).gameObject).name.Contains("Player")))
		{
			Vector3 position = ((Component)__instance).transform.position;
			Animator componentInChildren = ((Component)__instance).GetComponentInChildren<Animator>();
			int animatorStateHash = 0;
			float normalizedTime = 0f;
			float animatorSpeed = 1f;
			if ((Object)(object)componentInChildren != (Object)null && (Object)(object)componentInChildren.runtimeAnimatorController != (Object)null)
			{
				AnimatorStateInfo currentAnimatorStateInfo = componentInChildren.GetCurrentAnimatorStateInfo(0);
				animatorStateHash = ((AnimatorStateInfo)(ref currentAnimatorStateInfo)).shortNameHash;
				normalizedTime = ((AnimatorStateInfo)(ref currentAnimatorStateInfo)).normalizedTime;
				animatorSpeed = componentInChildren.speed;
			}
			LocalPlayerData = new PlayerDataPacket
			{
				PlayerId = "LocalPlayer",
				PositionX = position.x,
				PositionY = position.y,
				ScaleX = ((Component)__instance).transform.localScale.x,
				AnimationState = "Update",
				animatorStateHash = animatorStateHash,
				normalizedTime = normalizedTime,
				animatorSpeed = animatorSpeed
			};
		}
	}
}
[BepInPlugin("com.yourname.skul.multiplayer", "Skul Multiplayer Mod", "1.0.0")]
public class Plugin : BaseUnityPlugin
{
	public const string PLUGIN_GUID = "com.yourname.skul.multiplayer";

	public const string PLUGIN_NAME = "Skul Multiplayer Mod";

	public const string PLUGIN_VERSION = "1.0.0";

	private TextMeshProUGUI seedText;

	public static SteamNetworkManager SteamNet;

	private Harmony harmony;

	private RemotePlayerDummy remoteDummy;

	private bool isDummyActive = false;

	public static Plugin Instance { get; private set; }

	private void Awake()
	{
		//IL_001e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0024: Expected O, but got Unknown
		//IL_003c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0046: Expected O, but got Unknown
		Instance = this;
		((BaseUnityPlugin)this).Logger.LogInfo((object)"Skul Multiplayer Mod 스팀 P2P 모드 로드 완료!");
		GameObject val = new GameObject("SteamNetworkManager");
		SteamNet = val.AddComponent<SteamNetworkManager>();
		Object.DontDestroyOnLoad((Object)(object)val);
		harmony = new Harmony("com.yourname.skul.multiplayer");
		harmony.PatchAll();
	}

	private void Update()
	{
		if (Input.GetKeyDown((KeyCode)285))
		{
			ChangeRandomSeed(-1111111111);
			CreateSeedUI();
		}
		if (Input.GetKeyDown((KeyCode)286))
		{
			SteamNet.StartHost();
		}
		if (Input.GetKeyDown((KeyCode)287))
		{
			string systemCopyBuffer = GUIUtility.systemCopyBuffer;
			if (ulong.TryParse(systemCopyBuffer, out var result))
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"[SkulMulti] 클립보드에서 SteamID 감지: {result}");
				SteamNet.ConnectToTarget(result);
			}
			else
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("[SkulMulti] 클립보드 내용('" + systemCopyBuffer + "')이 올바른 SteamID64 번호 형태가 아닙니다!"));
			}
		}
		if (Input.GetKeyDown((KeyCode)288))
		{
			ToggleRemoteDummy();
		}
		if (Input.GetKeyDown((KeyCode)289))
		{
			SyncHostSeed();
		}
		SendLocalPlayerData();
	}

	private void ToggleRemoteDummy()
	{
		isDummyActive = !isDummyActive;
		if (isDummyActive)
		{
			EnsureDummyExists();
			if ((Object)(object)remoteDummy != (Object)null)
			{
				((Component)remoteDummy).gameObject.SetActive(true);
				((BaseUnityPlugin)this).Logger.LogInfo((object)"[SkulMulti] (F7) 상대 더미 표시: ON");
			}
		}
		else if ((Object)(object)remoteDummy != (Object)null)
		{
			((Component)remoteDummy).gameObject.SetActive(false);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"[SkulMulti] (F7) 상대 더미 표시: OFF");
		}
	}

	private void EnsureDummyExists()
	{
		//IL_0052: Unknown result type (might be due to invalid IL or missing references)
		//IL_0059: Expected O, but got Unknown
		if ((Object)(object)remoteDummy != (Object)null)
		{
			return;
		}
		GameObject val = GameObject.Find("Player(Clone)");
		if ((Object)(object)val != (Object)null)
		{
			RuntimeAnimatorController animatorController = null;
			Animator componentInChildren = val.GetComponentInChildren<Animator>();
			if ((Object)(object)componentInChildren != (Object)null)
			{
				animatorController = componentInChildren.runtimeAnimatorController;
			}
			GameObject val2 = new GameObject("RemotePlayer_Dummy");
			remoteDummy = val2.AddComponent<RemotePlayerDummy>();
			remoteDummy.Initialize(animatorController);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"[SkulMulti] 상대 플레이어 더미 생성 완료!");
		}
		else
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)"[SkulMulti] 플레이어(Player(Clone))를 찾을 수 없어 더미를 생성하지 못했습니다.");
		}
	}

	private void SyncHostSeed()
	{
		if ((Object)(object)SteamNet == (Object)null || !SteamNet.IsHost)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)"[SkulMulti] (F8 실패) 호스트 상태가 아닙니다!");
			return;
		}
		if (Save.instance == null)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)"[SkulMulti] (F8 실패) GameData.Save.instance 가 null 입니다!");
			return;
		}
		int randomSeed = Save.instance.randomSeed;
		UpdateSeedDisplay(randomSeed);
		NetworkPacket packet = new NetworkPacket
		{
			type = PacketType.SyncSeed,
			seedValue = randomSeed
		};
		SendNetworkPacket(packet);
	}

	public void OnReceiveP2PPacket(string jsonPacket)
	{
		NetworkPacket networkPacket = JsonUtility.FromJson<NetworkPacket>(jsonPacket);
		if (networkPacket == null)
		{
			return;
		}
		switch (networkPacket.type)
		{
		case PacketType.SyncSeed:
			ChangeRandomSeed(networkPacket.seedValue);
			CreateSeedUI();
			UpdateSeedDisplay(networkPacket.seedValue);
			((BaseUnityPlugin)this).Logger.LogInfo((object)$"[SkulMulti] 호스트로부터 시드를 성공적으로 수신하여 적용했습니다! (적용된 시드: {networkPacket.seedValue})");
			break;
		case PacketType.PlayerUpdate:
			if (isDummyActive)
			{
				if ((Object)(object)remoteDummy == (Object)null)
				{
					EnsureDummyExists();
				}
				if ((Object)(object)remoteDummy != (Object)null && networkPacket.playerData != null)
				{
					remoteDummy.UpdateState(networkPacket.playerData);
				}
				else if (networkPacket.playerData == null)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)"[SkulMulti] PlayerUpdate 패킷을 받았으나 playerData가 null입니다.");
				}
			}
			break;
		}
	}

	private void SendLocalPlayerData()
	{
		if (!((Object)(object)SteamNet == (Object)null) && SteamNet.IsConnected && ((CSteamID)(ref SteamNet.TargetPeerID)).IsValid())
		{
			PlayerDataPacket localPlayerData = PlayerUpdatePatch.LocalPlayerData;
			if (localPlayerData != null && !string.IsNullOrEmpty(localPlayerData.PlayerId))
			{
				NetworkPacket packet = new NetworkPacket
				{
					type = PacketType.PlayerUpdate,
					playerData = localPlayerData
				};
				SendNetworkPacket(packet);
			}
		}
	}

	private void SendNetworkPacket(NetworkPacket packet)
	{
		//IL_0063: 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_0074: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)SteamNet == (Object)null)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)"[SkulMulti] 패킷 전송 실패: SteamNet이 null입니다.");
		}
		else if (SteamNet.IsConnected && ((CSteamID)(ref SteamNet.TargetPeerID)).IsValid())
		{
			string data = JsonUtility.ToJson((object)packet);
			EP2PSend sendType = (EP2PSend)((packet.type == PacketType.SyncSeed) ? 2 : 0);
			SteamNet.SendP2PPacket(SteamNet.TargetPeerID, data, sendType);
			if (packet.type == PacketType.SyncSeed)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"[SkulMulti] (F8) 호스트 시드 전송 완료: {packet.seedValue}");
			}
		}
	}

	private void CreateSeedUI()
	{
		//IL_0035: Unknown result type (might be due to invalid IL or missing references)
		//IL_003b: Expected O, but got Unknown
		//IL_0097: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f6: 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_0122: Unknown result type (might be due to invalid IL or missing references)
		Canvas val = Object.FindObjectOfType<Canvas>();
		if (!((Object)(object)val == (Object)null) && !((Object)(object)seedText != (Object)null))
		{
			GameObject val2 = new GameObject("SeedDisplay_Text");
			val2.transform.SetParent(((Component)val).transform, false);
			seedText = val2.AddComponent<TextMeshProUGUI>();
			TextMeshProUGUI componentInChildren = ((Component)val).GetComponentInChildren<TextMeshProUGUI>();
			if ((Object)(object)componentInChildren != (Object)null)
			{
				((TMP_Text)seedText).font = ((TMP_Text)componentInChildren).font;
			}
			((TMP_Text)seedText).fontSize = 20f;
			((Graphic)seedText).color = Color.yellow;
			((TMP_Text)seedText).alignment = (TextAlignmentOptions)257;
			RectTransform rectTransform = ((TMP_Text)seedText).rectTransform;
			rectTransform.anchorMin = new Vector2(0f, 1f);
			rectTransform.anchorMax = new Vector2(0f, 1f);
			rectTransform.pivot = new Vector2(0f, 1f);
			rectTransform.anchoredPosition = new Vector2(20f, -20f);
			rectTransform.sizeDelta = new Vector2(300f, 50f);
			if (Save.instance != null)
			{
				UpdateSeedDisplay(Save.instance.randomSeed);
			}
		}
	}

	public void UpdateSeedDisplay(int newSeed)
	{
		if ((Object)(object)seedText == (Object)null)
		{
			CreateSeedUI();
		}
		if ((Object)(object)seedText != (Object)null)
		{
			if (newSeed == 0)
			{
				((TMP_Text)seedText).text = "SEED: Waiting...";
			}
			else
			{
				((TMP_Text)seedText).text = $"SEED: <color=#00FF88>{newSeed}</color>";
			}
		}
	}

	private static FieldInfo GetFieldInHierarchy(Type type, string fieldName)
	{
		while (type != null)
		{
			FieldInfo field = type.GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field != null)
			{
				return field;
			}
			type = type.BaseType;
		}
		return null;
	}

	public void ChangeRandomSeed(int newValue)
	{
		if (Save.instance == null)
		{
			return;
		}
		Type type = ((object)Save.instance).GetType();
		FieldInfo fieldInHierarchy = GetFieldInHierarchy(type, "_randomSeed");
		if (fieldInHierarchy == null)
		{
			return;
		}
		object value = fieldInHierarchy.GetValue(Save.instance);
		if (value != null)
		{
			Type type2 = value.GetType();
			FieldInfo fieldInfo = GetFieldInHierarchy(type2, "value") ?? GetFieldInHierarchy(type2, "_value") ?? GetFieldInHierarchy(type2, "m_value");
			if (fieldInfo != null)
			{
				fieldInfo.SetValue(value, newValue);
				Debug.Log((object)$"성공적으로 변경된 시드값: {newValue}");
			}
		}
	}
}
public class RemotePlayerDummy : MonoBehaviour
{
	private SpriteRenderer dummySR;

	private Animator dummyAnimator;

	public void Initialize(RuntimeAnimatorController animatorController)
	{
		dummySR = ((Component)this).gameObject.AddComponent<SpriteRenderer>();
		dummyAnimator = ((Component)this).gameObject.AddComponent<Animator>();
		if ((Object)(object)animatorController != (Object)null)
		{
			dummyAnimator.runtimeAnimatorController = animatorController;
		}
	}

	public void UpdateState(PlayerDataPacket packet)
	{
		//IL_0025: 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_003b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0063: Unknown result type (might be due to invalid IL or missing references)
		if (packet != null)
		{
			((Component)this).transform.position = new Vector3(packet.PositionX, packet.PositionY, -1f);
			Vector3 localScale = ((Component)this).transform.localScale;
			localScale.x = ((packet.ScaleX != 0f) ? packet.ScaleX : 1f);
			((Component)this).transform.localScale = localScale;
			if ((Object)(object)dummySR != (Object)null)
			{
				((Renderer)dummySR).sortingLayerName = "Foreground";
				((Renderer)dummySR).sortingOrder = 1000;
			}
			if ((Object)(object)dummyAnimator != (Object)null && packet.animatorStateHash != 0)
			{
				dummyAnimator.speed = packet.animatorSpeed;
				dummyAnimator.Play(packet.animatorStateHash, 0, packet.normalizedTime);
			}
		}
	}
}
public class SteamNetworkManager : MonoBehaviour
{
	public static SteamNetworkManager Instance;

	public CSteamID HostSteamID;

	public CSteamID TargetPeerID;

	protected Callback<P2PSessionRequest_t> p2pSessionRequestCallback;

	protected Callback<P2PSessionConnectFail_t> p2pSessionConnectFailCallback;

	public bool IsHost { get; private set; }

	public bool IsConnected { get; private set; }

	private void Awake()
	{
		Instance = this;
	}

	private void OnEnable()
	{
		if (SteamAPI.IsSteamRunning())
		{
			p2pSessionRequestCallback = Callback<P2PSessionRequest_t>.Create((DispatchDelegate<P2PSessionRequest_t>)OnP2PSessionRequest);
			p2pSessionConnectFailCallback = Callback<P2PSessionConnectFail_t>.Create((DispatchDelegate<P2PSessionConnectFail_t>)OnP2PSessionConnectFail);
		}
	}

	public void StartHost()
	{
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_0017: Unknown result type (might be due to invalid IL or missing references)
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		IsHost = true;
		IsConnected = true;
		HostSteamID = SteamUser.GetSteamID();
		Debug.Log((object)$"[SkulMulti] 스팀 호스트 시작! 내 SteamID: {HostSteamID}");
	}

	public void ConnectToTarget(ulong targetSteamId64)
	{
		//IL_000b: 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_0017: 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_0023: Unknown result type (might be due to invalid IL or missing references)
		//IL_0042: Unknown result type (might be due to invalid IL or missing references)
		IsHost = false;
		TargetPeerID = new CSteamID(targetSteamId64);
		HostSteamID = TargetPeerID;
		SendP2PPacket(TargetPeerID, "CONNECT_REQ", (EP2PSend)2);
		IsConnected = true;
		Debug.Log((object)$"[SkulMulti] {TargetPeerID} 님에게 스팀 P2P 접속 시도 중...");
	}

	private void Update()
	{
		if (SteamAPI.IsSteamRunning())
		{
			ReadP2PPackets();
		}
	}

	public void SendP2PPacket(CSteamID targetId, string data, EP2PSend sendType = (EP2PSend)0)
	{
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		byte[] bytes = Encoding.UTF8.GetBytes(data);
		SteamNetworking.SendP2PPacket(targetId, bytes, (uint)bytes.Length, sendType, 0);
	}

	private void ReadP2PPackets()
	{
		//IL_001f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		uint num = default(uint);
		uint count = default(uint);
		CSteamID targetPeerID = default(CSteamID);
		while (SteamNetworking.IsP2PPacketAvailable(ref num, 0))
		{
			byte[] array = new byte[num];
			if (SteamNetworking.ReadP2PPacket(array, num, ref count, ref targetPeerID, 0))
			{
				TargetPeerID = targetPeerID;
				string json = Encoding.UTF8.GetString(array, 0, (int)count);
				MultiplayerLogic.OnDataReceived(json);
			}
		}
	}

	private void OnP2PSessionRequest(P2PSessionRequest_t request)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_000e: 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_0014: Unknown result type (might be due to invalid IL or missing references)
		//IL_001e: 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)
		SteamNetworking.AcceptP2PSessionWithUser(request.m_steamIDRemote);
		TargetPeerID = request.m_steamIDRemote;
		Debug.Log((object)$"[SkulMulti] {request.m_steamIDRemote} 님의 P2P 연결 요청 수락됨!");
	}

	private void OnP2PSessionConnectFail(P2PSessionConnectFail_t fail)
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		Debug.LogError((object)$"[SkulMulti] P2P 세션 연결 실패! 에러 코드: {fail.m_eP2PSessionError}");
		IsConnected = false;
	}
}