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 Socialize v1.0.2
IronLabs.Socialize.dll
Decompiled 2 days agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Logging; using HarmonyLib; using IronLabs.SharedLib; using Splatform; using TMPro; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("IronLabs.Socialize")] [assembly: AssemblyDescription("Adds persistent groups and expanded chat channels to Valheim.")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("IronLabs")] [assembly: AssemblyProduct("IronLabs.Socialize")] [assembly: AssemblyCopyright("Copyright © 2026 End3rbyte")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("DA5DE9DF-7727-4D5F-8274-896B8EB80ED3")] [assembly: AssemblyFileVersion("1.0.2")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("1.0.2.17987")] namespace IronLabs.Socialize { internal static class ChatChannelState { private enum Channel { Normal, Shout, Whisper, Group } private static Channel current; private static string whisperTarget = ""; private static bool redirecting; internal static void SetNormal() { current = Channel.Normal; whisperTarget = ""; } internal static void SetShout() { current = Channel.Shout; whisperTarget = ""; } internal static void SetWhisper(string target) { current = Channel.Whisper; whisperTarget = target ?? ""; } internal static void SetGroup() { current = Channel.Group; whisperTarget = ""; } internal static string GetPrompt() { return current switch { Channel.Shout => "Shouting...", Channel.Whisper => "Talking to " + whisperTarget + "...", Channel.Group => "Speaking to the group...", _ => "Speaking...", }; } internal static bool TryRedirect(string text) { if (redirecting || current == Channel.Normal || string.IsNullOrWhiteSpace(text)) { return false; } redirecting = true; try { return Send(text); } finally { redirecting = false; } } private static bool Send(string text) { switch (current) { case Channel.Shout: SocialChatSender.SendShout(text); return true; case Channel.Whisper: return PrivateChat.Send(whisperTarget, text, (Terminal)(object)Chat.instance); case Channel.Group: GroupService.SendChat(text); return true; default: return false; } } } internal static class SocialChatSender { internal static void SendShout(string text) { Talker val = (((Object)(object)Player.m_localPlayer != (Object)null) ? ((Component)Player.m_localPlayer).GetComponent<Talker>() : null); if (!((Object)(object)val == (Object)null)) { val.Say((Type)2, text); } } internal static void ApplyShoutRange(Talker talker) { talker.m_shoutDistance = talker.m_normalDistance * 2f; } } internal static class ChatCommands { internal static void Register() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Expected O, but got Unknown //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Expected O, but got Unknown //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Expected O, but got Unknown //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Expected O, but got Unknown Register("sh", new ConsoleEventFailable(SendShout)); Register("shout", new ConsoleEventFailable(SendShout)); Register("s", new ConsoleEventFailable(SendSay)); Register("say", new ConsoleEventFailable(SendSay)); Register("w", new ConsoleEventFailable(SendWhisper)); Register("wping", new ConsoleEventFailable(SendTargetPing)); } private static void Register(string name, ConsoleEventFailable handler) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) new ConsoleCommand(name, GetDescription(name), handler, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); } private static string GetDescription(string name) { if (name == "w") { return "[player] [message] sends a private message"; } if (name == "wping") { return "[player] [message] sends a private message with a ping"; } return "[message] " + ((name == "s" || name == "say") ? "says something to nearby players" : "shouts so everyone around you can hear you"); } private static object SendShout(ConsoleEventArgs args) { if (!TryGetMessage(args, out var message)) { args.Context.AddString("Usage: /sh message"); return true; } ChatChannelState.SetShout(); SocialChatSender.SendShout(message); return true; } private static object SendSay(ConsoleEventArgs args) { if (!TryGetMessage(args, out var message)) { args.Context.AddString("Usage: /s message"); return true; } ChatChannelState.SetNormal(); Chat.instance.SendText((Type)1, message); return true; } private static object SendWhisper(ConsoleEventArgs args) { if (!TryParseTarget(args.FullLine, out var target, out var message)) { args.Context.AddString("Usage: /w PlayerName message"); return true; } if (!PrivateChat.Send(target, message, args.Context)) { return true; } ChatChannelState.SetWhisper(target); return true; } private static object SendTargetPing(ConsoleEventArgs args) { if (!TryParseTarget(args.FullLine, out var target, out var message)) { args.Context.AddString("Usage: /wping PlayerName message"); return true; } TargetPingService.Send(target, message, args.Context); return true; } private static bool TryGetMessage(ConsoleEventArgs args, out string message) { message = (args.ArgsAll ?? "").Trim(); if ((Object)(object)Chat.instance != (Object)null) { return !string.IsNullOrEmpty(message); } return false; } private static bool TryParseTarget(string fullLine, out string target, out string message) { target = ""; message = ""; int num = fullLine.IndexOf(' '); if (num < 0) { return false; } int num2 = fullLine.IndexOf(' ', num + 1); string text = ((num2 >= 0) ? fullLine.Substring(num + 1, num2 - num - 1) : fullLine.Substring(num + 1)); target = text.Trim(); if (target.StartsWith("@", StringComparison.Ordinal)) { target = target.Substring(1); } message = ((num2 >= 0) ? fullLine.Substring(num2 + 1) : ""); if (!string.IsNullOrWhiteSpace(target)) { return !string.IsNullOrWhiteSpace(message); } return false; } } internal static class PrivateChat { internal static bool Send(string targetName, string message, Terminal context) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) if (!TryFindPlayer(targetName, out var player)) { if (context != null) { context.AddString("No connected player named \"" + targetName + "\" was found."); } return false; } if (IsLocalPlayer(player)) { if (context != null) { context.AddString("You cannot whisper yourself."); } return false; } SendToTarget(player, message); string name = Game.instance.GetPlayerProfile().GetName(); ChatFormatting.AddPrivate(context, name, "to " + player.m_name + ": " + message, timestamp: false); return true; } private static void SendToTarget(PlayerInfo target, string message) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) UserInfo val = default(UserInfo); string text = default(string); Chat.GetChatMessageData(message, true, ref val, ref text); ZRoutedRpc.instance.InvokeRoutedRPC(((ZDOID)(ref target.m_characterID)).UserID, "ChatMessage", new object[4] { ((Character)Player.m_localPlayer).GetHeadPoint(), 0, val, text }); } private static bool TryFindPlayer(string name, out PlayerInfo player) { //IL_0014: 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) //IL_001a: 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_002b: 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) foreach (PlayerInfo player2 in ZNet.instance.GetPlayerList()) { if (string.Equals(player2.m_name, name, StringComparison.OrdinalIgnoreCase)) { player = player2; return true; } } player = default(PlayerInfo); return false; } private static bool IsLocalPlayer(PlayerInfo player) { //IL_001e: 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) if ((Object)(object)ZNet.instance != (Object)null) { long userID = ((ZDOID)(ref player.m_characterID)).UserID; ZDOID localPlayerCharacterID = ZNet.instance.LocalPlayerCharacterID; return userID == ((ZDOID)(ref localPlayerCharacterID)).UserID; } return false; } } internal static class ChatFormatting { private const string GroupColor = "#4A90E2"; private const string PrivateColor = "#2FAE5F"; private const string ShoutColor = "#FFFF00"; internal static string FormatGroup(string sender, string message) { return "<color=#4A90E2>" + sender + ": " + message + "</color>"; } internal static void AddPrivate(Terminal terminal, string user, string text, bool timestamp) { terminal.AddString(GetTimestamp(timestamp) + "<color=#2FAE5F>" + user + FormatPrivateText(text) + "</color>"); } internal static void AddShout(Terminal terminal, string user, string text, bool timestamp) { terminal.AddString(GetTimestamp(timestamp) + "<color=orange>" + user + "</color>: <color=#FFFF00>" + text + "</color>"); } internal static void AddPing(Terminal terminal, string user, string target, string message) { string text = (string.IsNullOrEmpty(target) ? ": " : (" to " + target + ": ")); terminal.AddString("<color=#2FAE5F>" + user + text + "</color><color=#FFFF00>((Ping))</color><color=#2FAE5F> " + message + "</color>"); } internal unsafe static string GetPlayerName(PlatformUserID user) { //IL_0000: 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) PlayerInfo val = default(PlayerInfo); if (!ZNet.TryGetPlayerByPlatformUserID(user, ref val)) { return ((object)(*(PlatformUserID*)(&user))/*cast due to .constrained prefix*/).ToString(); } return val.m_name; } private static string FormatPrivateText(string text) { if (!(text ?? "").StartsWith("to ", StringComparison.OrdinalIgnoreCase)) { return ": " + text; } return " " + text; } private static string GetTimestamp(bool enabled) { if (!enabled) { return ""; } return "[" + DateTime.Now.ToString("MM-dd-yyyy HH:mm:ss") + "] "; } } [HarmonyPatch(typeof(Terminal), "InitTerminal")] internal static class RegisterSocialCommandsPatch { private static bool registered; private static void Postfix() { if (!registered) { registered = true; ChatCommands.Register(); GroupCommands.Register(); } } } [HarmonyPatch(typeof(Chat), "AddInworldText")] internal static class DisablePrivateWorldTextPatch { private static bool Prefix(Type type) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 return (int)type > 0; } } [HarmonyPatch(typeof(Chat), "SendPing")] internal static class LimitMapPingToGroupPatch { private static bool Prefix() { if (GroupService.IsLocalPlayerInGroup()) { return true; } SocializePlugin.Log.LogDebug("Map ping ignored because the local player is not in a group."); return false; } } [HarmonyPatch(typeof(Chat), "SendInput")] internal static class PersistentChatInputPatch { private static bool Prefix(Chat __instance) { if ((Object)(object)((Terminal)__instance).m_input == (Object)null || string.IsNullOrWhiteSpace(((TMP_InputField)((Terminal)__instance).m_input).text) || ((TMP_InputField)((Terminal)__instance).m_input).text.StartsWith("/") || !ChatChannelState.TryRedirect(((TMP_InputField)((Terminal)__instance).m_input).text)) { return true; } ((TMP_InputField)((Terminal)__instance).m_input).text = ""; __instance.Hide(); return false; } } [HarmonyPatch(typeof(Chat), "SendText")] internal static class PersistentChatChannelPatch { private static bool Prefix(Type type, string text) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Invalid comparison between Unknown and I4 if ((int)type == 2) { SocialChatSender.SendShout(text); return false; } if ((int)type == 1) { return !ChatChannelState.TryRedirect(text); } return true; } } [HarmonyPatch(typeof(Talker), "Awake")] internal static class SocialShoutRangePatch { private static void Postfix(Talker __instance) { SocialChatSender.ApplyShoutRange(__instance); } } [HarmonyPatch(typeof(Chat), "Update")] internal static class ChatPresentationPatch { private static Chat owner; private static TMP_Text placeholder; private static float showUntil; private static void Postfix(Chat __instance) { if ((Object)(object)owner != (Object)(object)__instance) { owner = __instance; placeholder = null; } EnsurePlaceholder(__instance); if ((Object)(object)placeholder != (Object)null) { placeholder.text = ChatChannelState.GetPrompt(); } if ((Object)(object)((Terminal)__instance).m_chatWindow != (Object)null && Time.time < showUntil) { ((Component)((Terminal)__instance).m_chatWindow).gameObject.SetActive(true); } } internal static void Show(Terminal terminal) { Chat val = (Chat)(object)((terminal is Chat) ? terminal : null); if (!((Object)(object)val == (Object)null) && !val.HasFocus() && !((Object)(object)((Terminal)val).m_chatWindow == (Object)null)) { ((Component)((Terminal)val).m_chatWindow).gameObject.SetActive(true); showUntil = Time.time + val.m_hideDelay; } } private static void EnsurePlaceholder(Chat chat) { if (!((Object)(object)placeholder != (Object)null) && !((Object)(object)((Terminal)chat).m_input == (Object)null)) { TMP_InputField val = ((Component)((Terminal)chat).m_input).GetComponent<TMP_InputField>() ?? ((Component)((Terminal)chat).m_input).GetComponentInChildren<TMP_InputField>(true); placeholder = (TMP_Text)(((Object)(object)val != (Object)null) ? /*isinst with value type is only supported in some contexts*/: null); } } } [HarmonyPatch(typeof(Terminal), "AddString", new Type[] { typeof(string) })] internal static class AutoDisplaySimpleChatPatch { private static void Postfix(Terminal __instance) { ChatPresentationPatch.Show(__instance); } } [HarmonyPatch(typeof(Terminal), "AddString", new Type[] { typeof(string), typeof(string), typeof(Type), typeof(bool) })] internal static class FormatTitleChatPatch { private static bool Prefix(Terminal __instance, string title, string text, Type type, bool timestamp) { //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_0011: Invalid comparison between Unknown and I4 if ((int)type == 0) { ChatFormatting.AddPrivate(__instance, title, text, timestamp); return false; } if ((int)type == 2) { ChatFormatting.AddShout(__instance, title, text, timestamp); return false; } return true; } private static void Postfix(Terminal __instance) { ChatPresentationPatch.Show(__instance); } } [HarmonyPatch(typeof(Terminal), "AddString", new Type[] { typeof(PlatformUserID), typeof(string), typeof(Type), typeof(bool) })] internal static class FormatUserChatPatch { private static bool Prefix(Terminal __instance, PlatformUserID user, string text, Type type, bool timestamp) { //IL_0000: 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_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Invalid comparison between Unknown and I4 string playerName = ChatFormatting.GetPlayerName(user); if ((int)type == 0) { ChatFormatting.AddPrivate(__instance, playerName, text, timestamp); return false; } if ((int)type == 2) { ChatFormatting.AddShout(__instance, playerName, text, timestamp); return false; } return true; } private static void Postfix(Terminal __instance) { ChatPresentationPatch.Show(__instance); } } internal static class GroupCommands { internal static void Register() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown //IL_001f: 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_0049: Expected O, but got Unknown //IL_0044: Unknown result type (might be due to invalid IL or missing references) new ConsoleCommand("group", "[help|invite|leave|remove|promote|info] manages your group", new ConsoleEvent(HandleGroup), false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); new ConsoleCommand("g", "[message] sends a message to your group.", new ConsoleEvent(HandleGroupChat), false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); } private static void HandleGroup(ConsoleEventArgs args) { string text = (args.ArgsAll ?? "").Trim(); int num = text.IndexOf(' '); string text2 = ((num < 0) ? text.ToLowerInvariant() : text.Substring(0, num).ToLowerInvariant()); string argument = ((num < 0) ? "" : text.Substring(num + 1).Trim()); if (text2 == "help" || string.IsNullOrEmpty(text2)) { ShowHelp(args.Context); } else if (!IsValid(text2, argument)) { ShowHelp(args.Context); } else { GroupService.SendRequest(text2, argument); } } private static void HandleGroupChat(ConsoleEventArgs args) { string text = (args.ArgsAll ?? "").Trim(); if (string.IsNullOrEmpty(text)) { args.Context.AddString("Usage: /g message"); return; } ChatChannelState.SetGroup(); GroupService.SendChat(text); } private static bool IsValid(string action, string argument) { switch (action) { case "leave": case "info": return string.IsNullOrEmpty(argument); case "invite": case "remove": case "promote": return !string.IsNullOrEmpty(argument); default: return false; } } private static void ShowHelp(Terminal context) { context.AddString("/group invite <PlayerName> - Invites a connected player."); context.AddString("/group leave - Leaves your group."); context.AddString("/group remove <PlayerName> - Removes a member."); context.AddString("/group promote <PlayerName> - Promotes a member."); context.AddString("/group info - Lists group members."); context.AddString("/g <message> - Sends a group message."); } } internal sealed class SocialGroup { internal int Id; internal long Leader; internal readonly Dictionary<long, string> Members = new Dictionary<long, string>(); } internal static class GroupState { internal const int MaximumSize = 5; internal static readonly Dictionary<int, SocialGroup> Groups = new Dictionary<int, SocialGroup>(); internal static readonly Dictionary<long, int> PlayerGroups = new Dictionary<long, int>(); internal static readonly Dictionary<long, long> PeerPlayers = new Dictionary<long, long>(); internal static readonly Dictionary<long, long> Invitations = new Dictionary<long, long>(); internal static readonly HashSet<long> LocalMembers = new HashSet<long>(); internal static SocialGroup GetGroup(long playerId) { if (!PlayerGroups.TryGetValue(playerId, out var value) || !Groups.TryGetValue(value, out var value2)) { return null; } return value2; } internal static int GetNextGroupId() { int num = 0; foreach (int key in Groups.Keys) { num = ((key > num) ? key : num); } return num + 1; } internal static void ClearServer() { Groups.Clear(); PlayerGroups.Clear(); PeerPlayers.Clear(); Invitations.Clear(); } internal static void ClearAll() { ClearServer(); LocalMembers.Clear(); GroupMapSharing.Clear(); } } internal static class GroupMapSharing { private static readonly Dictionary<long, PlayerInfo> Players = new Dictionary<long, PlayerInfo>(); internal static void Clear() { Players.Clear(); } internal static void WritePosition(ZPackage package, long playerId) { //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) if (!TryGetPosition(playerId, out var characterId, out var position)) { package.Write(false); return; } package.Write(true); package.Write(characterId); package.Write(position); } internal static void ReadPosition(ZPackage package, long playerId, string name) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) if (!package.ReadBool()) { Players.Remove(playerId); return; } Players[playerId] = new PlayerInfo { m_name = name, m_characterID = package.ReadZDOID(), m_publicPosition = true, m_position = package.ReadVector3() }; } internal static void AddGroupMembers(List<PlayerInfo> players) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) foreach (KeyValuePair<long, PlayerInfo> player in Players) { if (!IsLocalPlayer(player.Key) && !Contains(players, player.Key)) { players.Add(player.Value); } } } private static bool TryGetPosition(long playerId, out ZDOID characterId, out Vector3 position) { //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_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) Player player = Player.GetPlayer(playerId); if ((Object)(object)player != (Object)null) { characterId = ((Character)player).GetZDOID(); position = ((Component)player).transform.position; return true; } return TryGetPeerPosition(playerId, out characterId, out position); } private static bool TryGetPeerPosition(long playerId, out ZDOID characterId, out Vector3 position) { //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0082: 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) //IL_0049: 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_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { if (GroupState.PeerPlayers.TryGetValue(peer.m_uid, out var value) && value == playerId && peer.IsReady() && !((ZDOID)(ref peer.m_characterID)).IsNone()) { characterId = peer.m_characterID; position = peer.m_refPos; return true; } } characterId = ZDOID.None; position = Vector3.zero; return false; } private static bool Contains(List<PlayerInfo> players, long playerId) { //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_0011: 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) //IL_0017: Unknown result type (might be due to invalid IL or missing references) foreach (PlayerInfo player in players) { ZDOID characterID = player.m_characterID; if (((ZDOID)(ref characterID)).UserID == playerId) { return true; } } return false; } private static bool IsLocalPlayer(long playerId) { if ((Object)(object)Game.instance != (Object)null) { return Game.instance.GetPlayerProfile().GetPlayerID() == playerId; } return false; } } internal static class GroupRpc { internal static void RPC_Request(long sender, ZPackage package) { if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && GroupStorage.TryLoad()) { string action = package.ReadString(); long playerId = package.ReadLong(); string playerName = package.ReadString(); string argument = package.ReadString().Trim(); GroupService.Dispatch(sender, playerId, playerName, action, argument); } } internal static void RPC_Response(long sender, ZPackage package) { GroupService.ReadResponse(package); } } internal static class GroupService { internal const string RequestRpc = "IronLabs_Social_GroupRequest"; internal const string ResponseRpc = "IronLabs_Social_GroupResponse"; private static ZRoutedRpc registeredRpc; private static float nextStateRequest; internal static void Update() { EnsureRpcs(); if (!((Object)(object)ZNet.instance == (Object)null) && ZRoutedRpc.instance != null) { if (ZNet.instance.IsServer()) { GroupStorage.TryLoad(); } if ((Object)(object)Player.m_localPlayer != (Object)null && Time.unscaledTime >= nextStateRequest) { nextStateRequest = Time.unscaledTime + 1f; SendRequest("state", ""); } } } internal static void Reset() { registeredRpc = null; nextStateRequest = 0f; GroupStorage.Reset(); GroupState.ClearAll(); } internal static bool IsLocalPlayerInGroup() { if ((Object)(object)Game.instance != (Object)null) { return GroupState.LocalMembers.Contains(Game.instance.GetPlayerProfile().GetPlayerID()); } return false; } internal static void SendChat(string message) { if (!IsLocalPlayerInGroup()) { Chat instance = Chat.instance; if (instance != null) { ((Terminal)instance).AddString("You are not in a group."); } } else { SendRequest("chat", message); } } internal static void SendRequest(string action, string argument) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Expected O, but got Unknown EnsureRpcs(); if (ZRoutedRpc.instance != null && !((Object)(object)Game.instance == (Object)null) && !((Object)(object)Player.m_localPlayer == (Object)null)) { ZPackage val = new ZPackage(); val.Write(action); val.Write(Game.instance.GetPlayerProfile().GetPlayerID()); val.Write(Game.instance.GetPlayerProfile().GetName()); val.Write(argument ?? ""); ZRoutedRpc.instance.InvokeRoutedRPC("IronLabs_Social_GroupRequest", new object[1] { val }); } } internal static void Dispatch(long sender, long playerId, string playerName, string action, string argument) { GroupState.PeerPlayers[sender] = playerId; if (action == null) { return; } switch (action.Length) { case 5: switch (action[0]) { case 's': if (action == "state") { SendSnapshot(sender, playerId); } break; case 'l': if (action == "leave") { Leave(sender, playerId); } break; } break; case 6: switch (action[2]) { case 'v': if (action == "invite") { Invite(sender, playerId, playerName, argument); } break; case 'c': if (action == "accept") { Accept(sender, playerId, playerName, argument); } break; case 'j': if (action == "reject") { Reject(sender, playerId, argument); } break; case 'm': if (action == "remove") { Remove(sender, playerId, argument); } break; } break; case 4: switch (action[0]) { case 'i': if (action == "info") { SendInfo(sender, playerId); } break; case 'c': if (action == "chat") { SendGroupChat(sender, playerId, argument); } break; } break; case 7: if (action == "promote") { Promote(sender, playerId, argument); } break; } } internal static void ReadResponse(ZPackage package) { switch (package.ReadString()) { case "message": { Chat instance = Chat.instance; if (instance != null) { ((Terminal)instance).AddString(package.ReadString()); } break; } case "snapshot": ReadSnapshot(package); break; case "invite": ShowInvite(package.ReadString(), package.ReadString()); break; } } private static void EnsureRpcs() { if (RpcRegistry.RegisterIfChanged(ref registeredRpc, RegisterRpcs)) { GroupStorage.Reset(); GroupState.ClearAll(); } } private static void RegisterRpcs(ZRoutedRpc rpc) { rpc.Register<ZPackage>("IronLabs_Social_GroupRequest", (Action<long, ZPackage>)GroupRpc.RPC_Request); rpc.Register<ZPackage>("IronLabs_Social_GroupResponse", (Action<long, ZPackage>)GroupRpc.RPC_Response); GameModeGroupIntegration.Register(rpc); } private static void Invite(long sender, long inviter, string inviterName, string targetName) { long num = FindPeerByName(targetName); if (!TryGetPlayerForPeer(num, out var playerId)) { SendMessage(sender, "Player not found or not ready."); } else if (CanInvite(sender, inviter, playerId)) { GroupState.Invitations[playerId] = inviter; ZPackage val = NewResponse("invite"); val.Write(inviter.ToString()); val.Write(inviterName); ZRoutedRpc.instance.InvokeRoutedRPC(num, "IronLabs_Social_GroupResponse", new object[1] { val }); SendMessage(sender, "Group invitation sent."); } } private static bool CanInvite(long sender, long inviter, long target) { SocialGroup socialGroup = GroupState.GetGroup(inviter); if (GroupState.PlayerGroups.ContainsKey(target)) { SendMessage(sender, "That player is already in a group."); return false; } if (socialGroup != null && socialGroup.Leader != inviter) { SendMessage(sender, "Only the group leader can invite players."); return false; } if (socialGroup != null && socialGroup.Members.Count >= 5) { SendMessage(sender, "Your group is full."); return false; } return inviter != target; } private static void Accept(long sender, long playerId, string playerName, string inviterText) { if (!long.TryParse(inviterText, out var result) || !GroupState.Invitations.TryGetValue(playerId, out var value) || value != result) { SendMessage(sender, "That group invitation is no longer valid."); return; } SocialGroup orCreateGroup = GetOrCreateGroup(result); if (orCreateGroup == null || orCreateGroup.Members.Count >= 5) { SendMessage(sender, "That group is no longer available."); return; } orCreateGroup.Members[playerId] = playerName; GroupState.PlayerGroups[playerId] = orCreateGroup.Id; GroupState.Invitations.Remove(playerId); SaveAndBroadcast(orCreateGroup, playerName + " joined the group."); } private static SocialGroup GetOrCreateGroup(long inviter) { SocialGroup socialGroup = GroupState.GetGroup(inviter); if (socialGroup != null) { if (socialGroup.Leader != inviter) { return null; } return socialGroup; } socialGroup = new SocialGroup { Id = GroupState.GetNextGroupId(), Leader = inviter }; socialGroup.Members[inviter] = GetPlayerName(inviter); GroupState.Groups[socialGroup.Id] = socialGroup; GroupState.PlayerGroups[inviter] = socialGroup.Id; return socialGroup; } private static void Reject(long sender, long playerId, string inviterText) { GroupState.Invitations.Remove(playerId); SendMessage(sender, "Group invitation rejected."); if (long.TryParse(inviterText, out var result)) { SendMessage(FindPeer(result), GetPlayerName(playerId) + " rejected the group invitation."); } } private static void Leave(long sender, long playerId) { SocialGroup socialGroup = GroupState.GetGroup(playerId); if (socialGroup == null) { SendMessage(sender, "You are not in a group."); return; } string text = socialGroup.Members[playerId]; socialGroup.Members.Remove(playerId); GroupState.PlayerGroups.Remove(playerId); NormalizeAfterRemoval(socialGroup); GroupStorage.Save(); SendMessage(sender, "You left the group."); Broadcast(socialGroup, text + " left the group."); BroadcastSnapshots(socialGroup); } private static void Remove(long sender, long actor, string targetName) { SocialGroup socialGroup = GroupState.GetGroup(actor); long num = FindMember(socialGroup, targetName); if (ValidateLeaderAction(sender, actor, targetName, socialGroup, num) && ValidateRemoveTarget(sender, actor, num)) { string text = socialGroup.Members[num]; socialGroup.Members.Remove(num); GroupState.PlayerGroups.Remove(num); NormalizeAfterRemoval(socialGroup); GroupStorage.Save(); SendMessage(FindPeer(num), "You were removed from the group."); Broadcast(socialGroup, text + " was removed from the group."); BroadcastSnapshots(socialGroup); } } private static void Promote(long sender, long actor, string targetName) { SocialGroup socialGroup = GroupState.GetGroup(actor); long num = FindMember(socialGroup, targetName); if (ValidateLeaderAction(sender, actor, targetName, socialGroup, num) && ValidatePromoteTarget(sender, actor, num)) { socialGroup.Leader = num; GroupStorage.Save(); Broadcast(socialGroup, socialGroup.Members[num] + " is now the group leader."); BroadcastSnapshots(socialGroup); } } private static bool ValidateLeaderAction(long sender, long actor, string targetName, SocialGroup group, long target) { if (group == null) { SendMessage(sender, "You are not in a group."); return false; } if (group.Leader != actor) { SendMessage(sender, "Only the group leader can do that."); return false; } if (target == 0L) { SendMessage(sender, "Player not found in your group: " + targetName); return false; } return true; } private static bool ValidateRemoveTarget(long sender, long actor, long target) { if (target != actor) { return true; } SendMessage(sender, "You cannot remove yourself."); return false; } private static bool ValidatePromoteTarget(long sender, long actor, long target) { if (target != actor) { return true; } SendMessage(sender, "You are already group leader."); return false; } private static void NormalizeAfterRemoval(SocialGroup group) { if (group.Members.Count <= 1) { foreach (long key in group.Members.Keys) { GroupState.PlayerGroups.Remove(key); SendMessage(FindPeer(key), "The group was disbanded."); SendSnapshot(FindPeer(key), key); } GroupState.Groups.Remove(group.Id); group.Members.Clear(); } else { if (group.Members.ContainsKey(group.Leader)) { return; } using Dictionary<long, string>.KeyCollection.Enumerator enumerator = group.Members.Keys.GetEnumerator(); if (enumerator.MoveNext()) { long current2 = enumerator.Current; group.Leader = current2; } } } private static void SendGroupChat(long sender, long actor, string message) { SocialGroup socialGroup = GroupState.GetGroup(actor); if (socialGroup == null) { SendMessage(sender, "You are not in a group."); } else if (!HasOtherOnlineMember(socialGroup, actor)) { SendMessage(sender, "No other group member is connected."); } else { Broadcast(socialGroup, ChatFormatting.FormatGroup(socialGroup.Members[actor], message)); } } private static bool HasOtherOnlineMember(SocialGroup group, long actor) { foreach (long key in group.Members.Keys) { if (key != actor && FindPeer(key) != 0L) { return true; } } return false; } private static void SendInfo(long sender, long playerId) { SocialGroup socialGroup = GroupState.GetGroup(playerId); if (socialGroup == null) { SendMessage(sender, "You are not in a group."); return; } List<string> list = new List<string> { "Group members:" }; foreach (KeyValuePair<long, string> member in socialGroup.Members) { string text = ((FindPeer(member.Key) != 0L) ? "Connected" : "Disconnected"); string text2 = ((member.Key == socialGroup.Leader) ? " - Group Leader" : ""); list.Add(member.Value + " - " + text + text2); } SendMessage(sender, string.Join("\n", list)); } private static void SaveAndBroadcast(SocialGroup group, string message) { GroupStorage.Save(); Broadcast(group, message); BroadcastSnapshots(group); SocializePlugin.Log.LogInfo(message); } private static void BroadcastSnapshots(SocialGroup group) { if (group == null) { return; } foreach (long key in group.Members.Keys) { SendSnapshot(FindPeer(key), key); } } private static void SendSnapshot(long peer, long playerId) { if (peer == 0L) { return; } SocialGroup socialGroup = GroupState.GetGroup(playerId); ZPackage val = NewResponse("snapshot"); val.Write(socialGroup?.Leader ?? 0); val.Write(socialGroup?.Members.Count ?? 0); if (socialGroup != null) { foreach (KeyValuePair<long, string> member in socialGroup.Members) { val.Write(member.Key); val.Write(member.Value); GroupMapSharing.WritePosition(val, member.Key); } } ZRoutedRpc.instance.InvokeRoutedRPC(peer, "IronLabs_Social_GroupResponse", new object[1] { val }); } private static void ReadSnapshot(ZPackage package) { package.ReadLong(); GroupState.LocalMembers.Clear(); GroupMapSharing.Clear(); int num = package.ReadInt(); for (int i = 0; i < num; i++) { long num2 = package.ReadLong(); string name = package.ReadString(); GroupState.LocalMembers.Add(num2); GroupMapSharing.ReadPosition(package, num2, name); } } private static void ShowInvite(string inviterId, string inviterName) { //IL_0024: 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_003b: Expected O, but got Unknown //IL_003b: Expected O, but got Unknown //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected O, but got Unknown UnifiedPopup.Push((PopupBase)new YesNoPopup("Group invitation", inviterName + " invited you to a group.", (PopupButtonCallback)delegate { RespondToInvite("accept", inviterId); }, (PopupButtonCallback)delegate { RespondToInvite("reject", inviterId); }, false)); } private static void RespondToInvite(string action, string inviterId) { UnifiedPopup.Pop(); SendRequest(action, inviterId); } private static void Broadcast(SocialGroup group, string message) { if (group == null) { return; } foreach (long key in group.Members.Keys) { SendMessage(FindPeer(key), message); } } private static void SendMessage(long peer, string message) { if (peer == 0L) { Chat instance = Chat.instance; if (instance != null) { ((Terminal)instance).AddString(message); } } else { ZPackage val = NewResponse("message"); val.Write(message ?? ""); ZRoutedRpc.instance.InvokeRoutedRPC(peer, "IronLabs_Social_GroupResponse", new object[1] { val }); } } private static ZPackage NewResponse(string type) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(type); return val; } private static long FindMember(SocialGroup group, string name) { if (group == null) { return 0L; } foreach (KeyValuePair<long, string> member in group.Members) { if (string.Equals(member.Value, name, StringComparison.OrdinalIgnoreCase)) { return member.Key; } } return 0L; } private static long FindPeerByName(string name) { //IL_0024: 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_0039: 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) //IL_003f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZNet.instance == (Object)null) { return 0L; } foreach (PlayerInfo player in ZNet.instance.GetPlayerList()) { if (string.Equals(player.m_name, name, StringComparison.OrdinalIgnoreCase)) { ZDOID characterID = player.m_characterID; return ((ZDOID)(ref characterID)).UserID; } } return 0L; } private static bool TryGetPlayerForPeer(long peer, out long playerId) { playerId = 0L; if (peer != 0L) { return GroupState.PeerPlayers.TryGetValue(peer, out playerId); } return false; } private static long FindPeer(long playerId) { foreach (KeyValuePair<long, long> peerPlayer in GroupState.PeerPlayers) { if (peerPlayer.Value == playerId) { return peerPlayer.Key; } } return 0L; } private static string GetPlayerName(long playerId) { SocialGroup socialGroup = GroupState.GetGroup(playerId); if (socialGroup != null && socialGroup.Members.TryGetValue(playerId, out var value)) { return value; } foreach (KeyValuePair<long, long> peerPlayer in GroupState.PeerPlayers) { if (peerPlayer.Value == playerId) { ZNetPeer peer = ZNet.instance.GetPeer(peerPlayer.Key); if (peer != null) { return peer.m_playerName; } } } return playerId.ToString(); } } internal static class GroupStorage { private const string PrefabName = "IronLabsSocialGroupStorage"; private const string DataKey = "IronLabs.Social.Groups"; private static ZDO storage; internal static bool TryLoad() { if (storage != null) { return true; } storage = FindOrCreate(); if (storage == null) { return false; } Load(storage.GetString("IronLabs.Social.Groups", "")); return true; } internal static void Save() { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Expected O, but got Unknown if (storage == null) { return; } ZPackage val = new ZPackage(); val.Write(GroupState.Groups.Count); foreach (SocialGroup value in GroupState.Groups.Values) { WriteGroup(val, value); } storage.Set("IronLabs.Social.Groups", Convert.ToBase64String(val.GetArray())); GameModeGroupIntegration.Broadcast(); } internal static void Reset() { storage = null; } private static ZDO FindOrCreate() { if (ZDOMan.instance == null || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return null; } List<ZDO> list = new List<ZDO>(); int num = 0; while (!ZDOMan.instance.GetAllZDOsWithPrefabIterative("IronLabsSocialGroupStorage", list, ref num)) { } if (list.Count <= 0) { return Create(); } return list[0]; } private static ZDO Create() { //IL_0026: Unknown result type (might be due to invalid IL or missing references) int stableHashCode = StringExtensionMethods.GetStableHashCode("IronLabsSocialGroupStorage"); Vector3 val = default(Vector3); ((Vector3)(ref val))..ctor(1000000f, -10000f, 1000000f); ZDO obj = ZDOMan.instance.CreateNewZDO(val, stableHashCode); obj.Persistent = true; obj.Distant = true; obj.SetPrefab(stableHashCode); SocializePlugin.Log.LogInfo("Created persistent social group storage for the world."); return obj; } private static void Load(string encoded) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown GroupState.ClearServer(); if (string.IsNullOrEmpty(encoded)) { return; } try { ReadGroups(new ZPackage(Convert.FromBase64String(encoded))); SocializePlugin.Log.LogInfo($"Loaded {GroupState.Groups.Count} social group(s)."); } catch (Exception ex) { SocializePlugin.Log.LogError("Could not load social groups: " + ex); } } private static void ReadGroups(ZPackage package) { int num = package.ReadInt(); for (int i = 0; i < num; i++) { SocialGroup socialGroup = ReadGroup(package); GroupState.Groups[socialGroup.Id] = socialGroup; } } private static SocialGroup ReadGroup(ZPackage package) { SocialGroup socialGroup = new SocialGroup { Id = package.ReadInt(), Leader = package.ReadLong() }; int num = package.ReadInt(); for (int i = 0; i < num; i++) { long key = package.ReadLong(); socialGroup.Members[key] = package.ReadString(); GroupState.PlayerGroups[key] = socialGroup.Id; } return socialGroup; } private static void WriteGroup(ZPackage package, SocialGroup group) { package.Write(group.Id); package.Write(group.Leader); package.Write(group.Members.Count); foreach (KeyValuePair<long, string> member in group.Members) { package.Write(member.Key); package.Write(member.Value); } } } internal static class RpcRegistry { internal static bool RegisterIfChanged(ref ZRoutedRpc registered, Action<ZRoutedRpc> register) { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance == null || instance == registered) { return false; } registered = instance; register(instance); return true; } } [HarmonyPatch(typeof(Minimap), "Update")] internal static class UpdateMapPingVisibilityPatch { private static void Postfix(Minimap __instance) { HidePublicPositionTogglePatch.UpdatePingVisibility(__instance); } } [HarmonyPatch(typeof(ZNet), "SetPublicReferencePosition")] internal static class DisablePublicPositionPatch { private static void Prefix(ref bool pub) { pub = false; } } [HarmonyPatch(typeof(ZNet), "GetOtherPublicPlayers")] internal static class ShowGroupMembersOnMapPatch { private static void Postfix(List<PlayerInfo> playerList) { GroupMapSharing.AddGroupMembers(playerList); } } [HarmonyPatch(typeof(Minimap), "Start")] internal static class HidePublicPositionTogglePatch { private static void Postfix(Minimap __instance) { Toggle publicPosition = __instance.m_publicPosition; if ((Object)(object)publicPosition != (Object)null) { publicPosition.isOn = false; ((Component)publicPosition).gameObject.SetActive(false); HideDedicatedContainer(__instance, publicPosition); } UpdatePingVisibility(__instance); } internal static void UpdatePingVisibility(Minimap minimap) { if (!((Object)(object)minimap.m_pingImageObject == (Object)null)) { GetPingButton(minimap).SetActive(GroupService.IsLocalPlayerInGroup()); } } private static GameObject GetPingButton(Minimap minimap) { Button componentInParent = ((Component)minimap.m_pingImageObject).GetComponentInParent<Button>(true); if ((Object)(object)componentInParent != (Object)null && !IsMapRoot(minimap, ((Component)componentInParent).gameObject)) { return ((Component)componentInParent).gameObject; } Transform parent = ((Component)minimap.m_pingImageObject).transform.parent; if ((Object)(object)parent != (Object)null && !IsMapRoot(minimap, ((Component)parent).gameObject) && ((Component)parent).GetComponentsInChildren<RawImage>(true).Length == 0) { return ((Component)parent).gameObject; } return ((Component)minimap.m_pingImageObject).gameObject; } private static void HideDedicatedContainer(Minimap minimap, Toggle toggle) { Transform parent = ((Component)toggle).transform.parent; if (!((Object)(object)parent == (Object)null) && !IsMapRoot(minimap, ((Component)parent).gameObject)) { bool num = ((Component)parent).GetComponentsInChildren<Toggle>(true).Length == 1; bool flag = ((Component)parent).GetComponentsInChildren<RawImage>(true).Length != 0; if (num && !flag) { ((Component)parent).gameObject.SetActive(false); } } } private static bool IsMapRoot(Minimap minimap, GameObject target) { if (!((Object)(object)target == (Object)(object)minimap.m_largeRoot) && !((Object)(object)target == (Object)(object)minimap.m_smallRoot) && !((Object)(object)target == (Object)(object)minimap.m_mapLarge)) { return (Object)(object)target == (Object)(object)minimap.m_mapSmall; } return true; } } [BepInPlugin("IronLabs.Socialize", "IronLabs.Socialize", "1.0.2")] public sealed class SocializePlugin : IronLabsPlugin { private const string PluginGuid = "IronLabs.Socialize"; private const string PluginName = "IronLabs.Socialize"; private const string PluginVersion = "1.0.2"; internal static ModLog Log { get; private set; } private void Awake() { Log = InitializePlugin("IronLabs.Socialize"); Log.LogInfo("IronLabs.Socialize 1.0.2 is loaded."); } private void Update() { GroupService.Update(); TargetPingService.Update(); } private void OnDestroy() { GroupService.Reset(); TargetPingService.Reset(); ShutdownPlugin(); Log = null; } } internal static class GameModeGroupIntegration { private const string WardenRequestRpc = "IronLabs_WardenGameMode_RequestGroups"; private const string WardenSyncRpc = "IronLabs_WardenGameMode_SyncGroups"; private const string VanillaRequestRpc = "IronLabs_VanillaGameMode_RequestGroups"; private const string VanillaSyncRpc = "IronLabs_VanillaGameMode_SyncGroups"; private static bool wardenRequested; private static bool vanillaRequested; internal static void Register(ZRoutedRpc rpc) { wardenRequested = false; vanillaRequested = false; rpc.Register("IronLabs_WardenGameMode_RequestGroups", (Action<long>)RPC_RequestWardenGroups); rpc.Register("IronLabs_VanillaGameMode_RequestGroups", (Action<long>)RPC_RequestVanillaGroups); } internal static void Broadcast() { if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && ZRoutedRpc.instance != null) { if (wardenRequested) { Send(ZRoutedRpc.Everybody, "IronLabs_WardenGameMode_SyncGroups"); } if (vanillaRequested) { Send(ZRoutedRpc.Everybody, "IronLabs_VanillaGameMode_SyncGroups"); } } } private static void RPC_RequestWardenGroups(long sender) { wardenRequested = true; SendRequestedSnapshot(sender, "IronLabs_WardenGameMode_SyncGroups"); } private static void RPC_RequestVanillaGroups(long sender) { vanillaRequested = true; SendRequestedSnapshot(sender, "IronLabs_VanillaGameMode_SyncGroups"); } private static void SendRequestedSnapshot(long sender, string syncRpc) { if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer()) { GroupStorage.TryLoad(); Send(sender, syncRpc); } } private static void Send(long target, string syncRpc) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(GroupState.PlayerGroups.Count); foreach (KeyValuePair<long, int> playerGroup in GroupState.PlayerGroups) { val.Write(playerGroup.Key); val.Write(playerGroup.Value); } ZRoutedRpc.instance.InvokeRoutedRPC(target, syncRpc, new object[1] { val }); } } internal static class TargetPingService { private const string RequestRpc = "IronLabs_Social_TargetPing"; private const string MessageRpc = "IronLabs_Social_TargetPingMessage"; private static ZRoutedRpc registeredRpc; internal static void Update() { RpcRegistry.RegisterIfChanged(ref registeredRpc, RegisterRpcs); } internal static void Reset() { registeredRpc = null; } internal static bool Send(string targetName, string message, Terminal context) { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: 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) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) if (!TryFindTarget(targetName, out var target) || (Object)(object)Player.m_localPlayer == (Object)null || ZRoutedRpc.instance == null) { if (context != null) { context.AddString("No connected player named \"" + targetName + "\" was found."); } return false; } EnsureRpcs(); Vector3 headPoint = ((Character)Player.m_localPlayer).GetHeadPoint(); UserInfo user = default(UserInfo); string message2 = default(string); Chat.GetChatMessageData(message, true, ref user, ref message2); long userID = ((ZDOID)(ref target.m_characterID)).UserID; ZDOID localPlayerCharacterID = ZNet.instance.LocalPlayerCharacterID; if (userID != ((ZDOID)(ref localPlayerCharacterID)).UserID) { SendRequest(headPoint, user, ((ZDOID)(ref target.m_characterID)).UserID, message2); } ShowLocalPing(headPoint, user, target.m_name, message2, context); return true; } internal static void RPC_TargetPing(long sender, Vector3 position, UserInfo user, long targetPlayerId, string message) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer()) { ZRoutedRpc.instance.InvokeRoutedRPC(targetPlayerId, "ChatMessage", new object[4] { position, 3, user, message }); ZRoutedRpc.instance.InvokeRoutedRPC(targetPlayerId, "IronLabs_Social_TargetPingMessage", new object[2] { user, message }); } } internal static void RPC_TargetPingMessage(long sender, UserInfo user, string message) { ChatFormatting.AddPing((Terminal)(object)Chat.instance, user.GetDisplayName(), "", message); } private static void EnsureRpcs() { RpcRegistry.RegisterIfChanged(ref registeredRpc, RegisterRpcs); } private static void RegisterRpcs(ZRoutedRpc rpc) { rpc.Register<Vector3, UserInfo, long, string>("IronLabs_Social_TargetPing", (Method<Vector3, UserInfo, long, string>)RPC_TargetPing); rpc.Register<UserInfo, string>("IronLabs_Social_TargetPingMessage", (Action<long, UserInfo, string>)RPC_TargetPingMessage); } private static void SendRequest(Vector3 position, UserInfo user, long target, string message) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) ZRoutedRpc.instance.InvokeRoutedRPC("IronLabs_Social_TargetPing", new object[4] { position, user, target, message }); } private static void ShowLocalPing(Vector3 position, UserInfo user, string targetName, string message, Terminal context) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) ZDOID localPlayerCharacterID = ZNet.instance.LocalPlayerCharacterID; long userID = ((ZDOID)(ref localPlayerCharacterID)).UserID; ZRoutedRpc.instance.InvokeRoutedRPC(userID, "ChatMessage", new object[4] { position, 3, user, message }); ChatFormatting.AddPing(context, user.GetDisplayName(), targetName, message); } private static bool TryFindTarget(string targetName, out PlayerInfo target) { //IL_0014: 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) //IL_001a: 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_002b: 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) foreach (PlayerInfo player in ZNet.instance.GetPlayerList()) { if (string.Equals(player.m_name, targetName, StringComparison.OrdinalIgnoreCase)) { target = player; return true; } } target = default(PlayerInfo); return false; } } } namespace IronLabs.SharedLib { public abstract class IronLabsPlugin : BaseUnityPlugin { private Harmony _harmony; private bool _patchesApplied; protected ModLog InitializePlugin(string pluginGuid) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown ModLog modLog = new ModLog(((BaseUnityPlugin)this).Logger); Version version = ((object)this).GetType().Assembly.GetName().Version; modLog.LogInfo($"AssemblyVersion: {version}."); _harmony = new Harmony(pluginGuid); PatchOwnNamespace(modLog); return modLog; } protected void PatchOwnNamespace(ModLog log) { if (_patchesApplied) { log.LogDebug("Harmony patches are already active; skipping registration."); return; } string text = ((object)this).GetType().Namespace; Type[] types = Assembly.GetExecutingAssembly().GetTypes(); foreach (Type type in types) { if (type.Namespace == text) { _harmony.CreateClassProcessor(type).Patch(); } } _patchesApplied = true; log.LogDebug("Harmony patches were applied for the plugin namespace."); } protected void ShutdownPlugin() { if (_patchesApplied) { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } _patchesApplied = false; } } } public sealed class ModLog { private readonly ManualLogSource _logger; public ModLog(ManualLogSource logger) { _logger = logger; } public void LogFatal(object message) { Write((LogLevel)1, message); } public void LogError(object message) { Write((LogLevel)2, message); } public void LogWarning(object message) { Write((LogLevel)4, message); } public void LogMessage(object message) { Write((LogLevel)8, message); } public void LogInfo(object message) { Write((LogLevel)16, message); } public void LogDebug(object message) { Write((LogLevel)32, message); } public void Log(LogLevel level, object message) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) Write(level, message); } private void Write(LogLevel level, object message) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) string arg = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"); _logger.Log(level, (object)$"[{arg}] {message}"); } } }