RemoteDebugCommand.cs 13 KB

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