/// <summary>
        /// Game session monitor (worker thread)
        /// </summary>
        public void GameSessionsMonitorThreadFunc()
        {
            NetworkBackgammonEventQueueElement queueElement = null;

            while (gameSessionsMonitorKeepRunning)
            {
                gameSessionsSemaphore.WaitOne();

                lock (gameSessionsEventQueue)
                {
                    if (gameSessionsEventQueue.Count > 0)
                    {
                        queueElement = gameSessionsEventQueue.Dequeue();
                    }
                }

                if (queueElement != null)
                {
                    if (queueElement.Notifier is NetworkBackgammonGameSession &&
                        queueElement.Event is NetworkBackgammonGameSessionEvent)
                    {
                        NetworkBackgammonGameSession      gameSession      = (NetworkBackgammonGameSession)queueElement.Notifier;
                        NetworkBackgammonGameSessionEvent gameSessionEvent = (NetworkBackgammonGameSessionEvent)queueElement.Event;

                        if (gameSessionEvent.EventType == NetworkBackgammonGameSessionEvent.GameSessionEventType.GameFinished)
                        {
                            // Broadcast the game active event to all registered listeners
                            Broadcast(new NetworkBackgammonGameRoomEvent(NetworkBackgammonGameRoomEvent.GameRoomEventType.PlayerFinished));

                            gameSession.Stop();

                            gameSessions.Remove(gameSession);

                            gameSession = null;

                            GC.Collect();
                        }
                    }
                }

                queueElement = null;
            }
        }
