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
21 /// Called when a session has been created or joined using CreateSession() or JoinSession().
23 /// <param name="session">The new session that was created or joined.</param>
24 /// <param name="networkGame">The NetworkGame that joined the session.</param>
25 public delegate void JoinedSessionDelegate(NetworkSession session, NetworkGame networkGame);
28 /// Called when sessions are found as a result of calling FindSessions().
30 /// <param name="sessions">A container of the available sessions.</param>
31 /// <param name="networkGame">The NetworkGame that searched for the sessions.</param>
32 public delegate void FoundSessionsDelegate(AvailableNetworkSessionCollection sessions, NetworkGame networkGame);
36 /// Construct a NetworkGame with a lobby and a game.
38 /// <param name="lobby">Provides an associated lobby to update and draw.</param>
39 /// <param name="game">Provides a game object to be played over the network.</param>
40 public NetworkGame(ILobby lobby, IDeterministicGame game)
42 Debug.Assert(lobby != null && game != null);
50 /// Get the Gamer object for the local player.
52 public LocalNetworkGamer LocalGamer
56 // TODO: Is this the correct way to get the single local gamer?
57 return mNetworkSession.LocalGamers[0];
62 /// Get all the gamers associated with the active network session.
64 public GamerCollection<NetworkGamer> NetworkGamers
68 return mNetworkSession.AllGamers;
74 /// Begin a new network session with the local gamer as the host. You must not
75 /// call this method or use JoinSession without first using LeaveSession.
77 /// <param name="callback">The delegate/method to call when the session is created.</param>
78 public void CreateSession(JoinedSessionDelegate callback)
80 CreateSession(mGame.MaximumSupportedPlayers, callback);
84 /// Begin a new network session with the local gamer as the host. You must not
85 /// call this method or use JoinSession without first using LeaveSession.
87 /// <param name="maxGamers">Provide the maximum number of players allowed to connect.</param>
88 /// <param name="callback">The delegate/method to call when the session is created.</param>
89 public void CreateSession(int maxGamers, JoinedSessionDelegate callback)
91 Debug.Assert(mNetworkSession == null);
93 mJoinedSessionDelegate = callback;
94 NetworkSession.BeginCreate(NetworkSessionType.SystemLink, 1, maxGamers, CreateSessionEnd, null);
96 private void CreateSessionEnd(IAsyncResult result)
98 Debug.Assert(mNetworkSession == null);
100 mNetworkSession = NetworkSession.EndCreate(result);
101 mNetworkSession.AllowHostMigration = true;
102 mNetworkSession.AllowJoinInProgress = false;
104 mJoinedSessionDelegate(mNetworkSession, this);
108 /// Determine whether or not the network game object is associated with any network session.
110 /// <returns>True if there exists a NetworkSession; false otherwise.</returns>
111 public bool HasActiveSession
115 return mNetworkSession != null;
121 /// Find available sessions to join. You should not already be in a session when
122 /// calling this method; call LeaveSession first.
124 /// <param name="callback">The delegate/method to call when the search finishes.</param>
125 public void FindSessions(FoundSessionsDelegate callback)
127 Debug.Assert(mNetworkSession == null);
129 mFoundSessionsDelegate = callback;
130 NetworkSession.BeginFind(NetworkSessionType.SystemLink, 1, null, new AsyncCallback(FindSessionsEnd), null);
132 private void FindSessionsEnd(IAsyncResult result)
134 AvailableNetworkSessionCollection sessions = NetworkSession.EndFind(result);
135 mFoundSessionsDelegate(sessions, this);
139 /// Join a network session found using FindSessions(). This is for joining a game that
140 /// somebody else has already started hosting. You must not already be in a session.
142 /// <param name="availableSession">Pass the session object to try to join.</param>
143 /// <param name="callback">The delegate/method to call when the search finishes.</param>
144 public void JoinSession(AvailableNetworkSession availableSession, JoinedSessionDelegate callback)
146 Debug.Assert(mNetworkSession == null);
148 mJoinedSessionDelegate = callback;
149 NetworkSession.BeginJoin(availableSession, JoinSessionEnd, null);
151 private void JoinSessionEnd(IAsyncResult result)
153 Debug.Assert(mNetworkSession == null);
155 mNetworkSession = NetworkSession.EndJoin(result);
157 mJoinedSessionDelegate(mNetworkSession, this);
158 mJoinedSessionDelegate = null;
163 /// Leave and dispose of any currently associated network session. You will find yourself
164 /// back in the lobby. You must already be in a session to leave it.
166 public void LeaveSession()
168 Debug.Assert(mNetworkSession != null);
170 mNetworkSession.Dispose();
171 mNetworkSession = null;
176 /// Set up the network session to simulate 200ms latency and 10% packet loss.
178 public void SimulateBadNetwork()
180 Debug.Assert(mNetworkSession != null);
182 mNetworkSession.SimulatedLatency = new TimeSpan(0, 0, 0, 0, 200);
183 mNetworkSession.SimulatedPacketLoss = 0.1f;
188 /// Indicate that the game should begin (moving players from the lobby to the game).
189 /// You must call CreateSession() before calling this.
191 public void StartGame()
193 Debug.Assert(mNetworkSession != null && mNetworkSession.IsHost);
195 mNetworkSession.StartGame();
196 mNetworkSession.ResetReady();
201 /// Manages the network session and allows either the lobby or game to update.
203 /// <param name="gameTime">Pass the time away.</param>
204 public void Update(GameTime gameTime)
206 if (mNetworkSession == null)
208 mLobby.Update(gameTime, this);
212 mNetworkSession.Update();
215 if (mNetworkSession.SessionState == NetworkSessionState.Lobby)
217 if (mNetworkSession.IsHost &&
218 mNetworkSession.AllGamers.Count >= mGame.MinimumSupportedPlayers &&
219 mNetworkSession.IsEveryoneReady)
221 mNetworkSession.StartGame();
222 mNetworkSession.ResetReady();
226 mLobby.Update(gameTime, this);
229 else if (mNetworkSession.SessionState == NetworkSessionState.Playing)
231 if (HaveNeededEvents)
233 if (IsLatencyAdjustmentFrame) AdjustLatency();
237 mGame.Update(mTargetTimeSpan);
247 /// Allows either the lobby or the game to draw, depending on the state
248 /// of the network connection and whether or not a game is in progress.
250 /// <param name="gameTime">Pass the time away.</param>
251 /// <param name="spriteBatch">The sprite batch.</param>
252 public void Draw(GameTime gameTime, SpriteBatch spriteBatch)
254 if (mNetworkSession == null)
256 mLobby.Draw(spriteBatch);
260 if (mNetworkSession.SessionState == NetworkSessionState.Lobby)
262 mLobby.Draw(spriteBatch);
264 else if (mNetworkSession.SessionState == NetworkSessionState.Playing)
266 mLobby.Draw(spriteBatch);
273 /// Get the chat messages that have been receive since the last time this
274 /// method was called.
276 /// <returns>List container of the chat messages.</returns>
277 public List<ChatPacket> ReceiveChats()
279 List<ChatPacket> chats = mChatPackets;
280 mChatPackets = new List<ChatPacket>();
285 /// Send a chat message to all gamers in the session. You should already be
286 /// in a session before calling this method.
288 /// <param name="message">The text of the message.</param>
289 public void SendChat(String message)
292 LocalGamer.SendData(mPacketWriter, SendDataOptions.ReliableInOrder);
296 /// Send a chat message to a specific gamer in the session. You should already
297 /// be in a session before calling this method.
299 /// <param name="message">The text of the message.</param>
300 /// <param name="recipient">The gamer to receive the message.</param>
301 public void SendChat(String message, NetworkGamer recipient)
304 LocalGamer.SendData(mPacketWriter, SendDataOptions.ReliableInOrder, recipient);
308 // Private class variable members
309 #region Instance Variables
311 NetworkSession mNetworkSession;
312 PacketReader mPacketReader = new PacketReader();
313 PacketWriter mPacketWriter = new PacketWriter();
315 JoinedSessionDelegate mJoinedSessionDelegate;
316 FoundSessionsDelegate mFoundSessionsDelegate;
319 IDeterministicGame mGame;
321 List<ChatPacket> mChatPackets = new List<ChatPacket>();
323 List<Keys> mLastPressedKeys = new List<Keys>();
324 bool mLastButtonPressed;
327 long mNextLatencyAdjustmentFrame;
331 TimeSpan mTargetTimeSpan = new TimeSpan(166666);
332 public TimeSpan TargetTimeSpan
336 return mTargetTimeSpan;
343 // Private implementation methods of the network protocol
344 #region Private Implementation Methods
364 /// Reinitialize the private variables in preparation for new game to start.
369 mNextLatencyAdjustmentFrame = 1;
371 mAverageOwd = AverageOneWayDelay;
373 // TODO: The game object needs to be reset, too.
374 //mGame.ResetGame(playerIdentifiers, playerIdentifiers[0]);
379 /// Allows either the lobby or the game to draw, depending on the state
380 /// of the network connection and whether or not a game is in progress.
382 /// <param name="gameTime">Pass the time away.</param>
383 /// <param name="spriteBatch">The sprite batch.</param>
386 foreach (LocalNetworkGamer gamer in mNetworkSession.LocalGamers)
388 while (gamer.IsDataAvailable)
392 gamer.ReceiveData(mPacketReader, out sender);
393 PacketType packetId = (PacketType)mPacketReader.ReadByte();
397 case PacketType.Chat:
399 short messageLength = mPacketReader.ReadInt16();
400 char[] message = mPacketReader.ReadChars(messageLength);
402 ChatPacket chatPacket = new ChatPacket(sender, new String(message));
403 mChatPackets.Add(chatPacket);
406 case PacketType.Event:
408 short stallCount = mPacketReader.ReadInt16();
409 short averageOwd = mPacketReader.ReadInt16();
410 int frameNumber = mPacketReader.ReadInt32();
411 byte numEvents = mPacketReader.ReadByte();
413 for (byte i = 0; i < numEvents; ++i)
415 ReadEvent(mPacketReader, sender);
420 case PacketType.Stall:
422 byte numStalledPeers = mPacketReader.ReadByte();
423 byte[] stalledPeers = mPacketReader.ReadBytes(numStalledPeers);
431 void ReadEvent(PacketReader packetReader, NetworkGamer sender)
433 EventType eventId = (EventType)packetReader.ReadByte();
434 long applicationFrame = packetReader.ReadInt32();
438 case EventType.KeyDown:
440 int keyCode1 = packetReader.ReadInt32();
444 case EventType.KeyUp:
446 int keyCode2 = packetReader.ReadInt32();
450 case EventType.MouseDown:
452 byte buttonId1 = packetReader.ReadByte();
456 case EventType.MouseUp:
458 byte buttonId2 = packetReader.ReadByte();
462 case EventType.MouseMove:
464 short x = packetReader.ReadInt16();
465 short y = packetReader.ReadInt16();
471 void WriteChat(String message)
473 mPacketWriter.Write((byte)PacketType.Chat);
474 mPacketWriter.Write((short)message.Length);
475 mPacketWriter.Write(message.ToCharArray());
479 bool IsLatencyAdjustmentFrame
494 void SendLocalEvents()
496 // TODO: Not finished.
498 KeyboardState keyState = Keyboard.GetState();
499 MouseState mouseState = Mouse.GetState();
501 // Make a list of the keys pressed or released this frame.
503 List<Keys> pressedKeys = new List<Keys>();
504 List<Keys> releasedKeys = new List<Keys>();
506 Keys[] pressedKeysArray = keyState.GetPressedKeys();
507 foreach (Keys k in pressedKeysArray)
508 if (!mLastPressedKeys.Contains(k))
511 mLastPressedKeys.Remove(k);
513 releasedKeys = mLastPressedKeys;
514 mLastPressedKeys = new List<Keys>(pressedKeysArray);
516 bool buttonPressed = mouseState.LeftButton == ButtonState.Pressed;
520 bool HaveNeededEvents
535 int AverageOneWayDelay