using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Net;
using System.Diagnostics;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
namespace CS_3505_Project_06
{
///
/// A manager class to handle network interactions between peers and
/// lobby/game switching.
///
public class NetworkGame
{
// Private class variable members
#region Instance Variables
NetworkSession mNetworkSession;
JoinedSessionDelegate mJoinedSessionDelegate;
FoundSessionsDelegate mFoundSessionsDelegate;
ILobby mLobby;
IDeterministicGame mGame;
List mLastPressedKeys = new List();
bool mLastButtonPressed;
int mLatency;
long mNextLatencyAdjustmentFrame;
int mStallCount;
int mAverageOwd;
TimeSpan mTargetTimeSpan = new TimeSpan(166666);
public TimeSpan TargetTimeSpan
{
get
{
return mTargetTimeSpan;
}
}
#endregion
///
/// Called when a session has been created or joined using CreateSession() or JoinSession().
///
/// The new session that was created or joined.
/// The NetworkGame that joined the session.
public delegate void JoinedSessionDelegate(NetworkSession session, NetworkGame networkGame);
///
/// Called when sessions are found as a result of calling FindSessions().
///
/// A container of the available sessions.
/// The NetworkGame that searched for the sessions.
public delegate void FoundSessionsDelegate(AvailableNetworkSessionCollection sessions, NetworkGame networkGame);
///
/// Construct a NetworkGame with a lobby and a game.
///
/// Provides an associated lobby to update and draw.
/// Provides a game object to be played over the network.
public NetworkGame(ILobby lobby, IDeterministicGame game)
{
Debug.Assert(lobby != null && game != null);
mLobby = lobby;
mGame = game;
}
///
/// Get the Gamer object for the local player.
///
public LocalNetworkGamer LocalGamer
{
get
{
// TODO: Is this the correct way to get the single local gamer?
return mNetworkSession.LocalGamers[0];
}
}
///
/// Get all the gamers associated with the active network session.
///
public GamerCollection NetworkGamers
{
get
{
return mNetworkSession.AllGamers;
}
}
///
/// Begin a new network session with the local gamer as the host. You must not
/// call this method or use JoinSession without first using LeaveSession.
///
/// The delegate/method to call when the session is created.
public void CreateSession(JoinedSessionDelegate callback)
{
CreateSession(mGame.MaximumSupportedPlayers, callback);
}
///
/// Begin a new network session with the local gamer as the host. You must not
/// call this method or use JoinSession without first using LeaveSession.
///
/// Provide the maximum number of players allowed to connect.
/// The delegate/method to call when the session is created.
public void CreateSession(int maxGamers, JoinedSessionDelegate callback)
{
Debug.Assert(mNetworkSession == null);
mJoinedSessionDelegate = callback;
NetworkSession.BeginCreate(NetworkSessionType.SystemLink, 1, maxGamers, CreateSessionEnd, null);
}
private void CreateSessionEnd(IAsyncResult result)
{
Debug.Assert(mNetworkSession == null);
mNetworkSession = NetworkSession.EndCreate(result);
mNetworkSession.AllowHostMigration = true;
mNetworkSession.AllowJoinInProgress = false;
mJoinedSessionDelegate(mNetworkSession, this);
}
///
/// Determine whether or not the network game object is associated with any network session.
///
/// True if there exists a NetworkSession; false otherwise.
public bool HasActiveSession
{
get
{
return mNetworkSession != null;
}
}
///
/// Find available sessions to join.
///
/// The delegate/method to call when the search finishes.
public void FindSessions(FoundSessionsDelegate callback)
{
Debug.Assert(mNetworkSession == null);
mFoundSessionsDelegate = callback;
NetworkSession.BeginFind(NetworkSessionType.SystemLink, 1, null, new AsyncCallback(FindSessionsEnd), null);
}
private void FindSessionsEnd(IAsyncResult result)
{
AvailableNetworkSessionCollection sessions = NetworkSession.EndFind(result);
mFoundSessionsDelegate(sessions, this);
}
///
/// Join a network session found using FindSessions(). This is for joining a game that
/// somebody else has already started hosting.
///
/// Pass the session object to try to join.
/// The delegate/method to call when the search finishes.
public void JoinSession(AvailableNetworkSession availableSession, JoinedSessionDelegate callback)
{
Debug.Assert(mNetworkSession == null);
mJoinedSessionDelegate = callback;
NetworkSession.BeginJoin(availableSession, JoinSessionEnd, null);
}
private void JoinSessionEnd(IAsyncResult result)
{
Debug.Assert(mNetworkSession == null);
mNetworkSession = NetworkSession.EndJoin(result);
mJoinedSessionDelegate(mNetworkSession, this);
mJoinedSessionDelegate = null;
}
///
/// Leave and dispose of any currently associated network session. You will find yourself
/// back in the lobby.
///
public void LeaveSession()
{
Debug.Assert(mNetworkSession != null);
mNetworkSession.Dispose();
mNetworkSession = null;
}
///
/// Set up the network session to simulate 200ms latency and 10% packet loss.
///
public void SimulateBadNetwork()
{
Debug.Assert(mNetworkSession != null);
mNetworkSession.SimulatedLatency = new TimeSpan(0, 0, 0, 0, 200);
mNetworkSession.SimulatedPacketLoss = 0.1f;
}
///
/// Indicate that the game should begin (moving players from the lobby to the game).
/// You must call CreateSession() before calling this.
///
public void StartGame()
{
Debug.Assert(mNetworkSession != null && mNetworkSession.IsHost);
mNetworkSession.StartGame();
mNetworkSession.ResetReady();
}
///
/// Manages the network session and allows either the lobby or game to update.
///
/// Pass the time away.
public void Update(GameTime gameTime)
{
if (mNetworkSession == null)
{
mLobby.Update(gameTime, this);
}
else
{
mNetworkSession.Update();
ReadPackets();
if (mNetworkSession.SessionState == NetworkSessionState.Lobby)
{
if (mNetworkSession.IsHost &&
mNetworkSession.AllGamers.Count >= mGame.MinimumSupportedPlayers &&
mNetworkSession.IsEveryoneReady)
{
mNetworkSession.StartGame();
mNetworkSession.ResetReady();
}
else
{
mLobby.Update(gameTime, this);
}
}
else if (mNetworkSession.SessionState == NetworkSessionState.Playing)
{
if (HaveNeededEvents)
{
if (IsLatencyAdjustmentFrame) AdjustLatency();
mStallCount = 0;
SendLocalEvents();
ApplyEvents();
mGame.Update(mTargetTimeSpan);
}
else // Stall!
{
}
}
}
}
///
/// Allows either the lobby or the game to draw, depending on the state
/// of the network connection and whether or not a game is in progress.
///
/// Pass the time away.
/// The sprite batch.
public void Draw(GameTime gameTime, SpriteBatch spriteBatch)
{
if (mNetworkSession == null)
{
mLobby.Draw(spriteBatch);
}
else
{
if (mNetworkSession.SessionState == NetworkSessionState.Lobby)
{
mLobby.Draw(spriteBatch);
}
else if (mNetworkSession.SessionState == NetworkSessionState.Playing)
{
mLobby.Draw(spriteBatch);
}
}
}
// Private implementation methods of the network protocol
#region Private Implementation Methods
///
/// Reinitialize the private variables in preparation for new game to start.
///
private void Reset()
{
mLatency = 1;
mNextLatencyAdjustmentFrame = 1;
mStallCount = 0;
mAverageOwd = AverageOneWayDelay;
// TODO: The game object needs to be reset, too.
//mGame.ResetGame(playerIdentifiers, playerIdentifiers[0]);
}
///
/// Allows either the lobby or the game to draw, depending on the state
/// of the network connection and whether or not a game is in progress.
///
/// Pass the time away.
/// The sprite batch.
private void ReadPackets()
{
PacketReader packetReader = new PacketReader();
foreach (LocalNetworkGamer gamer in mNetworkSession.LocalGamers)
{
while (gamer.IsDataAvailable)
{
NetworkGamer sender;
gamer.ReceiveData(packetReader, out sender);
byte packetId = packetReader.ReadByte();
switch (packetId)
{
// Chat Packet
case 1:
short messageLength = packetReader.ReadInt16();
char[] message = packetReader.ReadChars(messageLength);
ChatPacket chatPacket;
chatPacket.sender = sender;
chatPacket.message = new String(message);
break;
// Event Packet
case 2:
short stallCount = packetReader.ReadInt16();
short averageOwd = packetReader.ReadInt16();
int frameNumber = packetReader.ReadInt32();
byte numEvents = packetReader.ReadByte();
for (byte i = 0; i < numEvents; ++i)
{
ReadEvent(packetReader, sender);
}
break;
// Stall Packet
case 3:
byte numStalledPeers = packetReader.ReadByte();
byte[] stalledPeers = packetReader.ReadBytes(numStalledPeers);
break;
}
}
}
}
private void ReadEvent(PacketReader packetReader, NetworkGamer sender)
{
byte eventId = packetReader.ReadByte();
long applicationFrame = packetReader.ReadInt32();
switch (eventId)
{
// Key Down
case 1:
int keyCode1 = packetReader.ReadInt32();
break;
// Key Up
case 2:
int keyCode2 = packetReader.ReadInt32();
break;
// Mouse Down
case 3:
byte buttonId1 = packetReader.ReadByte();
break;
// Mouse Up
case 4:
byte buttonId2 = packetReader.ReadByte();
break;
// Mouse Move
case 5:
short x = packetReader.ReadInt16();
short y = packetReader.ReadInt16();
break;
}
}
private bool IsLatencyAdjustmentFrame
{
get
{
// TODO
return false;
}
}
private void AdjustLatency()
{
// TODO
}
private void SendLocalEvents()
{
// TODO: Not finished.
KeyboardState keyState = Keyboard.GetState();
MouseState mouseState = Mouse.GetState();
// Make a list of the keys pressed or released this frame.
List pressedKeys = new List();
List releasedKeys = new List();
Keys[] pressedKeysArray = keyState.GetPressedKeys();
foreach (Keys k in pressedKeysArray)
if (!mLastPressedKeys.Contains(k))
pressedKeys.Add(k);
else
mLastPressedKeys.Remove(k);
releasedKeys = mLastPressedKeys;
mLastPressedKeys = new List(pressedKeysArray);
bool buttonPressed = mouseState.LeftButton == ButtonState.Pressed;
}
private bool HaveNeededEvents
{
get
{
// TODO
return true;
}
}
private void ApplyEvents()
{
// TODO
}
private int AverageOneWayDelay
{
get
{
// TODO
return 12;
}
}
#endregion
}
}