using System; using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.Xna.Framework.Net; using System.Diagnostics; using Microsoft.Xna.Framework.GamerServices; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Input; namespace CS_3505_Project_06 { /// /// A manager class to handle network interactions between peers and /// lobby/game switching. /// public class NetworkGame { // Private class variable members #region Instance Variables NetworkSession mNetworkSession; JoinedSessionDelegate mJoinedSessionDelegate; FoundSessionsDelegate mFoundSessionsDelegate; ILobby mLobby; IDeterministicGame mGame; List mLastPressedKeys = new List(); bool mLastButtonPressed; int mLatency; long mNextLatencyAdjustmentFrame; int mStallCount; int mAverageOwd; TimeSpan mTargetTimeSpan = new TimeSpan(166666); public TimeSpan TargetTimeSpan { get { return mTargetTimeSpan; } } #endregion /// /// 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; } /// /// 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]; } } /// /// Get all the gamers associated with the active network session. /// public GamerCollection NetworkGamers { get { return mNetworkSession.AllGamers; } } /// /// 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) { CreateSession(mGame.MaximumSupportedPlayers, callback); } /// /// 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); 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; mJoinedSessionDelegate(mNetworkSession, this); } /// /// 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 { get { return mNetworkSession != null; } } /// /// Find available sessions to join. /// /// The delegate/method to call when the search finishes. public void FindSessions(FoundSessionsDelegate callback) { 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); } /// /// Join a network session found using FindSessions(). This is for joining a game that /// somebody else has already started hosting. /// /// 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); mJoinedSessionDelegate = callback; NetworkSession.BeginJoin(availableSession, JoinSessionEnd, null); } private void JoinSessionEnd(IAsyncResult result) { 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. /// public void LeaveSession() { Debug.Assert(mNetworkSession != null); mNetworkSession.Dispose(); mNetworkSession = null; } /// /// Set up the network session to simulate 200ms latency and 10% packet loss. /// public void SimulateBadNetwork() { Debug.Assert(mNetworkSession != null); mNetworkSession.SimulatedLatency = new TimeSpan(0, 0, 0, 0, 200); mNetworkSession.SimulatedPacketLoss = 0.1f; } /// /// 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.StartGame(); mNetworkSession.ResetReady(); } /// /// 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) { mLobby.Update(gameTime, this); } else { mNetworkSession.Update(); ReadPackets(); if (mNetworkSession.SessionState == NetworkSessionState.Lobby) { if (mNetworkSession.IsHost && mNetworkSession.AllGamers.Count >= mGame.MinimumSupportedPlayers && mNetworkSession.IsEveryoneReady) { mNetworkSession.StartGame(); mNetworkSession.ResetReady(); } else { mLobby.Update(gameTime, this); } } else if (mNetworkSession.SessionState == NetworkSessionState.Playing) { if (HaveNeededEvents) { if (IsLatencyAdjustmentFrame) AdjustLatency(); mStallCount = 0; SendLocalEvents(); ApplyEvents(); mGame.Update(mTargetTimeSpan); } else // Stall! { } } } } /// /// 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) { mLobby.Draw(spriteBatch); } } } // Private implementation methods of the network protocol #region Private Implementation Methods /// /// Reinitialize the private variables in preparation for new game to start. /// private void Reset() { mLatency = 1; mNextLatencyAdjustmentFrame = 1; mStallCount = 0; mAverageOwd = AverageOneWayDelay; // TODO: The game object needs to be reset, too. //mGame.ResetGame(playerIdentifiers, playerIdentifiers[0]); } /// /// 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. private void ReadPackets() { PacketReader packetReader = new PacketReader(); foreach (LocalNetworkGamer gamer in mNetworkSession.LocalGamers) { while (gamer.IsDataAvailable) { NetworkGamer sender; gamer.ReceiveData(packetReader, out sender); byte packetId = packetReader.ReadByte(); switch (packetId) { // Chat Packet case 1: short messageLength = packetReader.ReadInt16(); char[] message = packetReader.ReadChars(messageLength); ChatPacket chatPacket; chatPacket.sender = sender; chatPacket.message = new String(message); break; // Event Packet case 2: short stallCount = packetReader.ReadInt16(); short averageOwd = packetReader.ReadInt16(); int frameNumber = packetReader.ReadInt32(); byte numEvents = packetReader.ReadByte(); for (byte i = 0; i < numEvents; ++i) { ReadEvent(packetReader, sender); } break; // Stall Packet case 3: byte numStalledPeers = packetReader.ReadByte(); byte[] stalledPeers = packetReader.ReadBytes(numStalledPeers); break; } } } } private void ReadEvent(PacketReader packetReader, NetworkGamer sender) { byte eventId = packetReader.ReadByte(); long applicationFrame = packetReader.ReadInt32(); switch (eventId) { // Key Down case 1: int keyCode1 = packetReader.ReadInt32(); break; // Key Up case 2: int keyCode2 = packetReader.ReadInt32(); break; // Mouse Down case 3: byte buttonId1 = packetReader.ReadByte(); break; // Mouse Up case 4: byte buttonId2 = packetReader.ReadByte(); break; // Mouse Move case 5: short x = packetReader.ReadInt16(); short y = packetReader.ReadInt16(); break; } } private bool IsLatencyAdjustmentFrame { get { // TODO return false; } } private void AdjustLatency() { // TODO } private void SendLocalEvents() { // TODO: Not finished. KeyboardState keyState = Keyboard.GetState(); MouseState mouseState = Mouse.GetState(); // Make a list of the keys pressed or released this frame. List pressedKeys = new List(); List releasedKeys = new List(); Keys[] pressedKeysArray = keyState.GetPressedKeys(); foreach (Keys k in pressedKeysArray) if (!mLastPressedKeys.Contains(k)) pressedKeys.Add(k); else mLastPressedKeys.Remove(k); releasedKeys = mLastPressedKeys; mLastPressedKeys = new List(pressedKeysArray); bool buttonPressed = mouseState.LeftButton == ButtonState.Pressed; } private bool HaveNeededEvents { get { // TODO return true; } } private void ApplyEvents() { // TODO } private int AverageOneWayDelay { get { // TODO return 12; } } #endregion } }