Package org.gojul.fourinaline.gui

Source Code of org.gojul.fourinaline.gui.PlayerSelectionFrame$WaitThread

/*
* PlayerSelectionFrame.java
*
* Created: 12 mai 08
*
* Copyright (C) 2008 Julien Aubin
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/
package org.gojul.fourinaline.gui;

import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

import org.gojul.fourinaline.model.AIGameClient;
import org.gojul.fourinaline.model.DefaultEvalScore;
import org.gojul.fourinaline.model.GameClient;
import org.gojul.fourinaline.model.GamePlayer;
import org.gojul.fourinaline.model.GameServer;
import org.gojul.fourinaline.model.HumanGameClient;
import org.gojul.fourinaline.model.GameClient.ComputerGameClient;
import org.gojul.fourinaline.model.GameModel.PlayerMark;
import org.gojul.fourinaline.model.GameServer.PlayerRegisterException;
import org.gojul.fourinaline.model.GameServer.ServerTicket;
import org.gojul.fourinaline.model.GameServer.ServerTicketException;

/**
* The <code>PlayerSelectionFrame</code> is a simple window
* that makes it possible for a player to register.
*
* @author Julien Aubin
*/
@SuppressWarnings("serial")
final class PlayerSelectionFrame extends JDialog implements Runnable, WindowListener, ActionListener, KeyListener
{
 
  /**
   * The <code>AIGameLevel</code> class represents the game level the user
   * has selected.
   *
   * @author Julien Aubin
   */
  final static class AIGameLevel
  {   
    /**
     * The list of game levels.
     */
    private final static List<AIGameLevel> gameLevelList = new ArrayList<AIGameLevel>();
   
    /**
     * The level display message.
     */
    private GUIMessages levelDisplayMessage;
   
    /**
     * The deepness level.
     */
    private int deepnessLevel;
   
    /**
     * Constructor.
     * @param displayMessage the message to display to the user.
     * @param level the AI level.
     */
    private AIGameLevel(final GUIMessages displayMessage, final int level)
    {
      levelDisplayMessage = displayMessage;
      deepnessLevel = level;
      gameLevelList.add(this);
    }

    /**
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString()
    {     
      return levelDisplayMessage.toString();
    }
   
    /**
     * Returns the level.
     * @return the level.
     */
    public int getLevel()
    {
      return deepnessLevel;
    }
   
    /**
     * Returns the list of available levels.
     * @return the list of available levels.
     */
    public final static AIGameLevel[] getAvailableLevels()
    {
      AIGameLevel[] result = new AIGameLevel[gameLevelList.size()];     
      gameLevelList.toArray(result);     
      return result;
    }
   
    /**
     * The very weak AI game level.
     */
    public final static AIGameLevel VERY_WEAK_AI_GAME_LEVEL = new AIGameLevel(GUIMessages.VERY_WEAK_AI_LEVEL_MESSAGE, 4);
   
    /**
     * The weak AI game level.
     */
    public final static AIGameLevel WEAK_AI_GAME_LEVEL = new AIGameLevel(GUIMessages.WEAK_AI_LEVEL_MESSAGE, 5);
   
   
    /**
     * The intermediate AI game level.
     */
    public final static AIGameLevel INTERMEDIATE_AI_GAME_LEVEL = new AIGameLevel(GUIMessages.INTERMEDIATE_AI_LEVEL_MESSAGE, 6);
   
    /**
     * The strong AI game level.
     */
    public final static AIGameLevel STRONG_AI_GAME_LEVEL = new AIGameLevel(GUIMessages.STRONG_AI_LEVEL_MESSAGE, 7);
   
    /**
     * The very strong AI game level.
     */
    public final static AIGameLevel VERY_STRONG_AI_GAME_LEVEL = new AIGameLevel(GUIMessages.VERY_STRONG_AI_LEVEL_MESSAGE, 8);
   
  }
 
  /**
   * The game server.
   */
  private GameServer gameServer;
 
  /**
   * Boolean that indicates that the player list update is running.
   */
  private boolean isPlayerListUpdateRunning;
 
  /**
   * The player list.
   */
  private JList playerList;
 
  /**
   * The player name text field.
   */
  private JTextField playerNameTextField;
 
  /**
   * The OK button.
   */
  private JButton okButton;
 
  /**
   * The cancel button.
   */
  private JButton cancelButton;
 
