2 using System.Collections.Generic;
5 using Microsoft.Xna.Framework.Net;
6 using System.Diagnostics;
7 using Microsoft.Xna.Framework.GamerServices;
8 using Microsoft.Xna.Framework.Graphics;
9 using Microsoft.Xna.Framework;
10 using Microsoft.Xna.Framework.Input;
12 namespace CS_3505_Project_06
15 /// A manager class to handle network interactions between peers and
16 /// lobby/game switching.
18 public class NetworkGame
20 // Private class variable members
21 #region Instance Variables
23 NetworkSession mNetworkSession;
25 JoinedSessionDelegate mJoinedSessionDelegate;
26 FoundSessionsDelegate mFoundSessionsDelegate;
29 IDeterministicGame mGame;
31 List<Keys> mLastPressedKeys = new List<Keys>();
32 bool mLastButtonPressed;
35 long mNextLatencyAdjustmentFrame;
39 TimeSpan mTargetTimeSpan = new TimeSpan(166666);
40 public TimeSpan TargetTimeSpan
44 return mTargetTimeSpan;
52 /// Called when a session has been created or joined using CreateSession() or JoinSession().
54 /// <param name="session">The new session that was created or joined.</param>
55 /// <param name="networkGame">The NetworkGame that joined the session.</param>
56 public delegate void JoinedSessionDelegate(NetworkSession session, NetworkGame networkGame);
59 /// Called when sessions are found as a result of calling FindSessions().
61 /// <param name="sessions">A container of the available sessions.</param>
62 /// <param name="networkGame">The NetworkGame that searched for the sessions.</param>
63 public delegate void FoundSessionsDelegate(AvailableNetworkSessionCollection sessions, NetworkGame networkGame);
67 /// Construct a NetworkGame with a lobby and a game.
69 /// <param name="lobby">Provides an associated lobby to update and draw.</param>
70 /// <param name="game">Provides a game object to be played over the network.</param>
71 public NetworkGame(ILobby lobby, IDeterministicGame game)
73 Debug.Assert(lobby != null && game != null);
81 /// Get the Gamer object for the local player.
83 public LocalNetworkGamer LocalGamer
87 // TODO: Is this the correct way to get the single local gamer?
88 return mNetworkSession.LocalGamers[0];
93 /// Get all the gamers associated with the active network session.
95 public GamerCollection<NetworkGamer> NetworkGamers
99 return mNetworkSession.AllGamers;
105 /// Begin a new network session with the local gamer as the host. You must not
106 /// call this method or use JoinSession without first using LeaveSession.
108 /// <param name="callback">The delegate/method to call when the session is created.</param>
109 public void CreateSession(JoinedSessionDelegate callback)
111 CreateSession(mGame.MaximumSupportedPlayers, callback);
115 /// Begin a new network session with the local gamer as the host. You must not
116 /// call this method or use JoinSession without first using LeaveSession.
118 /// <param name="maxGamers">Provide the maximum number of players allowed to connect.</param>
119 /// <param name="callback">The delegate/method to call when the session is created.</param>
120 public void CreateSession(int maxGamers, JoinedSessionDelegate callback)
122 Debug.Assert(mNetworkSession == null);
124 mJoinedSessionDelegate = callback;
125 NetworkSession.BeginCreate(NetworkSessionType.SystemLink, 1, maxGamers, CreateSessionEnd, null);
127 private void CreateSessionEnd(IAsyncResult result)
129 Debug.Assert(mNetworkSession == null);
131 mNetworkSession = NetworkSession.EndCreate(result);
132 mNetworkSession.AllowHostMigration = true;
133 mNetworkSession.AllowJoinInProgress = false;
135 mJoinedSessionDelegate(mNetworkSession, this);
139 /// Determine whether or not the network game object is associated with any network session.
141 /// <returns>True if there exists a NetworkSession; false otherwise.</returns>
142 public bool HasActiveSession
146 return mNetworkSession != null;
152 /// Find available sessions to join.
154 /// <param name="callback">The delegate/method to call when the search finishes.</param>
155 public void FindSessions(FoundSessionsDelegate callback)
157 Debug.Assert(mNetworkSession == null);
159 mFoundSessionsDelegate = callback;
160 NetworkSession.BeginFind(NetworkSessionType.SystemLink, 1, null, new AsyncCallback(FindSessionsEnd), null);
162 private void FindSessionsEnd(IAsyncResult result)
164 AvailableNetworkSessionCollection sessions = NetworkSession.EndFind(result);
165 mFoundSessionsDelegate(sessions, this);
169 /// Join a network session found using FindSessions(). This is for joining a game that
170 /// somebody else has already started hosting.
172 /// <param name="availableSession">Pass the session object to try to join.</param>
173 /// <param name="callback">The delegate/method to call when the search finishes.</param>
174 public void JoinSession(AvailableNetworkSession availableSession, JoinedSessionDelegate callback)
176 Debug.Assert(mNetworkSession == null);
178 mJoinedSessionDelegate = callback;
179 NetworkSession.BeginJoin(availableSession, JoinSessionEnd, null);
181 private void JoinSessionEnd(IAsyncResult result)
183 Debug.Assert(mNetworkSession == null);
185 mNetworkSession = NetworkSession.EndJoin(result);
187 mJoinedSessionDelegate(mNetworkSession, this);
188 mJoinedSessionDelegate = null;
193 /// Leave and dispose of any currently associated network session. You will find yourself
194 /// back in the lobby.
196 public void LeaveSession()
198 Debug.Assert(mNetworkSession != null);
200 mNetworkSession.Dispose();
201 mNetworkSession = null;
206 /// Set up the network session to simulate 200ms latency and 10% packet loss.
208 public void SimulateBadNetwork()
210 Debug.Assert(mNetworkSession != null);
212 mNetworkSession.SimulatedLatency = new TimeSpan(0, 0, 0, 0, 200);
213 mNetworkSession.SimulatedPacketLoss = 0.1f;
218 /// Indicate that the game should begin (moving players from the lobby to the game).
219 /// You must call CreateSession() before calling this.
221 public void StartGame()
223 Debug.Assert(mNetworkSession != null && mNetworkSession.IsHost);
225 mNetworkSession.StartGame();
226 mNetworkSession.ResetReady();
231 /// Manages the network session and allows either the lobby or game to update.
233 /// <param name="gameTime">Pass the time away.</param>
234 public void Update(GameTime gameTime)
236 if (mNetworkSession == null)
238 mLobby.Update(gameTime, this);
242 mNetworkSession.Update();
245 if (mNetworkSession.SessionState == NetworkSessionState.Lobby)
247 if (mNetworkSession.IsHost &&
248 mNetworkSession.AllGamers.Count >= mGame.MinimumSupportedPlayers &&
249 mNetworkSession.IsEveryoneReady)
251 mNetworkSession.StartGame();
252 mNetworkSession.ResetReady();
256 mLobby.Update(gameTime, this);
259 else if (mNetworkSession.SessionState == NetworkSessionState.Playing)
261 if (HaveNeededEvents)
263 if (IsLatencyAdjustmentFrame) AdjustLatency();
267 mGame.Update(mTargetTimeSpan);
277 /// Allows either the lobby or the game to draw, depending on the state
278 /// of the network connection and whether or not a game is in progress.
280 /// <param name="gameTime">Pass the time away.</param>
281 /// <param name="spriteBatch">The sprite batch.</param>
282 public void Draw(GameTime gameTime, SpriteBatch spriteBatch)
284 if (mNetworkSession == null)
286 mLobby.Draw(spriteBatch);
290 if (mNetworkSession.SessionState == NetworkSessionState.Lobby)
292 mLobby.Draw(spriteBatch);
294 else if (mNetworkSession.SessionState == NetworkSessionState.Playing)
296 mLobby.Draw(spriteBatch);
302 // Private implementation methods of the network protocol
303 #region Private Implementation Methods
306 /// Reinitialize the private variables in preparation for new game to start.
311 mNextLatencyAdjustmentFrame = 1;
313 mAverageOwd = AverageOneWayDelay;
315 // TODO: The game object needs to be reset, too.
316 //mGame.ResetGame(playerIdentifiers, playerIdentifiers[0]);
321 /// Allows either the lobby or the game to draw, depending on the state
322 /// of the network connection and whether or not a game is in progress.
324 /// <param name="gameTime">Pass the time away.</param>
325 /// <param name="spriteBatch">The sprite batch.</param>
326 private void ReadPackets()
328 PacketReader packetReader = new PacketReader();
330 foreach (LocalNetworkGamer gamer in mNetworkSession.LocalGamers)
332 while (gamer.IsDataAvailable)
336 gamer.ReceiveData(packetReader, out sender);
337 byte packetId = packetReader.ReadByte();
343 short messageLength = packetReader.ReadInt16();
344 char[] message = packetReader.ReadChars(messageLength);
346 ChatPacket chatPacket;
347 chatPacket.sender = sender;
348 chatPacket.message = new String(message);
353 short stallCount = packetReader.ReadInt16();
354 short averageOwd = packetReader.ReadInt16();
355 int frameNumber = packetReader.ReadInt32();
356 byte numEvents = packetReader.ReadByte();
358 for (byte i = 0; i < numEvents; ++i)
360 ReadEvent(packetReader, sender);
367 byte numStalledPeers = packetReader.ReadByte();
368 byte[] stalledPeers = packetReader.ReadBytes(numStalledPeers);
376 private void ReadEvent(PacketReader packetReader, NetworkGamer sender)
378 byte eventId = packetReader.ReadByte();
379 long applicationFrame = packetReader.ReadInt32();
385 int keyCode1 = packetReader.ReadInt32();
391 int keyCode2 = packetReader.ReadInt32();
397 byte buttonId1 = packetReader.ReadByte();
403 byte buttonId2 = packetReader.ReadByte();
409 short x = packetReader.ReadInt16();
410 short y = packetReader.ReadInt16();
417 private bool IsLatencyAdjustmentFrame
426 private void AdjustLatency()
432 private void SendLocalEvents()
434 // TODO: Not finished.
436 KeyboardState keyState = Keyboard.GetState();
437 MouseState mouseState = Mouse.GetState();
439 // Make a list of the keys pressed or released this frame.
441 List<Keys> pressedKeys = new List<Keys>();
442 List<Keys> releasedKeys = new List<Keys>();
444 Keys[] pressedKeysArray = keyState.GetPressedKeys();
445 foreach (Keys k in pressedKeysArray)
446 if (!mLastPressedKeys.Contains(k))
449 mLastPressedKeys.Remove(k);
451 releasedKeys = mLastPressedKeys;
452 mLastPressedKeys = new List<Keys>(pressedKeysArray);
454 bool buttonPressed = mouseState.LeftButton == ButtonState.Pressed;
458 private bool HaveNeededEvents
467 private void ApplyEvents()
473 private int AverageOneWayDelay