// Make sure DEBUG is undefined when turning in the project
// or the grader will wonder why it's so laggy.
#undef DEBUG
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;
using System.Collections;
namespace CarFire
{
///
/// A manager class to handle network interactions between peers and
/// lobby/game switching.
///
public class NetworkManager
{
// Public methods and properties
#region Public Methods
///
/// 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, NetworkManager 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, NetworkManager networkGame);
///
/// Called when an exception is thrown during an asynchronous operation.
///
/// The exception that was thrown.
/// The NetworkGame that errored.
public delegate void CaughtErrorDelegate(Exception exception, NetworkManager networkGame);
///
/// Get and set the error delegate, called when an exception is thrown during
/// and asynchronous operation. This will occur if you try to create or join a
/// session without being logged into a profile.
///
public CaughtErrorDelegate ErrorDelegate;
///
/// 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 NetworkManager(IScreenManager 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);
}
void CreateSessionEnd(IAsyncResult result)
{
Debug.Assert(mNetworkSession == null);
try
{
mNetworkSession = NetworkSession.EndCreate(result);
mNetworkSession.AllowHostMigration = true;
mNetworkSession.AllowJoinInProgress = false;
mNetworkSession.GameStarted += new EventHandler(GameStartedEvent);
}
catch (Exception e)
{
if (ErrorDelegate != null) ErrorDelegate(e, this);
return;
}
mJoinedSessionDelegate(mNetworkSession, this);
mJoinedSessionDelegate = null;
}
void GameStartedEvent(object sender, GameStartedEventArgs e)
{
Reset();
}
///
/// 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. You should not already be in a session when
/// calling this method; call LeaveSession first.
///
/// 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);
}
void FindSessionsEnd(IAsyncResult result)
{
AvailableNetworkSessionCollection sessions;
try
{
sessions = NetworkSession.EndFind(result);
}
catch (Exception e)
{
if (ErrorDelegate != null) ErrorDelegate(e, this);
return;
}
mFoundSessionsDelegate(sessions, this);
mFoundSessionsDelegate = null;
}
///
/// Join a network session found using FindSessions(). This is for joining a game that
/// somebody else has already started hosting. You must not already be in a session.
///
/// 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);
}
void JoinSessionEnd(IAsyncResult result)
{
Debug.Assert(mNetworkSession == null);
try
{
mNetworkSession = NetworkSession.EndJoin(result);
mNetworkSession.GameStarted += new EventHandler(GameStartedEvent);
}
catch (Exception e)
{
if (ErrorDelegate != null) ErrorDelegate(e, this);
return;
}
mJoinedSessionDelegate(mNetworkSession, this);
mJoinedSessionDelegate = null;
}
///
/// Leave and dispose of any currently associated network session. You will find yourself
/// back in the lobby. You must already be in a session to leave it.
///
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.AllGamers.Count >= mGame.MinimumSupportedPlayers &&
mNetworkSession.IsEveryoneReady);
ForceStartGame();
}
///
/// Indicate that the game should begin. This is like StartGame() without the sanity
/// checks. Use this for debugging.
///
public void ForceStartGame()
{
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();
HandleIncomingPackets();
if (mNetworkSession.SessionState == NetworkSessionState.Lobby)
{
mLobby.Update(gameTime, this);
}
else if (mNetworkSession.SessionState == NetworkSessionState.Playing)
{
if (mGame.IsTerminated(LocalGamerInfo))
{
LeaveSession();
return;
}
else if (mGame.IsGameOver(LocalGamerInfo))
{
ApplyEvents(LocalGamerInfo, GetEventsFromInput());
mGame.Update(mTargetTimeSpan);
return;
}
if (HaveNeededEvents)
{
if (IsLatencyAdjustmentFrame)
{
AdjustLatency();
mLastStallCount = mStallCount;
mStallCount = 0;
}
mLocalEvents.AddRange(GetEventsFromInput());
SendLocalEvents();
ApplyEvents();
#if DEBUG
Console.WriteLine("HASH: " + mGame.CurrentFrameNumber + "\t" + mGame.CurrentChecksum);
#endif
mGame.Update(mTargetTimeSpan);
}
else // Stall!
{
mStallCount++;
// Send a reliable event packet to each stalled gamer.
if (mStallCount == 1)
{
#if DEBUG
Console.WriteLine("STAL: ====");
#endif
foreach (GamerInfo gamerInfo in GamerArray)
{
if (gamerInfo.HighestFrameNumber < mGame.CurrentFrameNumber)
{
SendLocalEvents(gamerInfo.Gamer);
}
}
}
else if (mStallCount > 600)
{
Console.WriteLine("One or more players have stalled excessively. Leaving session...");
LeaveSession();
}
}
}
}
}
///
/// 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)
{
mGame.Draw(spriteBatch);
}
}
}
///
/// Get the chat messages that have been received since the last time this
/// method was called.
///
/// List container of the chat messages.
public List ReceiveChats()
{
List chats = mChatPackets;
mChatPackets = new List();
return chats;
}
///
/// Send a chat message to all gamers in the session. You should already be
/// in a session before calling this method.
///
/// The text of the message.
public void SendChat(String message)
{
WriteChatPacket(message);
LocalGamer.SendData(mPacketWriter, SendDataOptions.ReliableInOrder);
}
///
/// Send a chat message to a specific gamer in the session. You should already
/// be in a session before calling this method.
///
/// The text of the message.
/// The gamer to receive the message.
public void SendChat(String message, NetworkGamer recipient)
{
Debug.Assert(recipient != null && !recipient.IsDisposed);
WriteChatPacket(message);
LocalGamer.SendData(mPacketWriter, SendDataOptions.ReliableInOrder, recipient);
}
#endregion
// Private class variable members
#region Instance Variables
NetworkSession mNetworkSession;
PacketReader mPacketReader = new PacketReader();
PacketWriter mPacketWriter = new PacketWriter();
JoinedSessionDelegate mJoinedSessionDelegate;
FoundSessionsDelegate mFoundSessionsDelegate;
IScreenManager mLobby;
IDeterministicGame mGame;
List mChatPackets = new List();
List mLocalEvents = new List();
List mLastLocalEvents = new List();
List mLastPressedKeys = new List();
bool mLastLeftButtonPressed;
bool mLastRightButtonPressed;
bool mLastMiddleButtonPressed;
int mLastMousePositionX;
int mLastMousePositionY;
int mLatency;
long mHighestFrameNumber;
long mNextLatencyAdjustmentFrame;
int mStallCount;
int mLastStallCount;
int mAverageOwd;
#if DEBUG
bool mDontSendEvents;
#endif
TimeSpan mTargetTimeSpan = new TimeSpan(166666);
public TimeSpan TargetTimeSpan
{
get
{
return mTargetTimeSpan;
}
}
Dictionary mGamers;
GamerInfo[] GamerArray
{
get
{
GamerInfo[] gamerList = mGamers.Values.ToArray();
Array.Sort(gamerList, delegate(GamerInfo a, GamerInfo b)
{
return a.Gamer.Id.CompareTo(b.Gamer.Id);
});
return gamerList;
}
}
GamerInfo LocalGamerInfo
{
get
{
return mGamers[LocalGamer.Id];
}
}
#endregion
// Private types for the implementation of the network protocol
#region Private Types
enum PacketType
{
Chat = 1,
Event = 2
}
enum EventType
{
KeyDown = 1,
KeyUp = 2,
MouseDown = 3,
MouseUp = 4,
MouseMove = 5
}
enum MouseButton
{
Left = 1,
Right = 2,
Middle = 3
}
abstract class EventInfo
{
public NetworkGamer Gamer;
public long FrameOfApplication;
public EventInfo(NetworkGamer gamer, long frameNumber)
{
Gamer = gamer;
FrameOfApplication = frameNumber;
}
public abstract EventType Id
{
get;
}
}
class KeyboardEventInfo : EventInfo
{
public Keys Key;
public bool IsKeyDown;
public KeyboardEventInfo(NetworkGamer gamer, long frameNumber, Keys key, bool isDown)
: base(gamer, frameNumber)
{
Key = key;
IsKeyDown = isDown;
}
public override EventType Id
{
get { return IsKeyDown ? EventType.KeyDown : EventType.KeyUp; }
}
}
class MouseButtonEventInfo : EventInfo
{
public MouseButton Button;
public bool IsButtonDown;
public MouseButtonEventInfo(NetworkGamer gamer, long frameNumber, MouseButton button, bool isDown)
: base(gamer, frameNumber)
{
Button = button;
IsButtonDown = isDown;
}
public override EventType Id
{
get { return IsButtonDown ? EventType.MouseDown : EventType.MouseUp; }
}
}
class MouseMotionEventInfo : EventInfo
{
public int X;
public int Y;
public MouseMotionEventInfo(NetworkGamer gamer, long frameNumber, int x, int y)
: base(gamer, frameNumber)
{
X = x;
Y = y;
}
public override EventType Id
{
get { return EventType.MouseMove; }
}
}
class GamerInfo
{
public NetworkGamer Gamer;
public long HighestFrameNumber = 0;
public int StallCount = 0;
public int AverageOwd = 0;
public int NextStallCount = 0;
public int NextAverageOwd = 0;
public bool IsWaitedOn = false;
public List[] Events = new List[MaximumLatency];
public GamerInfo(NetworkGamer gamer)
{
Gamer = gamer;
}
}
const int MaximumLatency = 120;
const int StallTimeout = 900;
#endregion
// Private implementation methods of the network protocol
#region Private Implementation Methods
///
/// Reinitialize the private variables in preparation for a new game to start.
///
void Reset()
{
mLatency = 1;
mHighestFrameNumber = 0;
mNextLatencyAdjustmentFrame = 1;
mStallCount = 0;
mLastStallCount = 0;
mAverageOwd = CurrentAverageOneWayDelay;
mGamers = new Dictionary();
foreach (NetworkGamer gamer in NetworkGamers)
{
mGamers.Add(gamer.Id, new GamerInfo(gamer));
}
mGame.ResetGame(GamerArray, LocalGamerInfo);
}
void HandleIncomingPackets()
{
LocalNetworkGamer localGamer = LocalGamer;
while (localGamer.IsDataAvailable)
{
NetworkGamer sender;
localGamer.ReceiveData(mPacketReader, out sender);
PacketType packetId = (PacketType)mPacketReader.ReadByte();
switch (packetId)
{
case PacketType.Chat:
short messageLength = mPacketReader.ReadInt16();
char[] message = mPacketReader.ReadChars(messageLength);
ChatInfo chatPacket = new ChatInfo(sender, new String(message));
mChatPackets.Add(chatPacket);
break;
case PacketType.Event:
GamerInfo senderInfo = mGamers[sender.Id];
int stallCount = mPacketReader.ReadInt16();
int averageOwd = mPacketReader.ReadInt16();
int frameNumber = mPacketReader.ReadInt32();
int numEvents = mPacketReader.ReadByte();
if (frameNumber <= mNextLatencyAdjustmentFrame)
{
senderInfo.StallCount = stallCount;
senderInfo.AverageOwd = averageOwd;
}
else
{
senderInfo.NextStallCount = stallCount;
senderInfo.NextAverageOwd = averageOwd;
}
if (frameNumber <= senderInfo.HighestFrameNumber)
{
#if DEBUG
Console.WriteLine("SKP" + (char)sender.Id + ": " + mGame.CurrentFrameNumber + "\t" + frameNumber + "\t<=\t" + senderInfo.HighestFrameNumber + "\t#" + numEvents);
#endif
// we know about all these events, so don't bother reading them
break;
}
#if DEBUG
Console.WriteLine(" GOT" + (char)sender.Id + ": " + mGame.CurrentFrameNumber + "\t" + frameNumber + "\t>\t" + senderInfo.HighestFrameNumber + "\t#" + numEvents);
#endif
for (int i = 0; i < numEvents; i++)
{
EventInfo eventInfo = ReadEvent(mPacketReader, sender);
if (eventInfo != null && eventInfo.FrameOfApplication > senderInfo.HighestFrameNumber)
{
int index = GetEventArrayIndexForFrame(eventInfo.FrameOfApplication);
if (senderInfo.Events[index] == null) senderInfo.Events[index] = new List();
senderInfo.Events[index].Add(eventInfo);
}
}
senderInfo.HighestFrameNumber = frameNumber;
break;
default:
Console.WriteLine("Received unknown packet type: " + (int)packetId);
break;
}
}
}
int CurrentEventArrayIndex
{
get { return GetEventArrayIndexForFrame(mGame.CurrentFrameNumber); }
}
int GetEventArrayIndexForFrame(long frame)
{
return (int)(frame % MaximumLatency);
}
EventInfo ReadEvent(PacketReader packetReader, NetworkGamer sender)
{
EventType eventId = (EventType)packetReader.ReadByte();
long frameNumber = packetReader.ReadInt32();
switch (eventId)
{
case EventType.KeyDown:
Keys keyCode1 = (Keys)packetReader.ReadInt32();
return new KeyboardEventInfo(sender, frameNumber, keyCode1, true);
case EventType.KeyUp:
Keys keyCode2 = (Keys)packetReader.ReadInt32();
return new KeyboardEventInfo(sender, frameNumber, keyCode2, false);
case EventType.MouseDown:
MouseButton buttonId1 = (MouseButton)packetReader.ReadByte();
return new MouseButtonEventInfo(sender, frameNumber, buttonId1, true);
case EventType.MouseUp:
MouseButton buttonId2 = (MouseButton)packetReader.ReadByte();
return new MouseButtonEventInfo(sender, frameNumber, buttonId2, false);
case EventType.MouseMove:
short x = packetReader.ReadInt16();
short y = packetReader.ReadInt16();
return new MouseMotionEventInfo(sender, frameNumber, x, y);
default:
Console.WriteLine("Received unknown event type: " + (int)eventId);
return null;
}
}
void WriteChatPacket(String message)
{
mPacketWriter.Write((byte)PacketType.Chat);
mPacketWriter.Write((short)message.Length);
mPacketWriter.Write(message.ToCharArray());
}
void WriteEventPacket(List events, long highestFrameNumber)
{
mPacketWriter.Write((byte)PacketType.Event);
mPacketWriter.Write((short)mLastStallCount);
mPacketWriter.Write((short)mAverageOwd);
mPacketWriter.Write((int)highestFrameNumber);
mPacketWriter.Write((byte)events.Count);
foreach (EventInfo eventInfo in events)
{
mPacketWriter.Write((byte)eventInfo.Id);
mPacketWriter.Write((int)eventInfo.FrameOfApplication);
KeyboardEventInfo keyboardEventInfo = eventInfo as KeyboardEventInfo;
if (keyboardEventInfo != null)
{
mPacketWriter.Write((int)keyboardEventInfo.Key);
continue;
}
MouseButtonEventInfo mouseButtonEventInfo = eventInfo as MouseButtonEventInfo;
if (mouseButtonEventInfo != null)
{
mPacketWriter.Write((byte)mouseButtonEventInfo.Button);
continue;
}
MouseMotionEventInfo mouseMotionEventInfo = eventInfo as MouseMotionEventInfo;
if (mouseMotionEventInfo != null)
{
mPacketWriter.Write((short)mouseMotionEventInfo.X);
mPacketWriter.Write((short)mouseMotionEventInfo.Y);
continue;
}
}
}
bool IsLatencyAdjustmentFrame
{
get
{
return mNextLatencyAdjustmentFrame == mGame.CurrentFrameNumber;
}
}
void AdjustLatency()
{
Debug.Assert(IsLatencyAdjustmentFrame);
#if DEBUG
if (mStallCount > 0)
{
Console.WriteLine("STL#: " + mGame.CurrentFrameNumber + "\t" + mStallCount);
}
#endif
int maxStallCount = 0;
int maxAverageOwd = 0;
foreach (GamerInfo gamerInfo in GamerArray)
{
if (gamerInfo.StallCount > maxStallCount) maxStallCount = gamerInfo.StallCount;
if (gamerInfo.AverageOwd > maxAverageOwd) maxAverageOwd = gamerInfo.AverageOwd;
gamerInfo.StallCount = gamerInfo.NextStallCount;
gamerInfo.AverageOwd = gamerInfo.NextAverageOwd;
}
#if DEBUG
int prevLatency = mLatency;
#endif
if (maxStallCount > 0)
{
mLatency += maxStallCount;
}
else
{
mLatency -= (int)(0.6 * (double)(mLatency - maxAverageOwd) + 1.0);
}
if (mLatency < 1) mLatency = 1;
if (mLatency > MaximumLatency) mLatency = MaximumLatency;
#if DEBUG
if (prevLatency != mLatency) Console.WriteLine("NLAG: " + mLatency);
#endif
mNextLatencyAdjustmentFrame = mGame.CurrentFrameNumber + mLatency;
mAverageOwd = CurrentAverageOneWayDelay;
mLastLocalEvents = mLocalEvents;
mLocalEvents = new List();
}
List GetEventsFromInput()
{
List events = new List();
long frameOfApplication = mGame.CurrentFrameNumber + mLatency;
if (frameOfApplication <= mHighestFrameNumber) return events;
else mHighestFrameNumber = frameOfApplication;
// 1. Find the keyboard differences; written by Peter.
KeyboardState keyState = Keyboard.GetState();
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;
foreach (Keys key in pressedKeys)
{
events.Add(new KeyboardEventInfo(LocalGamer, frameOfApplication, key, true));
}
foreach (Keys key in releasedKeys)
{
events.Add(new KeyboardEventInfo(LocalGamer, frameOfApplication, key, false));
}
#if DEBUG
if (pressedKeys.Contains(Keys.Escape)) mDontSendEvents = true;
if (releasedKeys.Contains(Keys.Escape)) mDontSendEvents = false;
#endif
// 2. Find the mouse differences.
MouseState mouseState = Mouse.GetState();
bool leftButtonPressed = mouseState.LeftButton == ButtonState.Pressed;
if (leftButtonPressed != mLastLeftButtonPressed)
{
events.Add(new MouseButtonEventInfo(LocalGamer, frameOfApplication, MouseButton.Left, leftButtonPressed));
}
bool rightButtonPressed = mouseState.RightButton == ButtonState.Pressed;
if (rightButtonPressed != mLastRightButtonPressed)
{
events.Add(new MouseButtonEventInfo(LocalGamer, frameOfApplication, MouseButton.Right, rightButtonPressed));
}
bool middleButtonPressed = mouseState.MiddleButton == ButtonState.Pressed;
if (middleButtonPressed != mLastMiddleButtonPressed)
{
events.Add(new MouseButtonEventInfo(LocalGamer, frameOfApplication, MouseButton.Middle, middleButtonPressed));
}
int mousePositionX = mouseState.X;
int mousePositionY = mouseState.Y;
if (mousePositionX != mLastMousePositionX || mousePositionY != mLastMousePositionY)
{
events.Add(new MouseMotionEventInfo(LocalGamer, frameOfApplication, mousePositionX, mousePositionY));
}
// 3. Save the current peripheral state.
mLastPressedKeys = new List(pressedKeysArray);
mLastLeftButtonPressed = leftButtonPressed;
mLastRightButtonPressed = rightButtonPressed;
mLastMiddleButtonPressed = middleButtonPressed;
mLastMousePositionX = mousePositionX;
mLastMousePositionY = mousePositionY;
return events;
}
void SendLocalEvents()
{
SendLocalEvents((NetworkGamer)null);
}
void SendLocalEvents(List recipicents)
{
foreach (NetworkGamer gamer in recipicents)
{
SendLocalEvents(gamer);
}
}
void SendLocalEvents(NetworkGamer recipient)
{
#if DEBUG
if (mDontSendEvents) return;
#endif
List events = new List(mLocalEvents);
events.AddRange(mLastLocalEvents);
if (recipient != null && !recipient.IsDisposed)
{
// if there is a recipient, we are resending old events
WriteEventPacket(events, mGame.CurrentFrameNumber - 1);
LocalGamer.SendData(mPacketWriter, SendDataOptions.Reliable, recipient);
}
else
{
WriteEventPacket(events, mGame.CurrentFrameNumber + mLatency);
LocalGamer.SendData(mPacketWriter, SendDataOptions.None);
}
}
bool HaveNeededEvents
{
get
{
long currentFrame = mGame.CurrentFrameNumber;
foreach (GamerInfo gamerInfo in mGamers.Values)
{
if (mGame.IsGameOver(gamerInfo)) continue;
if (gamerInfo.HighestFrameNumber < currentFrame) return false;
}
return true;
}
}
void ApplyEvents()
{
int index = CurrentEventArrayIndex;
foreach (GamerInfo gamerInfo in GamerArray)
{
if (gamerInfo.Events[index] == null) continue;
ApplyEvents(gamerInfo, gamerInfo.Events[index]);
gamerInfo.Events[index] = null;
}
}
void ApplyEvents(GamerInfo gamerInfo, List events)
{
foreach (EventInfo eventInfo in events)
{
KeyboardEventInfo keyboardEventInfo = eventInfo as KeyboardEventInfo;
if (keyboardEventInfo != null)
{
#if DEBUG
Console.WriteLine(" KEY: " + keyboardEventInfo.FrameOfApplication + "\t" + keyboardEventInfo.Key + "," + keyboardEventInfo.IsKeyDown);
#endif
mGame.ApplyKeyInput(gamerInfo, keyboardEventInfo.Key, keyboardEventInfo.IsKeyDown);
continue;
}
MouseButtonEventInfo mouseButtonEventInfo = eventInfo as MouseButtonEventInfo;
if (mouseButtonEventInfo != null)
{
#if DEBUG
Console.WriteLine(" BTN: " + mouseButtonEventInfo.FrameOfApplication + "\t" + mouseButtonEventInfo.IsButtonDown);
#endif
mGame.ApplyMouseButtonInput(gamerInfo, mouseButtonEventInfo.IsButtonDown);
continue;
}
MouseMotionEventInfo mouseMotionEventInfo = eventInfo as MouseMotionEventInfo;
if (mouseMotionEventInfo != null)
{
#if DEBUG
Console.WriteLine(" MMV: " + mouseMotionEventInfo.FrameOfApplication + "\t" + mouseMotionEventInfo.X + "," + mouseMotionEventInfo.Y);
#endif
mGame.ApplyMouseLocationInput(gamerInfo, mouseMotionEventInfo.X, mouseMotionEventInfo.Y);
continue;
}
}
}
int CurrentAverageOneWayDelay
{
get
{
Debug.Assert(mNetworkSession != null);
double numRemoteGamersTwice = 2 * mNetworkSession.RemoteGamers.Count;
double averageOwd = 0;
foreach (NetworkGamer gamer in mNetworkSession.RemoteGamers)
{
TimeSpan timeSpan = gamer.RoundtripTime;
averageOwd += timeSpan.TotalMilliseconds;
}
return (int)((averageOwd / numRemoteGamersTwice) / 16.6666);
}
}
#endregion
}
}