Exemple #2
0
        /// <summary>
        /// Game session state machine (worker thread)
        /// </summary>
        private void Run()
        {
            GameSessionState currentState = GameSessionState.InitialDiceRoll;

            NetworkBackgammonEventQueueElement eventQueueElement = null;

            // Number of moves left for the active player
            UInt32 activePlayerMovesLeft   = 0;
            bool   activePlayerMoveDoubles = false;

            while (bStateMachineKeepRunning)
            {
                // Wait for another event
                semStateMachine.WaitOne();

                // HACK: Add function here to deal with the big switch statement
                if (!bStateMachineKeepRunning)
                {
                    break;
                }

                // Read the event element queue to get information on the next event to be processed
                if (eventQueue.Count > 0)
                {
                    eventQueueElement = eventQueue.Dequeue();
                }

                #region State Machine Pre-Processing

                // Handle special events that interfere with the regular game play (e.g. player resignation)
                if (eventQueueElement != null)
                {
                    if (eventQueueElement.Event is GameSessionPlayerResignationEvent)
                    {
                        currentState = GameSessionState.PlayerResigned;
                    }
                }

                #endregion

                #region State Machine Main Processing

                switch (currentState)
                {
                    #region State: Initial Dice Roll

                case GameSessionState.InitialDiceRoll:
                {
                    // Initialize both players checkers to their intial configuration
                    // (start positions)
                    player1.InitCheckers();
                    player2.InitCheckers();

                    // Use random number generator to figure out which player starts
                    RollDice();

                    player1.Active = false;
                    player2.Active = false;

                    player1.InitialDice = dice[0];
                    player2.InitialDice = dice[1];

                    Broadcast(new GameSessionInitialDiceRollEvent(
                                  player1.PlayerName, player1.InitialDice,
                                  player2.PlayerName, player2.InitialDice));

                    currentState = GameSessionState.InitialDiceRollAcknowledgeExpected;
                }
                break;

                    #endregion

                    #region State: Initial Dice Roll Acknowledge Expected

                case GameSessionState.InitialDiceRollAcknowledgeExpected:
                {
                    if (eventQueueElement != null)
                    {
                        try
                        {
                            if (eventQueueElement.Event is GameSessionInitialDiceRollAcknowledgeEvent)
                            {
                                GameSessionInitialDiceRollAcknowledgeEvent gameSessionEvent = (GameSessionInitialDiceRollAcknowledgeEvent)eventQueueElement.Event;

                                NetworkBackgammonPlayer sendingPlayer = (NetworkBackgammonPlayer)eventQueueElement.Notifier;

                                // Latch (flag) acknowledge of initial dice roll from respective player
                                sendingPlayer.InitialDice.FlagUsed = true;

                                // Check whether both players have acknowledged initial dice roll
                                if (player1.InitialDice.FlagUsed && player2.InitialDice.FlagUsed)
                                {
                                    // If dice rolled are a tie, roll again
                                    if (dice[0].CurrentValue == dice[1].CurrentValue)
                                    {
                                        RollDice();

                                        player1.InitialDice = dice[0];
                                        player2.InitialDice = dice[1];

                                        Broadcast(new GameSessionInitialDiceRollEvent(
                                                      player1.PlayerName, player1.InitialDice,
                                                      player2.PlayerName, player2.InitialDice));
                                    }
                                    else
                                    {
                                        // Determine active player (the one who won the initial dice roll
                                        if (dice[0].CurrentValueUInt32 > dice[1].CurrentValueUInt32)
                                        {
                                            player1.Active = true;
                                        }
                                        else
                                        {
                                            player2.Active = true;
                                        }

                                        // Calculate number of moves left for active player (based on dice values)
                                        activePlayerMoveDoubles = dice[0].CurrentValue == dice[1].CurrentValue;
                                        activePlayerMovesLeft   = (UInt32)(activePlayerMoveDoubles ? 4 : 2);

                                        // Calculate possible moves for active player (and figure out whether active player
                                        // actually has possible moves)
                                        bool activePlayerHasPossibleMoves =
                                            NetworkBackgammonGameEngine.CalculatePossibleMoves(
                                                ref player1,
                                                ref player2,
                                                activePlayerMoveDoubles ?
                                                new NetworkBackgammonDice[] { dice[0] } :
                                                dice);

                                        // Send initial checkers with positions (and possible valid moves
                                        // for the active player) to both players
                                        Broadcast(new GameSessionCheckerUpdatedEvent(player1, player2, dice[0], dice[1]));

                                        if (activePlayerHasPossibleMoves)
                                        {
                                            // Inform players about who's expected to make the next move
                                            Broadcast(new GameSessionMoveExpectedEvent(player1.Active ? player1.PlayerName : player2.PlayerName));

                                            // Set next iteration's state
                                            currentState = GameSessionState.MoveExpected;
                                        }
                                        else
                                        {
                                            // Inform both players that currently active player has no moves left
                                            // which needs to be acknowledged by the active player
                                            Broadcast(new GameSessionNoPossibleMovesEvent(player1.Active ? player1.PlayerName : player2.PlayerName));

                                            currentState = GameSessionState.NoPossibleMovesAcknowledgeExpected;
                                        }
                                    }
                                }
                            }
                        }
                        catch (Exception)
                        {
                            Broadcast(new NetworkBackgammonGameSessionEvent(NetworkBackgammonGameSessionEvent.GameSessionEventType.Error));
                        }
                    }
                }
                break;

                    #endregion

                    #region State: Move Expected

                case GameSessionState.MoveExpected:
                {
                    if (eventQueueElement != null)
                    {
                        try
                        {
                            NetworkBackgammonPlayer      sendingPlayer     = (NetworkBackgammonPlayer)eventQueueElement.Notifier;
                            GameSessionMoveSelectedEvent moveSelectedEvent = (GameSessionMoveSelectedEvent)eventQueueElement.Event;

                            // Check whether the active player attempted to move
                            if (sendingPlayer.Active)
                            {
                                // Perform the selected move of the active player (and check whether the active player won the game
                                if (!NetworkBackgammonGameEngine.ExecuteMove(ref player1, ref player2, moveSelectedEvent.CheckerSelected, moveSelectedEvent.MoveSelected))
                                {
                                    // Figure out the next active player (could be the current
                                    // active player since up to 4 moves are allowed per turn)
                                    if (--activePlayerMovesLeft <= 0)
                                    {
                                        player1.Active = !player1.Active;
                                        player2.Active = !player2.Active;

                                        // Roll dice for active player
                                        RollDice();

                                        // Calculate number of moves left for active player (based on dice values)
                                        activePlayerMoveDoubles = dice[0].CurrentValue == dice[1].CurrentValue;
                                        activePlayerMovesLeft   = (UInt32)(activePlayerMoveDoubles ? 4 : 2);
                                    }

                                    // Calculate possible moves for active player (and figure out whether active player
                                    // actually has possible moves)
                                    bool activePlayerHasPossibleMoves = NetworkBackgammonGameEngine.CalculatePossibleMoves(
                                        ref player1,
                                        ref player2,
                                        activePlayerMoveDoubles ?
                                        new NetworkBackgammonDice[] { dice[0] } :
                                        activePlayerMovesLeft == 2 ?
                                        dice :
                                        new NetworkBackgammonDice[] { dice[0] == moveSelectedEvent.MoveSelected ? dice[1] : dice[0] });

                                    // Send updated checkers with positions (and possible valid moves
                                    // for the active player) to both players
                                    Broadcast(new GameSessionCheckerUpdatedEvent(player1, player2, dice[0], dice[1]));

                                    if (activePlayerHasPossibleMoves)
                                    {
                                        // Inform players about who's expected to make the next move
                                        Broadcast(new GameSessionMoveExpectedEvent(player1.Active ? player1.PlayerName : player2.PlayerName));
                                    }
                                    else
                                    {
                                        // Inform both players that currently active player has no moves left
                                        // which needs to be acknowledged by the active player
                                        Broadcast(new GameSessionNoPossibleMovesEvent(player1.Active ? player1.PlayerName : player2.PlayerName));

                                        currentState = GameSessionState.NoPossibleMovesAcknowledgeExpected;
                                    }
                                }
                                else
                                {
                                    // Inform both players that the game has been won by the active player
                                    Broadcast(new GameSessionPlayerWonEvent(sendingPlayer.PlayerName));

                                    currentState = GameSessionState.GameFinished;
                                }
                            }
                            else
                            {
                                Broadcast(new NetworkBackgammonGameSessionEvent(NetworkBackgammonGameSessionEvent.GameSessionEventType.Error));
                            }
                        }
                        catch (Exception)
                        {
                            Broadcast(new NetworkBackgammonGameSessionEvent(NetworkBackgammonGameSessionEvent.GameSessionEventType.Error));
                        }
                    }
                }
                break;

                    #endregion

                    #region State: No Possible Moves Acknowledge Expected

                case GameSessionState.NoPossibleMovesAcknowledgeExpected:
                {
                    if (eventQueueElement != null)
                    {
                        try
                        {
                            GameSessionNoPossibleMovesAcknowledgeEvent gameSessionNoPossibleMovesAcknowledgeEvent = (GameSessionNoPossibleMovesAcknowledgeEvent)eventQueueElement.Event;

                            // Make sure that the active player acknowledges (and not the waiting player)
                            if (gameSessionNoPossibleMovesAcknowledgeEvent.PlayerName == (player1.Active ? player1.PlayerName : player2.PlayerName))
                            {
                                player1.Active = !player1.Active;
                                player2.Active = !player2.Active;

                                // Roll dice for (new) active player
                                RollDice();

                                // Calculate number of moves left for active player (based on dice values)
                                activePlayerMoveDoubles = dice[0].CurrentValue == dice[1].CurrentValue;
                                activePlayerMovesLeft   = (UInt32)(activePlayerMoveDoubles ? 4 : 2);

                                bool activePlayerHasPossibleMoves = NetworkBackgammonGameEngine.CalculatePossibleMoves(
                                    ref player1,
                                    ref player2,
                                    activePlayerMoveDoubles ?
                                    new NetworkBackgammonDice[] { dice[0] } :
                                    activePlayerMovesLeft == 2 ?
                                    dice :
                                    new NetworkBackgammonDice[] { dice[0], dice[1] });

                                // Send updated checkers with positions (and possible valid moves
                                // for the active player) to both players
                                Broadcast(new GameSessionCheckerUpdatedEvent(player1, player2, dice[0], dice[1]));

                                if (activePlayerHasPossibleMoves)
                                {
                                    // Inform players about who's expected to make the next move
                                    Broadcast(new GameSessionMoveExpectedEvent(player1.Active ? player1.PlayerName : player2.PlayerName));

                                    currentState = GameSessionState.MoveExpected;
                                }
                                else
                                {
                                    // Inform both players that currently active player has no moves left
                                    // which needs to be acknowledged by the active player
                                    Broadcast(new GameSessionNoPossibleMovesEvent(player1.Active ? player1.PlayerName : player2.PlayerName));
                                }
                            }
                            else
                            {
                                Broadcast(new NetworkBackgammonGameSessionEvent(NetworkBackgammonGameSessionEvent.GameSessionEventType.Error));
                            }
                        }
                        catch (Exception)
                        {
                            Broadcast(new NetworkBackgammonGameSessionEvent(NetworkBackgammonGameSessionEvent.GameSessionEventType.Error));
                        }
                    }
                }
                break;

                    #endregion

                    #region State: Player Resigned

                case GameSessionState.PlayerResigned:
                {
                    if (eventQueueElement != null)
                    {
                        try
                        {
                            NetworkBackgammonPlayer resignedPlayer = (NetworkBackgammonPlayer)eventQueueElement.Notifier;

                            Broadcast(new GameSessionPlayerResignationEvent(resignedPlayer.PlayerName));

                            currentState = GameSessionState.GameFinished;
                        }
                        catch (Exception ex)
                        {
                            Broadcast(new NetworkBackgammonGameSessionEvent(NetworkBackgammonGameSessionEvent.GameSessionEventType.Error));
                        }
                    }
                }
                break;

                    #endregion

                default:
                    break;
                }

                #endregion

                #region State Machine Post-Processing

                if (currentState == GameSessionState.GameFinished)
                {
                    Broadcast(new NetworkBackgammonGameSessionEvent(NetworkBackgammonGameSessionEvent.GameSessionEventType.GameFinished));

                    bStateMachineKeepRunning = false;
                }

                #endregion

                eventQueueElement = null;
            }
        }
        public void OnEventNotification(INetworkBackgammonNotifier sender, INetworkBackgammonEvent e)
        {
            if (sender is NetworkBackgammonPlayer)
            {
                NetworkBackgammonPlayer player = (NetworkBackgammonPlayer)sender;

                if (e is NetworkBackgammonChallengeResponseEvent)
                {
                    NetworkBackgammonChallengeResponseEvent challengeResponseEvent = (NetworkBackgammonChallengeResponseEvent)e;

                    if (challengeSyncList.Keys.Contains(player))
                    {
                        if (challengeSyncList[player] != null)
                        {
                            challengeSyncList[player].ChallengeAccepted = challengeResponseEvent.ChallengeAccepted;

                            try
                            {
                                challengeSyncList[player].ChallengeSemaphore.Release();
                            }
                            catch (SemaphoreFullException ex)
                            {
                                // TODO: If this exception occurs calling Release too many times...
                            }
                        }
                    }
                }
                else if (e is NetworkBackgammonChatEvent)
                {
                    // Pass the message through to the listeners
                    Broadcast((NetworkBackgammonChatEvent)e);
                }
            }
            else if (sender is NetworkBackgammonGameSession)
            {
                NetworkBackgammonGameSession gameSession = (NetworkBackgammonGameSession)sender;

                if (e is NetworkBackgammonGameSessionEvent)
                {
                    NetworkBackgammonGameSessionEvent gameSessionEvent = (NetworkBackgammonGameSessionEvent)e;

                    if (gameSessionEvent.EventType == NetworkBackgammonGameSessionEvent.GameSessionEventType.GameFinished)
                    {
                        bool newQueueItemAdd = true;

                        NetworkBackgammonEventQueueElement newQueueItem = new NetworkBackgammonEventQueueElement(e, sender);

                        lock (newQueueItem)
                        {
                            /*
                             * if (gameSessionsEventQueue.Count > 0)
                             * {
                             *  NetworkBackgammonEventQueueElement lastQueueItem = gameSessionsEventQueue.Last();
                             *  // Avoid adding the same event (from the same sender) twice
                             *  // Reason: Game Room listens to events from all players, i.e. also
                             *  // both players that are in one Game Session. Thus, all events broadcasted
                             *  // by the Game Session arrive here (at the Game Room) twice
                             *  if (gameSessionsEventQueue.Last().Notifier == sender &&
                             *      gameSessionsEventQueue.Last().Event == e)
                             *  {
                             *      newQueueItemAdd = false;
                             *  }
                             * }
                             */

                            if (newQueueItemAdd)
                            {
                                gameSessionsEventQueue.Enqueue(new NetworkBackgammonEventQueueElement(e, sender));

                                gameSessionsSemaphore.Release();
                            }
                        }
                    }
                }
            }
        }