Discord integration for Gamecraft
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

195 lines
8.7KB

  1. using System;
  2. using System.Reflection;
  3. //using Microsoft.Win32;
  4. using IllusionPlugin;
  5. using GamecraftModdingAPI.Events;
  6. using GamecraftModdingAPI.Commands;
  7. using Discord;
  8. namespace GamecraftRPC
  9. {
  10. public class Plugin : IPlugin // the Illusion Plugin Architecture (IPA) will ignore classes that don't implement IPlugin'
  11. {
  12. public string Name { get; } = Assembly.GetExecutingAssembly().GetName().Name;
  13. public string Version { get; } = Assembly.GetExecutingAssembly().GetName().Version.ToString();
  14. private static readonly long CLIENT_ID =
  15. #if DEBUG
  16. 692733325902872619;
  17. #else
  18. 696732441012076605;
  19. #endif
  20. private Discord.Discord discordRPC;
  21. // called when Gamecraft shuts down
  22. public void OnApplicationQuit()
  23. {
  24. // Shutdown this mod
  25. if (discordRPC != null)
  26. {
  27. discordRPC.GetActivityManager().ClearActivity((result) => { GamecraftModdingAPI.Utility.Logging.LogDebug($"Cleared status: {result}"); });
  28. discordRPC.Dispose();
  29. }
  30. GamecraftModdingAPI.Utility.Logging.LogDebug($"{Name} has shutdown");
  31. // Shutdown the Gamecraft modding API last
  32. GamecraftModdingAPI.Main.Shutdown();
  33. }
  34. // called when Gamecraft starts up
  35. public void OnApplicationStart()
  36. {
  37. // Initialize the Gamecraft modding API first
  38. GamecraftModdingAPI.Main.Init();
  39. // detect Wine (maybe?)
  40. bool isWineDetected = false;
  41. foreach (var key in Microsoft.Win32.Registry.CurrentUser.OpenSubKey("Software").GetSubKeyNames())
  42. {
  43. if (key == "Wine")
  44. {
  45. isWineDetected = true;
  46. break;
  47. }
  48. }
  49. if (isWineDetected)
  50. {
  51. // check for or install fake Discord
  52. GamecraftModdingAPI.Utility.Logging.MetaLog("\n--------------------------------\n\nIt looks like you may be using Wine/Proton, cool!\nPlease install https://github.com/0e4ef622/wine-discord-ipc-bridge to get this to work.\n\n--------------------------------");
  53. }
  54. // Initialize this mod
  55. discordRPC = new Discord.Discord(CLIENT_ID, (UInt64)Discord.CreateFlags.Default);
  56. discordRPC.SetLogHook(LogLevel.Debug, (_, msg) => { GamecraftModdingAPI.Utility.Logging.MetaLog(msg); });
  57. discordRPC.GetRelationshipManager().OnRefresh += () =>
  58. {
  59. discordRPC.GetRelationshipManager().Filter((ref Relationship r) => { return r.Presence.Status == Status.Online && r.Type == RelationshipType.Friend; });
  60. PresenceUtility.Users = new Relationship[discordRPC.GetRelationshipManager().Count()];
  61. for (uint i = 0; i < discordRPC.GetRelationshipManager().Count(); i++)
  62. {
  63. PresenceUtility.Users[i] = discordRPC.GetRelationshipManager().GetAt(i);
  64. }
  65. };
  66. SetDiscordActivity(discordRPC, state: "Loading...", details: "Initializing Gamecraft", start: (int)(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds);
  67. EventManager.AddEventHandler(new Events.GamePresenceHandler(discordRPC));
  68. EventManager.AddEventHandler(new Events.MenuPresenceHandler(discordRPC));
  69. EventManager.AddEventHandler(new Events.EditPresenceHandler(discordRPC));
  70. EventManager.AddEventHandler(new Events.SimulatePresenceHandler(discordRPC));
  71. SimpleCustomCommandEngine<string> rpcCommand = new SimpleCustomCommandEngine<string>(
  72. (s) => { SetDiscordActivity(discordRPC, state: s); }, // TODO: command action
  73. "SetRichPresence", // command name (used to invoke it in the console)
  74. "Set Discord status (experimental)" // command description (displayed when help command is executed)
  75. ); // this command can also be executed using the Command Computer
  76. SimpleCustomCommandEngine inviteNGniusCommand = new SimpleCustomCommandEngine(() =>
  77. discordRPC.GetActivityManager().SendInvite(106537989684887552, ActivityActionType.Join, "Join the Borg", (res) => { GamecraftModdingAPI.Utility.Logging.LogDebug($"Send invite {res}"); }),
  78. "InviteNGnius",
  79. "Send a Discord lobby invite to NGnius");
  80. SimpleCustomCommandEngine<long> inviteDiscordCommand = new SimpleCustomCommandEngine<long>((id) =>
  81. discordRPC.GetActivityManager().SendInvite(id, ActivityActionType.Join, "Join the Borg", (res) => { GamecraftModdingAPI.Utility.Logging.LogDebug($"Send invite {res}"); }),
  82. "InviteDiscord",
  83. "Send a game invite to a Discord user");
  84. SimpleCustomCommandEngine listDiscordUsersCommand = new SimpleCustomCommandEngine(
  85. () =>
  86. {
  87. string result = "Online Friends\n";
  88. for (int i = 0; i < PresenceUtility.Users.Length; i++)
  89. {
  90. result += $"{PresenceUtility.Users[i].User.Username} ({PresenceUtility.Users[i].User.Id})\n";
  91. }
  92. GamecraftModdingAPI.Utility.Logging.CommandLog(result);
  93. },
  94. "ListDiscordUsers",
  95. "List online Discord friends");
  96. // register the command so the modding API knows about it
  97. CommandManager.AddCommand(rpcCommand);
  98. CommandManager.AddCommand(inviteNGniusCommand);
  99. CommandManager.AddCommand(inviteDiscordCommand);
  100. CommandManager.AddCommand(listDiscordUsersCommand);
  101. GamecraftModdingAPI.Utility.GameEngineManager.AddGameEngine(new Engines.PlayerCountEngine(discordRPC));
  102. GamecraftModdingAPI.Utility.Logging.LogDebug($"{Name} has started up");
  103. }
  104. // unused methods
  105. public void OnFixedUpdate() { } // called once per physics update
  106. public void OnLevelWasInitialized(int level) { } // called after a level is initialized
  107. public void OnLevelWasLoaded(int level) { } // called after a level is loaded
  108. public void OnUpdate() // called once per rendered frame (frame update)
  109. {
  110. if (discordRPC != null ) discordRPC.RunCallbacks();
  111. }
  112. public static void SetDiscordActivity(Discord.Discord discordRPC, string state = null, string details = null, int start = 0, int end = 0, string largeImg = "gamecraft-logo-g", string largeTxt = "Gamecraft", string smallImg = "exmods-logo-xm2", string smallTxt = "Exmods", string partyId = null, int partyCurrentSize = 0, int partyMaxSize = 0, string matchSecret = null, string joinSecret = null, string spectateSecret = null, bool instance = true, string debug = "")
  113. {
  114. if (discordRPC == null) return;
  115. ref Activity activity = ref PresenceUtility.Activity;
  116. activity.Instance = instance;
  117. if (state != null) activity.State = state;
  118. if (details != null) activity.Details = details;
  119. if (start != 0) activity.Timestamps.Start = start;
  120. if (end != 0) activity.Timestamps.End = end;
  121. if (!string.IsNullOrEmpty(largeImg))
  122. {
  123. activity.Assets.LargeImage = largeImg;
  124. activity.Assets.LargeText = largeTxt;
  125. }
  126. if (!string.IsNullOrEmpty(smallImg))
  127. {
  128. activity.Assets.SmallImage = smallImg;
  129. activity.Assets.SmallText = smallTxt;
  130. }
  131. if (!string.IsNullOrEmpty(partyId))
  132. {
  133. activity.Party.Id = partyId;
  134. activity.Party.Size.CurrentSize = partyCurrentSize;
  135. activity.Party.Size.MaxSize = partyMaxSize;
  136. }
  137. if (!string.IsNullOrEmpty(matchSecret) || !string.IsNullOrEmpty(joinSecret) || !string.IsNullOrEmpty(spectateSecret))
  138. {
  139. activity.Secrets.Match = matchSecret;
  140. activity.Secrets.Join = joinSecret;
  141. activity.Secrets.Spectate = spectateSecret;
  142. }
  143. discordRPC.GetActivityManager().UpdateActivity(activity, result =>
  144. {
  145. GamecraftModdingAPI.Utility.Logging.MetaLog($"Update Activity Result: {result} {debug}");
  146. });
  147. }
  148. public static void SetDiscordActivity(Discord.Discord discordRPC, Discord.Activity activity, string debug = "")
  149. {
  150. PresenceUtility.Activity = activity;
  151. discordRPC.GetActivityManager().UpdateActivity(PresenceUtility.Activity, result =>
  152. {
  153. GamecraftModdingAPI.Utility.Logging.MetaLog($"Update Activity Result: {result} {debug}");
  154. });
  155. }
  156. public static void SetDiscordActivity(Discord.Discord discordRPC, string debug = "")
  157. {
  158. discordRPC.GetActivityManager().UpdateActivity(PresenceUtility.Activity, result =>
  159. {
  160. GamecraftModdingAPI.Utility.Logging.MetaLog($"Update Activity Result: {result} {debug}");
  161. });
  162. }
  163. }
  164. }