  /**
   * The AI player game level, if applicable, or null
   * if this is not the case.
   */
  private AIGameLevel aiPlayerGameLevel;
 
  /**
   * The server ticket used for authentication.
   */
  private ServerTicket serverTicket;
 
  /**
   * Constructor.
   * @param server the game server.
   * @param aiGameLevel the AI game level, in case the adversory is an AI,
   * null otherwise.
   * @throws NullPointerException if <code>server</code> is null.
   * @throws ServerTicketException if the connection to the server fails.
   * @throws RemoteException if the connection to the server fails.
   */
  public PlayerSelectionFrame(final GameServer server, final AIGameLevel aiGameLevel)
    throws NullPointerException, ServerTicketException, RemoteException
  {
    super();
    setSize(500, 250);
    setResizable(false);
    setDefaultCloseOperation(DISPOSE_ON_CLOSE);
    setTitle(GUIMessages.MAIN_FRAME_TITLE.toString());
    setLocationRelativeTo(null);
    addWindowListener(this);
    getContentPane().setLayout(new GridBagLayout());
   
    if (server == null)
      throw new NullPointerException();
   
    gameServer = server;
    serverTicket = gameServer.getTicket();
    isPlayerListUpdateRunning = true;
    aiPlayerGameLevel = aiGameLevel;
   
    initTopPanel();
    initNamePanel();
    initBottomPanel();
   
    // The thread here is started at initialization time
    // in order to update regularly the player list.
    new Thread(this).start();
  }
 
  /**
   * Inits the top panel, which contains the player list.
   */
  private void initTopPanel()
  {
    JPanel topPanel = new JPanel();
    GridBagConstraints topPanelConstraints = new GridBagConstraints();
    topPanelConstraints.gridx = 0;
    topPanelConstraints.gridy = 0;
    topPanelConstraints.fill = GridBagConstraints.BOTH;
    topPanelConstraints.weightx = 1.0;
    topPanelConstraints.weighty = 1.0;
    topPanelConstraints.insets = new Insets(5, 5, 5, 5);
   
    getContentPane().add(topPanel, topPanelConstraints);
   
    topPanel.setLayout(new BorderLayout(5, 5));
   
    JLabel registeredLabel = new JLabel(GUIMessages.LIST_OF_PLAYERS_MESSAGE.toString());
    topPanel.add(registeredLabel, BorderLayout.NORTH);
   
    playerList = new JList();
    topPanel.add(new JScrollPane(playerList), BorderLayout.CENTER);
  }
 
  /**
   * Inits the name panel.
   */
  private void initNamePanel()
  {
    JPanel namePanel = new JPanel();
    GridBagConstraints namePanelConstraints = new GridBagConstraints();
    namePanelConstraints.gridx = 0;
    namePanelConstraints.gridy = 1;
    namePanelConstraints.fill = GridBagConstraints.BOTH;
    namePanelConstraints.weightx = 1.0;
   
    getContentPane().add(namePanel, namePanelConstraints);
   
    namePanel.setLayout(new GridBagLayout());
   
    JLabel nameLabel = new JLabel("<html>" + GUIMessages.ENTER_THE_NAME_OF_YOUR_PLAYER_MESSAGE.toString()
        + "<br>" + GUIMessages.PLAYER_NAME_MUST_BE_DIFFERENT_FROM_THE_OTHER_NAMES_MESSAGE.toString() + "</html>");
    GridBagConstraints nameLabelConstraints = new GridBagConstraints();
    nameLabelConstraints.gridx = 0;
    nameLabelConstraints.gridy = 0;
    nameLabelConstraints.fill = GridBagConstraints.BOTH;
    nameLabelConstraints.weightx = 1.0;
    nameLabelConstraints.insets = new Insets(5, 5, 5, 5);
    namePanel.add(nameLabel, nameLabelConstraints);
   
    playerNameTextField = new JTextField();
    GridBagConstraints playerNameTextFieldConstraints = new GridBagConstraints();
    playerNameTextFieldConstraints.gridx = 0;
    playerNameTextFieldConstraints.gridy = 1;
    playerNameTextFieldConstraints.fill = GridBagConstraints.HORIZONTAL;
    playerNameTextFieldConstraints.insets = new Insets(5, 5, 5, 5);
    playerNameTextFieldConstraints.weightx = 1.0;
    namePanel.add(playerNameTextField, playerNameTextFieldConstraints);
    playerNameTextField.addKeyListener(this);
  }
 
