#region File Description //----------------------------------------------------------------------------- // RemoteDebugCommands.cs // // Microsoft XNA Community Game Platform // Copyright (C) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- #endregion // Remote debugging doesn't work on Windows Phone because it relies on // Microsoft.Xna.Framework.Net. We therefore can't compile this class // for the Windows Phone platform. #if !WINDOWS_PHONE #region Using Statements using System; using System.Collections.Generic; using Microsoft.Xna.Framework.Net; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.GamerServices; using System.Text.RegularExpressions; #endregion namespace PerformanceMeasuring.GameDebugTools { /// /// Remote debug component. /// /// /// This is the game component that supports remote debug command. /// When you use "remote" command on Windows side, it connect to Xbox 360 game via /// SystemLink. /// /// After you connected to Xbox 360, you can type debug command on Windows side that /// actually executed on Xbox 360 game. So, you can run debug command without /// connect a keyboard to the Xbox 360 console. /// /// To quit remote debug command more, simply type 'quit' command. /// public class RemoteDebugCommand : GameComponent, IDebugCommandExecutioner, IDebugEchoListner { #region Properties /// /// Sets/Get NetworkSession for Remote Debug Command. /// public NetworkSession NetworkSession { get; set; } /// /// Represents NetworkSession created by this component or not. /// public bool OwnsNetworkSession { get; private set; } #endregion #region Constants const string StartPacketHeader = "RmtStart"; const string ExecutePacketHeader = "RmtCmd"; const string EchoPacketHeader = "RmtEcho"; const string ErrorPacketHeader = "RmtErr"; const string WarningPacketHeader = "RmtWrn"; const string QuitPacketHeader = "RmtQuit"; #endregion #region Fields IDebugCommandHost commandHost; bool IsHost = false; Regex packetRe = new Regex(@"\$(?
[^$]+)\$:(?.+)"); PacketReader packetReader = new PacketReader(); PacketWriter packetWriter = new PacketWriter(); IAsyncResult asyncResult; enum ConnectionPahse { None, EnsureSignedIn, FindSessions, Joining, } ConnectionPahse phase = ConnectionPahse.None; #endregion #region Initialization public RemoteDebugCommand(Game game) : base(game) { commandHost = game.Services.GetService(typeof(IDebugCommandHost)) as IDebugCommandHost; if (!IsHost) { commandHost.RegisterCommand("remote", "Start remote command", ExecuteRemoteCommand); } } public override void Initialize() { if (IsHost) { commandHost.RegisterEchoListner(this); // Create network session if NetworkSession is not set. if (NetworkSession == null) { GamerServicesDispatcher.WindowHandle = Game.Window.Handle; GamerServicesDispatcher.Initialize(Game.Services); NetworkSession = NetworkSession.Create(NetworkSessionType.SystemLink, 1, 2); OwnsNetworkSession = true; } } base.Initialize(); } #endregion /// /// Process received packet string. /// /// You can call this method if you own network session on the game side. /// /// /// Processed this packet? public bool ProcessRecievedPacket(string packetString) { bool processed = false; Match mc = packetRe.Match(packetString); if (mc.Success) { string packetHeader = mc.Groups["header"].Value; string text = mc.Groups["text"].Value; switch (packetHeader) { case ExecutePacketHeader: commandHost.ExecuteCommand(text); processed = true; break; case EchoPacketHeader: commandHost.Echo(text); processed = true; break; case ErrorPacketHeader: commandHost.EchoError(text); processed = true; break; case WarningPacketHeader: commandHost.EchoWarning(text); processed = true; break; case StartPacketHeader: ConnectedToRemote(); commandHost.Echo(text); processed = true; break; case QuitPacketHeader: commandHost.Echo(text); DisconnectedFromRemote(); processed = true; break; } } return processed; } #region Implementations /// /// Update /// public override void Update(GameTime gameTime) { // Process different phases. switch (phase) { case ConnectionPahse.EnsureSignedIn: GamerServicesDispatcher.Update(); break; case ConnectionPahse.FindSessions: GamerServicesDispatcher.Update(); if (asyncResult.IsCompleted) { AvailableNetworkSessionCollection sessions = NetworkSession.EndFind(asyncResult); if (sessions.Count > 0) { asyncResult = NetworkSession.BeginJoin( sessions[0], null, null ); commandHost.EchoError("Connecting to the host..."); phase = ConnectionPahse.Joining; } else { commandHost.EchoError("Couldn't find a session."); phase = ConnectionPahse.None; } } break; case ConnectionPahse.Joining: GamerServicesDispatcher.Update(); if (asyncResult.IsCompleted) { NetworkSession = NetworkSession.EndJoin(asyncResult); NetworkSession.SessionEnded += new EventHandler( NetworkSession_SessionEnded); OwnsNetworkSession = true; commandHost.EchoError("Connected to the host."); phase = ConnectionPahse.None; asyncResult = null; ConnectedToRemote(); } break; } // Update Network session. if (OwnsNetworkSession) { GamerServicesDispatcher.Update(); NetworkSession.Update(); if (NetworkSession != null) { // Process received packets. foreach (LocalNetworkGamer gamer in NetworkSession.LocalGamers) { while (gamer.IsDataAvailable) { NetworkGamer sender; gamer.ReceiveData(packetReader, out sender); if (!sender.IsLocal) ProcessRecievedPacket(packetReader.ReadString()); } } } } base.Update(gameTime); } /// /// Send remote debug command packet. /// void SendPacket(string header, string text) { if (NetworkSession != null) { packetWriter.Write("$" + header + "$:" + text); NetworkSession.LocalGamers[0].SendData(packetWriter, SendDataOptions.ReliableInOrder); } } /// /// Start remote debug command. /// void ConnectedToRemote() { DebugCommandUI commandUI = commandHost as DebugCommandUI; if (IsHost) { if ( commandUI != null ) commandUI.Prompt = "[Host]>"; } else { if (commandUI != null) commandUI.Prompt = "[Client]>"; commandHost.PushExecutioner(this); SendPacket(StartPacketHeader, "Remote Debug Command Started!!"); } commandHost.RegisterCommand("quit", "Quit from remote command", ExecuteQuitCommand); } /// /// End remote debug command. /// void DisconnectedFromRemote() { DebugCommandUI commandUI = commandHost as DebugCommandUI; if (commandUI != null) commandUI.Prompt = DebugCommandUI.DefaultPrompt; commandHost.UnregisterCommand("quit"); if (!IsHost) { commandHost.PopExecutioner(); if (OwnsNetworkSession) { NetworkSession.Dispose(); NetworkSession = null; OwnsNetworkSession = false; } } } #region DebugCommand implementations private void ExecuteRemoteCommand(IDebugCommandHost host, string command, IList arguments) { if (NetworkSession == null) { try { GamerServicesDispatcher.WindowHandle = Game.Window.Handle; GamerServicesDispatcher.Initialize(Game.Services); } catch { } if (SignedInGamer.SignedInGamers.Count > 0) { commandHost.Echo("Finding available sessions..."); asyncResult = NetworkSession.BeginFind( NetworkSessionType.SystemLink, 1, null, null, null); phase = ConnectionPahse.FindSessions; } else { host.Echo("Please signed in."); phase = ConnectionPahse.EnsureSignedIn; } } else { ConnectedToRemote(); } } private void ExecuteQuitCommand(IDebugCommandHost host, string command, IList arguments) { SendPacket(QuitPacketHeader, "End Remote Debug Command."); DisconnectedFromRemote(); } #endregion #region IDebugCommandExecutioner and IDebugEchoListner public void ExecuteCommand(string command) { SendPacket(ExecutePacketHeader, command); } public void Echo(DebugCommandMessage messageType, string text) { switch (messageType) { case DebugCommandMessage.Standard: SendPacket(EchoPacketHeader, text ); break; case DebugCommandMessage.Warning: SendPacket(WarningPacketHeader, text); break; case DebugCommandMessage.Error: SendPacket(ErrorPacketHeader, text); break; } } #endregion /// /// Handle the case host machine is gone. /// void NetworkSession_SessionEnded(object sender, NetworkSessionEndedEventArgs e) { DisconnectedFromRemote(); commandHost.EchoWarning("Disconnected from the Host."); } #endregion } } #endif