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.

Plugin.cs 9.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. using System;
  2. using System.Reflection;
  3. //using Microsoft.Win32;
  4. using IllusionPlugin;
  5. using GamecraftModdingAPI.App;
  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; } =
  14. #if DEBUG
  15. Assembly.GetExecutingAssembly().GetName().Version.ToString() + "alpha";
  16. #else
  17. Assembly.GetExecutingAssembly().GetName().Version.ToString();
  18. #endif
  19. private const long CLIENT_ID =
  20. #if DEBUG
  21. 692733325902872619;
  22. #else
  23. 696732441012076605;
  24. #endif
  25. private const LogLevel LOG_LEVEL =
  26. #if DEBUG
  27. LogLevel.Debug;
  28. #else
  29. LogLevel.Warn;
  30. #endif
  31. internal static Discord.Discord DiscordRPC;
  32. // called when Gamecraft shuts down
  33. public void OnApplicationQuit()
  34. {
  35. // Shutdown this mod
  36. if (DiscordRPC != null)
  37. {
  38. DiscordRPC.GetActivityManager().ClearActivity((result) => { GamecraftModdingAPI.Utility.Logging.LogDebug($"Cleared status: {result}"); DiscordRPC.Dispose(); });
  39. }
  40. GamecraftModdingAPI.Utility.Logging.LogDebug($"{Name} has shutdown");
  41. // Shutdown the Gamecraft modding API last
  42. GamecraftModdingAPI.Main.Shutdown();
  43. }
  44. // called when Gamecraft starts up
  45. public void OnApplicationStart()
  46. {
  47. // Initialize the Gamecraft modding API first
  48. GamecraftModdingAPI.Main.Init();
  49. // detect Wine (maybe?)
  50. bool isWineDetected = false;
  51. foreach (var key in Microsoft.Win32.Registry.CurrentUser.OpenSubKey("Software").GetSubKeyNames())
  52. {
  53. if (key == "Wine")
  54. {
  55. isWineDetected = true;
  56. break;
  57. }
  58. }
  59. if (isWineDetected)
  60. {
  61. // info for getting this to work through Wine/Proton
  62. 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--------------------------------");
  63. }
  64. // Initialize this mod
  65. DiscordRPC = new Discord.Discord(CLIENT_ID, (UInt64)Discord.CreateFlags.NoRequireDiscord);
  66. DiscordRPC.SetLogHook(LOG_LEVEL, (_, msg) => { GamecraftModdingAPI.Utility.Logging.MetaLog(msg); });
  67. //DiscordRPC.GetActivityManager().RegisterSteam(1078000);
  68. ActivityManager am = DiscordRPC.GetActivityManager();
  69. am.OnActivityJoinRequest += CallbackUtility.ActivityJoinRequest;
  70. am.OnActivityJoin += CallbackUtility.ActivityJoin;
  71. am.OnActivityInvite += CallbackUtility.ActivityInvite;
  72. LobbyManager lm = DiscordRPC.GetLobbyManager();
  73. lm.OnMemberConnect += CallbackUtility.DiscordUserJoin;
  74. SetDiscordActivity(state: $"{UnityEngine.Application.version} ({Version})", details: $"Initializing...", start: (int)(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds);
  75. Game.Edit += CallbackUtility.BuildEnter;
  76. Game.Enter += CallbackUtility.GameEnter;
  77. Game.Simulate += CallbackUtility.SimulationEnter;
  78. Client.EnterMenu += CallbackUtility.MenuEnter;
  79. GamecraftModdingAPI.Utility.GameEngineManager.AddGameEngine(new Engines.PlayerCountEngine());
  80. CommandBuilder.Builder()
  81. .Name("JoinDiscord")
  82. .Description("Join the Exmods server for help or more information")
  83. .Action(() =>
  84. {
  85. if (DiscordRPC != null)
  86. {
  87. DiscordRPC.GetOverlayManager().OpenGuildInvite("2CtWzZT", CallbackUtility.NobodyCares);
  88. }
  89. else
  90. {
  91. GamecraftModdingAPI.Utility.Logging.CommandLogError("Discord GameSDK functionality is unavailable. Please make sure Discord is open when launching Gamecraft.");
  92. }
  93. })
  94. .Build();
  95. CommandBuilder.Builder()
  96. .Name("InviteDiscordUser")
  97. .Description("Invite a Discord user (by id) to your game")
  98. .Action<long>((userId) =>
  99. {
  100. if (DiscordRPC != null)
  101. {
  102. Game game = Game.CurrentGame();
  103. DiscordRPC.GetActivityManager().SendInvite(userId, Discord.ActivityActionType.Join, $"Let's play Gamecraft together! (requires the GamecraftRPC mod)", CallbackUtility.NobodyCares);
  104. }
  105. else
  106. {
  107. GamecraftModdingAPI.Utility.Logging.CommandLogError("Discord GameSDK functionality is unavailable. Please make sure Discord is open when launching Gamecraft.");
  108. }
  109. })
  110. .Build();
  111. CommandBuilder.Builder()
  112. .Name(Name + "Info")
  113. .Description("Build information for the GamecraftRPC mod.")
  114. .Action(() =>
  115. {
  116. if (DiscordRPC != null)
  117. {
  118. Game game = Game.CurrentGame();
  119. Client client = new Client();
  120. GamecraftModdingAPI.Utility.Logging.CommandLog($"Gamecraft {client.Version}\nUnity {client.UnityVersion}\n{Name} {Version}\nSDK {DiscordRPC.ToString()}\nGame {game.Name}");
  121. }
  122. else
  123. {
  124. GamecraftModdingAPI.Utility.Logging.CommandLogError("Discord GameSDK functionality is unavailable. Please make sure Discord is open when launching Gamecraft.");
  125. }
  126. })
  127. .Build();
  128. CommandBuilder.Builder()
  129. .Name("DiscordVoice")
  130. .Description("Connect to Discord Voice Lobby")
  131. .Action(() =>
  132. {
  133. if (DiscordRPC != null)
  134. {
  135. GamecraftModdingAPI.Utility.Logging.CommandLogError("Discord GameSDK is unavailable. Please make sure Discord is open when launching Gamecraft.");
  136. return;
  137. }
  138. if (PresenceUtility.Lobby.HasValue)
  139. {
  140. //LobbyManager lm = DiscordRPC.GetLobbyManager();
  141. if (PresenceUtility.IsVoiceConnected)
  142. {
  143. lm.DisconnectLobby(PresenceUtility.Lobby.Value.Id, (result) => { GamecraftModdingAPI.Utility.Logging.CommandLog($"Disconnected voice (Result: {result})"); });
  144. PresenceUtility.IsVoiceConnected = false;
  145. }
  146. else
  147. {
  148. lm.ConnectVoice(PresenceUtility.Lobby.Value.Id, (result) => { GamecraftModdingAPI.Utility.Logging.CommandLog($"Connected voice (Result: {result})"); });
  149. PresenceUtility.IsVoiceConnected = true;
  150. }
  151. }
  152. })
  153. .Build();
  154. #if !RELEASE
  155. bool alreadyWarned = false;
  156. Client.EnterMenu += (_, args) =>
  157. {
  158. if (alreadyWarned) return;
  159. //GamecraftModdingAPI.Utility.Logging.LogDebug($"Displaying {Name} {Version} DEBUG warning");
  160. /*
  161. RobocraftX.Services.ErrorBuilder.DisplayCustomError(
  162. $"THIS IS NOT ACTUALLY AN ERROR! {Name} v{Version} is a pre-release. If you encounter a bug or other issue, please report it to NGnius or Exmods on Discord.",
  163. "Discord", () => { UnityEngine.Application.OpenURL("https://discord.exmods.org"); },
  164. "Ok!", () => { GamecraftModdingAPI.Utility.Logging.LogDebug($"Dismissed {Name} {Version} DEBUG warning"); }
  165. );
  166. */
  167. RobocraftX.Services.ErrorBuilder.DisplayWarning($"{Name} v{Version} is a pre-release. If you encounter a bug or other issue, please report it to NGnius or Exmods on Discord.");
  168. alreadyWarned = true;
  169. };
  170. #endif
  171. GamecraftModdingAPI.Utility.Logging.LogDebug($"{Name} has started up");
  172. }
  173. public void OnFixedUpdate() { } // called once per physics update
  174. public void OnLevelWasInitialized(int level) { } // called after a level is initialized
  175. public void OnLevelWasLoaded(int level) { } // called after a level is loaded
  176. public void OnUpdate() // called once per rendered frame (frame update)
  177. {
  178. if (DiscordRPC != null ) DiscordRPC.RunCallbacks();
  179. }
  180. public static void SetDiscordActivity(string state = null, string details = null, long start = 0, long 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 = "")
  181. {
  182. if (DiscordRPC == null) return;
  183. ref Activity activity = ref PresenceUtility.Activity;
  184. activity.Instance = instance;
  185. if (state != null) activity.State = state;
  186. if (details != null) activity.Details = details;
  187. if (start != 0) activity.Timestamps.Start = start;
  188. if (end != 0) activity.Timestamps.End = end;
  189. if (!string.IsNullOrEmpty(largeImg))
  190. {
  191. activity.Assets.LargeImage = largeImg;
  192. activity.Assets.LargeText = largeTxt;
  193. }
  194. if (!string.IsNullOrEmpty(smallImg))
  195. {
  196. activity.Assets.SmallImage = smallImg;
  197. activity.Assets.SmallText = smallTxt;
  198. }
  199. if (!string.IsNullOrEmpty(partyId))
  200. {
  201. activity.Party.Id = partyId;
  202. activity.Party.Size.CurrentSize = partyCurrentSize;
  203. activity.Party.Size.MaxSize = partyMaxSize;
  204. }
  205. if (!string.IsNullOrEmpty(matchSecret) || !string.IsNullOrEmpty(joinSecret) || !string.IsNullOrEmpty(spectateSecret))
  206. {
  207. activity.Secrets.Match = matchSecret;
  208. activity.Secrets.Join = joinSecret;
  209. activity.Secrets.Spectate = spectateSecret;
  210. }
  211. DiscordRPC.GetActivityManager().UpdateActivity(activity, result =>
  212. {
  213. GamecraftModdingAPI.Utility.Logging.MetaLog($"Update Activity Result: {result} {debug}");
  214. });
  215. }
  216. public static void SetDiscordActivity(Discord.Activity activity, string debug = "")
  217. {
  218. if (DiscordRPC == null) return;
  219. PresenceUtility.Activity = activity;
  220. DiscordRPC.GetActivityManager().UpdateActivity(PresenceUtility.Activity, result =>
  221. {
  222. GamecraftModdingAPI.Utility.Logging.MetaLog($"Update Activity Result: {result} {debug}");
  223. });
  224. }
  225. public static void SetDiscordActivity(string debug = "")
  226. {
  227. if (DiscordRPC == null) return;
  228. DiscordRPC.GetActivityManager().UpdateActivity(PresenceUtility.Activity, result =>
  229. {
  230. GamecraftModdingAPI.Utility.Logging.MetaLog($"Update Activity Result: {result} {debug}");
  231. });
  232. }
  233. }
  234. }