  /**
   * Inits the bottom panel.
   */
  private void initBottomPanel()
  {
    JPanel bottomPanel = new JPanel();
    GridBagConstraints bottomPanelConstraints = new GridBagConstraints();
    bottomPanelConstraints.gridx = 0;
    bottomPanelConstraints.gridy = 2;
    bottomPanelConstraints.fill = GridBagConstraints.BOTH;
    bottomPanelConstraints.weightx = 1.0;
   
    getContentPane().add(bottomPanel, bottomPanelConstraints);
   
    bottomPanel.setLayout(new FlowLayout());
   
    okButton = new JButton(GUIMessages.OK_TEXT.toString());
    okButton.addActionListener(this);
    bottomPanel.add(okButton);
   
    cancelButton = new JButton(GUIMessages.CANCEL_TEXT.toString());
    cancelButton.addActionListener(this);
    bottomPanel.add(cancelButton);
  }
 
  /**
   * @see java.lang.Runnable#run()
   */
  public void run()
  {
    // This thread updates regularly the player list.
    boolean isRunning = false;
    Set<GamePlayer> currentPlayersLocal = new LinkedHashSet<GamePlayer>();
   
    synchronized(this)
    {
      isRunning = isPlayerListUpdateRunning;
    }
   
    while (isRunning)
    {
     
      Set<GamePlayer> currentPlayersServer = null;
     
      try
      {
        currentPlayersServer = new LinkedHashSet<GamePlayer>(gameServer.getPlayers());
      }
      catch (RemoteException e)
      {
        e.printStackTrace();
        JOptionPane.showMessageDialog(this, GUIMessages.LOST_CONNECTION_TO_SERVER_MESSAGE, GUIMessages.ERROR_TEXT.toString(), JOptionPane.ERROR_MESSAGE);
        quit();
      }
     
      if (currentPlayersServer != null && !currentPlayersServer.equals(currentPlayersLocal))
      {
        currentPlayersLocal = currentPlayersServer;
       
        final String[] playerNames = new String[currentPlayersLocal.size()];
        int i = 0;
                             
        for (GamePlayer player: currentPlayersLocal)
        {
          playerNames[i] = player.getName();
          i++;
        }
       
        // Thread safety with Swing
        SwingUtilities.invokeLater(new Runnable()
        {
          /**
           * @see java.lang.Runnable#run()
           */
          public void run()
          {
            playerList.setListData(playerNames);
          }
        });           

      }
     
      try
      {
        // Sleeps some time in order to avoid synchronization issues.
        Thread.sleep(100);
      }
      catch (Throwable t)
      {
        t.printStackTrace();
      }
     
      synchronized(this)
      {
        isRunning = isPlayerListUpdateRunning;
      }
    }
   
  }

  /**
   * Quits the program.
   */
  private void quit()
  {
    synchronized(this)
    {
      isPlayerListUpdateRunning = false;
    }   
    ComputerGameClient.disconnectLocalComputerClients();
   
    try
    {
      gameServer.releaseTicket(serverTicket);
    }
    catch (Throwable t)
    {
      t.printStackTrace();
    }
   
    System.exit(0);
  }
 
  /**
   * Creates an AI game client if this is necessary.<br/>
   * Note that this creation may fail if two human clients
   * connect to the server before the AI game client could be created.
   */
  private void createAIGameClientIfNecessary()
  {
    assert aiPlayerGameLevel != null: "At this stage, the AI player game level must not be null.";
   
    boolean continueTryingCreateAIGameClient = true;
    int computerPlayerIndex = 0;
   
    ServerTicket aiServerTicket = null;
   
    while (continueTryingCreateAIGameClient)
    {
      try
      {
        if (aiServerTicket == null)
          aiServerTicket = gameServer.getTicket();
       
        String playerName = GUIMessages.COMPUTER_ADVERSORY_TEXT.toString();
       
        if (computerPlayerIndex != 0)
          playerName += " " + computerPlayerIndex;
       
        GameClient AIclient = new AIGameClient(gameServer, aiServerTicket, playerName, new DefaultEvalScore(), aiPlayerGameLevel.getLevel());
        new Thread(AIclient).start();
        continueTryingCreateAIGameClient = false;
      }
      catch (PlayerRegisterException e)
      {
        e.printStackTrace();
        computerPlayerIndex++;
      }
      catch (RemoteException e)
      {
        JOptionPane.showMessageDialog(this, GUIMessages.DISCONNECTED_FROM_SERVER_MESSAGE, GUIMessages.ERROR_TEXT.toString(), JOptionPane.ERROR_MESSAGE);
        quit();
      }
      catch (ServerTicketException ex)
      {
        JOptionPane.showMessageDialog(this, GUIMessages.NO_PLAYER_AVAILABLE_MESSAGE, GUIMessages.ERROR_TEXT.toString(), JOptionPane.ERROR_MESSAGE);
        continueTryingCreateAIGameClient = false;
      }
    }
  }
 
