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