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;
}
}