2 // Make sure DEBUG is undefined when turning in the project
3 // or the grader will wonder why it's so laggy.
7 using System.Collections.Generic;
10 using Microsoft.Xna.Framework.Net;
11 using System.Diagnostics;
12 using Microsoft.Xna.Framework.GamerServices;
13 using Microsoft.Xna.Framework.Graphics;
14 using Microsoft.Xna.Framework;
15 using Microsoft.Xna.Framework.Input;
16 using System.Collections;
18 namespace CS_3505_Project_06
21 /// A manager class to handle network interactions between peers and
22 /// lobby/game switching.
24 public class NetworkGame
26 // Public methods and properties
27 #region Public Methods
30 /// Called when a session has been created or joined using CreateSession() or JoinSession().
32 /// <param name="session">The new session that was created or joined.</param>
33 /// <param name="networkGame">The NetworkGame that joined the session.</param>
34 public delegate void JoinedSessionDelegate(NetworkSession session, NetworkGame networkGame);
37 /// Called when sessions are found as a result of calling FindSessions().
39 /// <param name="sessions">A container of the available sessions.</param>
40 /// <param name="networkGame">The NetworkGame that searched for the sessions.</param>
41 public delegate void FoundSessionsDelegate(AvailableNetworkSessionCollection sessions, NetworkGame networkGame);
45 /// Called when an exception is thrown during an asynchronous operation.
47 /// <param name="exception">The exception that was thrown.</param>
48 /// <param name="networkGame">The NetworkGame that errored.</param>
49 public delegate void CaughtErrorDelegate(Exception exception, NetworkGame networkGame);
52 /// Get and set the error delegate, called when an exception is thrown during
53 /// and asynchronous operation. This will occur if you try to create or join a
54 /// session without being logged into a profile.
56 public CaughtErrorDelegate ErrorDelegate;
60 /// Construct a NetworkGame with a lobby and a game.
62 /// <param name="lobby">Provides an associated lobby to update and draw.</param>
63 /// <param name="game">Provides a game object to be played over the network.</param>
64 public NetworkGame(ILobby lobby, IDeterministicGame game)
66 Debug.Assert(lobby != null && game != null);
74 /// Get the Gamer object for the local player.
76 public LocalNetworkGamer LocalGamer
80 // TODO: Is this the correct way to get the single local gamer?
81 return mNetworkSession.LocalGamers[0];
86 /// Get all the gamers associated with the active network session.
88 public GamerCollection<NetworkGamer> NetworkGamers
92 return mNetworkSession.AllGamers;
98 /// Begin a new network session with the local gamer as the host. You must not
99 /// call this method or use JoinSession without first using LeaveSession.
101 /// <param name="callback">The delegate/method to call when the session is created.</param>
102 public void CreateSession(JoinedSessionDelegate callback)
104 CreateSession(mGame.MaximumSupportedPlayers, callback);
108 /// Begin a new network session with the local gamer as the host. You must not
109 /// call this method or use JoinSession without first using LeaveSession.
111 /// <param name="maxGamers">Provide the maximum number of players allowed to connect.</param>
112 /// <param name="callback">The delegate/method to call when the session is created.</param>
113 public void CreateSession(int maxGamers, JoinedSessionDelegate callback)
115 Debug.Assert(mNetworkSession == null);
117 mJoinedSessionDelegate = callback;
118 NetworkSession.BeginCreate(NetworkSessionType.SystemLink, 1, maxGamers, CreateSessionEnd, null);
120 void CreateSessionEnd(IAsyncResult result)
122 Debug.Assert(mNetworkSession == null);
126 mNetworkSession = NetworkSession.EndCreate(result);
127 mNetworkSession.AllowHostMigration = true;
128 mNetworkSession.AllowJoinInProgress = false;
129 mNetworkSession.GameStarted += new EventHandler<GameStartedEventArgs>(GameStartedEvent);
133 if (ErrorDelegate != null) ErrorDelegate(e, this);
136 mJoinedSessionDelegate(mNetworkSession, this);
137 mJoinedSessionDelegate = null;
139 void GameStartedEvent(object sender, GameStartedEventArgs e)
145 /// Determine whether or not the network game object is associated with any network session.
147 /// <returns>True if there exists a NetworkSession; false otherwise.</returns>
148 public bool HasActiveSession
152 return mNetworkSession != null;
158 /// Find available sessions to join. You should not already be in a session when
159 /// calling this method; call LeaveSession first.
161 /// <param name="callback">The delegate/method to call when the search finishes.</param>
162 public void FindSessions(FoundSessionsDelegate callback)
164 Debug.Assert(mNetworkSession == null);
166 mFoundSessionsDelegate = callback;
167 NetworkSession.BeginFind(NetworkSessionType.SystemLink, 1, null, new AsyncCallback(FindSessionsEnd), null);
169 void FindSessionsEnd(IAsyncResult result)
171 AvailableNetworkSessionCollection sessions;
174 sessions = NetworkSession.EndFind(result);
178 if (ErrorDelegate != null) ErrorDelegate(e, this);
181 mFoundSessionsDelegate(sessions, this);
182 mFoundSessionsDelegate = null;
186 /// Join a network session found using FindSessions(). This is for joining a game that
187 /// somebody else has already started hosting. You must not already be in a session.
189 /// <param name="availableSession">Pass the session object to try to join.</param>
190 /// <param name="callback">The delegate/method to call when the search finishes.</param>
191 public void JoinSession(AvailableNetworkSession availableSession, JoinedSessionDelegate callback)
193 Debug.Assert(mNetworkSession == null);
195 mJoinedSessionDelegate = callback;
196 NetworkSession.BeginJoin(availableSession, JoinSessionEnd, null);
198 void JoinSessionEnd(IAsyncResult result)
200 Debug.Assert(mNetworkSession == null);
204 mNetworkSession = NetworkSession.EndJoin(result);
205 mNetworkSession.GameStarted += new EventHandler<GameStartedEventArgs>(GameStartedEvent);
209 if (ErrorDelegate != null) ErrorDelegate(e, this);
212 mJoinedSessionDelegate(mNetworkSession, this);
213 mJoinedSessionDelegate = null;
218 /// Leave and dispose of any currently associated network session. You will find yourself
219 /// back in the lobby. You must already be in a session to leave it.
221 public void LeaveSession()
223 Debug.Assert(mNetworkSession != null);
225 mNetworkSession.Dispose();
226 mNetworkSession = null;
231 /// Set up the network session to simulate 200ms latency and 10% packet loss.
233 public void SimulateBadNetwork()
235 Debug.Assert(mNetworkSession != null);
237 mNetworkSession.SimulatedLatency = new TimeSpan(0, 0, 0, 0, 200);
238 mNetworkSession.SimulatedPacketLoss = 0.1f;
243 /// Indicate that the game should begin (moving players from the lobby to the game).
244 /// You must call CreateSession() before calling this.
246 public void StartGame()
248 Debug.Assert(mNetworkSession != null && mNetworkSession.IsHost &&
249 mNetworkSession.AllGamers.Count >= mGame.MinimumSupportedPlayers &&
250 mNetworkSession.IsEveryoneReady);
256 /// Indicate that the game should begin. This is like StartGame() without the sanity
257 /// checks. Use this for debugging.
259 public void ForceStartGame()
261 mNetworkSession.StartGame();
262 mNetworkSession.ResetReady();
267 /// Manages the network session and allows either the lobby or game to update.
269 /// <param name="gameTime">Pass the time away.</param>
270 public void Update(GameTime gameTime)
272 if (mNetworkSession == null)
274 mLobby.Update(gameTime, this);
278 mNetworkSession.Update();
279 HandleIncomingPackets();
281 if (mNetworkSession.SessionState == NetworkSessionState.Lobby)
283 mLobby.Update(gameTime, this);
285 else if (mNetworkSession.SessionState == NetworkSessionState.Playing)
287 if (mGame.IsTerminated(LocalGamerInfo))
292 else if (mGame.IsGameOver(LocalGamerInfo))
294 ApplyEvents(LocalGamerInfo, GetEventsFromInput());
295 mGame.Update(mTargetTimeSpan);
299 if (HaveNeededEvents)
301 if (IsLatencyAdjustmentFrame)
304 mLastStallCount = mStallCount;
307 mLocalEvents.AddRange(GetEventsFromInput());
312 Console.WriteLine("HASH: " + mGame.CurrentFrameNumber + "\t" + mGame.CurrentChecksum);
315 mGame.Update(mTargetTimeSpan);
321 // Send a reliable event packet to each stalled gamer.
322 if (mStallCount == 1)
325 Console.WriteLine("STAL: ====");
328 foreach (GamerInfo gamerInfo in GamerArray)
330 if (gamerInfo.HighestFrameNumber < mGame.CurrentFrameNumber)
332 SendLocalEvents(gamerInfo.Gamer);
336 else if (mStallCount > 600)
338 Console.WriteLine("One or more players have stalled excessively. Leaving session...");
347 /// Allows either the lobby or the game to draw, depending on the state
348 /// of the network connection and whether or not a game is in progress.
350 /// <param name="gameTime">Pass the time away.</param>
351 /// <param name="spriteBatch">The sprite batch.</param>
352 public void Draw(GameTime gameTime, SpriteBatch spriteBatch)
354 if (mNetworkSession == null)
356 mLobby.Draw(spriteBatch);
360 if (mNetworkSession.SessionState == NetworkSessionState.Lobby)
362 mLobby.Draw(spriteBatch);
364 else if (mNetworkSession.SessionState == NetworkSessionState.Playing)
366 mGame.Draw(spriteBatch);
373 /// Get the chat messages that have been received since the last time this
374 /// method was called.
376 /// <returns>List container of the chat messages.</returns>
377 public List<ChatInfo> ReceiveChats()
379 List<ChatInfo> chats = mChatPackets;
380 mChatPackets = new List<ChatInfo>();
385 /// Send a chat message to all gamers in the session. You should already be
386 /// in a session before calling this method.
388 /// <param name="message">The text of the message.</param>
389 public void SendChat(String message)
391 WriteChatPacket(message);
392 LocalGamer.SendData(mPacketWriter, SendDataOptions.ReliableInOrder);
396 /// Send a chat message to a specific gamer in the session. You should already
397 /// be in a session before calling this method.
399 /// <param name="message">The text of the message.</param>
400 /// <param name="recipient">The gamer to receive the message.</param>
401 public void SendChat(String message, NetworkGamer recipient)
403 Debug.Assert(recipient != null && !recipient.IsDisposed);
405 WriteChatPacket(message);
406 LocalGamer.SendData(mPacketWriter, SendDataOptions.ReliableInOrder, recipient);
412 // Private class variable members
413 #region Instance Variables
415 NetworkSession mNetworkSession;
416 PacketReader mPacketReader = new PacketReader();
417 PacketWriter mPacketWriter = new PacketWriter();
419 JoinedSessionDelegate mJoinedSessionDelegate;
420 FoundSessionsDelegate mFoundSessionsDelegate;
423 IDeterministicGame mGame;
425 List<ChatInfo> mChatPackets = new List<ChatInfo>();
427 List<EventInfo> mLocalEvents = new List<EventInfo>();
428 List<EventInfo> mLastLocalEvents = new List<EventInfo>();
430 List<Keys> mLastPressedKeys = new List<Keys>();
431 bool mLastLeftButtonPressed;
432 bool mLastRightButtonPressed;
433 bool mLastMiddleButtonPressed;
434 int mLastMousePositionX;
435 int mLastMousePositionY;
438 long mHighestFrameNumber;
439 long mNextLatencyAdjustmentFrame;
445 bool mDontSendEvents;
448 TimeSpan mTargetTimeSpan = new TimeSpan(166666);
449 public TimeSpan TargetTimeSpan
453 return mTargetTimeSpan;
457 Dictionary<byte, GamerInfo> mGamers;
458 GamerInfo[] GamerArray
462 GamerInfo[] gamerList = mGamers.Values.ToArray();
463 Array.Sort(gamerList, delegate(GamerInfo a, GamerInfo b)
465 return a.Gamer.Id.CompareTo(b.Gamer.Id);
470 GamerInfo LocalGamerInfo
474 return mGamers[LocalGamer.Id];
481 // Private types for the implementation of the network protocol
482 #region Private Types
506 abstract class EventInfo
508 public NetworkGamer Gamer;
509 public long FrameOfApplication;
511 public EventInfo(NetworkGamer gamer, long frameNumber)
514 FrameOfApplication = frameNumber;
517 public abstract EventType Id
523 class KeyboardEventInfo : EventInfo
526 public bool IsKeyDown;
528 public KeyboardEventInfo(NetworkGamer gamer, long frameNumber, Keys key, bool isDown)
529 : base(gamer, frameNumber)
535 public override EventType Id
537 get { return IsKeyDown ? EventType.KeyDown : EventType.KeyUp; }
541 class MouseButtonEventInfo : EventInfo
543 public MouseButton Button;
544 public bool IsButtonDown;
546 public MouseButtonEventInfo(NetworkGamer gamer, long frameNumber, MouseButton button, bool isDown)
547 : base(gamer, frameNumber)
550 IsButtonDown = isDown;
553 public override EventType Id
555 get { return IsButtonDown ? EventType.MouseDown : EventType.MouseUp; }
559 class MouseMotionEventInfo : EventInfo
564 public MouseMotionEventInfo(NetworkGamer gamer, long frameNumber, int x, int y)
565 : base(gamer, frameNumber)
571 public override EventType Id
573 get { return EventType.MouseMove; }
579 public NetworkGamer Gamer;
580 public long HighestFrameNumber = 0;
581 public int StallCount = 0;
582 public int AverageOwd = 0;
583 public int NextStallCount = 0;
584 public int NextAverageOwd = 0;
585 public bool IsWaitedOn = false;
586 public List<EventInfo>[] Events = new List<EventInfo>[MaximumLatency];
588 public GamerInfo(NetworkGamer gamer)
594 const int MaximumLatency = 120;
595 const int StallTimeout = 900;
600 // Private implementation methods of the network protocol
601 #region Private Implementation Methods
604 /// Reinitialize the private variables in preparation for a new game to start.
609 mHighestFrameNumber = 0;
610 mNextLatencyAdjustmentFrame = 1;
613 mAverageOwd = CurrentAverageOneWayDelay;
615 mGamers = new Dictionary<byte, GamerInfo>();
616 foreach (NetworkGamer gamer in NetworkGamers)
618 mGamers.Add(gamer.Id, new GamerInfo(gamer));
621 mGame.ResetGame(GamerArray, LocalGamerInfo);
625 void HandleIncomingPackets()
627 LocalNetworkGamer localGamer = LocalGamer;
629 while (localGamer.IsDataAvailable)
633 localGamer.ReceiveData(mPacketReader, out sender);
635 PacketType packetId = (PacketType)mPacketReader.ReadByte();
638 case PacketType.Chat:
640 short messageLength = mPacketReader.ReadInt16();
641 char[] message = mPacketReader.ReadChars(messageLength);
643 ChatInfo chatPacket = new ChatInfo(sender, new String(message));
644 mChatPackets.Add(chatPacket);
647 case PacketType.Event:
649 GamerInfo senderInfo = mGamers[sender.Id];
651 int stallCount = mPacketReader.ReadInt16();
652 int averageOwd = mPacketReader.ReadInt16();
653 int frameNumber = mPacketReader.ReadInt32();
654 int numEvents = mPacketReader.ReadByte();
656 if (frameNumber <= mNextLatencyAdjustmentFrame)
658 senderInfo.StallCount = stallCount;
659 senderInfo.AverageOwd = averageOwd;
663 senderInfo.NextStallCount = stallCount;
664 senderInfo.NextAverageOwd = averageOwd;
667 if (frameNumber <= senderInfo.HighestFrameNumber)
670 Console.WriteLine("SKP" + (char)sender.Id + ": " + mGame.CurrentFrameNumber + "\t" + frameNumber + "\t<=\t" + senderInfo.HighestFrameNumber + "\t#" + numEvents);
673 // we know about all these events, so don't bother reading them
678 Console.WriteLine(" GOT" + (char)sender.Id + ": " + mGame.CurrentFrameNumber + "\t" + frameNumber + "\t>\t" + senderInfo.HighestFrameNumber + "\t#" + numEvents);
681 for (int i = 0; i < numEvents; i++)
683 EventInfo eventInfo = ReadEvent(mPacketReader, sender);
685 if (eventInfo != null && eventInfo.FrameOfApplication > senderInfo.HighestFrameNumber)
687 int index = GetEventArrayIndexForFrame(eventInfo.FrameOfApplication);
688 if (senderInfo.Events[index] == null) senderInfo.Events[index] = new List<EventInfo>();
689 senderInfo.Events[index].Add(eventInfo);
693 senderInfo.HighestFrameNumber = frameNumber;
698 Console.WriteLine("Received unknown packet type: " + (int)packetId);
705 int CurrentEventArrayIndex
707 get { return GetEventArrayIndexForFrame(mGame.CurrentFrameNumber); }
710 int GetEventArrayIndexForFrame(long frame)
712 return (int)(frame % MaximumLatency);
715 EventInfo ReadEvent(PacketReader packetReader, NetworkGamer sender)
717 EventType eventId = (EventType)packetReader.ReadByte();
718 long frameNumber = packetReader.ReadInt32();
722 case EventType.KeyDown:
724 Keys keyCode1 = (Keys)packetReader.ReadInt32();
725 return new KeyboardEventInfo(sender, frameNumber, keyCode1, true);
727 case EventType.KeyUp:
729 Keys keyCode2 = (Keys)packetReader.ReadInt32();
730 return new KeyboardEventInfo(sender, frameNumber, keyCode2, false);
732 case EventType.MouseDown:
734 MouseButton buttonId1 = (MouseButton)packetReader.ReadByte();
735 return new MouseButtonEventInfo(sender, frameNumber, buttonId1, true);
737 case EventType.MouseUp:
739 MouseButton buttonId2 = (MouseButton)packetReader.ReadByte();
740 return new MouseButtonEventInfo(sender, frameNumber, buttonId2, false);
742 case EventType.MouseMove:
744 short x = packetReader.ReadInt16();
745 short y = packetReader.ReadInt16();
746 return new MouseMotionEventInfo(sender, frameNumber, x, y);
750 Console.WriteLine("Received unknown event type: " + (int)eventId);
756 void WriteChatPacket(String message)
758 mPacketWriter.Write((byte)PacketType.Chat);
759 mPacketWriter.Write((short)message.Length);
760 mPacketWriter.Write(message.ToCharArray());
763 void WriteEventPacket(List<EventInfo> events, long highestFrameNumber)
765 mPacketWriter.Write((byte)PacketType.Event);
766 mPacketWriter.Write((short)mLastStallCount);
767 mPacketWriter.Write((short)mAverageOwd);
768 mPacketWriter.Write((int)highestFrameNumber);
769 mPacketWriter.Write((byte)events.Count);
771 foreach (EventInfo eventInfo in events)
773 mPacketWriter.Write((byte)eventInfo.Id);
774 mPacketWriter.Write((int)eventInfo.FrameOfApplication);
776 KeyboardEventInfo keyboardEventInfo = eventInfo as KeyboardEventInfo;
777 if (keyboardEventInfo != null)
779 mPacketWriter.Write((int)keyboardEventInfo.Key);
783 MouseButtonEventInfo mouseButtonEventInfo = eventInfo as MouseButtonEventInfo;
784 if (mouseButtonEventInfo != null)
786 mPacketWriter.Write((byte)mouseButtonEventInfo.Button);
790 MouseMotionEventInfo mouseMotionEventInfo = eventInfo as MouseMotionEventInfo;
791 if (mouseMotionEventInfo != null)
793 mPacketWriter.Write((short)mouseMotionEventInfo.X);
794 mPacketWriter.Write((short)mouseMotionEventInfo.Y);
801 bool IsLatencyAdjustmentFrame
805 return mNextLatencyAdjustmentFrame == mGame.CurrentFrameNumber;
811 Debug.Assert(IsLatencyAdjustmentFrame);
816 Console.WriteLine("STL#: " + mGame.CurrentFrameNumber + "\t" + mStallCount);
820 int maxStallCount = 0;
821 int maxAverageOwd = 0;
823 foreach (GamerInfo gamerInfo in GamerArray)
825 if (gamerInfo.StallCount > maxStallCount) maxStallCount = gamerInfo.StallCount;
826 if (gamerInfo.AverageOwd > maxAverageOwd) maxAverageOwd = gamerInfo.AverageOwd;
828 gamerInfo.StallCount = gamerInfo.NextStallCount;
829 gamerInfo.AverageOwd = gamerInfo.NextAverageOwd;
833 int prevLatency = mLatency;
836 if (maxStallCount > 0)
838 mLatency += maxStallCount;
842 mLatency -= (int)(0.6 * (double)(mLatency - maxAverageOwd) + 1.0);
845 if (mLatency < 1) mLatency = 1;
846 if (mLatency > MaximumLatency) mLatency = MaximumLatency;
849 if (prevLatency != mLatency) Console.WriteLine("NLAG: " + mLatency);
852 mNextLatencyAdjustmentFrame = mGame.CurrentFrameNumber + mLatency;
853 mAverageOwd = CurrentAverageOneWayDelay;
855 mLastLocalEvents = mLocalEvents;
856 mLocalEvents = new List<EventInfo>();
861 List<EventInfo> GetEventsFromInput()
863 List<EventInfo> events = new List<EventInfo>();
865 long frameOfApplication = mGame.CurrentFrameNumber + mLatency;
866 if (frameOfApplication <= mHighestFrameNumber) return events;
867 else mHighestFrameNumber = frameOfApplication;
869 // 1. Find the keyboard differences; written by Peter.
871 KeyboardState keyState = Keyboard.GetState();
873 List<Keys> pressedKeys = new List<Keys>();
874 List<Keys> releasedKeys = new List<Keys>();
876 Keys[] pressedKeysArray = keyState.GetPressedKeys();
877 foreach (Keys k in pressedKeysArray)
879 if (!mLastPressedKeys.Contains(k)) pressedKeys.Add(k);
880 else mLastPressedKeys.Remove(k);
883 releasedKeys = mLastPressedKeys;
885 foreach (Keys key in pressedKeys)
887 events.Add(new KeyboardEventInfo(LocalGamer, frameOfApplication, key, true));
889 foreach (Keys key in releasedKeys)
891 events.Add(new KeyboardEventInfo(LocalGamer, frameOfApplication, key, false));
895 if (pressedKeys.Contains(Keys.Escape)) mDontSendEvents = true;
896 if (releasedKeys.Contains(Keys.Escape)) mDontSendEvents = false;
899 // 2. Find the mouse differences.
901 MouseState mouseState = Mouse.GetState();
903 bool leftButtonPressed = mouseState.LeftButton == ButtonState.Pressed;
904 if (leftButtonPressed != mLastLeftButtonPressed)
906 events.Add(new MouseButtonEventInfo(LocalGamer, frameOfApplication, MouseButton.Left, leftButtonPressed));
909 bool rightButtonPressed = mouseState.RightButton == ButtonState.Pressed;
910 if (rightButtonPressed != mLastRightButtonPressed)
912 events.Add(new MouseButtonEventInfo(LocalGamer, frameOfApplication, MouseButton.Right, rightButtonPressed));
915 bool middleButtonPressed = mouseState.MiddleButton == ButtonState.Pressed;
916 if (middleButtonPressed != mLastMiddleButtonPressed)
918 events.Add(new MouseButtonEventInfo(LocalGamer, frameOfApplication, MouseButton.Middle, middleButtonPressed));
921 int mousePositionX = mouseState.X;
922 int mousePositionY = mouseState.Y;
923 if (mousePositionX != mLastMousePositionX || mousePositionY != mLastMousePositionY)
925 events.Add(new MouseMotionEventInfo(LocalGamer, frameOfApplication, mousePositionX, mousePositionY));
928 // 3. Save the current peripheral state.
930 mLastPressedKeys = new List<Keys>(pressedKeysArray);
931 mLastLeftButtonPressed = leftButtonPressed;
932 mLastRightButtonPressed = rightButtonPressed;
933 mLastMiddleButtonPressed = middleButtonPressed;
934 mLastMousePositionX = mousePositionX;
935 mLastMousePositionY = mousePositionY;
940 void SendLocalEvents()
942 SendLocalEvents((NetworkGamer)null);
945 void SendLocalEvents(List<NetworkGamer> recipicents)
947 foreach (NetworkGamer gamer in recipicents)
949 SendLocalEvents(gamer);
953 void SendLocalEvents(NetworkGamer recipient)
956 if (mDontSendEvents) return;
959 List<EventInfo> events = new List<EventInfo>(mLocalEvents);
960 events.AddRange(mLastLocalEvents);
962 if (recipient != null && !recipient.IsDisposed)
964 // if there is a recipient, we are resending old events
965 WriteEventPacket(events, mGame.CurrentFrameNumber - 1);
966 LocalGamer.SendData(mPacketWriter, SendDataOptions.Reliable, recipient);
970 WriteEventPacket(events, mGame.CurrentFrameNumber + mLatency);
971 LocalGamer.SendData(mPacketWriter, SendDataOptions.None);
976 bool HaveNeededEvents
980 long currentFrame = mGame.CurrentFrameNumber;
982 foreach (GamerInfo gamerInfo in mGamers.Values)
984 if (mGame.IsGameOver(gamerInfo)) continue;
985 if (gamerInfo.HighestFrameNumber < currentFrame) return false;
994 int index = CurrentEventArrayIndex;
996 foreach (GamerInfo gamerInfo in GamerArray)
998 if (gamerInfo.Events[index] == null) continue;
999 ApplyEvents(gamerInfo, gamerInfo.Events[index]);
1000 gamerInfo.Events[index] = null;
1004 void ApplyEvents(GamerInfo gamerInfo, List<EventInfo> events)
1006 foreach (EventInfo eventInfo in events)
1008 KeyboardEventInfo keyboardEventInfo = eventInfo as KeyboardEventInfo;
1009 if (keyboardEventInfo != null)
1012 Console.WriteLine(" KEY: " + keyboardEventInfo.FrameOfApplication + "\t" + keyboardEventInfo.Key + "," + keyboardEventInfo.IsKeyDown);
1015 mGame.ApplyKeyInput(gamerInfo, keyboardEventInfo.Key, keyboardEventInfo.IsKeyDown);
1019 MouseButtonEventInfo mouseButtonEventInfo = eventInfo as MouseButtonEventInfo;
1020 if (mouseButtonEventInfo != null)
1023 Console.WriteLine(" BTN: " + mouseButtonEventInfo.FrameOfApplication + "\t" + mouseButtonEventInfo.IsButtonDown);
1026 mGame.ApplyMouseButtonInput(gamerInfo, mouseButtonEventInfo.IsButtonDown);
1030 MouseMotionEventInfo mouseMotionEventInfo = eventInfo as MouseMotionEventInfo;
1031 if (mouseMotionEventInfo != null)
1034 Console.WriteLine(" MMV: " + mouseMotionEventInfo.FrameOfApplication + "\t" + mouseMotionEventInfo.X + "," + mouseMotionEventInfo.Y);
1037 mGame.ApplyMouseLocationInput(gamerInfo, mouseMotionEventInfo.X, mouseMotionEventInfo.Y);
1044 int CurrentAverageOneWayDelay
1048 Debug.Assert(mNetworkSession != null);
1050 double numRemoteGamersTwice = 2 * mNetworkSession.RemoteGamers.Count;
1051 double averageOwd = 0;
1053 foreach (NetworkGamer gamer in mNetworkSession.RemoteGamers)
1055 TimeSpan timeSpan = gamer.RoundtripTime;
1056 averageOwd += timeSpan.TotalMilliseconds;
1059 return (int)((averageOwd / numRemoteGamersTwice) / 16.6666);