X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;ds=inline;f=Project06%2FCS%203505%20Project%2006%2FCS%203505%20Project%2006%2FNetworkGame.cs;h=2386ef60717983e77271ee1aa4a81a16683769c9;hb=9187849a37e447f2440ef8257e15e8652f0c51e8;hp=68230aa23902fc678beacc23355c37deb417a5c7;hpb=59582c23e256206e8494f8e9752042ef1681a6e1;p=chaz%2Fcarfire
diff --git a/Project06/CS 3505 Project 06/CS 3505 Project 06/NetworkGame.cs b/Project06/CS 3505 Project 06/CS 3505 Project 06/NetworkGame.cs
index 68230aa..2386ef6 100644
--- a/Project06/CS 3505 Project 06/CS 3505 Project 06/NetworkGame.cs
+++ b/Project06/CS 3505 Project 06/CS 3505 Project 06/NetworkGame.cs
@@ -8,67 +8,63 @@ using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
+using System.Collections;
namespace CS_3505_Project_06
{
+ ///
+ /// A manager class to handle network interactions between peers and
+ /// lobby/game switching.
+ ///
public class NetworkGame
{
- NetworkSession mNetworkSession;
-
- ILobby mLobby;
- IDeterministicGame mGame;
-
- TimeSpan mTargetTimeSpan = new TimeSpan(166666);
- public TimeSpan TargetTimeSpan
- {
- get
- {
- return mTargetTimeSpan;
- }
- }
-
- List lastPressedKeys;
- bool lastButtonPressed;
-
- Object[] playerIdentifiers = { "One", "Two", "Three", "Four" }; // Any objects will do, strings are easy to debug.
-
- // For debugging
-
- Object activePlayer;
- bool paused;
- long lastAutoPause;
-
- public SpriteFont font;
-
-
+ // Public methods and properties
+ #region Public Methods
+
+ ///
+ /// Called when a session has been created or joined using CreateSession() or JoinSession().
+ ///
+ /// The new session that was created or joined.
+ /// The NetworkGame that joined the session.
+ public delegate void JoinedSessionDelegate(NetworkSession session, NetworkGame networkGame);
+
+ ///
+ /// Called when sessions are found as a result of calling FindSessions().
+ ///
+ /// A container of the available sessions.
+ /// The NetworkGame that searched for the sessions.
+ public delegate void FoundSessionsDelegate(AvailableNetworkSessionCollection sessions, NetworkGame networkGame);
+
+
+ ///
+ /// Construct a NetworkGame with a lobby and a game.
+ ///
+ /// Provides an associated lobby to update and draw.
+ /// Provides a game object to be played over the network.
public NetworkGame(ILobby lobby, IDeterministicGame game)
{
Debug.Assert(lobby != null && game != null);
mLobby = lobby;
mGame = game;
-
- // Begin: Test harness stuff
- lastPressedKeys = new List();
- activePlayer = playerIdentifiers[0];
- paused = false;
-
- // Reset the game - indicate that player #1 (player 0) owns this instance of the game.
-
- mGame.ResetGame(playerIdentifiers, playerIdentifiers[0]);
}
+ ///
+ /// Get the Gamer object for the local player.
+ ///
public LocalNetworkGamer LocalGamer
{
get
{
+ // TODO: Is this the correct way to get the single local gamer?
return mNetworkSession.LocalGamers[0];
}
}
- // I added this as I needed a way to display all gamers not just the first gamer
- // -Brady
+ ///
+ /// Get all the gamers associated with the active network session.
+ ///
public GamerCollection NetworkGamers
{
get
@@ -78,53 +74,99 @@ namespace CS_3505_Project_06
}
- public NetworkSession CreateSession()
+ ///
+ /// Begin a new network session with the local gamer as the host. You must not
+ /// call this method or use JoinSession without first using LeaveSession.
+ ///
+ /// The delegate/method to call when the session is created.
+ public void CreateSession(JoinedSessionDelegate callback)
{
- return CreateSession(mGame.MaximumSupportedPlayers);
+ CreateSession(mGame.MaximumSupportedPlayers, callback);
}
- public NetworkSession CreateSession(int maxGamers)
+ ///
+ /// Begin a new network session with the local gamer as the host. You must not
+ /// call this method or use JoinSession without first using LeaveSession.
+ ///
+ /// Provide the maximum number of players allowed to connect.
+ /// The delegate/method to call when the session is created.
+ public void CreateSession(int maxGamers, JoinedSessionDelegate callback)
{
- Debug.Assert(mNetworkSession == null);
+ Debug.Assert(mNetworkSession == null);
- mNetworkSession = NetworkSession.Create(NetworkSessionType.SystemLink, 1, maxGamers);
+ mJoinedSessionDelegate = callback;
+ NetworkSession.BeginCreate(NetworkSessionType.SystemLink, 1, maxGamers, CreateSessionEnd, null);
+ }
+ private void CreateSessionEnd(IAsyncResult result)
+ {
+ Debug.Assert(mNetworkSession == null);
+
+ mNetworkSession = NetworkSession.EndCreate(result);
mNetworkSession.AllowHostMigration = true;
mNetworkSession.AllowJoinInProgress = false;
- return mNetworkSession;
+ mJoinedSessionDelegate(mNetworkSession, this);
}
- // added so I can test if sessionExists and thus be able to call things on NetworkGame safely
- // -Brady
- public bool sessionExists()
+ ///
+ /// Determine whether or not the network game object is associated with any network session.
+ ///
+ /// True if there exists a NetworkSession; false otherwise.
+ public bool HasActiveSession
{
- return mNetworkSession != null;
+ get
+ {
+ return mNetworkSession != null;
+ }
}
- public AvailableNetworkSessionCollection FindSessions()
+
+ ///
+ /// Find available sessions to join. You should not already be in a session when
+ /// calling this method; call LeaveSession first.
+ ///
+ /// The delegate/method to call when the search finishes.
+ public void FindSessions(FoundSessionsDelegate callback)
{
- return NetworkSession.Find(NetworkSessionType.SystemLink, 1, new NetworkSessionProperties());
+ Debug.Assert(mNetworkSession == null);
+
+ mFoundSessionsDelegate = callback;
+ NetworkSession.BeginFind(NetworkSessionType.SystemLink, 1, null, new AsyncCallback(FindSessionsEnd), null);
+ }
+ private void FindSessionsEnd(IAsyncResult result)
+ {
+ AvailableNetworkSessionCollection sessions = NetworkSession.EndFind(result);
+ mFoundSessionsDelegate(sessions, this);
}
- public NetworkSession JoinSession(AvailableNetworkSession availableSession)
+ ///
+ /// Join a network session found using FindSessions(). This is for joining a game that
+ /// somebody else has already started hosting. You must not already be in a session.
+ ///
+ /// Pass the session object to try to join.
+ /// The delegate/method to call when the search finishes.
+ public void JoinSession(AvailableNetworkSession availableSession, JoinedSessionDelegate callback)
{
Debug.Assert(mNetworkSession == null);
- mNetworkSession = NetworkSession.Join(availableSession);
-
- return mNetworkSession;
+ mJoinedSessionDelegate = callback;
+ NetworkSession.BeginJoin(availableSession, JoinSessionEnd, null);
}
-
- // added to begin the game. I made the LobbyGUI make sure that only the host will call it when everyone is ready.
- // This is already taken care of in the update method below. But it may be nice to allow the host to signal the start
- // rather then having it start automatically. Just a suggestion.
- // -Brady
- public void StartGame()
+ private void JoinSessionEnd(IAsyncResult result)
{
- mNetworkSession.StartGame();
- mNetworkSession.ResetReady();
+ Debug.Assert(mNetworkSession == null);
+
+ mNetworkSession = NetworkSession.EndJoin(result);
+
+ mJoinedSessionDelegate(mNetworkSession, this);
+ mJoinedSessionDelegate = null;
}
+
+ ///
+ /// Leave and dispose of any currently associated network session. You will find yourself
+ /// back in the lobby. You must already be in a session to leave it.
+ ///
public void LeaveSession()
{
Debug.Assert(mNetworkSession != null);
@@ -134,6 +176,9 @@ namespace CS_3505_Project_06
}
+ ///
+ /// Set up the network session to simulate 200ms latency and 10% packet loss.
+ ///
public void SimulateBadNetwork()
{
Debug.Assert(mNetworkSession != null);
@@ -143,6 +188,36 @@ namespace CS_3505_Project_06
}
+ ///
+ /// Indicate that the game should begin (moving players from the lobby to the game).
+ /// You must call CreateSession() before calling this.
+ ///
+ public void StartGame()
+ {
+ Debug.Assert(mNetworkSession != null && mNetworkSession.IsHost &&
+ mNetworkSession.AllGamers.Count >= mGame.MinimumSupportedPlayers &&
+ mNetworkSession.IsEveryoneReady);
+
+ ForceStartGame();
+ }
+
+ ///
+ /// Indicate that the game should begin. This is like StartGame() without the sanity
+ /// checks. Use this for debugging.
+ ///
+ public void ForceStartGame()
+ {
+ mNetworkSession.StartGame();
+ mNetworkSession.ResetReady();
+
+ //Reset();
+ }
+
+
+ ///
+ /// Manages the network session and allows either the lobby or game to update.
+ ///
+ /// Pass the time away.
public void Update(GameTime gameTime)
{
if (mNetworkSession == null)
@@ -152,136 +227,719 @@ namespace CS_3505_Project_06
else
{
mNetworkSession.Update();
+ HandleIncomingPackets();
if (mNetworkSession.SessionState == NetworkSessionState.Lobby)
{
- if (mNetworkSession.IsHost &&
- mNetworkSession.AllGamers.Count >= mGame.MinimumSupportedPlayers &&
- mNetworkSession.IsEveryoneReady)
+ mLobby.Update(gameTime, this);
+
+ //if state started as lobby and was changed to playing, call reset.
+ if (mNetworkSession.SessionState == NetworkSessionState.Playing)
+ Reset();
+ }
+ else if (mNetworkSession.SessionState == NetworkSessionState.Playing)
+ {
+ //TODO reset needs to be called for all players, currently LocalGamerInfo throughs exception for all nonhosts
+ // because in start game reset is only called on hosts games thus other clients never get an updated list
+ // gamers.
+
+
+//thoughs exeption see TODO above
+ //if (mGame.IsGameOver(LocalGamerInfo) || mGame.IsTerminated(LocalGamerInfo))
+ //{
+ // // TODO: Should support moving back to the session lobby.
+ // LeaveSession();
+ // return;
+ //}
+
+ if (HaveNeededEvents)
{
- mNetworkSession.StartGame();
- mNetworkSession.ResetReady();
+ if (IsLatencyAdjustmentFrame)
+ {
+ AdjustLatency();
+ mStallCount = 0;
+ }
+ mLocalEvents.AddRange(GetEventsFromInput());
+ SendLocalEvents();
+ ApplyEvents();
+ mGame.Update(mTargetTimeSpan);
}
- else
+ else // Stall!
{
- mLobby.Update(gameTime, this);
+ mStallCount++;
+
+ if (mStallCount % 60 == 0)
+ {
+ Console.WriteLine("Stalled for " + mStallCount + " frames.");
+ }
+
+ /*if (mStallCount > StallTimeout)
+ {
+ DropLostGamers();
+ mStallCount = 0;
+ }
+ else if (mStallCount == 1)
+ {
+ SendLocalEvents
+ }
+ else if (mStallCount % 60 == 0)
+ {
+ } TODO */
}
}
+ }
+ }
+
+ ///
+ /// Allows either the lobby or the game to draw, depending on the state
+ /// of the network connection and whether or not a game is in progress.
+ ///
+ /// Pass the time away.
+ /// The sprite batch.
+ public void Draw(GameTime gameTime, SpriteBatch spriteBatch)
+ {
+ if (mNetworkSession == null)
+ {
+ mLobby.Draw(spriteBatch);
+ }
+ else
+ {
+ if (mNetworkSession.SessionState == NetworkSessionState.Lobby)
+ {
+ mLobby.Draw(spriteBatch);
+ }
else if (mNetworkSession.SessionState == NetworkSessionState.Playing)
{
- // TODO: in-game update stuff
- UpdateTestHarness(gameTime);
+ mGame.Draw(spriteBatch);
+ }
+ }
+ }
+
+
+ ///
+ /// Get the chat messages that have been receive since the last time this
+ /// method was called.
+ ///
+ /// List container of the chat messages.
+ public List ReceiveChats()
+ {
+ List chats = mChatPackets;
+ mChatPackets = new List();
+ return chats;
+ }
+
+ ///
+ /// Send a chat message to all gamers in the session. You should already be
+ /// in a session before calling this method.
+ ///
+ /// The text of the message.
+ public void SendChat(String message)
+ {
+ WriteChatPacket(message);
+ LocalGamer.SendData(mPacketWriter, SendDataOptions.ReliableInOrder);
+ }
+
+ ///
+ /// Send a chat message to a specific gamer in the session. You should already
+ /// be in a session before calling this method.
+ ///
+ /// The text of the message.
+ /// The gamer to receive the message.
+ public void SendChat(String message, NetworkGamer recipient)
+ {
+ WriteChatPacket(message);
+ LocalGamer.SendData(mPacketWriter, SendDataOptions.ReliableInOrder, recipient);
+ }
+
+ #endregion
+
+
+ // Private class variable members
+ #region Instance Variables
+
+ NetworkSession mNetworkSession;
+ PacketReader mPacketReader = new PacketReader();
+ PacketWriter mPacketWriter = new PacketWriter();
+
+ JoinedSessionDelegate mJoinedSessionDelegate;
+ FoundSessionsDelegate mFoundSessionsDelegate;
+
+ ILobby mLobby;
+ IDeterministicGame mGame;
+
+ List mChatPackets = new List();
+
+ List mLocalEvents = new List();
+ List mLastLocalEvents = new List();
+
+ List mLastPressedKeys = new List();
+ bool mLastLeftButtonPressed;
+ bool mLastRightButtonPressed;
+ bool mLastMiddleButtonPressed;
+ int mLastMousePositionX;
+ int mLastMousePositionY;
+
+ int mLatency;
+ long mNextLatencyAdjustmentFrame;
+ int mStallCount;
+ int mAverageOwd;
+
+ TimeSpan mTargetTimeSpan = new TimeSpan(166666);
+ public TimeSpan TargetTimeSpan
+ {
+ get
+ {
+ return mTargetTimeSpan;
+ }
+ }
+
+ Dictionary mGamers;
+ GamerInfo[] GamerArray
+ {
+ get
+ {
+ GamerInfo[] gamerList = mGamers.Values.ToArray();
+ Array.Sort(gamerList, delegate(GamerInfo a, GamerInfo b)
+ {
+ return a.Gamer.Id.CompareTo(b.Gamer.Id);
+ });
+ return gamerList;
+ }
+ }
+ GamerInfo LocalGamerInfo
+ {
+ get
+ {
+ return mGamers[LocalGamer.Id];
+ }
+ }
+
+ #endregion
+
+
+ // Private types for the implementation of the network protocol
+ #region Private Types
+
+ enum PacketType
+ {
+ Chat = 1,
+ Event = 2,
+ Stall = 3
+ }
+
+ enum EventType
+ {
+ KeyDown = 1,
+ KeyUp = 2,
+ MouseDown = 3,
+ MouseUp = 4,
+ MouseMove = 5
+ }
+
+ enum MouseButton
+ {
+ Left = 1,
+ Right = 2,
+ Middle = 3
+ }
+
+ abstract class EventInfo
+ {
+ public NetworkGamer Gamer;
+ public long FrameOfApplication;
+
+ public EventInfo(NetworkGamer gamer, long frameNumber)
+ {
+ Gamer = gamer;
+ FrameOfApplication = frameNumber;
+ }
+
+ public abstract EventType Id
+ {
+ get;
+ }
+ }
+
+ class KeyboardEventInfo : EventInfo
+ {
+ public Keys Key;
+ public bool IsKeyDown;
+
+ public KeyboardEventInfo(NetworkGamer gamer, long frameNumber, Keys key, bool isDown)
+ : base(gamer, frameNumber)
+ {
+ Key = key;
+ IsKeyDown = isDown;
+ }
+
+ public override EventType Id
+ {
+ get { return IsKeyDown ? EventType.KeyDown : EventType.KeyUp; }
+ }
+ }
+
+ class MouseButtonEventInfo : EventInfo
+ {
+ public MouseButton Button;
+ public bool IsButtonDown;
+
+ public MouseButtonEventInfo(NetworkGamer gamer, long frameNumber, MouseButton button, bool isDown)
+ : base(gamer, frameNumber)
+ {
+ Button = button;
+ IsButtonDown = isDown;
+ }
+
+ public override EventType Id
+ {
+ get { return IsButtonDown ? EventType.MouseDown : EventType.MouseUp; }
+ }
+ }
+
+ class MouseMotionEventInfo : EventInfo
+ {
+ public int X;
+ public int Y;
+
+ public MouseMotionEventInfo(NetworkGamer gamer, long frameNumber, int x, int y)
+ : base(gamer, frameNumber)
+ {
+ X = x;
+ Y = y;
+ }
+
+ public override EventType Id
+ {
+ get { return EventType.MouseMove; }
+ }
+ }
+
+ class GamerInfo
+ {
+ public NetworkGamer Gamer;
+ public long HighestFrameNumber = 0;
+ public int StallCount = 0;
+ public int AverageOwd = 0;
+ public bool IsWaitedOn = false;
+ public List[] Events = new List[MaximumLatency];
+
+ public GamerInfo(NetworkGamer gamer)
+ {
+ Gamer = gamer;
+ }
+ }
+
+ const int MaximumLatency = 120;
+ const int StallTimeout = 900;
+
+ #endregion
+
+
+ // Private implementation methods of the network protocol
+ #region Private Implementation Methods
+
+ ///
+ /// Reinitialize the private variables in preparation for a new game to start.
+ ///
+ void Reset()
+ {
+ mLatency = 1;
+ mNextLatencyAdjustmentFrame = 1;
+ mStallCount = 0;
+ mAverageOwd = CurrentAverageOneWayDelay;
+
+ mGamers = new Dictionary();
+ foreach (NetworkGamer gamer in NetworkGamers)
+ {
+ mGamers.Add(gamer.Id, new GamerInfo(gamer));
+ }
+
+ mGame.ResetGame(GamerArray, LocalGamerInfo);
+ }
+
+
+ void HandleIncomingPackets()
+ {
+ LocalNetworkGamer localGamer = LocalGamer;
+
+ while (localGamer.IsDataAvailable)
+ {
+ NetworkGamer sender;
+
+ localGamer.ReceiveData(mPacketReader, out sender);
+ GamerInfo senderInfo = mGamers[sender.Id];
+
+ PacketType packetId = (PacketType)mPacketReader.ReadByte();
+ switch (packetId)
+ {
+ case PacketType.Chat:
+
+ short messageLength = mPacketReader.ReadInt16();
+ char[] message = mPacketReader.ReadChars(messageLength);
+
+ ChatInfo chatPacket = new ChatInfo(sender, new String(message));
+ mChatPackets.Add(chatPacket);
+ break;
+
+ case PacketType.Event:
+
+ short stallCount = mPacketReader.ReadInt16();
+ short averageOwd = mPacketReader.ReadInt16();
+ int frameNumber = mPacketReader.ReadInt32();
+ byte numEvents = mPacketReader.ReadByte();
+
+ if (frameNumber <= senderInfo.HighestFrameNumber)
+ {
+ // we know about all these events, so don't bother reading them
+ break;
+ }
+
+ for (byte i = 0; i < numEvents; ++i)
+ {
+ EventInfo eventInfo = ReadEvent(mPacketReader, sender);
+
+ if (eventInfo != null && eventInfo.FrameOfApplication < senderInfo.HighestFrameNumber)
+ {
+ int index = EventArrayIndex;
+ if (senderInfo.Events[index] == null) senderInfo.Events[index] = new List();
+ senderInfo.Events[index].Add(eventInfo);
+ }
+ }
+
+ senderInfo.StallCount = stallCount;
+ senderInfo.AverageOwd = averageOwd;
+ senderInfo.HighestFrameNumber = frameNumber;
+ break;
+
+ case PacketType.Stall:
+
+ byte numStalledPeers = mPacketReader.ReadByte();
+ byte[] stalledPeers = mPacketReader.ReadBytes(numStalledPeers);
+
+ // TODO
+
+ break;
- mGame.Update(mTargetTimeSpan);
+ default:
+
+ Console.WriteLine("Received unknown packet type: " + (int)packetId);
+ break;
}
}
}
- public void Draw(GameTime gameTime, SpriteBatch spriteBatch)
+
+ int EventArrayIndex
{
- if (mNetworkSession.SessionState != NetworkSessionState.Playing)
- DrawTestHarness(gameTime, spriteBatch);
- else
- mLobby.Draw(spriteBatch);
-
-
+ get { return (int)(mGame.CurrentFrameNumber % MaximumLatency); }
}
+ EventInfo ReadEvent(PacketReader packetReader, NetworkGamer sender)
+ {
+ EventType eventId = (EventType)packetReader.ReadByte();
+ long frameNumber = packetReader.ReadInt32();
+
+ switch (eventId)
+ {
+ case EventType.KeyDown:
+
+ int keyCode1 = packetReader.ReadInt32();
+ return new KeyboardEventInfo(sender, frameNumber, (Keys)keyCode1, true);
+
+ case EventType.KeyUp:
+
+ int keyCode2 = packetReader.ReadInt32();
+ return new KeyboardEventInfo(sender, frameNumber, (Keys)keyCode2, false);
+ case EventType.MouseDown:
+ byte buttonId1 = packetReader.ReadByte();
+ return new MouseButtonEventInfo(sender, frameNumber, (MouseButton)buttonId1, true);
- void UpdateTestHarness(GameTime gameTime)
+ case EventType.MouseUp:
+
+ byte buttonId2 = packetReader.ReadByte();
+ return new MouseButtonEventInfo(sender, frameNumber, (MouseButton)buttonId2, false);
+
+ case EventType.MouseMove:
+
+ short x = packetReader.ReadInt16();
+ short y = packetReader.ReadInt16();
+ return new MouseMotionEventInfo(sender, frameNumber, x, y);
+
+ default:
+
+ Console.WriteLine("Received unknown event type: " + (int)eventId);
+ return null;
+ }
+ }
+
+
+ void WriteChatPacket(String message)
{
- // Get user's input state.
+ mPacketWriter.Write((byte)PacketType.Chat);
+ mPacketWriter.Write((short)message.Length);
+ mPacketWriter.Write(message.ToCharArray());
+ }
- KeyboardState keyState = Keyboard.GetState();
- MouseState mouseState = Mouse.GetState();
+ void WriteEventPacket(List events)
+ {
+ mPacketWriter.Write((byte)PacketType.Event);
+ mPacketWriter.Write((short)mStallCount);
+ mPacketWriter.Write((short)mAverageOwd);
+ mPacketWriter.Write((int)(mGame.CurrentFrameNumber + mLatency));
+ mPacketWriter.Write((byte)events.Count);
+
+ foreach (EventInfo eventInfo in events)
+ {
+ mPacketWriter.Write((byte)eventInfo.Id);
+ mPacketWriter.Write((int)eventInfo.FrameOfApplication);
+
+ KeyboardEventInfo keyboardEventInfo = eventInfo as KeyboardEventInfo;
+ if (keyboardEventInfo != null)
+ {
+ mPacketWriter.Write((int)keyboardEventInfo.Key);
+ continue;
+ }
+
+ MouseButtonEventInfo mouseButtonEventInfo = eventInfo as MouseButtonEventInfo;
+ if (mouseButtonEventInfo != null)
+ {
+ mPacketWriter.Write((byte)mouseButtonEventInfo.Button);
+ continue;
+ }
+
+ MouseMotionEventInfo mouseMotionEventInfo = eventInfo as MouseMotionEventInfo;
+ if (mouseMotionEventInfo != null)
+ {
+ mPacketWriter.Write((short)mouseMotionEventInfo.X);
+ mPacketWriter.Write((short)mouseMotionEventInfo.Y);
+ continue;
+ }
+ }
+ }
- // Make a list of the keys pressed or released this frame.
+
+ bool IsLatencyAdjustmentFrame
+ {
+ get
+ {
+ return mNextLatencyAdjustmentFrame == mGame.CurrentFrameNumber;
+ }
+ }
+
+ void AdjustLatency()
+ {
+ Debug.Assert(IsLatencyAdjustmentFrame);
+
+ int maxStallCount = 0;
+ int maxAverageOwd = 0;
+
+ foreach (GamerInfo gamerInfo in GamerArray)
+ {
+ if (gamerInfo.StallCount > maxStallCount) maxStallCount = gamerInfo.StallCount;
+ if (gamerInfo.AverageOwd > maxAverageOwd) maxAverageOwd = gamerInfo.AverageOwd;
+ }
+
+ // DEBUG
+ int prevLatency = mLatency;
+
+ if (maxStallCount > 0)
+ {
+ mLatency += maxStallCount;
+ }
+ else
+ {
+ mLatency = (int)(0.6 * (double)(mLatency - maxAverageOwd) + 1.0);
+ }
+
+ // DEBUG OUTPUT
+ if (prevLatency != mLatency) Console.WriteLine("Latency readjusted to " + mLatency);
+
+ if (mLatency < 1) mLatency = 1;
+ if (mLatency > MaximumLatency) mLatency = MaximumLatency;
+
+ mNextLatencyAdjustmentFrame = mGame.CurrentFrameNumber + mLatency;
+ mAverageOwd = CurrentAverageOneWayDelay;
+
+ mLastLocalEvents = mLocalEvents;
+ mLocalEvents = new List();
+ }
+
+
+
+ List GetEventsFromInput()
+ {
+ List events = new List();
+
+ // 1. Find the keyboard differences; written by Peter.
+
+ KeyboardState keyState = Keyboard.GetState();
List pressedKeys = new List();
List releasedKeys = new List();
Keys[] pressedKeysArray = keyState.GetPressedKeys();
foreach (Keys k in pressedKeysArray)
- if (!lastPressedKeys.Contains(k))
- pressedKeys.Add(k);
- else
- lastPressedKeys.Remove(k);
+ {
+ if (!mLastPressedKeys.Contains(k)) pressedKeys.Add(k);
+ else mLastPressedKeys.Remove(k);
+ }
- releasedKeys = lastPressedKeys;
- lastPressedKeys = new List(pressedKeysArray);
+ releasedKeys = mLastPressedKeys;
- // Get mouse button state.
+ foreach (Keys key in pressedKeys)
+ {
+ events.Add(new KeyboardEventInfo(LocalGamer, mGame.CurrentFrameNumber, key, true));
+ }
+ foreach (Keys key in releasedKeys)
+ {
+ events.Add(new KeyboardEventInfo(LocalGamer, mGame.CurrentFrameNumber, key, false));
+ }
- bool buttonPressed = mouseState.LeftButton == ButtonState.Pressed;
+ // 2. Find the mouse differences.
- /***** Begining of game logic. *****/
+ MouseState mouseState = Mouse.GetState();
- // Debug - allow user on this machine to direct input to any player's state in the game.
+ bool leftButtonPressed = mouseState.LeftButton == ButtonState.Pressed;
+ if (leftButtonPressed != mLastLeftButtonPressed)
+ {
+ events.Add(new MouseButtonEventInfo(LocalGamer, mGame.CurrentFrameNumber, MouseButton.Left, leftButtonPressed));
+ }
- if (pressedKeys.Contains(Keys.F1)) activePlayer = playerIdentifiers[0];
- if (pressedKeys.Contains(Keys.F2)) activePlayer = playerIdentifiers[1];
- if (pressedKeys.Contains(Keys.F3)) activePlayer = playerIdentifiers[2];
- if (pressedKeys.Contains(Keys.F4)) activePlayer = playerIdentifiers[3];
+ bool rightButtonPressed = mouseState.LeftButton == ButtonState.Pressed;
+ if (rightButtonPressed != mLastRightButtonPressed)
+ {
+ events.Add(new MouseButtonEventInfo(LocalGamer, mGame.CurrentFrameNumber, MouseButton.Right, rightButtonPressed));
+ }
- // Debug - allow user on this machine to pause/resume game state advances.
+ bool middleButtonPressed = mouseState.LeftButton == ButtonState.Pressed;
+ if (middleButtonPressed != mLastMiddleButtonPressed)
+ {
+ events.Add(new MouseButtonEventInfo(LocalGamer, mGame.CurrentFrameNumber, MouseButton.Middle, middleButtonPressed));
+ }
- if (pressedKeys.Contains(Keys.F12) ||
- pressedKeys.Contains(Keys.P) && (keyState.IsKeyDown(Keys.LeftControl) || keyState.IsKeyDown(Keys.RightControl)))
+ int mousePositionX = mouseState.X;
+ int mousePositionY = mouseState.Y;
+ if (mousePositionX != mLastMousePositionX || mousePositionY != mLastMousePositionY)
{
- paused = !paused;
- return; // Don't update on pause start or stop
+ events.Add(new MouseMotionEventInfo(LocalGamer, mGame.CurrentFrameNumber, mousePositionX, mousePositionY));
}
- // Debug - automatically pause every 1000 frames.
+ // 3. Save the current peripheral state.
+
+ mLastPressedKeys = new List(pressedKeysArray);
+ mLastLeftButtonPressed = leftButtonPressed;
+ mLastRightButtonPressed = rightButtonPressed;
+ mLastMiddleButtonPressed = middleButtonPressed;
+ mLastMousePositionX = mousePositionX;
+ mLastMousePositionY = mousePositionY;
- if (mGame.CurrentFrameNumber % 1000 == 0 && mGame.CurrentFrameNumber != lastAutoPause)
+ return events;
+ }
+
+ void SendLocalEvents()
+ {
+ SendLocalEvents((NetworkGamer)null);
+ }
+
+ void SendLocalEvents(List recipicents)
+ {
+ foreach (NetworkGamer gamer in recipicents)
{
- paused = true;
- lastAutoPause = mGame.CurrentFrameNumber;
+ SendLocalEvents(gamer);
}
+ }
+ void SendLocalEvents(NetworkGamer recipient)
+ {
+ List events = new List(mLocalEvents);
+ events.AddRange(mLastLocalEvents);
- //if (pressedKeys.Contains(Keys.Escape))
- // this.Exit();
-
- // Game update
+ WriteEventPacket(events);
- // Direct inputs to the game engine - only report changes.
+ if (recipient != null)
+ {
+ LocalGamer.SendData(mPacketWriter, SendDataOptions.Reliable, recipient);
+ }
+ else
+ {
+ LocalGamer.SendData(mPacketWriter, SendDataOptions.None);
+ }
+ }
- foreach (Keys k in pressedKeys)
- mGame.ApplyKeyInput(activePlayer, k, true);
- foreach (Keys k in releasedKeys)
- mGame.ApplyKeyInput(activePlayer, k, false);
+ bool HaveNeededEvents
+ {
+ get
+ {
+ long currentFrame = mGame.CurrentFrameNumber;
- mGame.ApplyMouseLocationInput(activePlayer, mouseState.X, mouseState.Y);
+ foreach (GamerInfo gamerInfo in mGamers.Values)
+ {
+ if (gamerInfo.HighestFrameNumber < currentFrame) return false;
+ }
- if (lastButtonPressed != buttonPressed)
- mGame.ApplyMouseButtonInput(activePlayer, buttonPressed);
+ return true;
+ }
+ }
- lastButtonPressed = buttonPressed;
+ void ApplyEvents()
+ {
+ int index = EventArrayIndex;
- if (!paused)
+ foreach (GamerInfo gamerInfo in GamerArray)
{
- // Advance the game engine.
+ if (gamerInfo.Events[index] == null) continue;
+
+ foreach (EventInfo eventInfo in gamerInfo.Events[index])
+ {
+ KeyboardEventInfo keyboardEventInfo = eventInfo as KeyboardEventInfo;
+ if (keyboardEventInfo != null)
+ {
+ mGame.ApplyKeyInput(gamerInfo, keyboardEventInfo.Key, keyboardEventInfo.IsKeyDown);
+ continue;
+ }
+
+ MouseButtonEventInfo mouseButtonEventInfo = eventInfo as MouseButtonEventInfo;
+ if (mouseButtonEventInfo != null)
+ {
+ mGame.ApplyMouseButtonInput(gamerInfo, mouseButtonEventInfo.IsButtonDown);
+ continue;
+ }
+
+ MouseMotionEventInfo mouseMotionEventInfo = eventInfo as MouseMotionEventInfo;
+ if (mouseMotionEventInfo != null)
+ {
+ mGame.ApplyMouseLocationInput(gamerInfo, mouseMotionEventInfo.X, mouseMotionEventInfo.Y);
+ continue;
+ }
+ }
- mGame.Update(mTargetTimeSpan);
+ gamerInfo.Events[index] = null;
}
}
- void DrawTestHarness(GameTime gameTime, SpriteBatch spriteBatch)
+
+ int CurrentAverageOneWayDelay
{
+ get
+ {
+ Debug.Assert(mNetworkSession != null);
- // BEGIN: Test harness stuff.
- if (paused && gameTime.TotalRealTime.Milliseconds < 500)
- spriteBatch.DrawString(font, "-=> Paused <=-", new Vector2(10, 130), Color.White);
+ double numRemoteGamersTwice = 2 * mNetworkSession.RemoteGamers.Count;
+ double averageOwd = 0;
- spriteBatch.DrawString(font, "Press [F1]...[F4] to simulate input for each player. Click X's to end game or terminate player.", new Vector2(10, 540), Color.White);
- spriteBatch.DrawString(font, "Press [ESC] to exit and [F12] to pause/unpause. Game auto-pauses every 1000 frames.", new Vector2(10, 570), Color.White);
- //END: Test harness stuff.
+ foreach (NetworkGamer gamer in mNetworkSession.RemoteGamers)
+ {
+ TimeSpan timeSpan = gamer.RoundtripTime;
+ averageOwd += timeSpan.TotalMilliseconds;
+ }
+ return (int)(averageOwd / numRemoteGamersTwice / 16.6666);
+ }
}
+
+ #endregion
}
}