Program.cs 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Reflection;
  5. using System.Text;
  6. using System.Threading.Tasks;
  7. using GodotTools.IdeMessaging.Requests;
  8. using Newtonsoft.Json;
  9. namespace GodotTools.IdeMessaging.CLI
  10. {
  11. internal static class Program
  12. {
  13. private static readonly ILogger Logger = new CustomLogger();
  14. public static int Main(string[] args)
  15. {
  16. try
  17. {
  18. var mainTask = StartAsync(args, Console.OpenStandardInput(), Console.OpenStandardOutput());
  19. mainTask.Wait();
  20. return mainTask.Result;
  21. }
  22. catch (Exception ex)
  23. {
  24. Logger.LogError("Unhandled exception: ", ex);
  25. return 1;
  26. }
  27. }
  28. private static async Task<int> StartAsync(string[] args, Stream inputStream, Stream outputStream)
  29. {
  30. var inputReader = new StreamReader(inputStream, Encoding.UTF8);
  31. var outputWriter = new StreamWriter(outputStream, Encoding.UTF8);
  32. try
  33. {
  34. if (args.Length == 0)
  35. {
  36. Logger.LogError("Expected at least 1 argument");
  37. return 1;
  38. }
  39. string godotProjectDir = args[0];
  40. if (!Directory.Exists(godotProjectDir))
  41. {
  42. Logger.LogError($"The specified Godot project directory does not exist: {godotProjectDir}");
  43. return 1;
  44. }
  45. var forwarder = new ForwarderMessageHandler(outputWriter);
  46. using (var fwdClient = new Client("VisualStudioCode", godotProjectDir, forwarder, Logger))
  47. {
  48. fwdClient.Start();
  49. // ReSharper disable AccessToDisposedClosure
  50. fwdClient.Connected += async () => await forwarder.WriteLineToOutput("Event=Connected");
  51. fwdClient.Disconnected += async () => await forwarder.WriteLineToOutput("Event=Disconnected");
  52. // ReSharper restore AccessToDisposedClosure
  53. // TODO: Await connected with timeout
  54. while (!fwdClient.IsDisposed)
  55. {
  56. string? firstLine = await inputReader.ReadLineAsync();
  57. if (firstLine == null || firstLine == "QUIT")
  58. goto ExitMainLoop;
  59. string messageId = firstLine;
  60. string? messageArgcLine = await inputReader.ReadLineAsync();
  61. if (messageArgcLine == null)
  62. {
  63. Logger.LogInfo("EOF when expecting argument count");
  64. goto ExitMainLoop;
  65. }
  66. if (!int.TryParse(messageArgcLine, out int messageArgc))
  67. {
  68. Logger.LogError("Received invalid line for argument count: " + firstLine);
  69. continue;
  70. }
  71. var body = new StringBuilder();
  72. for (int i = 0; i < messageArgc; i++)
  73. {
  74. string? bodyLine = await inputReader.ReadLineAsync();
  75. if (bodyLine == null)
  76. {
  77. Logger.LogInfo($"EOF when expecting body line #{i + 1}");
  78. goto ExitMainLoop;
  79. }
  80. body.AppendLine(bodyLine);
  81. }
  82. var response = await SendRequest(fwdClient, messageId, new MessageContent(MessageStatus.Ok, body.ToString()));
  83. if (response == null)
  84. {
  85. Logger.LogError($"Failed to write message to the server: {messageId}");
  86. }
  87. else
  88. {
  89. var content = new MessageContent(response.Status, JsonConvert.SerializeObject(response));
  90. await forwarder.WriteResponseToOutput(messageId, content);
  91. }
  92. }
  93. ExitMainLoop:
  94. await forwarder.WriteLineToOutput("Event=Quit");
  95. }
  96. return 0;
  97. }
  98. catch (Exception e)
  99. {
  100. Logger.LogError("Unhandled exception", e);
  101. return 1;
  102. }
  103. }
  104. private static async Task<Response?> SendRequest(Client client, string id, MessageContent content)
  105. {
  106. var handlers = new Dictionary<string, Func<Task<Response?>>>
  107. {
  108. [PlayRequest.Id] = async () =>
  109. {
  110. var request = JsonConvert.DeserializeObject<PlayRequest>(content.Body);
  111. return await client.SendRequest<PlayResponse>(request!);
  112. },
  113. [DebugPlayRequest.Id] = async () =>
  114. {
  115. var request = JsonConvert.DeserializeObject<DebugPlayRequest>(content.Body);
  116. return await client.SendRequest<DebugPlayResponse>(request!);
  117. },
  118. [ReloadScriptsRequest.Id] = async () =>
  119. {
  120. var request = JsonConvert.DeserializeObject<ReloadScriptsRequest>(content.Body);
  121. return await client.SendRequest<ReloadScriptsResponse>(request!);
  122. },
  123. [CodeCompletionRequest.Id] = async () =>
  124. {
  125. var request = JsonConvert.DeserializeObject<CodeCompletionRequest>(content.Body);
  126. return await client.SendRequest<CodeCompletionResponse>(request!);
  127. }
  128. };
  129. if (handlers.TryGetValue(id, out var handler))
  130. return await handler();
  131. Console.WriteLine("INVALID REQUEST");
  132. return null;
  133. }
  134. private class CustomLogger : ILogger
  135. {
  136. private static string ThisAppPath => Assembly.GetExecutingAssembly().Location;
  137. private static string ThisAppPathWithoutExtension => Path.ChangeExtension(ThisAppPath, null);
  138. private static readonly string LogPath = $"{ThisAppPathWithoutExtension}.log";
  139. private static StreamWriter NewWriter() => new StreamWriter(LogPath, append: true, encoding: Encoding.UTF8);
  140. private static void Log(StreamWriter writer, string message)
  141. {
  142. writer.WriteLine($"{DateTime.Now:HH:mm:ss.ffffff}: {message}");
  143. }
  144. public void LogDebug(string message)
  145. {
  146. using (var writer = NewWriter())
  147. {
  148. Log(writer, "DEBUG: " + message);
  149. }
  150. }
  151. public void LogInfo(string message)
  152. {
  153. using (var writer = NewWriter())
  154. {
  155. Log(writer, "INFO: " + message);
  156. }
  157. }
  158. public void LogWarning(string message)
  159. {
  160. using (var writer = NewWriter())
  161. {
  162. Log(writer, "WARN: " + message);
  163. }
  164. }
  165. public void LogError(string message)
  166. {
  167. using (var writer = NewWriter())
  168. {
  169. Log(writer, "ERROR: " + message);
  170. }
  171. }
  172. public void LogError(string message, Exception e)
  173. {
  174. using (var writer = NewWriter())
  175. {
  176. Log(writer, "EXCEPTION: " + message + '\n' + e);
  177. }
  178. }
  179. }
  180. }
  181. }