]> Dogcows Code - chaz/carfire/blob - CarFire/CarFire/CarFire/NetworkManager.cs
git-svn-id: https://bd85.net/svn/cs3505_group@114 92bb83a3-7c8f-8a45-bc97-515c4e399668
[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
367 mGame.Draw(spriteBatch);
368 }
369 }
370 }
371
372
373 /// <summary>
374 /// Get the chat messages that have been received since the last time this
375 /// method was called.
376 /// </summary>
377 /// <returns>List container of the chat messages.</returns>
378 public List<ChatInfo> ReceiveChats()
379 {
380 List<ChatInfo> chats = mChatPackets;
381 mChatPackets = new List<ChatInfo>();
382 return chats;
383 }
384
385 /// <summary>
386 /// Send a chat message to all gamers in the session. You should already be
387 /// in a session before calling this method.
388 /// </summary>
389 /// <param name="message">The text of the message.</param>
390 public void SendChat(String message)
391 {
392 WriteChatPacket(message);
393 LocalGamer.SendData(mPacketWriter, SendDataOptions.ReliableInOrder);
394 }
395
396 /// <summary>
397 /// Send a chat message to a specific gamer in the session. You should already
398 /// be in a session before calling this method.
399 /// </summary>
400 /// <param name="message">The text of the message.</param>
401 /// <param name="recipient">The gamer to receive the message.</param>
402 public void SendChat(String message, NetworkGamer recipient)
403 {
404 Debug.Assert(recipient != null && !recipient.IsDisposed);
405
406 WriteChatPacket(message);
407 LocalGamer.SendData(mPacketWriter, SendDataOptions.ReliableInOrder, recipient);
408 }
409
410 #endregion
411
412
413 // Private class variable members
414 #region Instance Variables
415
416 NetworkSession mNetworkSession;
417 PacketReader mPacketReader = new PacketReader();
418 PacketWriter mPacketWriter = new PacketWriter();
419
420 JoinedSessionDelegate mJoinedSessionDelegate;
421 FoundSessionsDelegate mFoundSessionsDelegate;
422
423 IScreenManager mLobby;
424 IDeterministicGame mGame;
425
426 List<ChatInfo> mChatPackets = new List<ChatInfo>();
427
428 List<EventInfo> mLocalEvents = new List<EventInfo>();
429 List<EventInfo> mLastLocalEvents = new List<EventInfo>();
430
431 List<Keys> mLastPressedKeys = new List<Keys>();
432 bool mLastLeftButtonPressed;
433 bool mLastRightButtonPressed;
434 bool mLastMiddleButtonPressed;
435 int mLastMousePositionX;
436 int mLastMousePositionY;
437
438 int mLatency;
439 long mHighestFrameNumber;
440 long mNextLatencyAdjustmentFrame;
441 int mStallCount;
442 int mLastStallCount;
443 int mAverageOwd;
444
445 #if DEBUG
446 bool mDontSendEvents;
447 #endif
448
449 TimeSpan mTargetTimeSpan = new TimeSpan(166666);
450 public TimeSpan TargetTimeSpan
451 {
452 get
453 {
454 return mTargetTimeSpan;
455 }
456 }
457
458 Dictionary<byte, GamerInfo> mGamers;
459 GamerInfo[] GamerArray
460 {
461 get
462 {
463 GamerInfo[] gamerList = mGamers.Values.ToArray();
464 Array.Sort(gamerList, delegate(GamerInfo a, GamerInfo b)
465 {
466 return a.Gamer.Id.CompareTo(b.Gamer.Id);
467 });
468 return gamerList;
469 }
470 }
471 GamerInfo LocalGamerInfo
472 {
473 get
474 {
475 return mGamers[LocalGamer.Id];
476 }
477 }
478
479 #endregion
480
481
482 // Private types for the implementation of the network protocol
483 #region Private Types
484
485 enum PacketType
486 {
487 Chat = 1,
488 Event = 2
489 }
490
491 enum EventType
492 {
493 KeyDown = 1,
494 KeyUp = 2,
495 MouseDown = 3,
496 MouseUp = 4,
497 MouseMove = 5
498 }
499
500 enum MouseButton
501 {
502 Left = 1,
503 Right = 2,
504 Middle = 3
505 }
506
507 abstract class EventInfo
508 {
509 public NetworkGamer Gamer;
510 public long FrameOfApplication;
511
512 public EventInfo(NetworkGamer gamer, long frameNumber)
513 {
514 Gamer = gamer;
515 FrameOfApplication = frameNumber;
516 }
517
518 public abstract EventType Id
519 {
520 get;
521 }
522 }
523
524 class KeyboardEventInfo : EventInfo
525 {
526 public Keys Key;
527 public bool IsKeyDown;
528
529 public KeyboardEventInfo(NetworkGamer gamer, long frameNumber, Keys key, bool isDown)
530 : base(gamer, frameNumber)
531 {
532 Key = key;
533 IsKeyDown = isDown;
534 }
535
536 public override EventType Id
537 {
538 get { return IsKeyDown ? EventType.KeyDown : EventType.KeyUp; }
539 }
540 }
541
542 class MouseButtonEventInfo : EventInfo
543 {
544 public MouseButton Button;
545 public bool IsButtonDown;
546
547 public MouseButtonEventInfo(NetworkGamer gamer, long frameNumber, MouseButton button, bool isDown)
548 : base(gamer, frameNumber)
549 {
550 Button = button;
551 IsButtonDown = isDown;
552 }
553
554 public override EventType Id
555 {
556 get { return IsButtonDown ? EventType.MouseDown : EventType.MouseUp; }
557 }
558 }
559
560 class MouseMotionEventInfo : EventInfo
561 {
562 public int X;
563 public int Y;
564
565 public MouseMotionEventInfo(NetworkGamer gamer, long frameNumber, int x, int y)
566 : base(gamer, frameNumber)
567 {
568 X = x;
569 Y = y;
570 }
571
572 public override EventType Id
573 {
574 get { return EventType.MouseMove; }
575 }
576 }
577
578 class GamerInfo
579 {
580 public NetworkGamer Gamer;
581 public long HighestFrameNumber = 0;
582 public int StallCount = 0;
583 public int AverageOwd = 0;
584 public int NextStallCount = 0;
585 public int NextAverageOwd = 0;
586 public bool IsWaitedOn = false;
587 public List<EventInfo>[] Events = new List<EventInfo>[MaximumLatency];
588
589 public GamerInfo(NetworkGamer gamer)
590 {
591 Gamer = gamer;
592 }
593 }
594
595 const int MaximumLatency = 120;
596 const int StallTimeout = 900;
597
598 #endregion
599
600
601 // Private implementation methods of the network protocol
602 #region Private Implementation Methods
603
604 /// <summary>
605 /// Reinitialize the private variables in preparation for a new game to start.
606 /// </summary>
607 void Reset()
608 {
609 mLatency = 1;
610 mHighestFrameNumber = 0;
611 mNextLatencyAdjustmentFrame = 1;
612 mStallCount = 0;
613 mLastStallCount = 0;
614 mAverageOwd = CurrentAverageOneWayDelay;
615
616 mGamers = new Dictionary<byte, GamerInfo>();
617 foreach (NetworkGamer gamer in NetworkGamers)
618 {
619 mGamers.Add(gamer.Id, new GamerInfo(gamer));
620 }
621
622 mGame.ResetGame(GamerArray, LocalGamerInfo);
623 }
624
625
626 void HandleIncomingPackets()
627 {
628 LocalNetworkGamer localGamer = LocalGamer;
629
630 while (localGamer.IsDataAvailable)
631 {
632 NetworkGamer sender;
633
634 localGamer.ReceiveData(mPacketReader, out sender);
635
636 PacketType packetId = (PacketType)mPacketReader.ReadByte();
637 switch (packetId)
638 {
639 case PacketType.Chat:
640
641 short messageLength = mPacketReader.ReadInt16();
642 char[] message = mPacketReader.ReadChars(messageLength);
643
644 ChatInfo chatPacket = new ChatInfo(sender, new String(message));
645 mChatPackets.Add(chatPacket);
646 break;
647
648 case PacketType.Event:
649
650 GamerInfo senderInfo = mGamers[sender.Id];
651
652 int stallCount = mPacketReader.ReadInt16();
653 int averageOwd = mPacketReader.ReadInt16();
654 int frameNumber = mPacketReader.ReadInt32();
655 int numEvents = mPacketReader.ReadByte();
656
657 if (frameNumber <= mNextLatencyAdjustmentFrame)
658 {
659 senderInfo.StallCount = stallCount;
660 senderInfo.AverageOwd = averageOwd;
661 }
662 else
663 {
664 senderInfo.NextStallCount = stallCount;
665 senderInfo.NextAverageOwd = averageOwd;
666 }
667
668 if (frameNumber <= senderInfo.HighestFrameNumber)
669 {
670 #if DEBUG
671 Console.WriteLine("SKP" + (char)sender.Id + ": " + mGame.CurrentFrameNumber + "\t" + frameNumber + "\t<=\t" + senderInfo.HighestFrameNumber + "\t#" + numEvents);
672 #endif
673
674 // we know about all these events, so don't bother reading them
675 break;
676 }
677
678 #if DEBUG
679 Console.WriteLine(" GOT" + (char)sender.Id + ": " + mGame.CurrentFrameNumber + "\t" + frameNumber + "\t>\t" + senderInfo.HighestFrameNumber + "\t#" + numEvents);
680 #endif
681
682 for (int i = 0; i < numEvents; i++)
683 {
684 EventInfo eventInfo = ReadEvent(mPacketReader, sender);
685
686 if (eventInfo != null && eventInfo.FrameOfApplication > senderInfo.HighestFrameNumber)
687 {
688 int index = GetEventArrayIndexForFrame(eventInfo.FrameOfApplication);
689 if (senderInfo.Events[index] == null) senderInfo.Events[index] = new List<EventInfo>();
690 senderInfo.Events[index].Add(eventInfo);
691 }
692 }
693
694 senderInfo.HighestFrameNumber = frameNumber;
695 break;
696
697 default:
698
699 Console.WriteLine("Received unknown packet type: " + (int)packetId);
700 break;
701 }
702 }
703 }
704
705
706 int CurrentEventArrayIndex
707 {
708 get { return GetEventArrayIndexForFrame(mGame.CurrentFrameNumber); }
709 }
710
711 int GetEventArrayIndexForFrame(long frame)
712 {
713 return (int)(frame % MaximumLatency);
714 }
715
716 EventInfo ReadEvent(PacketReader packetReader, NetworkGamer sender)
717 {
718 EventType eventId = (EventType)packetReader.ReadByte();
719 long frameNumber = packetReader.ReadInt32();
720
721 switch (eventId)
722 {
723 case EventType.KeyDown:
724
725 Keys keyCode1 = (Keys)packetReader.ReadInt32();
726 return new KeyboardEventInfo(sender, frameNumber, keyCode1, true);
727
728 case EventType.KeyUp:
729
730 Keys keyCode2 = (Keys)packetReader.ReadInt32();
731 return new KeyboardEventInfo(sender, frameNumber, keyCode2, false);
732
733 case EventType.MouseDown:
734
735 MouseButton buttonId1 = (MouseButton)packetReader.ReadByte();
736 return new MouseButtonEventInfo(sender, frameNumber, buttonId1, true);
737
738 case EventType.MouseUp:
739
740 MouseButton buttonId2 = (MouseButton)packetReader.ReadByte();
741 return new MouseButtonEventInfo(sender, frameNumber, buttonId2, false);
742
743 case EventType.MouseMove:
744
745 short x = packetReader.ReadInt16();
746 short y = packetReader.ReadInt16();
747 return new MouseMotionEventInfo(sender, frameNumber, x, y);
748
749 default:
750
751 Console.WriteLine("Received unknown event type: " + (int)eventId);
752 return null;
753 }
754 }
755
756
757 void WriteChatPacket(String message)
758 {
759 mPacketWriter.Write((byte)PacketType.Chat);
760 mPacketWriter.Write((short)message.Length);
761 mPacketWriter.Write(message.ToCharArray());
762 }
763
764 void WriteEventPacket(List<EventInfo> events, long highestFrameNumber)
765 {
766 mPacketWriter.Write((byte)PacketType.Event);
767 mPacketWriter.Write((short)mLastStallCount);
768 mPacketWriter.Write((short)mAverageOwd);
769 mPacketWriter.Write((int)highestFrameNumber);
770 mPacketWriter.Write((byte)events.Count);
771
772 foreach (EventInfo eventInfo in events)
773 {
774 mPacketWriter.Write((byte)eventInfo.Id);
775 mPacketWriter.Write((int)eventInfo.FrameOfApplication);
776
777 KeyboardEventInfo keyboardEventInfo = eventInfo as KeyboardEventInfo;
778 if (keyboardEventInfo != null)
779 {
780 mPacketWriter.Write((int)keyboardEventInfo.Key);
781 continue;
782 }
783
784 MouseButtonEventInfo mouseButtonEventInfo = eventInfo as MouseButtonEventInfo;
785 if (mouseButtonEventInfo != null)
786 {
787 mPacketWriter.Write((byte)mouseButtonEventInfo.Button);
788 continue;
789 }
790
791 MouseMotionEventInfo mouseMotionEventInfo = eventInfo as MouseMotionEventInfo;
792 if (mouseMotionEventInfo != null)
793 {
794 mPacketWriter.Write((short)mouseMotionEventInfo.X);
795 mPacketWriter.Write((short)mouseMotionEventInfo.Y);
796 continue;
797 }
798 }
799 }
800
801
802 bool IsLatencyAdjustmentFrame
803 {
804 get
805 {
806 return mNextLatencyAdjustmentFrame == mGame.CurrentFrameNumber;
807 }
808 }
809
810 void AdjustLatency()
811 {
812 Debug.Assert(IsLatencyAdjustmentFrame);
813
814 #if DEBUG
815 if (mStallCount > 0)
816 {
817 Console.WriteLine("STL#: " + mGame.CurrentFrameNumber + "\t" + mStallCount);
818 }
819 #endif
820
821 int maxStallCount = 0;
822 int maxAverageOwd = 0;
823
824 foreach (GamerInfo gamerInfo in GamerArray)
825 {
826 if (gamerInfo.StallCount > maxStallCount) maxStallCount = gamerInfo.StallCount;
827 if (gamerInfo.AverageOwd > maxAverageOwd) maxAverageOwd = gamerInfo.AverageOwd;
828
829 gamerInfo.StallCount = gamerInfo.NextStallCount;
830 gamerInfo.AverageOwd = gamerInfo.NextAverageOwd;
831 }
832
833 #if DEBUG
834 int prevLatency = mLatency;
835 #endif
836
837 if (maxStallCount > 0)
838 {
839 mLatency += maxStallCount;
840 }
841 else
842 {
843 mLatency -= (int)(0.6 * (double)(mLatency - maxAverageOwd) + 1.0);
844 }
845
846 if (mLatency < 1) mLatency = 1;
847 if (mLatency > MaximumLatency) mLatency = MaximumLatency;
848
849 #if DEBUG
850 if (prevLatency != mLatency) Console.WriteLine("NLAG: " + mLatency);
851 #endif
852
853 mNextLatencyAdjustmentFrame = mGame.CurrentFrameNumber + mLatency;
854 mAverageOwd = CurrentAverageOneWayDelay;
855
856 mLastLocalEvents = mLocalEvents;
857 mLocalEvents = new List<EventInfo>();
858 }
859
860
861
862 List<EventInfo> GetEventsFromInput()
863 {
864 List<EventInfo> events = new List<EventInfo>();
865
866 long frameOfApplication = mGame.CurrentFrameNumber + mLatency;
867 if (frameOfApplication <= mHighestFrameNumber) return events;
868 else mHighestFrameNumber = frameOfApplication;
869
870 // 1. Find the keyboard differences; written by Peter.
871
872 KeyboardState keyState = Keyboard.GetState();
873
874 List<Keys> pressedKeys = new List<Keys>();
875 List<Keys> releasedKeys = new List<Keys>();
876
877 Keys[] pressedKeysArray = keyState.GetPressedKeys();
878 foreach (Keys k in pressedKeysArray)
879 {
880 if (!mLastPressedKeys.Contains(k)) pressedKeys.Add(k);
881 else mLastPressedKeys.Remove(k);
882 }
883
884 releasedKeys = mLastPressedKeys;
885
886 foreach (Keys key in pressedKeys)
887 {
888 events.Add(new KeyboardEventInfo(LocalGamer, frameOfApplication, key, true));
889 }
890 foreach (Keys key in releasedKeys)
891 {
892 events.Add(new KeyboardEventInfo(LocalGamer, frameOfApplication, key, false));
893 }
894
895 #if DEBUG
896 if (pressedKeys.Contains(Keys.Escape)) mDontSendEvents = true;
897 if (releasedKeys.Contains(Keys.Escape)) mDontSendEvents = false;
898 #endif
899
900 // 2. Find the mouse differences.
901
902 MouseState mouseState = Mouse.GetState();
903
904 bool leftButtonPressed = mouseState.LeftButton == ButtonState.Pressed;
905 if (leftButtonPressed != mLastLeftButtonPressed)
906 {
907 events.Add(new MouseButtonEventInfo(LocalGamer, frameOfApplication, MouseButton.Left, leftButtonPressed));
908 }
909
910 bool rightButtonPressed = mouseState.RightButton == ButtonState.Pressed;
911 if (rightButtonPressed != mLastRightButtonPressed)
912 {
913 events.Add(new MouseButtonEventInfo(LocalGamer, frameOfApplication, MouseButton.Right, rightButtonPressed));
914 }
915
916 bool middleButtonPressed = mouseState.MiddleButton == ButtonState.Pressed;
917 if (middleButtonPressed != mLastMiddleButtonPressed)
918 {
919 events.Add(new MouseButtonEventInfo(LocalGamer, frameOfApplication, MouseButton.Middle, middleButtonPressed));
920 }
921
922 int mousePositionX = mouseState.X;
923 int mousePositionY = mouseState.Y;
924 if (mousePositionX != mLastMousePositionX || mousePositionY != mLastMousePositionY)
925 {
926 events.Add(new MouseMotionEventInfo(LocalGamer, frameOfApplication, mousePositionX, mousePositionY));
927 }
928
929 // 3. Save the current peripheral state.
930
931 mLastPressedKeys = new List<Keys>(pressedKeysArray);
932 mLastLeftButtonPressed = leftButtonPressed;
933 mLastRightButtonPressed = rightButtonPressed;
934 mLastMiddleButtonPressed = middleButtonPressed;
935 mLastMousePositionX = mousePositionX;
936 mLastMousePositionY = mousePositionY;
937
938 return events;
939 }
940
941 void SendLocalEvents()
942 {
943 SendLocalEvents((NetworkGamer)null);
944 }
945
946 void SendLocalEvents(List<NetworkGamer> recipicents)
947 {
948 foreach (NetworkGamer gamer in recipicents)
949 {
950 SendLocalEvents(gamer);
951 }
952 }
953
954 void SendLocalEvents(NetworkGamer recipient)
955 {
956 #if DEBUG
957 if (mDontSendEvents) return;
958 #endif
959
960 List<EventInfo> events = new List<EventInfo>(mLocalEvents);
961 events.AddRange(mLastLocalEvents);
962
963 if (recipient != null && !recipient.IsDisposed)
964 {
965 // if there is a recipient, we are resending old events
966 WriteEventPacket(events, mGame.CurrentFrameNumber - 1);
967 LocalGamer.SendData(mPacketWriter, SendDataOptions.Reliable, recipient);
968 }
969 else
970 {
971 WriteEventPacket(events, mGame.CurrentFrameNumber + mLatency);
972 LocalGamer.SendData(mPacketWriter, SendDataOptions.None);
973 }
974 }
975
976
977 bool HaveNeededEvents
978 {
979 get
980 {
981 long currentFrame = mGame.CurrentFrameNumber;
982
983 foreach (GamerInfo gamerInfo in mGamers.Values)
984 {
985 if (mGame.IsGameOver(gamerInfo)) continue;
986 if (gamerInfo.HighestFrameNumber < currentFrame) return false;
987 }
988
989 return true;
990 }
991 }
992
993 void ApplyEvents()
994 {
995 int index = CurrentEventArrayIndex;
996
997 foreach (GamerInfo gamerInfo in GamerArray)
998 {
999 if (gamerInfo.Events[index] == null) continue;
1000 ApplyEvents(gamerInfo, gamerInfo.Events[index]);
1001 gamerInfo.Events[index] = null;
1002 }
1003 }
1004
1005 void ApplyEvents(GamerInfo gamerInfo, List<EventInfo> events)
1006 {
1007 foreach (EventInfo eventInfo in events)
1008 {
1009 KeyboardEventInfo keyboardEventInfo = eventInfo as KeyboardEventInfo;
1010 if (keyboardEventInfo != null)
1011 {
1012 #if DEBUG
1013 Console.WriteLine(" KEY: " + keyboardEventInfo.FrameOfApplication + "\t" + keyboardEventInfo.Key + "," + keyboardEventInfo.IsKeyDown);
1014 #endif
1015
1016 mGame.ApplyKeyInput(gamerInfo, keyboardEventInfo.Key, keyboardEventInfo.IsKeyDown);
1017 continue;
1018 }
1019
1020 MouseButtonEventInfo mouseButtonEventInfo = eventInfo as MouseButtonEventInfo;
1021 if (mouseButtonEventInfo != null)
1022 {
1023 #if DEBUG
1024 Console.WriteLine(" BTN: " + mouseButtonEventInfo.FrameOfApplication + "\t" + mouseButtonEventInfo.IsButtonDown);
1025 #endif
1026
1027 mGame.ApplyMouseButtonInput(gamerInfo, mouseButtonEventInfo.IsButtonDown);
1028 continue;
1029 }
1030
1031 MouseMotionEventInfo mouseMotionEventInfo = eventInfo as MouseMotionEventInfo;
1032 if (mouseMotionEventInfo != null)
1033 {
1034 #if DEBUG
1035 Console.WriteLine(" MMV: " + mouseMotionEventInfo.FrameOfApplication + "\t" + mouseMotionEventInfo.X + "," + mouseMotionEventInfo.Y);
1036 #endif
1037
1038 mGame.ApplyMouseLocationInput(gamerInfo, mouseMotionEventInfo.X, mouseMotionEventInfo.Y);
1039 continue;
1040 }
1041 }
1042 }
1043
1044
1045 int CurrentAverageOneWayDelay
1046 {
1047 get
1048 {
1049 Debug.Assert(mNetworkSession != null);
1050
1051 double numRemoteGamersTwice = 2 * mNetworkSession.RemoteGamers.Count;
1052 double averageOwd = 0;
1053
1054 foreach (NetworkGamer gamer in mNetworkSession.RemoteGamers)
1055 {
1056 TimeSpan timeSpan = gamer.RoundtripTime;
1057 averageOwd += timeSpan.TotalMilliseconds;
1058 }
1059
1060 return (int)((averageOwd / numRemoteGamersTwice) / 16.6666);
1061 }
1062 }
1063
1064 #endregion
1065 }
1066 }
This page took 0.097689 seconds and 4 git commands to generate.