#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 HoneycombRush.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;
#if WINDOWS
bool IsHost = false;
#else
bool IsHost = true;
#endif
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