  /**
   * Launches the game, or connects
   * to it.<br/>
   * In case there's an error while launching the game,
   * halts the program.
   */
  private void launchGame()
  {
    String playerName = playerNameTextField.getText();
   
    if (playerName == null || playerName.trim().length() == 0)
    {
      JOptionPane.showMessageDialog(this, GUIMessages.YOU_MUST_SPECIFY_A_PLAYER_NAME_MESSAGE, GUIMessages.ERROR_TEXT.toString(), JOptionPane.ERROR_MESSAGE);
      playerNameTextField.requestFocusInWindow();         
      return;
    }
   
    playerName = playerName.trim();
   
    HumanGameClient gameClient = null;
   
    try
    {
      gameClient = new HumanGameClient(gameServer, serverTicket, playerName);
    }
    catch (PlayerRegisterException ex)
    {
      JOptionPane.showMessageDialog(this, GUIMessages.FAILED_TO_REGISTER_MESSAGE, GUIMessages.ERROR_TEXT.toString(), JOptionPane.ERROR_MESSAGE);
      return;
    }
    catch (RemoteException ex)
    {
      JOptionPane.showMessageDialog(this, GUIMessages.DISCONNECTED_FROM_SERVER_MESSAGE, GUIMessages.ERROR_TEXT.toString(), JOptionPane.ERROR_MESSAGE);
      quit();
    }
    catch (ServerTicketException ex)
    {
      JOptionPane.showMessageDialog(this, GUIMessages.NO_PLAYER_AVAILABLE_MESSAGE, GUIMessages.ERROR_TEXT.toString(), JOptionPane.ERROR_MESSAGE);
      return;
    }
   
    synchronized(this)
    {
      isPlayerListUpdateRunning = false;
    }
   
    if (aiPlayerGameLevel != null)
      createAIGameClientIfNecessary();
   
    okButton.setEnabled(false);
    cancelButton.setEnabled(false);
    playerNameTextField.setEnabled(false);
    setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
   
    new Thread(new WaitThread(this, gameClient)).start();
  }

  /**
   * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
   */
  public void actionPerformed(final ActionEvent e)
  {
    if (e.getSource() == okButton)
    {
      launchGame();
    }
    else if (e.getSource() == cancelButton)
    {
      quit();
    }
  }
 
  /**
   * @see java.awt.event.KeyListener#keyPressed(java.awt.event.KeyEvent)
   */
  public void keyPressed(final KeyEvent e)
  {
   
  }

  /**
   * @see java.awt.event.KeyListener#keyReleased(java.awt.event.KeyEvent)
   */
  public void keyReleased(final KeyEvent e)
  {
   
  }

  /**
   * @see java.awt.event.KeyListener#keyTyped(java.awt.event.KeyEvent)
   */
  public void keyTyped(final KeyEvent e)
  {
    if (e.getSource() == playerNameTextField)
    {
      if (e.getKeyChar() == KeyEvent.VK_ENTER)
      {
        e.consume();
        launchGame();
      }
    }
  }

  /**
   * The wait thread is a small thread that disables the player selection
   * frame until all the players have been logged.
   *
   * @author Julien Aubin
   */
  private final static class WaitThread implements Runnable, ActionListener
  {
   
    /**
     * The server query period, in order to avoid a query overload.
     */
    private final static int PLAYER_QUERY_PERIOD = 100;
   
    /**
     * The time out period : 2 minutes.
     */
    private final static int TIME_OUT_PERIOD = 2 * 60 * 1000;
   
    /**
     * The player selection frame.
     */
    private PlayerSelectionFrame playerSelectionFrame;
   
    /**
     * The game client.
     */
    private HumanGameClient gameClient;
   
    /**
     * The time out timer.
     */
    private Timer timer;
   
