RemoteDebugCommand.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  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 HoneycombRush.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. #if WINDOWS
  61. bool IsHost = false;
  62. #else
  63. bool IsHost = true;
  64. #endif
  65. Regex packetRe = new Regex(@"\$(?<header>[^$]+)\$:(?<text>.+)");
  66. PacketReader packetReader = new PacketReader();
  67. PacketWriter packetWriter = new PacketWriter();
  68. IAsyncResult asyncResult;
  69. enum ConnectionPahse
  70. {
  71. None,
  72. EnsureSignedIn,
  73. FindSessions,
  74. Joining,
  75. }
  76. ConnectionPahse phase = ConnectionPahse.None;
  77. #endregion
  78. #region Initialization
  79. public RemoteDebugCommand(Game game)
  80. : base(game)
  81. {
  82. commandHost =
  83. game.Services.GetService(typeof(IDebugCommandHost)) as IDebugCommandHost;
  84. if (!IsHost)
  85. {
  86. commandHost.RegisterCommand("remote", "Start remote command",
  87. ExecuteRemoteCommand);
  88. }
  89. }
  90. public override void Initialize()
  91. {
  92. if (IsHost)
  93. {
  94. commandHost.RegisterEchoListner(this);
  95. // Create network session if NetworkSession is not set.
  96. if (NetworkSession == null)
  97. {
  98. // GamerServicesDispatcher.WindowHandle = Game.Window.Handle;
  99. // GamerServicesDispatcher.Initialize(Game.Services);
  100. NetworkSession =
  101. NetworkSession.Create(NetworkSessionType.SystemLink, 1, 2);
  102. OwnsNetworkSession = true;
  103. }
  104. }
  105. base.Initialize();
  106. }
  107. #endregion
  108. /// <summary>
  109. /// Process received packet string.
  110. /// </summary>
  111. /// <remarks>You can call this method if you own network session on the game side.
  112. /// </remarks>
  113. /// <param name="packetString"></param>
  114. /// <returns>Processed this packet?</returns>
  115. public bool ProcessRecievedPacket(string packetString)
  116. {
  117. bool processed = false;
  118. Match mc = packetRe.Match(packetString);
  119. if (mc.Success)
  120. {
  121. string packetHeader = mc.Groups["header"].Value;
  122. string text = mc.Groups["text"].Value;
  123. switch (packetHeader)
  124. {
  125. case ExecutePacketHeader:
  126. commandHost.ExecuteCommand(text);
  127. processed = true;
  128. break;
  129. case EchoPacketHeader:
  130. commandHost.Echo(text);
  131. processed = true;
  132. break;
  133. case ErrorPacketHeader:
  134. commandHost.EchoError(text);
  135. processed = true;
  136. break;
  137. case WarningPacketHeader:
  138. commandHost.EchoWarning(text);
  139. processed = true;
  140. break;
  141. case StartPacketHeader:
  142. ConnectedToRemote();
  143. commandHost.Echo(text);
  144. processed = true;
  145. break;
  146. case QuitPacketHeader:
  147. commandHost.Echo(text);
  148. DisconnectedFromRemote();
  149. processed = true;
  150. break;
  151. }
  152. }
  153. return processed;
  154. }
  155. #region Implementations
  156. /// <summary>
  157. /// Update
  158. /// </summary>
  159. public override void Update(GameTime gameTime)
  160. {
  161. // Process different phases.
  162. switch (phase)
  163. {
  164. case ConnectionPahse.EnsureSignedIn:
  165. // GamerServicesDispatcher.Update();
  166. break;
  167. case ConnectionPahse.FindSessions:
  168. // GamerServicesDispatcher.Update();
  169. if (asyncResult.IsCompleted)
  170. {
  171. AvailableNetworkSessionCollection sessions =
  172. NetworkSession.EndFind(asyncResult);
  173. if (sessions.Count > 0)
  174. {
  175. asyncResult = NetworkSession.BeginJoin( sessions[0],
  176. null, null );
  177. commandHost.EchoError("Connecting to the host...");
  178. phase = ConnectionPahse.Joining;
  179. }
  180. else
  181. {
  182. commandHost.EchoError("Couldn't find a session.");
  183. phase = ConnectionPahse.None;
  184. }
  185. }
  186. break;
  187. case ConnectionPahse.Joining:
  188. // GamerServicesDispatcher.Update();
  189. if (asyncResult.IsCompleted)
  190. {
  191. NetworkSession = NetworkSession.EndJoin(asyncResult);
  192. NetworkSession.SessionEnded +=
  193. new EventHandler<NetworkSessionEndedEventArgs>(
  194. NetworkSession_SessionEnded);
  195. OwnsNetworkSession = true;
  196. commandHost.EchoError("Connected to the host.");
  197. phase = ConnectionPahse.None;
  198. asyncResult = null;
  199. ConnectedToRemote();
  200. }
  201. break;
  202. }
  203. // Update Network session.
  204. if (OwnsNetworkSession)
  205. {
  206. // GamerServicesDispatcher.Update();
  207. NetworkSession.Update();
  208. if (NetworkSession != null)
  209. {
  210. // Process received packets.
  211. foreach (LocalNetworkGamer gamer in NetworkSession.LocalGamers)
  212. {
  213. while (gamer.IsDataAvailable)
  214. {
  215. NetworkGamer sender;
  216. gamer.ReceiveData(packetReader, out sender);
  217. if (!sender.IsLocal)
  218. ProcessRecievedPacket(packetReader.ReadString());
  219. }
  220. }
  221. }
  222. }
  223. base.Update(gameTime);
  224. }
  225. /// <summary>
  226. /// Send remote debug command packet.
  227. /// </summary>
  228. void SendPacket(string header, string text)
  229. {
  230. if (NetworkSession != null)
  231. {
  232. packetWriter.Write("$" + header + "$:" + text);
  233. NetworkSession.LocalGamers[0].SendData(packetWriter,
  234. SendDataOptions.ReliableInOrder);
  235. }
  236. }
  237. /// <summary>
  238. /// Start remote debug command.
  239. /// </summary>
  240. void ConnectedToRemote()
  241. {
  242. DebugCommandUI commandUI = commandHost as DebugCommandUI;
  243. if (IsHost)
  244. {
  245. if ( commandUI != null )
  246. commandUI.Prompt = "[Host]>";
  247. }
  248. else
  249. {
  250. if (commandUI != null)
  251. commandUI.Prompt = "[Client]>";
  252. commandHost.PushExecutioner(this);
  253. SendPacket(StartPacketHeader, "Remote Debug Command Started!!");
  254. }
  255. commandHost.RegisterCommand("quit", "Quit from remote command",
  256. ExecuteQuitCommand);
  257. }
  258. /// <summary>
  259. /// End remote debug command.
  260. /// </summary>
  261. void DisconnectedFromRemote()
  262. {
  263. DebugCommandUI commandUI = commandHost as DebugCommandUI;
  264. if (commandUI != null)
  265. commandUI.Prompt = DebugCommandUI.DefaultPrompt;
  266. commandHost.UnregisterCommand("quit");
  267. if (!IsHost)
  268. {
  269. commandHost.PopExecutioner();
  270. if (OwnsNetworkSession)
  271. {
  272. NetworkSession.Dispose();
  273. NetworkSession = null;
  274. OwnsNetworkSession = false;
  275. }
  276. }
  277. }
  278. #region DebugCommand implementations
  279. private void ExecuteRemoteCommand(IDebugCommandHost host, string command,
  280. IList<string> arguments)
  281. {
  282. if (NetworkSession == null)
  283. {
  284. try
  285. {
  286. // GamerServicesDispatcher.WindowHandle = Game.Window.Handle;
  287. // GamerServicesDispatcher.Initialize(Game.Services);
  288. }
  289. catch { }
  290. if (SignedInGamer.SignedInGamers.Count > 0)
  291. {
  292. commandHost.Echo("Finding available sessions...");
  293. asyncResult = NetworkSession.BeginFind(
  294. NetworkSessionType.SystemLink, 1, null, null, null);
  295. phase = ConnectionPahse.FindSessions;
  296. }
  297. else
  298. {
  299. host.Echo("Please signed in.");
  300. phase = ConnectionPahse.EnsureSignedIn;
  301. }
  302. }
  303. else
  304. {
  305. ConnectedToRemote();
  306. }
  307. }
  308. private void ExecuteQuitCommand(IDebugCommandHost host, string command,
  309. IList<string> arguments)
  310. {
  311. SendPacket(QuitPacketHeader, "End Remote Debug Command.");
  312. DisconnectedFromRemote();
  313. }
  314. #endregion
  315. #region IDebugCommandExecutioner and IDebugEchoListner
  316. public void ExecuteCommand(string command)
  317. {
  318. SendPacket(ExecutePacketHeader, command);
  319. }
  320. public void Echo(DebugCommandMessage messageType, string text)
  321. {
  322. switch (messageType)
  323. {
  324. case DebugCommandMessage.Standard:
  325. SendPacket(EchoPacketHeader, text );
  326. break;
  327. case DebugCommandMessage.Warning:
  328. SendPacket(WarningPacketHeader, text);
  329. break;
  330. case DebugCommandMessage.Error:
  331. SendPacket(ErrorPacketHeader, text);
  332. break;
  333. }
  334. }
  335. #endregion
  336. /// <summary>
  337. /// Handle the case host machine is gone.
  338. /// </summary>
  339. void NetworkSession_SessionEnded(object sender, NetworkSessionEndedEventArgs e)
  340. {
  341. DisconnectedFromRemote();
  342. commandHost.EchoWarning("Disconnected from the Host.");
  343. }
  344. #endregion
  345. }
  346. }
  347. #endif