diff --git a/Editor/DiscordRPCPanel.cs b/Editor/DiscordRPCPanel.cs new file mode 100644 index 0000000..bb56ab1 --- /dev/null +++ b/Editor/DiscordRPCPanel.cs @@ -0,0 +1,194 @@ +using UnityEditor; +using UnityEditor.SceneManagement; +using UnityEngine; +using System; +using System.IO.Pipes; +using System.Text; +using System.Threading.Tasks; +using System.Diagnostics; + +public class DiscordRPCPanel : EditorWindow +{ + // === HIER DEINE DISCORD APPLICATION ID EINTRAGEN === + private const string clientId = "1490767097096048780"; + + private static bool isEnabled = false; + private static bool hideSceneName = false; + private static string customStatus = "Building a VRChat World/Avatar"; + + private static NamedPipeClientStream pipe; + private static long startTime; + + [MenuItem("Tools/MrUnknownDE/Discord RPC")] + public static void ShowWindow() + { + DiscordRPCPanel window = GetWindow("Discord RPC"); + window.minSize = new Vector2(300, 250); + } + + private void OnEnable() + { + // Lädt gespeicherte Einstellungen + isEnabled = EditorPrefs.GetBool("DiscordRPC_Enabled", false); + hideSceneName = EditorPrefs.GetBool("DiscordRPC_HideScene", false); + customStatus = EditorPrefs.GetString("DiscordRPC_Status", "Building a VRChat World"); + + if (isEnabled) ConnectToDiscord(); + + // Hooks in Unity einhängen + EditorApplication.playModeStateChanged -= OnPlayModeChanged; + EditorApplication.playModeStateChanged += OnPlayModeChanged; + EditorSceneManager.sceneOpened -= OnSceneOpened; + EditorSceneManager.sceneOpened += OnSceneOpened; + } + + private void OnDisable() + { + if (pipe != null && pipe.IsConnected) pipe.Close(); + } + + private void OnGUI() + { + GUILayout.Space(10); + GUILayout.Label("DISCORD RICH PRESENCE", EditorStyles.boldLabel); + GUILayout.Space(5); + + EditorGUI.BeginChangeCheck(); + + // Status Toggle + GUI.backgroundColor = isEnabled ? new Color(0.2f, 0.8f, 0.2f) : new Color(0.8f, 0.2f, 0.2f); + if (GUILayout.Button(isEnabled ? "Status: ONLINE" : "Status: OFFLINE", GUILayout.Height(30))) + { + isEnabled = !isEnabled; + EditorPrefs.SetBool("DiscordRPC_Enabled", isEnabled); + + if (isEnabled) ConnectToDiscord(); + else DisconnectDiscord(); + } + GUI.backgroundColor = Color.white; + + GUILayout.Space(15); + GUILayout.Label("SETTINGS", EditorStyles.boldLabel); + + // Custom Status Input + GUILayout.Label("Custom Status (Line 1):"); + customStatus = EditorGUILayout.TextField(customStatus); + + // Privacy Toggle + hideSceneName = EditorGUILayout.Toggle("Hide Scene Name (Privacy)", hideSceneName); + + if (EditorGUI.EndChangeCheck()) + { + EditorPrefs.SetBool("DiscordRPC_HideScene", hideSceneName); + EditorPrefs.SetString("DiscordRPC_Status", customStatus); + if (isEnabled) UpdatePresence(); + } + + GUILayout.Space(15); + EditorGUILayout.HelpBox("Wenn aktiv, sieht dein Discord-Server in deinem Profil, an welcher Szene du gerade baust.", MessageType.Info); + } + + // --- UNITY EVENT HOOKS --- + private void OnPlayModeChanged(PlayModeStateChange state) + { + if (isEnabled) UpdatePresence(); + } + + private void OnSceneOpened(UnityEngine.SceneManagement.Scene scene, OpenSceneMode mode) + { + if (isEnabled) UpdatePresence(); + } + + // --- DISCORD IPC LOGIK --- + private async void ConnectToDiscord() + { + if (pipe != null && pipe.IsConnected) return; + + try + { + pipe = new NamedPipeClientStream(".", "discord-ipc-0", PipeDirection.InOut, PipeOptions.Asynchronous); + await pipe.ConnectAsync(2000); + + // Handshake (Opcode 0) + string handshake = "{\"v\": 1, \"client_id\": \"" + clientId + "\"}"; + SendFrame(0, handshake); + + startTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); + UpdatePresence(); + + UnityEngine.Debug.Log("Discord RPC Connected!"); + } + catch (Exception) + { + UnityEngine.Debug.LogWarning("Discord RPC: Konnte keine Verbindung zu Discord herstellen. Läuft Discord?"); + isEnabled = false; + } + } + + private void DisconnectDiscord() + { + if (pipe != null && pipe.IsConnected) + { + pipe.Close(); + pipe.Dispose(); + } + pipe = null; + UnityEngine.Debug.Log("Discord RPC Disconnected."); + } + + private void UpdatePresence() + { + if (pipe == null || !pipe.IsConnected) return; + + string sceneName = hideSceneName ? "Secret Map" : EditorSceneManager.GetActiveScene().name; + if (string.IsNullOrEmpty(sceneName)) sceneName = "Unsaved Scene"; + + string stateText = EditorApplication.isPlaying ? "Testet im Playmode" : $"Editiert: {sceneName}.unity"; + + // Flieht die Strings für sicheres JSON + string safeStatus = customStatus.Replace("\"", "\\\""); + string safeState = stateText.Replace("\"", "\\\""); + + // Das Activity Payload (Opcode 1) + string json = $@"{{ + ""cmd"": ""SET_ACTIVITY"", + ""args"": {{ + ""pid"": {Process.GetCurrentProcess().Id}, + ""activity"": {{ + ""details"": ""{safeStatus}"", + ""state"": ""{safeState}"", + ""timestamps"": {{ + ""start"": {startTime} + }}, + ""instance"": false + }} + }}, + ""nonce"": ""1"" + }}"; + + SendFrame(1, json); + } + + private void SendFrame(int opcode, string payload) + { + if (pipe == null || !pipe.IsConnected) return; + + byte[] payloadBytes = Encoding.UTF8.GetBytes(payload); + int length = payloadBytes.Length; + byte[] buffer = new byte[8 + length]; + + BitConverter.GetBytes(opcode).CopyTo(buffer, 0); + BitConverter.GetBytes(length).CopyTo(buffer, 4); + payloadBytes.CopyTo(buffer, 8); + + try + { + pipe.Write(buffer, 0, buffer.Length); + } + catch (Exception e) + { + UnityEngine.Debug.LogError("Discord RPC Error: " + e.Message); + DisconnectDiscord(); + } + } +} \ No newline at end of file