]> Dogcows Code - chaz/carfire/blob - CarFire/CarFire/CarFire/NetworkManager.cs
Implemented the base architecture we are bound to because of how the network code...
[chaz/carfire] / CarFire / CarFire / CarFire / NetworkManager.cs
1 
2 // Make sure DEBUG is undefined when turning in the project
3 // or the grader will wonder why it's so laggy.
4 #undef DEBUG
5
6 using System;
7 using System.Collections.Generic;
8 using System.Linq;
9 using System.Text;
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;
17
18 namespace CarFire
19 {
20 /// <summary>
21 /// A manager class to handle network interactions between peers and
22 /// lobby/game switching.
23 /// </summary>
24 public class NetworkManager
25 {
26 // Public methods and properties
27 #region Public Methods
28
29 /// <summary>
30 /// Called when a session has been created or joined using CreateSession() or JoinSession().
31 /// </summary>
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, NetworkManager networkGame);
35
36 /// <summary>
37 /// Called when sessions are found as a result of calling FindSessions().
38 /// </summary>
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, NetworkManager networkGame);
42
43
44 /// <summary>
45 /// Called when an exception is thrown during an asynchronous operation.
46 /// </summary>
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, NetworkManager networkGame);
50
51 /// <summary>
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.
55 /// </summary>
56 public CaughtErrorDelegate ErrorDelegate;
57
58
59 /// <summary>
60 /// Construct a NetworkGame with a lobby and a game.
61 /// </summary>
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 NetworkManager(IScreenManager lobby, IDeterministicGame game)
65 {
66 Debug.Assert(lobby != null && game != null);
67
68 mLobby = lobby;
69 mGame = game;
70 }
71
72
73 /// <summary>
74 /// Get the Gamer object for the local player.
75 /// </summary>
76 public LocalNetworkGamer LocalGamer
77 {
78 get
79 {
80 // TODO: Is this the correct way to get the single local gamer?
81 return mNetworkSession.LocalGamers[0];
82 }
83 }
84
85 /// <summary>
86 /// Get all the gamers associated with the active network session.
87 /// </summary>
88 public GamerCollection<NetworkGamer> NetworkGamers
89 {
90 get
91 {
92 return mNetworkSession.AllGamers;
93 }
94 }
95
96
97 /// <summary>
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.
100 /// </summary>
101 /// <param name="callback">The delegate/method to call when the session is created.</param>
102 public void CreateSession(JoinedSessionDelegate callback)
103 {
104 CreateSession(mGame.MaximumSupportedPlayers, callback);
105 }
106
107 /// <summary>
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.
110 /// </summary>
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)
114 {
115 Debug.Assert(mNetworkSession == null);
116
117 mJoinedSessionDelegate = callback;
118 NetworkSession.BeginCreate(NetworkSessionType.SystemLink, 1, maxGamers, CreateSessionEnd, null);
119 }
120 void CreateSessionEnd(IAsyncResult result)
121 {
122 Debug.Assert(mNetworkSession == null);
123
124 try
125 {
126 mNetworkSession = NetworkSession.EndCreate(result);
127 mNetworkSession.AllowHostMigration = true;
128 mNetworkSession.AllowJoinInProgress = false;
129 mNetworkSession.GameStarted += new EventHandler<GameStartedEventArgs>(GameStartedEvent);
130 }
131 catch (Exception e)
132 {
133 if (ErrorDelegate != null) ErrorDelegate(e, this);
134 return;
135 }
136 mJoinedSessionDelegate(mNetworkSession, this);
137 mJoinedSessionDelegate = null;
138 }
139 void GameStartedEvent(object sender, GameStartedEventArgs e)
140 {
141 Reset();
142 }
143
144 /// <summary>
145 /// Determine whether or not the network game object is associated with any network session.
146 /// </summary>
147 /// <returns>True if there exists a NetworkSession; false otherwise.</returns>
148 public bool HasActiveSession
149 {
150 get
151 {
152 return mNetworkSession != null;
153 }
154 }
155
156
157 /// <summary>
158 /// Find available sessions to join. You should not already be in a session when
159 /// calling this method; call LeaveSession first.
160 /// </summary>
161 /// <param name="callback">The delegate/method to call when the search finishes.</param>
162 public void FindSessions(FoundSessionsDelegate callback)
163 {
164 Debug.Assert(mNetworkSession == null);
165
166 mFoundSessionsDelegate = callback;
167 NetworkSession.BeginFind(NetworkSessionType.SystemLink, 1, null, new AsyncCallback(FindSessionsEnd), null);
168 }
169 void FindSessionsEnd(IAsyncResult result)
170 {
171 AvailableNetworkSessionCollection sessions;
172 try
173 {
174 sessions = NetworkSession.EndFind(result);
175 }
176 catch (Exception e)
177 {
178 if (ErrorDelegate != null) ErrorDelegate(e, this);
179 return;
180 }
181 mFoundSessionsDelegate(sessions, this);
182 mFoundSessionsDelegate = null;
183 }
184
185 /// <summary>
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.
188 /// </summary>
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)
192 {
193 Debug.Assert(mNetworkSession == null);
194
195 mJoinedSessionDelegate = callback;
196 NetworkSession.BeginJoin(availableSession, JoinSessionEnd, null);
197 }
198 void JoinSessionEnd(IAsyncResult result)
199 {
200 Debug.Assert(mNetworkSession == null);
201
202 try
203 {
204 mNetworkSession = NetworkSession.EndJoin(result);
205 mNetworkSession.GameStarted += new EventHandler<GameStartedEventArgs>(GameStartedEvent);
206 }
207 catch (Exception e)
208 {
209 if (ErrorDelegate != null) ErrorDelegate(e, this);
210 return;
211 }
212 mJoinedSessionDelegate(mNetworkSession, this);
213 mJoinedSessionDelegate = null;
214 }
215
216
217 /// <summary>
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.
220 /// </summary>
221 public void LeaveSession()
222 {
223 Debug.Assert(mNetworkSession != null);
224
225 mNetworkSession.Dispose();
226 mNetworkSession = null;
227 }
228
229
230 /// <summary>
231 /// Set up the network session to simulate 200ms latency and 10% packet loss.
232 /// </summary>
233 public void SimulateBadNetwork()
234 {
235 Debug.Assert(mNetworkSession != null);
236
237 mNetworkSession.SimulatedLatency = new TimeSpan(0, 0, 0, 0, 200);
238 mNetworkSession.SimulatedPacketLoss = 0.1f;
239 }
240
241
242 /// <summary>
243 /// Indicate that the game should begin (moving players from the lobby to the game).
244 /// You must call CreateSession() before calling this.
245 /// </summary>
246 public void StartGame()
247 {
248 Debug.Assert(mNetworkSession != null && mNetworkSession.IsHost &&
249 mNetworkSession.AllGamers.Count >= mGame.MinimumSupportedPlayers &&
250 mNetworkSession.IsEveryoneReady);
251
252 ForceStartGame();
253 }
254
255 /// <summary>
256 /// Indicate that the game should begin. This is like StartGame() without the sanity
257 /// checks. Use this for debugging.
258 /// </summary>
259 public void ForceStartGame()
260 {
261 mNetworkSession.StartGame();
262 mNetworkSession.ResetReady();
263 }
264
265
266 /// <summary>
267 /// Manages the network session and allows either the lobby or game to update.
268 /// </summary>
269 /// <param name="gameTime">Pass the time away.</param>
270 public void Update(GameTime gameTime)
271 {
272 if (mNetworkSession == null)
273 {
274 mLobby.Update(gameTime, this);
275 }
276 else
277 {
278 mNetworkSession.Update();
279 HandleIncomingPackets();
280
281 if (mNetworkSession.SessionState == NetworkSessionState.Lobby)
282 {
283 mLobby.Update(gameTime, this);
284 }
285 else if (mNetworkSession.SessionState == NetworkSessionState.Playing)
286 {
287 if (mGame.IsTerminated(LocalGamerInfo))
288 {
289 LeaveSession();
290 return;
291 }
292 else if (mGame.IsGameOver(LocalGamerInfo))
293 {
294 ApplyEvents(LocalGamerInfo, GetEventsFromInput());
295 mGame.Update(mTargetTimeSpan);
296 return;
297 }
298
299 if (HaveNeededEvents)
300 {
301 if (IsLatencyAdjustmentFrame)
302 {
303 AdjustLatency();
304 mLastStallCount = mStallCount;
305 mStallCount = 0;
306 }
307 mLocalEvents.AddRange(GetEventsFromInput());
308 SendLocalEvents();
309 ApplyEvents();
310
311 #if DEBUG
312 Console.WriteLine("HASH: " + mGame.CurrentFrameNumber + "\t" + mGame.CurrentChecksum);
313 #endif
314
315 mGame.Update(mTargetTimeSpan);
316 }
317 else // Stall!
318 {
319 mStallCount++;
320
321 // Send a reliable event packet to each stalled gamer.
322 if (mStallCount == 1)
323 {
324 #if DEBUG
325 Console.WriteLine("STAL: ====");
326 #endif
327
328 foreach (GamerInfo gamerInfo in GamerArray)
329 {
330 if (gamerInfo.HighestFrameNumber < mGame.CurrentFrameNumber)
331 {
332 SendLocalEvents(gamerInfo.Gamer);
333 }
334 }
335 }
336 else if (mStallCount > 600)
337 {
338 Console.WriteLine("One or more players have stalled excessively. Leaving session...");
339 LeaveSession();
340 }
341 }
342 }
343 }
344 }
345
346 /// <summary>
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.
349 /// </summary>
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)
353 {
354 if (mNetworkSession == null)
355 {
356 mLobby.Draw(spriteBatch);
357 }
358 else
359 {
360 if (mNetworkSession.SessionState == NetworkSessionState.Lobby)
361 {
362 mLobby.Draw(spriteBatch);
363 }
364 else if (mNetworkSession.SessionState == NetworkSessionState.Playing)
365 {
366 mGame.Draw(spriteBatch);
367 }
368 }
369 }
370
371
372 /// <summary>
373 /// Get the chat messages that have been received since the last time this
374 /// method was called.
375 /// </summary>
376 /// <returns>List container of the chat messages.</returns>
377 public List<ChatInfo> ReceiveChats()
378 {
379 List<ChatInfo> chats = mChatPackets;
380 mChatPackets = new List<ChatInfo>();
381 return chats;
382 }
383
384 /// <summary>
385 /// Send a chat message to all gamers in the session. You should already be
386 /// in a session before calling this method.
387 /// </summary>
388 /// <param name="message">The text of the message.</param>
389 public void SendChat(String message)
390 {
391 WriteChatPacket(message);
392 LocalGamer.SendData(mPacketWriter, SendDataOptions.ReliableInOrder);
393 }
394
395 /// <summary>
396 /// Send a chat message to a specific gamer in the session. You should already
397 /// be in a session before calling this method.
398 /// </summary>
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)
402 {
403 Debug.Assert(recipient != null && !recipient.IsDisposed);
404
405 WriteChatPacket(message);
406 LocalGamer.SendData(mPacketWriter, SendDataOptions.ReliableInOrder, recipient);
407 }
408
409 #endregion
410
411
412 // Private class variable members
413 #region Instance Variables
414
415 NetworkSession mNetworkSession;
416 PacketReader mPacketReader = new PacketReader();
417 PacketWriter mPacketWriter = new PacketWriter();
418
419 JoinedSessionDelegate mJoinedSessionDelegate;
420 FoundSessionsDelegate mFoundSessionsDelegate;
421
422 IScreenManager mLobby;
423 IDeterministicGame mGame;
424
425 List<ChatInfo> mChatPackets = new List<ChatInfo>();
426
427 List<EventInfo> mLocalEvents = new List<EventInfo>();
428 List<EventInfo> mLastLocalEvents = new List<EventInfo>();
429
430 List<Keys> mLastPressedKeys = new List<Keys>();
431 bool mLastLeftButtonPressed;
432 bool mLastRightButtonPressed;
433 bool mLastMiddleButtonPressed;
434 int mLastMousePositionX;
435 int mLastMousePositionY;
436
437 int mLatency;
438 long mHighestFrameNumber;
439 long mNextLatencyAdjustmentFrame;
440 int mStallCount;
441 int mLastStallCount;
442 int mAverageOwd;
443
444 #if DEBUG
445 bool mDontSendEvents;
446 #endif
447
448 TimeSpan mTargetTimeSpan = new TimeSpan(166666);
449 public TimeSpan TargetTimeSpan
450 {
451 get
452 {
453 return mTargetTimeSpan;
454 }
455 }
456
457 Dictionary<byte, GamerInfo> mGamers;
458 GamerInfo[] GamerArray
459 {
460 get
461 {
462 GamerInfo[] gamerList = mGamers.Values.ToArray();
463 Array.Sort(gamerList, delegate(GamerInfo a, GamerInfo b)
464 {
465 return a.Gamer.Id.CompareTo(b.Gamer.Id);
466 });
467 return gamerList;
468 }
469 }
470 GamerInfo LocalGamerInfo
471 {
472 get
473 {
474 return mGamers[LocalGamer.Id];
475 }
476 }
477
478 #endregion
479
480
481 // Private types for the implementation of the network protocol
482 #region Private Types
483
484 enum PacketType
485 {
486 Chat = 1,
487 Event = 2
488 }
489
490 enum EventType
491 {
492 KeyDown = 1,
493 KeyUp = 2,
494 MouseDown = 3,
495 MouseUp = 4,
496 MouseMove = 5
497 }
498
499 enum MouseButton
500 {
501 Left = 1,
502 Right = 2,
503 Middle = 3
504 }
505
506 abstract class EventInfo
507 {
508 public NetworkGamer Gamer;
509 public long FrameOfApplication;
510
511 public EventInfo(NetworkGamer gamer, long frameNumber)
512 {
513 Gamer = gamer;
514 FrameOfApplication = frameNumber;
515 }
516
517 public abstract EventType Id
518 {
519 get;
520 }
521 }
522
523 class KeyboardEventInfo : EventInfo
524 {
525 public Keys Key;
526 public bool IsKeyDown;
527
528 public KeyboardEventInfo(NetworkGamer gamer, long frameNumber, Keys key, bool isDown)
529 : base(gamer, frameNumber)
530 {
531 Key = key;
532 IsKeyDown = isDown;
533 }
534
535 public override EventType Id
536 {
537 get { return IsKeyDown ? EventType.KeyDown : EventType.KeyUp; }
538 }
539 }
540
541 class MouseButtonEventInfo : EventInfo
542 {
543 public MouseButton Button;
544 public bool IsButtonDown;
545
546 public MouseButtonEventInfo(NetworkGamer gamer, long frameNumber, MouseButton button, bool isDown)
547 : base(gamer, frameNumber)
548 {
549 Button = button;
550 IsButtonDown = isDown;
551 }
552
553 public override EventType Id
554 {
555 get { return IsButtonDown ? EventType.MouseDown : EventType.MouseUp; }
556 }
557 }
558
559 class MouseMotionEventInfo : EventInfo
560 {
561 public int X;
562 public int Y;
563
564 public MouseMotionEventInfo(NetworkGamer gamer, long frameNumber, int x, int y)
565 : base(gamer, frameNumber)
566 {
567 X = x;
568 Y = y;
569 }
570
571 public override EventType Id
572 {
573 get { return EventType.MouseMove; }
574 }
575 }
576
577 class GamerInfo
578 {
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];
587
588 public GamerInfo(NetworkGamer gamer)
589 {
590 Gamer = gamer;
591 }
592 }
593
594 const int MaximumLatency = 120;
595 const int StallTimeout = 900;
596
597 #endregion
598
599
600 // Private implementation methods of the network protocol
601 #region Private Implementation Methods
602
603 /// <summary>
604 /// Reinitialize the private variables in preparation for a new game to start.
605 /// </summary>
606 void Reset()
607 {
608 mLatency = 1;
609 mHighestFrameNumber = 0;
610 mNextLatencyAdjustmentFrame = 1;
611 mStallCount = 0;
612 mLastStallCount = 0;
613 mAverageOwd = CurrentAverageOneWayDelay;
614
615 mGamers = new Dictionary<byte, GamerInfo>();
616 foreach (NetworkGamer gamer in NetworkGamers)
617 {
618 mGamers.Add(gamer.Id, new GamerInfo(gamer));
619 }
620
621 mGame.ResetGame(GamerArray, LocalGamerInfo);
622 }
623
624
625 void HandleIncomingPackets()
626 {
627 LocalNetworkGamer localGamer = LocalGamer;
628
629 while (localGamer.IsDataAvailable)
630 {
631 NetworkGamer sender;
632
633 localGamer.ReceiveData(mPacketReader, out sender);
634
635 PacketType packetId = (PacketType)mPacketReader.ReadByte();
636 switch (packetId)
637 {
638 case PacketType.Chat:
639
640 short messageLength = mPacketReader.ReadInt16();
641 char[] message = mPacketReader.ReadChars(messageLength);
642
643 ChatInfo chatPacket = new ChatInfo(sender, new String(message));
644 mChatPackets.Add(chatPacket);
645 break;
646
647 case PacketType.Event:
648
649 GamerInfo senderInfo = mGamers[sender.Id];
650
651 int stallCount = mPacketReader.ReadInt16();
652 int averageOwd = mPacketReader.ReadInt16();
653 int frameNumber = mPacketReader.ReadInt32();
654 int numEvents = mPacketReader.ReadByte();
655
656 if (frameNumber <= mNextLatencyAdjustmentFrame)
657 {
658 senderInfo.StallCount = stallCount;
659 senderInfo.AverageOwd = averageOwd;
660 }
661 else
662 {
663 senderInfo.NextStallCount = stallCount;
664 senderInfo.NextAverageOwd = averageOwd;
665 }
666
667 if (frameNumber <= senderInfo.HighestFrameNumber)
668 {
669 #if DEBUG
670 Console.WriteLine("SKP" + (char)sender.Id + ": " + mGame.CurrentFrameNumber + "\t" + frameNumber + "\t<=\t" + senderInfo.HighestFrameNumber + "\t#" + numEvents);
671 #endif
672
673 // we know about all these events, so don't bother reading them
674 break;
675 }
676
677 #if DEBUG
678 Console.WriteLine(" GOT" + (char)sender.Id + ": " + mGame.CurrentFrameNumber + "\t" + frameNumber + "\t>\t" + senderInfo.HighestFrameNumber + "\t#" + numEvents);
679 #endif
680
681 for (int i = 0; i < numEvents; i++)
682 {
683 EventInfo eventInfo = ReadEvent(mPacketReader, sender);
684
685 if (eventInfo != null && eventInfo.FrameOfApplication > senderInfo.HighestFrameNumber)
686 {
687 int index = GetEventArrayIndexForFrame(eventInfo.FrameOfApplication);
688 if (senderInfo.Events[index] == null) senderInfo.Events[index] = new List<EventInfo>();
689 senderInfo.Events[index].Add(eventInfo);
690 }
691 }
692
693 senderInfo.HighestFrameNumber = frameNumber;
694 break;
695
696 default:
697
698 Console.WriteLine("Received unknown packet type: " + (int)packetId);
699 break;
700 }
701 }
702 }
703
704
705 int CurrentEventArrayIndex
706 {
707 get { return GetEventArrayIndexForFrame(mGame.CurrentFrameNumber); }
708 }
709
710 int GetEventArrayIndexForFrame(long frame)
711 {
712 return (int)(frame % MaximumLatency);
713 }
714
715 EventInfo ReadEvent(PacketReader packetReader, NetworkGamer sender)
716 {
717 EventType eventId = (EventType)packetReader.ReadByte();
718 long frameNumber = packetReader.ReadInt32();
719
720 switch (eventId)
721 {
722 case EventType.KeyDown:
723
724 Keys keyCode1 = (Keys)packetReader.ReadInt32();
725 return new KeyboardEventInfo(sender, frameNumber, keyCode1, true);
726
727 case EventType.KeyUp:
728
729 Keys keyCode2 = (Keys)packetReader.ReadInt32();
730 return new KeyboardEventInfo(sender, frameNumber, keyCode2, false);
731
732 case EventType.MouseDown:
733
734 MouseButton buttonId1 = (MouseButton)packetReader.ReadByte();
735 return new MouseButtonEventInfo(sender, frameNumber, buttonId1, true);
736
737 case EventType.MouseUp:
738
739 MouseButton buttonId2 = (MouseButton)packetReader.ReadByte();
740 return new MouseButtonEventInfo(sender, frameNumber, buttonId2, false);
741
742 case EventType.MouseMove:
743
744 short x = packetReader.ReadInt16();
745 short y = packetReader.ReadInt16();
746 return new MouseMotionEventInfo(sender, frameNumber, x, y);
747
748 default:
749
750 Console.WriteLine("Received unknown event type: " + (int)eventId);
751 return null;
752 }
753 }
754
755
756 void WriteChatPacket(String message)
757 {
758 mPacketWriter.Write((byte)PacketType.Chat);
759 mPacketWriter.Write((short)message.Length);
760 mPacketWriter.Write(message.ToCharArray());
761 }
762
763 void WriteEventPacket(List<EventInfo> events, long highestFrameNumber)
764 {
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);
770
771 foreach (EventInfo eventInfo in events)
772 {
773 mPacketWriter.Write((byte)eventInfo.Id);
774 mPacketWriter.Write((int)eventInfo.FrameOfApplication);
775
776 KeyboardEventInfo keyboardEventInfo = eventInfo as KeyboardEventInfo;
777 if (keyboardEventInfo != null)
778 {
779 mPacketWriter.Write((int)keyboardEventInfo.Key);
780 continue;
781 }
782
783 MouseButtonEventInfo mouseButtonEventInfo = eventInfo as MouseButtonEventInfo;
784 if (mouseButtonEventInfo != null)
785 {
786 mPacketWriter.Write((byte)mouseButtonEventInfo.Button);
787 continue;
788 }
789
790 MouseMotionEventInfo mouseMotionEventInfo = eventInfo as MouseMotionEventInfo;
791 if (mouseMotionEventInfo != null)
792 {
793 mPacketWriter.Write((short)mouseMotionEventInfo.X);
794 mPacketWriter.Write((short)mouseMotionEventInfo.Y);
795 continue;
796 }
797 }
798 }
799
800
801 bool IsLatencyAdjustmentFrame
802 {
803 get
804 {
805 return mNextLatencyAdjustmentFrame == mGame.CurrentFrameNumber;
806 }
807 }
808
809 void AdjustLatency()
810 {
811 Debug.Assert(IsLatencyAdjustmentFrame);
812
813 #if DEBUG
814 if (mStallCount > 0)
815 {
816 Console.WriteLine("STL#: " + mGame.CurrentFrameNumber + "\t" + mStallCount);
817 }
818 #endif
819
820 int maxStallCount = 0;
821 int maxAverageOwd = 0;
822
823 foreach (GamerInfo gamerInfo in GamerArray)
824 {
825 if (gamerInfo.StallCount > maxStallCount) maxStallCount = gamerInfo.StallCount;
826 if (gamerInfo.AverageOwd > maxAverageOwd) maxAverageOwd = gamerInfo.AverageOwd;
827
828 gamerInfo.StallCount = gamerInfo.NextStallCount;
829 gamerInfo.AverageOwd = gamerInfo.NextAverageOwd;
830 }
831
832 #if DEBUG
833 int prevLatency = mLatency;
834 #endif
835
836 if (maxStallCount > 0)
837 {
838 mLatency += maxStallCount;
839 }
840 else
841 {
842 mLatency -= (int)(0.6 * (double)(mLatency - maxAverageOwd) + 1.0);
843 }
844
845 if (mLatency < 1) mLatency = 1;
846 if (mLatency > MaximumLatency) mLatency = MaximumLatency;
847
848 #if DEBUG
849 if (prevLatency != mLatency) Console.WriteLine("NLAG: " + mLatency);
850 #endif
851
852 mNextLatencyAdjustmentFrame = mGame.CurrentFrameNumber + mLatency;
853 mAverageOwd = CurrentAverageOneWayDelay;
854
855 mLastLocalEvents = mLocalEvents;
856 mLocalEvents = new List<EventInfo>();
857 }
858
859
860
861 List<EventInfo> GetEventsFromInput()
862 {
863 List<EventInfo> events = new List<EventInfo>();
864
865 long frameOfApplication = mGame.CurrentFrameNumber + mLatency;
866 if (frameOfApplication <= mHighestFrameNumber) return events;
867 else mHighestFrameNumber = frameOfApplication;
868
869 // 1. Find the keyboard differences; written by Peter.
870
871 KeyboardState keyState = Keyboard.GetState();
872
873 List<Keys> pressedKeys = new List<Keys>();
874 List<Keys> releasedKeys = new List<Keys>();
875
876 Keys[] pressedKeysArray = keyState.GetPressedKeys();
877 foreach (Keys k in pressedKeysArray)
878 {
879 if (!mLastPressedKeys.Contains(k)) pressedKeys.Add(k);
880 else mLastPressedKeys.Remove(k);
881 }
882
883 releasedKeys = mLastPressedKeys;
884
885 foreach (Keys key in pressedKeys)
886 {
887 events.Add(new KeyboardEventInfo(LocalGamer, frameOfApplication, key, true));
888 }
889 foreach (Keys key in releasedKeys)
890 {
891 events.Add(new KeyboardEventInfo(LocalGamer, frameOfApplication, key, false));
892 }
893
894 #if DEBUG
895 if (pressedKeys.Contains(Keys.Escape)) mDontSendEvents = true;
896 if (releasedKeys.Contains(Keys.Escape)) mDontSendEvents = false;
897 #endif
898
899 // 2. Find the mouse differences.
900
901 MouseState mouseState = Mouse.GetState();
902
903 bool leftButtonPressed = mouseState.LeftButton == ButtonState.Pressed;
904 if (leftButtonPressed != mLastLeftButtonPressed)
905 {
906 events.Add(new MouseButtonEventInfo(LocalGamer, frameOfApplication, MouseButton.Left, leftButtonPressed));
907 }
908
909 bool rightButtonPressed = mouseState.RightButton == ButtonState.Pressed;
910 if (rightButtonPressed != mLastRightButtonPressed)
911 {
912 events.Add(new MouseButtonEventInfo(LocalGamer, frameOfApplication, MouseButton.Right, rightButtonPressed));
913 }
914
915 bool middleButtonPressed = mouseState.MiddleButton == ButtonState.Pressed;
916 if (middleButtonPressed != mLastMiddleButtonPressed)
917 {
918 events.Add(new MouseButtonEventInfo(LocalGamer, frameOfApplication, MouseButton.Middle, middleButtonPressed));
919 }
920
921 int mousePositionX = mouseState.X;
922 int mousePositionY = mouseState.Y;
923 if (mousePositionX != mLastMousePositionX || mousePositionY != mLastMousePositionY)
924 {
925 events.Add(new MouseMotionEventInfo(LocalGamer, frameOfApplication, mousePositionX, mousePositionY));
926 }
927
928 // 3. Save the current peripheral state.
929
930 mLastPressedKeys = new List<Keys>(pressedKeysArray);
931 mLastLeftButtonPressed = leftButtonPressed;
932 mLastRightButtonPressed = rightButtonPressed;
933 mLastMiddleButtonPressed = middleButtonPressed;
934 mLastMousePositionX = mousePositionX;
935 mLastMousePositionY = mousePositionY;
936
937 return events;
938 }
939
940 void SendLocalEvents()
941 {
942 SendLocalEvents((NetworkGamer)null);
943 }
944
945 void SendLocalEvents(List<NetworkGamer> recipicents)
946 {
947 foreach (NetworkGamer gamer in recipicents)
948 {
949 SendLocalEvents(gamer);
950 }
951 }
952
953 void SendLocalEvents(NetworkGamer recipient)
954 {
955 #if DEBUG
956 if (mDontSendEvents) return;
957 #endif
958
959 List<EventInfo> events = new List<EventInfo>(mLocalEvents);
960 events.AddRange(mLastLocalEvents);
961
962 if (recipient != null && !recipient.IsDisposed)
963 {
964 // if there is a recipient, we are resending old events
965 WriteEventPacket(events, mGame.CurrentFrameNumber - 1);
966 LocalGamer.SendData(mPacketWriter, SendDataOptions.Reliable, recipient);
967 }
968 else
969 {
970 WriteEventPacket(events, mGame.CurrentFrameNumber + mLatency);
971 LocalGamer.SendData(mPacketWriter, SendDataOptions.None);
972 }
973 }
974
975
976 bool HaveNeededEvents
977 {
978 get
979 {
980 long currentFrame = mGame.CurrentFrameNumber;
981
982 foreach (GamerInfo gamerInfo in mGamers.Values)
983 {
984 if (mGame.IsGameOver(gamerInfo)) continue;
985 if (gamerInfo.HighestFrameNumber < currentFrame) return false;
986 }
987
988 return true;
989 }
990 }
991
992 void ApplyEvents()
993 {
994 int index = CurrentEventArrayIndex;
995
996 foreach (GamerInfo gamerInfo in GamerArray)
997 {
998 if (gamerInfo.Events[index] == null) continue;
999 ApplyEvents(gamerInfo, gamerInfo.Events[index]);
1000 gamerInfo.Events[index] = null;
1001 }
1002 }
1003
1004 void ApplyEvents(GamerInfo gamerInfo, List<EventInfo> events)
1005 {
1006 foreach (EventInfo eventInfo in events)
1007 {
1008 KeyboardEventInfo keyboardEventInfo = eventInfo as KeyboardEventInfo;
1009 if (keyboardEventInfo != null)
1010 {
1011 #if DEBUG
1012 Console.WriteLine(" KEY: " + keyboardEventInfo.FrameOfApplication + "\t" + keyboardEventInfo.Key + "," + keyboardEventInfo.IsKeyDown);
1013 #endif
1014
1015 mGame.ApplyKeyInput(gamerInfo, keyboardEventInfo.Key, keyboardEventInfo.IsKeyDown);
1016 continue;
1017 }
1018
1019 MouseButtonEventInfo mouseButtonEventInfo = eventInfo as MouseButtonEventInfo;
1020 if (mouseButtonEventInfo != null)
1021 {
1022 #if DEBUG
1023 Console.WriteLine(" BTN: " + mouseButtonEventInfo.FrameOfApplication + "\t" + mouseButtonEventInfo.IsButtonDown);
1024 #endif
1025
1026 mGame.ApplyMouseButtonInput(gamerInfo, mouseButtonEventInfo.IsButtonDown);
1027 continue;
1028 }
1029
1030 MouseMotionEventInfo mouseMotionEventInfo = eventInfo as MouseMotionEventInfo;
1031 if (mouseMotionEventInfo != null)
1032 {
1033 #if DEBUG
1034 Console.WriteLine(" MMV: " + mouseMotionEventInfo.FrameOfApplication + "\t" + mouseMotionEventInfo.X + "," + mouseMotionEventInfo.Y);
1035 #endif
1036
1037 mGame.ApplyMouseLocationInput(gamerInfo, mouseMotionEventInfo.X, mouseMotionEventInfo.Y);
1038 continue;
1039 }
1040 }
1041 }
1042
1043
1044 int CurrentAverageOneWayDelay
1045 {
1046 get
1047 {
1048 Debug.Assert(mNetworkSession != null);
1049
1050 double numRemoteGamersTwice = 2 * mNetworkSession.RemoteGamers.Count;
1051 double averageOwd = 0;
1052
1053 foreach (NetworkGamer gamer in mNetworkSession.RemoteGamers)
1054 {
1055 TimeSpan timeSpan = gamer.RoundtripTime;
1056 averageOwd += timeSpan.TotalMilliseconds;
1057 }
1058
1059 return (int)((averageOwd / numRemoteGamersTwice) / 16.6666);
1060 }
1061 }
1062
1063 #endregion
1064 }
1065 }
This page took 0.081066 seconds and 5 git commands to generate.