From: Charles Date: Tue, 23 Mar 2010 20:57:42 +0000 (+0000) Subject: Event packets implemented with complete latency readjusting. X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Fcarfire;a=commitdiff_plain;h=8c50e423a4325590132a2e921c73ed3439db7bbc Event packets implemented with complete latency readjusting. Testing with more than one player needed. git-svn-id: https://bd85.net/svn/cs3505_group@29 92bb83a3-7c8f-8a45-bc97-515c4e399668 --- diff --git a/Project06/CS 3505 Project 06/CS 3505 Project 06/CS 3505 Project 06.csproj b/Project06/CS 3505 Project 06/CS 3505 Project 06/CS 3505 Project 06.csproj index 99e7d27..cdf4d76 100644 --- a/Project06/CS 3505 Project 06/CS 3505 Project 06/CS 3505 Project 06.csproj +++ b/Project06/CS 3505 Project 06/CS 3505 Project 06/CS 3505 Project 06.csproj @@ -84,7 +84,7 @@ - + diff --git a/Project06/CS 3505 Project 06/CS 3505 Project 06/CS 3505/TestHarness.cs b/Project06/CS 3505 Project 06/CS 3505 Project 06/CS 3505/TestHarness.cs index 76bc066..883e8a0 100644 --- a/Project06/CS 3505 Project 06/CS 3505 Project 06/CS 3505/TestHarness.cs +++ b/Project06/CS 3505 Project 06/CS 3505 Project 06/CS 3505/TestHarness.cs @@ -224,12 +224,14 @@ namespace CS_3505_Project_06.CS_3505 public void ResetGame(Object[] playerIdentifiers, Object thisPlayer) { - if (playerIdentifiers.Length != 4) - throw new Exception("This game requires four players."); + //if (playerIdentifiers.Length != 4) + // throw new Exception("This game requires four players."); // Copy the player identifiers - do not rely on the array parameter not changing. - for (int i = 0; i < 4; i++) + // Now the test harness will at least run with less than 4 players... + int numPlayers = playerIdentifiers.Count(); + for (int i = 0; i < numPlayers; i++) this.playerIdentifiers[i] = playerIdentifiers[i]; // Create new game state and inputs objects. diff --git a/Project06/CS 3505 Project 06/CS 3505 Project 06/ChatPacket.cs b/Project06/CS 3505 Project 06/CS 3505 Project 06/ChatInfo.cs similarity index 84% rename from Project06/CS 3505 Project 06/CS 3505 Project 06/ChatPacket.cs rename to Project06/CS 3505 Project 06/CS 3505 Project 06/ChatInfo.cs index 15e4470..39af420 100644 --- a/Project06/CS 3505 Project 06/CS 3505 Project 06/ChatPacket.cs +++ b/Project06/CS 3505 Project 06/CS 3505 Project 06/ChatInfo.cs @@ -10,7 +10,7 @@ namespace CS_3505_Project_06 /// Small container class for the information concerning a chat packet. /// It is immutable. /// - public class ChatPacket + public class ChatInfo { // Private member variables #region Instance Variables @@ -22,7 +22,7 @@ namespace CS_3505_Project_06 /// - /// Get the game who sent the chat packet. + /// Get the sender who sent the chat packet. /// public NetworkGamer Sender { @@ -43,7 +43,7 @@ namespace CS_3505_Project_06 /// /// The chat sender. /// The chat message. - public ChatPacket(NetworkGamer sender, String message) + public ChatInfo(NetworkGamer sender, String message) { mSender = sender; mMessage = message; diff --git a/Project06/CS 3505 Project 06/CS 3505 Project 06/Game06.cs b/Project06/CS 3505 Project 06/CS 3505 Project 06/Game06.cs index 348655e..8953452 100644 --- a/Project06/CS 3505 Project 06/CS 3505 Project 06/Game06.cs +++ b/Project06/CS 3505 Project 06/CS 3505 Project 06/Game06.cs @@ -51,7 +51,6 @@ namespace CS_3505_Project_06 protected override void Initialize() { // Set a fixed time span of 1/60th of a second. - IsFixedTimeStep = true; TargetElapsedTime = networkGame.TargetTimeSpan; @@ -59,7 +58,6 @@ namespace CS_3505_Project_06 Mouse.SetPosition(400, 300); // Allow the base class to initialize. - base.Initialize(); } diff --git a/Project06/CS 3505 Project 06/CS 3505 Project 06/LobbyGUI.cs b/Project06/CS 3505 Project 06/CS 3505 Project 06/LobbyGUI.cs index 3c157dc..4526bef 100644 --- a/Project06/CS 3505 Project 06/CS 3505 Project 06/LobbyGUI.cs +++ b/Project06/CS 3505 Project 06/CS 3505 Project 06/LobbyGUI.cs @@ -254,11 +254,11 @@ namespace CS_3505_Project_06 } //allows host to start the game when all players are ready, change count below to different number for testing with less then 4 players - if(allReady && players.Count == 2 && localPlayer == players[0]) + if(allReady && players.Count == 1 && localPlayer == players[0]) { if (currentKeyboardState.IsKeyDown(Keys.B) && previousKeyboardState.IsKeyUp(Keys.B)) { - networkGame.StartGame(); + networkGame.ForceStartGame(); } } } diff --git a/Project06/CS 3505 Project 06/CS 3505 Project 06/NetworkGame.cs b/Project06/CS 3505 Project 06/CS 3505 Project 06/NetworkGame.cs index 03b6e90..3ce0372 100644 --- a/Project06/CS 3505 Project 06/CS 3505 Project 06/NetworkGame.cs +++ b/Project06/CS 3505 Project 06/CS 3505 Project 06/NetworkGame.cs @@ -8,6 +8,7 @@ using Microsoft.Xna.Framework.GamerServices; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Input; +using System.Collections; namespace CS_3505_Project_06 { @@ -17,6 +18,9 @@ namespace CS_3505_Project_06 /// public class NetworkGame { + // Public methods and properties + #region Public Methods + /// /// Called when a session has been created or joined using CreateSession() or JoinSession(). /// @@ -190,10 +194,23 @@ namespace CS_3505_Project_06 /// public void StartGame() { - Debug.Assert(mNetworkSession != null && mNetworkSession.IsHost); + 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(); + + Reset(); } @@ -210,34 +227,54 @@ namespace CS_3505_Project_06 else { mNetworkSession.Update(); - ReadPackets(); + HandleIncomingPackets(); if (mNetworkSession.SessionState == NetworkSessionState.Lobby) { - if (mNetworkSession.IsHost && - mNetworkSession.AllGamers.Count >= mGame.MinimumSupportedPlayers && - mNetworkSession.IsEveryoneReady) - { - mNetworkSession.StartGame(); - mNetworkSession.ResetReady(); - } - else - { - mLobby.Update(gameTime, this); - } + mLobby.Update(gameTime, this); } else if (mNetworkSession.SessionState == NetworkSessionState.Playing) { + if (mGame.IsGameOver(LocalGamerInfo) || mGame.IsTerminated(LocalGamerInfo)) + { + // TODO: Should support moving back to the session lobby. + LeaveSession(); + return; + } + if (HaveNeededEvents) { - if (IsLatencyAdjustmentFrame) AdjustLatency(); - mStallCount = 0; + if (IsLatencyAdjustmentFrame) + { + AdjustLatency(); + mStallCount = 0; + } + mLocalEvents.AddRange(GetEventsFromInput()); SendLocalEvents(); ApplyEvents(); mGame.Update(mTargetTimeSpan); } else // Stall! { + mStallCount++; + + if (mStallCount % 60 == 0) + { + Console.WriteLine("Stalled for " + mStallCount + " frames."); + } + + /*if (mStallCount > StallTimeout) + { + DropLostGamers(); + mStallCount = 0; + } + else if (mStallCount == 1) + { + SendLocalEvents + } + else if (mStallCount % 60 == 0) + { + } TODO */ } } } @@ -263,7 +300,7 @@ namespace CS_3505_Project_06 } else if (mNetworkSession.SessionState == NetworkSessionState.Playing) { - mLobby.Draw(spriteBatch); + mGame.Draw(spriteBatch); } } } @@ -274,10 +311,10 @@ namespace CS_3505_Project_06 /// method was called. /// /// List container of the chat messages. - public List ReceiveChats() + public List ReceiveChats() { - List chats = mChatPackets; - mChatPackets = new List(); + List chats = mChatPackets; + mChatPackets = new List(); return chats; } @@ -288,7 +325,7 @@ namespace CS_3505_Project_06 /// The text of the message. public void SendChat(String message) { - WriteChat(message); + WriteChatPacket(message); LocalGamer.SendData(mPacketWriter, SendDataOptions.ReliableInOrder); } @@ -300,10 +337,12 @@ namespace CS_3505_Project_06 /// The gamer to receive the message. public void SendChat(String message, NetworkGamer recipient) { - WriteChat(message); + WriteChatPacket(message); LocalGamer.SendData(mPacketWriter, SendDataOptions.ReliableInOrder, recipient); } + #endregion + // Private class variable members #region Instance Variables @@ -318,10 +357,17 @@ namespace CS_3505_Project_06 ILobby mLobby; IDeterministicGame mGame; - List mChatPackets = new List(); + List mChatPackets = new List(); + + List mLocalEvents = new List(); + List mLastLocalEvents = new List(); List mLastPressedKeys = new List(); - bool mLastButtonPressed; + bool mLastLeftButtonPressed; + bool mLastRightButtonPressed; + bool mLastMiddleButtonPressed; + int mLastMousePositionX; + int mLastMousePositionY; int mLatency; long mNextLatencyAdjustmentFrame; @@ -337,11 +383,32 @@ namespace CS_3505_Project_06 } } + 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 implementation methods of the network protocol - #region Private Implementation Methods + // Private types for the implementation of the network protocol + #region Private Types enum PacketType { @@ -359,161 +426,436 @@ namespace CS_3505_Project_06 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 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 new game to start. + /// Reinitialize the private variables in preparation for a new game to start. /// void Reset() { mLatency = 1; mNextLatencyAdjustmentFrame = 1; mStallCount = 0; - mAverageOwd = AverageOneWayDelay; + mAverageOwd = CurrentAverageOneWayDelay; + + mGamers = new Dictionary(); + foreach (NetworkGamer gamer in NetworkGamers) + { + mGamers.Add(gamer.Id, new GamerInfo(gamer)); + } - // TODO: The game object needs to be reset, too. - //mGame.ResetGame(playerIdentifiers, playerIdentifiers[0]); + mGame.ResetGame(GamerArray, LocalGamerInfo); } - /// - /// 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. - void ReadPackets() + void HandleIncomingPackets() { - foreach (LocalNetworkGamer gamer in mNetworkSession.LocalGamers) + LocalNetworkGamer localGamer = LocalGamer; + + while (localGamer.IsDataAvailable) { - while (gamer.IsDataAvailable) - { - NetworkGamer sender; + NetworkGamer sender; - gamer.ReceiveData(mPacketReader, out sender); - PacketType packetId = (PacketType)mPacketReader.ReadByte(); + localGamer.ReceiveData(mPacketReader, out sender); + GamerInfo senderInfo = mGamers[sender.Id]; - switch (packetId) - { - case PacketType.Chat: + PacketType packetId = (PacketType)mPacketReader.ReadByte(); + switch (packetId) + { + case PacketType.Chat: - short messageLength = mPacketReader.ReadInt16(); - char[] message = mPacketReader.ReadChars(messageLength); + short messageLength = mPacketReader.ReadInt16(); + char[] message = mPacketReader.ReadChars(messageLength); - ChatPacket chatPacket = new ChatPacket(sender, new String(message)); - mChatPackets.Add(chatPacket); - break; + ChatInfo chatPacket = new ChatInfo(sender, new String(message)); + mChatPackets.Add(chatPacket); + break; + + case PacketType.Event: - case PacketType.Event: + short stallCount = mPacketReader.ReadInt16(); + short averageOwd = mPacketReader.ReadInt16(); + int frameNumber = mPacketReader.ReadInt32(); + byte numEvents = mPacketReader.ReadByte(); - short stallCount = mPacketReader.ReadInt16(); - short averageOwd = mPacketReader.ReadInt16(); - int frameNumber = mPacketReader.ReadInt32(); - byte numEvents = mPacketReader.ReadByte(); + if (frameNumber <= senderInfo.HighestFrameNumber) + { + // we know about all these events, so don't bother reading them + break; + } - for (byte i = 0; i < numEvents; ++i) + for (byte i = 0; i < numEvents; ++i) + { + EventInfo eventInfo = ReadEvent(mPacketReader, sender); + + if (eventInfo != null && eventInfo.FrameOfApplication < senderInfo.HighestFrameNumber) { - ReadEvent(mPacketReader, sender); + int index = EventArrayIndex; + if (senderInfo.Events[index] == null) senderInfo.Events[index] = new List(); + senderInfo.Events[index].Add(eventInfo); } + } - break; + senderInfo.StallCount = stallCount; + senderInfo.AverageOwd = averageOwd; + senderInfo.HighestFrameNumber = frameNumber; + break; - case PacketType.Stall: + case PacketType.Stall: - byte numStalledPeers = mPacketReader.ReadByte(); - byte[] stalledPeers = mPacketReader.ReadBytes(numStalledPeers); + byte numStalledPeers = mPacketReader.ReadByte(); + byte[] stalledPeers = mPacketReader.ReadBytes(numStalledPeers); - break; - } + // TODO + + break; + + default: + + Console.WriteLine("Received unknown packet type: " + (int)packetId); + break; } } } - void ReadEvent(PacketReader packetReader, NetworkGamer sender) + + int EventArrayIndex + { + get { return (int)(mGame.CurrentFrameNumber % MaximumLatency); } + } + + EventInfo ReadEvent(PacketReader packetReader, NetworkGamer sender) { EventType eventId = (EventType)packetReader.ReadByte(); - long applicationFrame = packetReader.ReadInt32(); + long frameNumber = packetReader.ReadInt32(); switch (eventId) { case EventType.KeyDown: int keyCode1 = packetReader.ReadInt32(); - - break; + return new KeyboardEventInfo(sender, frameNumber, (Keys)keyCode1, true); case EventType.KeyUp: int keyCode2 = packetReader.ReadInt32(); - - break; + return new KeyboardEventInfo(sender, frameNumber, (Keys)keyCode2, false); case EventType.MouseDown: byte buttonId1 = packetReader.ReadByte(); - - break; + return new MouseButtonEventInfo(sender, frameNumber, (MouseButton)buttonId1, true); case EventType.MouseUp: byte buttonId2 = packetReader.ReadByte(); - - break; + return new MouseButtonEventInfo(sender, frameNumber, (MouseButton)buttonId2, false); case EventType.MouseMove: short x = packetReader.ReadInt16(); short y = packetReader.ReadInt16(); + return new MouseMotionEventInfo(sender, frameNumber, x, y); - break; + default: + + Console.WriteLine("Received unknown event type: " + (int)eventId); + return null; } } - void WriteChat(String message) + + void WriteChatPacket(String message) { mPacketWriter.Write((byte)PacketType.Chat); mPacketWriter.Write((short)message.Length); mPacketWriter.Write(message.ToCharArray()); } + void WriteEventPacket(List events) + { + mPacketWriter.Write((byte)PacketType.Event); + mPacketWriter.Write((short)mStallCount); + mPacketWriter.Write((short)mAverageOwd); + mPacketWriter.Write((int)(mGame.CurrentFrameNumber + mLatency)); + 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 { - // TODO - return false; + return mNextLatencyAdjustmentFrame == mGame.CurrentFrameNumber; } } void AdjustLatency() { - // TODO + Debug.Assert(IsLatencyAdjustmentFrame); + + 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; + } + + // DEBUG + int prevLatency = mLatency; + + if (maxStallCount > 0) + { + mLatency += maxStallCount; + } + else + { + mLatency = (int)(0.6 * (double)(mLatency - maxAverageOwd) + 1.0); + } + + // DEBUG OUTPUT + if (prevLatency != mLatency) Console.WriteLine("Latency readjusted to " + mLatency); + + if (mLatency < 1) mLatency = 1; + if (mLatency > MaximumLatency) mLatency = MaximumLatency; + + mNextLatencyAdjustmentFrame = mGame.CurrentFrameNumber + mLatency; + mAverageOwd = CurrentAverageOneWayDelay; + + mLastLocalEvents = mLocalEvents; + mLocalEvents = new List(); } - void SendLocalEvents() + + List GetEventsFromInput() { - // TODO: Not finished. + List events = new List(); - KeyboardState keyState = Keyboard.GetState(); - MouseState mouseState = Mouse.GetState(); + // 1. Find the keyboard differences; written by Peter. - // Make a list of the keys pressed or released this frame. + 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); + { + if (!mLastPressedKeys.Contains(k)) pressedKeys.Add(k); + else mLastPressedKeys.Remove(k); + } releasedKeys = mLastPressedKeys; + + foreach (Keys key in pressedKeys) + { + events.Add(new KeyboardEventInfo(LocalGamer, mGame.CurrentFrameNumber, key, true)); + } + foreach (Keys key in releasedKeys) + { + events.Add(new KeyboardEventInfo(LocalGamer, mGame.CurrentFrameNumber, key, false)); + } + + // 2. Find the mouse differences. + + MouseState mouseState = Mouse.GetState(); + + bool leftButtonPressed = mouseState.LeftButton == ButtonState.Pressed; + if (leftButtonPressed != mLastLeftButtonPressed) + { + events.Add(new MouseButtonEventInfo(LocalGamer, mGame.CurrentFrameNumber, MouseButton.Left, leftButtonPressed)); + } + + bool rightButtonPressed = mouseState.LeftButton == ButtonState.Pressed; + if (rightButtonPressed != mLastRightButtonPressed) + { + events.Add(new MouseButtonEventInfo(LocalGamer, mGame.CurrentFrameNumber, MouseButton.Right, rightButtonPressed)); + } + + bool middleButtonPressed = mouseState.LeftButton == ButtonState.Pressed; + if (middleButtonPressed != mLastMiddleButtonPressed) + { + events.Add(new MouseButtonEventInfo(LocalGamer, mGame.CurrentFrameNumber, MouseButton.Middle, middleButtonPressed)); + } + + int mousePositionX = mouseState.X; + int mousePositionY = mouseState.Y; + if (mousePositionX != mLastMousePositionX || mousePositionY != mLastMousePositionY) + { + events.Add(new MouseMotionEventInfo(LocalGamer, mGame.CurrentFrameNumber, mousePositionX, mousePositionY)); + } + + // 3. Save the current peripheral state. + mLastPressedKeys = new List(pressedKeysArray); + mLastLeftButtonPressed = leftButtonPressed; + mLastRightButtonPressed = rightButtonPressed; + mLastMiddleButtonPressed = middleButtonPressed; + mLastMousePositionX = mousePositionX; + mLastMousePositionY = mousePositionY; - bool buttonPressed = mouseState.LeftButton == ButtonState.Pressed; + return events; + } + + void SendLocalEvents() + { + SendLocalEvents((NetworkGamer)null); + } + + void SendLocalEvents(List recipicents) + { + foreach (NetworkGamer gamer in recipicents) + { + SendLocalEvents(gamer); + } + } + + void SendLocalEvents(NetworkGamer recipient) + { + List events = new List(mLocalEvents); + events.AddRange(mLastLocalEvents); + + WriteEventPacket(events); + + if (recipient != null) + { + LocalGamer.SendData(mPacketWriter, SendDataOptions.Reliable, recipient); + } + else + { + LocalGamer.SendData(mPacketWriter, SendDataOptions.None); + } } @@ -521,23 +863,70 @@ namespace CS_3505_Project_06 { get { - // TODO + long currentFrame = mGame.CurrentFrameNumber; + + foreach (GamerInfo gamerInfo in mGamers.Values) + { + if (gamerInfo.HighestFrameNumber < currentFrame) return false; + } + return true; } } void ApplyEvents() { - // TODO + int index = EventArrayIndex; + + foreach (GamerInfo gamerInfo in GamerArray) + { + if (gamerInfo.Events[index] == null) continue; + + foreach (EventInfo eventInfo in gamerInfo.Events[index]) + { + KeyboardEventInfo keyboardEventInfo = eventInfo as KeyboardEventInfo; + if (keyboardEventInfo != null) + { + mGame.ApplyKeyInput(gamerInfo, keyboardEventInfo.Key, keyboardEventInfo.IsKeyDown); + continue; + } + + MouseButtonEventInfo mouseButtonEventInfo = eventInfo as MouseButtonEventInfo; + if (mouseButtonEventInfo != null) + { + mGame.ApplyMouseButtonInput(gamerInfo, mouseButtonEventInfo.IsButtonDown); + continue; + } + + MouseMotionEventInfo mouseMotionEventInfo = eventInfo as MouseMotionEventInfo; + if (mouseMotionEventInfo != null) + { + mGame.ApplyMouseLocationInput(gamerInfo, mouseMotionEventInfo.X, mouseMotionEventInfo.Y); + continue; + } + } + + gamerInfo.Events[index] = null; + } } - int AverageOneWayDelay + int CurrentAverageOneWayDelay { get { - // TODO - return 12; + 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); } }