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