]> Dogcows Code - chaz/carfire/blob - Project06/CS 3505 Project 06/CS 3505 Project 06/NetworkGame.cs
Chat implemented. Needs testing.
[chaz/carfire] / Project06 / CS 3505 Project 06 / CS 3505 Project 06 / NetworkGame.cs
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
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;
11
12 namespace CS_3505_Project_06
13 {
14 /// <summary>
15 /// A manager class to handle network interactions between peers and
16 /// lobby/game switching.
17 /// </summary>
18 public class NetworkGame
19 {
20 /// <summary>
21 /// Called when a session has been created or joined using CreateSession() or JoinSession().
22 /// </summary>
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);
26
27 /// <summary>
28 /// Called when sessions are found as a result of calling FindSessions().
29 /// </summary>
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);
33
34
35 /// <summary>
36 /// Construct a NetworkGame with a lobby and a game.
37 /// </summary>
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)
41 {
42 Debug.Assert(lobby != null && game != null);
43
44 mLobby = lobby;
45 mGame = game;
46 }
47
48
49 /// <summary>
50 /// Get the Gamer object for the local player.
51 /// </summary>
52 public LocalNetworkGamer LocalGamer
53 {
54 get
55 {
56 // TODO: Is this the correct way to get the single local gamer?
57 return mNetworkSession.LocalGamers[0];
58 }
59 }
60
61 /// <summary>
62 /// Get all the gamers associated with the active network session.
63 /// </summary>
64 public GamerCollection<NetworkGamer> NetworkGamers
65 {
66 get
67 {
68 return mNetworkSession.AllGamers;
69 }
70 }
71
72
73 /// <summary>
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.
76 /// </summary>
77 /// <param name="callback">The delegate/method to call when the session is created.</param>
78 public void CreateSession(JoinedSessionDelegate callback)
79 {
80 CreateSession(mGame.MaximumSupportedPlayers, callback);
81 }
82
83 /// <summary>
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.
86 /// </summary>
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)
90 {
91 Debug.Assert(mNetworkSession == null);
92
93 mJoinedSessionDelegate = callback;
94 NetworkSession.BeginCreate(NetworkSessionType.SystemLink, 1, maxGamers, CreateSessionEnd, null);
95 }
96 private void CreateSessionEnd(IAsyncResult result)
97 {
98 Debug.Assert(mNetworkSession == null);
99
100 mNetworkSession = NetworkSession.EndCreate(result);
101 mNetworkSession.AllowHostMigration = true;
102 mNetworkSession.AllowJoinInProgress = false;
103
104 mJoinedSessionDelegate(mNetworkSession, this);
105 }
106
107 /// <summary>
108 /// Determine whether or not the network game object is associated with any network session.
109 /// </summary>
110 /// <returns>True if there exists a NetworkSession; false otherwise.</returns>
111 public bool HasActiveSession
112 {
113 get
114 {
115 return mNetworkSession != null;
116 }
117 }
118
119
120 /// <summary>
121 /// Find available sessions to join. You should not already be in a session when
122 /// calling this method; call LeaveSession first.
123 /// </summary>
124 /// <param name="callback">The delegate/method to call when the search finishes.</param>
125 public void FindSessions(FoundSessionsDelegate callback)
126 {
127 Debug.Assert(mNetworkSession == null);
128
129 mFoundSessionsDelegate = callback;
130 NetworkSession.BeginFind(NetworkSessionType.SystemLink, 1, null, new AsyncCallback(FindSessionsEnd), null);
131 }
132 private void FindSessionsEnd(IAsyncResult result)
133 {
134 AvailableNetworkSessionCollection sessions = NetworkSession.EndFind(result);
135 mFoundSessionsDelegate(sessions, this);
136 }
137
138 /// <summary>
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.
141 /// </summary>
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)
145 {
146 Debug.Assert(mNetworkSession == null);
147
148 mJoinedSessionDelegate = callback;
149 NetworkSession.BeginJoin(availableSession, JoinSessionEnd, null);
150 }
151 private void JoinSessionEnd(IAsyncResult result)
152 {
153 Debug.Assert(mNetworkSession == null);
154
155 mNetworkSession = NetworkSession.EndJoin(result);
156
157 mJoinedSessionDelegate(mNetworkSession, this);
158 mJoinedSessionDelegate = null;
159 }
160
161
162 /// <summary>
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.
165 /// </summary>
166 public void LeaveSession()
167 {
168 Debug.Assert(mNetworkSession != null);
169
170 mNetworkSession.Dispose();
171 mNetworkSession = null;
172 }
173
174
175 /// <summary>
176 /// Set up the network session to simulate 200ms latency and 10% packet loss.
177 /// </summary>
178 public void SimulateBadNetwork()
179 {
180 Debug.Assert(mNetworkSession != null);
181
182 mNetworkSession.SimulatedLatency = new TimeSpan(0, 0, 0, 0, 200);
183 mNetworkSession.SimulatedPacketLoss = 0.1f;
184 }
185
186
187 /// <summary>
188 /// Indicate that the game should begin (moving players from the lobby to the game).
189 /// You must call CreateSession() before calling this.
190 /// </summary>
191 public void StartGame()
192 {
193 Debug.Assert(mNetworkSession != null && mNetworkSession.IsHost);
194
195 mNetworkSession.StartGame();
196 mNetworkSession.ResetReady();
197 }
198
199
200 /// <summary>
201 /// Manages the network session and allows either the lobby or game to update.
202 /// </summary>
203 /// <param name="gameTime">Pass the time away.</param>
204 public void Update(GameTime gameTime)
205 {
206 if (mNetworkSession == null)
207 {
208 mLobby.Update(gameTime, this);
209 }
210 else
211 {
212 mNetworkSession.Update();
213 ReadPackets();
214
215 if (mNetworkSession.SessionState == NetworkSessionState.Lobby)
216 {
217 if (mNetworkSession.IsHost &&
218 mNetworkSession.AllGamers.Count >= mGame.MinimumSupportedPlayers &&
219 mNetworkSession.IsEveryoneReady)
220 {
221 mNetworkSession.StartGame();
222 mNetworkSession.ResetReady();
223 }
224 else
225 {
226 mLobby.Update(gameTime, this);
227 }
228 }
229 else if (mNetworkSession.SessionState == NetworkSessionState.Playing)
230 {
231 if (HaveNeededEvents)
232 {
233 if (IsLatencyAdjustmentFrame) AdjustLatency();
234 mStallCount = 0;
235 SendLocalEvents();
236 ApplyEvents();
237 mGame.Update(mTargetTimeSpan);
238 }
239 else // Stall!
240 {
241 }
242 }
243 }
244 }
245
246 /// <summary>
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.
249 /// </summary>
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)
253 {
254 if (mNetworkSession == null)
255 {
256 mLobby.Draw(spriteBatch);
257 }
258 else
259 {
260 if (mNetworkSession.SessionState == NetworkSessionState.Lobby)
261 {
262 mLobby.Draw(spriteBatch);
263 }
264 else if (mNetworkSession.SessionState == NetworkSessionState.Playing)
265 {
266 mLobby.Draw(spriteBatch);
267 }
268 }
269 }
270
271
272 /// <summary>
273 /// Get the chat messages that have been receive since the last time this
274 /// method was called.
275 /// </summary>
276 /// <returns>List container of the chat messages.</returns>
277 public List<ChatPacket> ReceiveChats()
278 {
279 List<ChatPacket> chats = mChatPackets;
280 mChatPackets = new List<ChatPacket>();
281 return chats;
282 }
283
284 /// <summary>
285 /// Send a chat message to all gamers in the session. You should already be
286 /// in a session before calling this method.
287 /// </summary>
288 /// <param name="message">The text of the message.</param>
289 public void SendChat(String message)
290 {
291 WriteChat(message);
292 LocalGamer.SendData(mPacketWriter, SendDataOptions.ReliableInOrder);
293 }
294
295 /// <summary>
296 /// Send a chat message to a specific gamer in the session. You should already
297 /// be in a session before calling this method.
298 /// </summary>
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)
302 {
303 WriteChat(message);
304 LocalGamer.SendData(mPacketWriter, SendDataOptions.ReliableInOrder, recipient);
305 }
306
307
308 // Private class variable members
309 #region Instance Variables
310
311 NetworkSession mNetworkSession;
312 PacketReader mPacketReader = new PacketReader();
313 PacketWriter mPacketWriter = new PacketWriter();
314
315 JoinedSessionDelegate mJoinedSessionDelegate;
316 FoundSessionsDelegate mFoundSessionsDelegate;
317
318 ILobby mLobby;
319 IDeterministicGame mGame;
320
321 List<ChatPacket> mChatPackets = new List<ChatPacket>();
322
323 List<Keys> mLastPressedKeys = new List<Keys>();
324 bool mLastButtonPressed;
325
326 int mLatency;
327 long mNextLatencyAdjustmentFrame;
328 int mStallCount;
329 int mAverageOwd;
330
331 TimeSpan mTargetTimeSpan = new TimeSpan(166666);
332 public TimeSpan TargetTimeSpan
333 {
334 get
335 {
336 return mTargetTimeSpan;
337 }
338 }
339
340 #endregion
341
342
343 // Private implementation methods of the network protocol
344 #region Private Implementation Methods
345
346 enum PacketType
347 {
348 Chat = 1,
349 Event = 2,
350 Stall = 3
351 }
352
353 enum EventType
354 {
355 KeyDown = 1,
356 KeyUp = 2,
357 MouseDown = 3,
358 MouseUp = 4,
359 MouseMove = 5
360 }
361
362
363 /// <summary>
364 /// Reinitialize the private variables in preparation for new game to start.
365 /// </summary>
366 void Reset()
367 {
368 mLatency = 1;
369 mNextLatencyAdjustmentFrame = 1;
370 mStallCount = 0;
371 mAverageOwd = AverageOneWayDelay;
372
373 // TODO: The game object needs to be reset, too.
374 //mGame.ResetGame(playerIdentifiers, playerIdentifiers[0]);
375 }
376
377
378 /// <summary>
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.
381 /// </summary>
382 /// <param name="gameTime">Pass the time away.</param>
383 /// <param name="spriteBatch">The sprite batch.</param>
384 void ReadPackets()
385 {
386 foreach (LocalNetworkGamer gamer in mNetworkSession.LocalGamers)
387 {
388 while (gamer.IsDataAvailable)
389 {
390 NetworkGamer sender;
391
392 gamer.ReceiveData(mPacketReader, out sender);
393 PacketType packetId = (PacketType)mPacketReader.ReadByte();
394
395 switch (packetId)
396 {
397 case PacketType.Chat:
398
399 short messageLength = mPacketReader.ReadInt16();
400 char[] message = mPacketReader.ReadChars(messageLength);
401
402 ChatPacket chatPacket = new ChatPacket(sender, new String(message));
403 mChatPackets.Add(chatPacket);
404 break;
405
406 case PacketType.Event:
407
408 short stallCount = mPacketReader.ReadInt16();
409 short averageOwd = mPacketReader.ReadInt16();
410 int frameNumber = mPacketReader.ReadInt32();
411 byte numEvents = mPacketReader.ReadByte();
412
413 for (byte i = 0; i < numEvents; ++i)
414 {
415 ReadEvent(mPacketReader, sender);
416 }
417
418 break;
419
420 case PacketType.Stall:
421
422 byte numStalledPeers = mPacketReader.ReadByte();
423 byte[] stalledPeers = mPacketReader.ReadBytes(numStalledPeers);
424
425 break;
426 }
427 }
428 }
429 }
430
431 void ReadEvent(PacketReader packetReader, NetworkGamer sender)
432 {
433 EventType eventId = (EventType)packetReader.ReadByte();
434 long applicationFrame = packetReader.ReadInt32();
435
436 switch (eventId)
437 {
438 case EventType.KeyDown:
439
440 int keyCode1 = packetReader.ReadInt32();
441
442 break;
443
444 case EventType.KeyUp:
445
446 int keyCode2 = packetReader.ReadInt32();
447
448 break;
449
450 case EventType.MouseDown:
451
452 byte buttonId1 = packetReader.ReadByte();
453
454 break;
455
456 case EventType.MouseUp:
457
458 byte buttonId2 = packetReader.ReadByte();
459
460 break;
461
462 case EventType.MouseMove:
463
464 short x = packetReader.ReadInt16();
465 short y = packetReader.ReadInt16();
466
467 break;
468 }
469 }
470
471 void WriteChat(String message)
472 {
473 mPacketWriter.Write((byte)PacketType.Chat);
474 mPacketWriter.Write((short)message.Length);
475 mPacketWriter.Write(message.ToCharArray());
476 }
477
478
479 bool IsLatencyAdjustmentFrame
480 {
481 get
482 {
483 // TODO
484 return false;
485 }
486 }
487
488 void AdjustLatency()
489 {
490 // TODO
491 }
492
493
494 void SendLocalEvents()
495 {
496 // TODO: Not finished.
497
498 KeyboardState keyState = Keyboard.GetState();
499 MouseState mouseState = Mouse.GetState();
500
501 // Make a list of the keys pressed or released this frame.
502
503 List<Keys> pressedKeys = new List<Keys>();
504 List<Keys> releasedKeys = new List<Keys>();
505
506 Keys[] pressedKeysArray = keyState.GetPressedKeys();
507 foreach (Keys k in pressedKeysArray)
508 if (!mLastPressedKeys.Contains(k))
509 pressedKeys.Add(k);
510 else
511 mLastPressedKeys.Remove(k);
512
513 releasedKeys = mLastPressedKeys;
514 mLastPressedKeys = new List<Keys>(pressedKeysArray);
515
516 bool buttonPressed = mouseState.LeftButton == ButtonState.Pressed;
517 }
518
519
520 bool HaveNeededEvents
521 {
522 get
523 {
524 // TODO
525 return true;
526 }
527 }
528
529 void ApplyEvents()
530 {
531 // TODO
532 }
533
534
535 int AverageOneWayDelay
536 {
537 get
538 {
539 // TODO
540 return 12;
541 }
542 }
543
544 #endregion
545 }
546 }
This page took 0.054795 seconds and 4 git commands to generate.