    /**
     * Constructor.
     * @param frame the player selection frame.
     * @param client the human game client.
     * @throws NullPointerException if any of the method parameter is null.
     */
    public WaitThread(final PlayerSelectionFrame frame, final HumanGameClient client) throws NullPointerException
    {
      if (frame == null || client == null)
        throw new NullPointerException();
     
      playerSelectionFrame = frame;
      gameClient = client;
     
      timer = new Timer(TIME_OUT_PERIOD, this);
      timer.start();
    }

    /**
     * @see java.lang.Runnable#run()
     */
    public void run()
    {
      GameServer gameServer = playerSelectionFrame.gameServer;
     
      try
      {
        while (gameServer.getPlayers().size() < PlayerMark.getNumberOfPlayerMarks())
        {
          // We wait a small amount of time there in order to avoid overloading
          // the server with requests.
          try
          {
            Thread.sleep(PLAYER_QUERY_PERIOD);
          }
          catch (InterruptedException e)
          {
           
          }
        }
      }
      catch (RemoteException e)
      {         
        e.printStackTrace();
        JOptionPane.showMessageDialog(playerSelectionFrame, GUIMessages.DISCONNECTED_FROM_SERVER_MESSAGE, GUIMessages.ERROR_TEXT.toString(), JOptionPane.ERROR_MESSAGE);
       
        // Disconnects the client  in order to void server blocking.
        try
        {
          gameClient.disconnect();
        }
        catch (RuntimeException ex)
        {
          ex.printStackTrace();
        }
        finally
        {
          ComputerGameClient.disconnectLocalComputerClients();
        }
       
        System.exit(0);
      }
     
      // Avoids the program exiting surprisingly.
      timer.stop();
     
      SwingUtilities.invokeLater(new Runnable()
      {
        /**
         * @see java.lang.Runnable#run()
         */
        public void run()
        {
          playerSelectionFrame.dispose();
          new MainFrame(gameClient).setVisible(true);
        }
      });
   
      // Starts the client thread AFTER the main frame in order to ensure
      // to capture all the update events.
      new Thread(gameClient).start();
     
    }

    /**
     * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
     */
    public void actionPerformed(final ActionEvent e)
    {
      JOptionPane.showMessageDialog(playerSelectionFrame, GUIMessages.DISCONNECTED_FROM_SERVER_MESSAGE, GUIMessages.ERROR_TEXT.toString(), JOptionPane.ERROR_MESSAGE);
     
      // Disconnects the client  in order to void server blocking.
      try
      {
        gameClient.disconnect();
      }
      catch (RuntimeException ex)
      {
        ex.printStackTrace();
      }
      finally
      {
        ComputerGameClient.disconnectLocalComputerClients();
      }
     
      System.exit(0);
    }
   
   
   
  }

  /**
   * @see java.awt.event.WindowListener#windowActivated(java.awt.event.WindowEvent)
   */
  public void windowActivated(final WindowEvent e)
  {
   
  }

  /**
   * @see java.awt.event.WindowListener#windowClosed(java.awt.event.WindowEvent)
   */
  public void windowClosed(final WindowEvent e)
  {
   
  }

  /**
   * @see java.awt.event.WindowListener#windowClosing(java.awt.event.WindowEvent)
   */
  public void windowClosing(final WindowEvent e)
  {
    // Does nothing if the default close operation is not : Do nothing one close.
    if (getDefaultCloseOperation() != DO_NOTHING_ON_CLOSE)
      quit();     
  }

  /**
   * @see java.awt.event.WindowListener#windowDeactivated(java.awt.event.WindowEvent)
   */
  public void windowDeactivated(final WindowEvent e)
  {
         
  }

  /**
   * @see java.awt.event.WindowListener#windowDeiconified(java.awt.event.WindowEvent)
   */
  public void windowDeiconified(final WindowEvent e)
  {
    // TODO Raccord de méthode auto-généré
   
  }

  /**
   * @see java.awt.event.WindowListener#windowIconified(java.awt.event.WindowEvent)
   */
  public void windowIconified(final WindowEvent e)
  {
    // TODO Raccord de méthode auto-généré
   
  }

  /**
   * @see java.awt.event.WindowListener#windowOpened(java.awt.event.WindowEvent)
   */
  public void windowOpened(final WindowEvent e)
  {
    // TODO Raccord de méthode auto-généré
   
  }
 
 
}
TOP

Related Classes of org.gojul.fourinaline.gui.PlayerSelectionFrame$WaitThread

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.