RemoteDebugCommand.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. //-----------------------------------------------------------------------------
  2. // RemoteDebugCommands.cs
  3. //
  4. // Microsoft XNA Community Game Platform
  5. // Copyright (C) Microsoft Corporation. All rights reserved.
  6. //-----------------------------------------------------------------------------
  7. // Remote debugging doesn't work on Windows Phone because it relies on
  8. // Microsoft.Xna.Framework.Net. We therefore can't compile this class
  9. // for the Windows Phone platform.
  10. #if !WINDOWS_PHONE
  11. using System;
  12. using System.Collections.Generic;
  13. using Microsoft.Xna.Framework.Net;
  14. using Microsoft.Xna.Framework;
  15. using Microsoft.Xna.Framework.GamerServices;
  16. using System.Text.RegularExpressions;
  17. namespace PerformanceMeasuring.GameDebugTools
  18. {
  19. /// <summary>
  20. /// Remote debug component.
  21. /// </summary>
  22. /// <remarks>
  23. /// This is the game component that supports remote debug command.
  24. /// When you use "remote" command on Windows side, it connect to Xbox 360 game via
  25. /// SystemLink.
  26. ///
  27. /// After you connected to Xbox 360, you can type debug command on Windows side that
  28. /// actually executed on Xbox 360 game. So, you can run debug command without
  29. /// connect a keyboard to the Xbox 360 console.
  30. ///
  31. /// To quit remote debug command more, simply type 'quit' command.
  32. /// </remarks>
  33. public class RemoteDebugCommand : GameComponent,
  34. IDebugCommandExecutioner, IDebugEchoListner
  35. {
  36. /// <summary>
  37. /// Sets/Get NetworkSession for Remote Debug Command.
  38. /// </summary>
  39. public NetworkSession NetworkSession { get; set; }
  40. /// <summary>
  41. /// Represents NetworkSession created by this component or not.
  42. /// </summary>
  43. public bool OwnsNetworkSession { get; private set; }
  44. const string StartPacketHeader = "RmtStart";
  45. const string ExecutePacketHeader = "RmtCmd";
  46. const string EchoPacketHeader = "RmtEcho";
  47. const string ErrorPacketHeader = "RmtErr";
  48. const string WarningPacketHeader = "RmtWrn";
  49. const string QuitPacketHeader = "RmtQuit";
  50. IDebugCommandHost commandHost;
  51. bool IsHost = false;
  52. Regex packetRe = new Regex(@"\$(?<header>[^$]+)\$:(?<text>.+)");
  53. PacketReader packetReader = new PacketReader();
  54. PacketWriter packetWriter = new PacketWriter();
  55. IAsyncResult asyncResult;
  56. enum ConnectionPahse
  57. {
  58. None,
  59. EnsureSignedIn,
  60. FindSessions,
  61. Joining,
  62. }
  63. ConnectionPahse phase = ConnectionPahse.None;
  64. public RemoteDebugCommand(Game game)
  65. : base(game)
  66. {
  67. commandHost =
  68. game.Services.GetService(typeof(IDebugCommandHost)) as IDebugCommandHost;
  69. if (!IsHost)
  70. {
  71. commandHost.RegisterCommand("remote", "Start remote command",
  72. ExecuteRemoteCommand);
  73. }
  74. }
  75. public override void Initialize()
  76. {
  77. if (IsHost)
  78. {
  79. commandHost.RegisterEchoListner(this);
  80. // Create network session if NetworkSession is not set.
  81. if (NetworkSession == null)
  82. {
  83. GamerServicesDispatcher.WindowHandle = Game.Window.Handle;
  84. GamerServicesDispatcher.Initialize(Game.Services);
  85. NetworkSession =
  86. NetworkSession.Create(NetworkSessionType.SystemLink, 1, 2);
  87. OwnsNetworkSession = true;
  88. }
  89. }
  90. base.Initialize();
  91. }
  92. /// <summary>
  93. /// Process received packet string.
  94. /// </summary>
  95. /// <remarks>You can call this method if you own network session on the game side.
  96. /// </remarks>
  97. /// <param name="packetString"></param>
  98. /// <returns>Processed this packet?</returns>
  99. public bool ProcessRecievedPacket(string packetString)
  100. {
  101. bool processed = false;
  102. Match mc = packetRe.Match(packetString);
  103. if (mc.Success)
  104. {
  105. string packetHeader = mc.Groups["header"].Value;
  106. string text = mc.Groups["text"].Value;
  107. switch (packetHeader)
  108. {
  109. case ExecutePacketHeader:
  110. commandHost.ExecuteCommand(text);
  111. processed = true;
  112. break;
  113. case EchoPacketHeader:
  114. commandHost.Echo(text);
  115. processed = true;
  116. break;
  117. case ErrorPacketHeader:
  118. commandHost.EchoError(text);
  119. processed = true;
  120. break;
  121. case WarningPacketHeader:
  122. commandHost.EchoWarning(text);
  123. processed = true;
  124. break;
  125. case StartPacketHeader:
  126. ConnectedToRemote();
  127. commandHost.Echo(text);
  128. processed = true;
  129. break;
  130. case QuitPacketHeader:
  131. commandHost.Echo(text);
  132. DisconnectedFromRemote();
  133. processed = true;
  134. break;
  135. }
  136. }
  137. return processed;
  138. }
  139. /// <summary>
  140. /// Update
  141. /// </summary>
  142. public override void Update(GameTime gameTime)
  143. {
  144. // Process different phases.
  145. switch (phase)
  146. {
  147. case ConnectionPahse.EnsureSignedIn:
  148. GamerServicesDispatcher.Update();
  149. break;
  150. case ConnectionPahse.FindSessions:
  151. GamerServicesDispatcher.Update();
  152. if (asyncResult.IsCompleted)
  153. {
  154. AvailableNetworkSessionCollection sessions =
  155. NetworkSession.EndFind(asyncResult);
  156. if (sessions.Count > 0)
  157. {
  158. asyncResult = NetworkSession.BeginJoin( sessions[0],
  159. null, null );
  160. commandHost.EchoError("Connecting to the host...");
  161. phase = ConnectionPahse.Joining;
  162. }
  163. else
  164. {
  165. commandHost.EchoError("Couldn't find a session.");
  166. phase = ConnectionPahse.None;
  167. }
  168. }
  169. break;
  170. case ConnectionPahse.Joining:
  171. GamerServicesDispatcher.Update();
  172. if (asyncResult.IsCompleted)
  173. {
  174. NetworkSession = NetworkSession.EndJoin(asyncResult);
  175. NetworkSession.SessionEnded +=
  176. new EventHandler<NetworkSessionEndedEventArgs>(
  177. NetworkSession_SessionEnded);
  178. OwnsNetworkSession = true;
  179. commandHost.EchoError("Connected to the host.");
  180. phase = ConnectionPahse.None;
  181. asyncResult = null;
  182. ConnectedToRemote();
  183. }
  184. break;
  185. }
  186. // Update Network session.
  187. if (OwnsNetworkSession)
  188. {
  189. GamerServicesDispatcher.Update();
  190. NetworkSession.Update();
  191. if (NetworkSession != null)
  192. {
  193. // Process received packets.
  194. foreach (LocalNetworkGamer gamer in NetworkSession.LocalGamers)
  195. {
  196. while (gamer.IsDataAvailable)
  197. {
  198. NetworkGamer sender;
  199. gamer.ReceiveData(packetReader, out sender);
  200. if (!sender.IsLocal)
  201. ProcessRecievedPacket(packetReader.ReadString());
  202. }
  203. }
  204. }
  205. }
  206. base.Update(gameTime);
  207. }
  208. /// <summary>
  209. /// Send remote debug command packet.
  210. /// </summary>
  211. void SendPacket(string header, string text)
  212. {
  213. if (NetworkSession != null)
  214. {
  215. packetWriter.Write("$" + header + "$:" + text);
  216. NetworkSession.LocalGamers[0].SendData(packetWriter,
  217. SendDataOptions.ReliableInOrder);
  218. }
  219. }
  220. /// <summary>
  221. /// Start remote debug command.
  222. /// </summary>
  223. void ConnectedToRemote()
  224. {
  225. DebugCommandUI commandUI = commandHost as DebugCommandUI;
  226. if (IsHost)
  227. {
  228. if ( commandUI != null )
  229. commandUI.Prompt = "[Host]>";
  230. }
  231. else
  232. {
  233. if (commandUI != null)
  234. commandUI.Prompt = "[Client]>";
  235. commandHost.PushExecutioner(this);
  236. SendPacket(StartPacketHeader, "Remote Debug Command Started!!");
  237. }
  238. commandHost.RegisterCommand("quit", "Quit from remote command",
  239. ExecuteQuitCommand);
  240. }
  241. /// <summary>
  242. /// End remote debug command.
  243. /// </summary>
  244. void DisconnectedFromRemote()
  245. {
  246. DebugCommandUI commandUI = commandHost as DebugCommandUI;
  247. if (commandUI != null)
  248. commandUI.Prompt = DebugCommandUI.DefaultPrompt;
  249. commandHost.UnregisterCommand("quit");
  250. if (!IsHost)
  251. {
  252. commandHost.PopExecutioner();
  253. if (OwnsNetworkSession)
  254. {
  255. NetworkSession.Dispose();
  256. NetworkSession = null;
  257. OwnsNetworkSession = false;
  258. }
  259. }
  260. }
  261. private void ExecuteRemoteCommand(IDebugCommandHost host, string command,
  262. IList<string> arguments)
  263. {
  264. if (NetworkSession == null)
  265. {
  266. try
  267. {
  268. GamerServicesDispatcher.WindowHandle = Game.Window.Handle;
  269. GamerServicesDispatcher.Initialize(Game.Services);
  270. }
  271. catch { }
  272. if (SignedInGamer.SignedInGamers.Count > 0)
  273. {
  274. commandHost.Echo("Finding available sessions...");
  275. asyncResult = NetworkSession.BeginFind(
  276. NetworkSessionType.SystemLink, 1, null, null, null);
  277. phase = ConnectionPahse.FindSessions;
  278. }
  279. else
  280. {
  281. host.Echo("Please signed in.");
  282. phase = ConnectionPahse.EnsureSignedIn;
  283. }
  284. }
  285. else
  286. {
  287. ConnectedToRemote();
  288. }
  289. }
  290. private void ExecuteQuitCommand(IDebugCommandHost host, string command,
  291. IList<string> arguments)
  292. {
  293. SendPacket(QuitPacketHeader, "End Remote Debug Command.");
  294. DisconnectedFromRemote();
  295. }
  296. public void ExecuteCommand(string command)
  297. {
  298. SendPacket(ExecutePacketHeader, command);
  299. }
  300. public void Echo(DebugCommandMessage messageType, string text)
  301. {
  302. switch (messageType)
  303. {
  304. case DebugCommandMessage.Standard:
  305. SendPacket(EchoPacketHeader, text );
  306. break;
  307. case DebugCommandMessage.Warning:
  308. SendPacket(WarningPacketHeader, text);
  309. break;
  310. case DebugCommandMessage.Error:
  311. SendPacket(ErrorPacketHeader, text);
  312. break;
  313. }
  314. }
  315. /// <summary>
  316. /// Handle the case host machine is gone.
  317. /// </summary>
  318. void NetworkSession_SessionEnded(object sender, NetworkSessionEndedEventArgs e)
  319. {
  320. DisconnectedFromRemote();
  321. commandHost.EchoWarning("Disconnected from the Host.");
  322. }
  323. }
  324. }
  325. #endif