I'm working on a problem from Java How-to-Code, question 28.20. The book set up a multithreaded client/server to allow two users to play tic tac toe against each other. The problem asks us to modify the existing code to add certain features. The one that I am stuck on is to add a "New Game" button. I have added the button, and gotten the client that presses the button to notify the server, but what I can't figure out how to do, is have the server send a message to the other client. Right now, it will only respond back to the one that pressed the button. How can I get it to send a message to either the other or to both clients? Thanks.
Here is the client code:
// Fig. 27.15: TicTacToeClient.java
// Client side of client/server Tic-Tac-Toe program.
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.net.Socket;
import java.net.InetAddress;
import java.io.IOException;
import javax.swing.*;
import java.util.Formatter;
import java.util.Scanner;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
public class TicTacToeClient extends JFrame implements Runnable
{
private JTextField idField; // textfield to display player's mark
private JTextArea displayArea; // JTextArea to display output
private JPanel boardPanel; // panel for tic-tac-toe board
private JPanel panel2; // panel to hold board
private JButton playAgain;
private JButton disconnect;
private Square[][] board; // tic-tac-toe board
private Square currentSquare; // current square
private Socket connection; // connection to server
private Scanner input; // input from server
private Formatter output; // output to server
private String ticTacToeHost; // host name for server
private String myMark; // this client's mark
private boolean myTurn; // determines which client's turn it is
private final String X_MARK = "X"; // mark for first client
private final String O_MARK = "O"; // mark for second client
// set up user-interface and board
public TicTacToeClient( String host )
{
ticTacToeHost = host; // set name of server
displayArea = new JTextArea( 4, 30 ); // set up JTextArea
displayArea.setEditable( false );
add( new JScrollPane( displayArea ), BorderLayout.SOUTH );
boardPanel = new JPanel(); // set up panel for squares in board
boardPanel.setLayout( new GridLayout( 3, 3, 0, 0 ) );
board = new Square[ 3 ][ 3 ]; // create board
// loop over the rows in the board
for ( int row = 0; row < board.length; row++ )
{
// loop over the columns in the board
for ( int column = 0; column < board[ row ].length; column++ )
{
// create square
board[ row ][ column ] = new Square( " ", row * 3 + column );
boardPanel.add( board[ row ][ column ] ); // add square
} // end inner for
} // end outer for
idField = new JTextField(); // set up textfield
idField.setEditable( false );
add(idField, BorderLayout.NORTH);
panel2 = new JPanel(); // set up panel to contain boardPanel
panel2.add(boardPanel, BorderLayout.CENTER); // add board panel
add( panel2, BorderLayout.CENTER ); // add container panel
JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new BorderLayout(0,5));
playAgain = new JButton("Play again?");
playAgain.setFont(new Font("NewFont",Font.PLAIN,12));
playAgain.setEnabled(false);
buttonPanel.add(playAgain, BorderLayout.NORTH);
disconnect = new JButton("Disconnect");
disconnect.setFont(new Font("NewFont2", Font.PLAIN, 12));
buttonPanel.add(disconnect, BorderLayout.SOUTH);
add(buttonPanel, BorderLayout.EAST);
setSize( 350, 250 ); // set size of window
setVisible( true ); // show window
startClient();
} // end TicTacToeClient constructor
// start the client thread
public void startClient()
{
try // connect to server and get streams
{
// make connection to server
connection = new Socket(
InetAddress.getByName( ticTacToeHost ), 12345 );
// get streams for input and output
input = new Scanner( connection.getInputStream() );
output = new Formatter( connection.getOutputStream() );
} // end try
catch ( IOException ioException )
{
ioException.printStackTrace();
} // end catch
// create and start worker thread for this client
ExecutorService worker = Executors.newFixedThreadPool( 1 );
worker.execute( this ); // execute client
} // end method startClient
// control thread that allows continuous update of displayArea
public void run()
{
myMark = input.nextLine(); // get player's mark (X or O)
SwingUtilities.invokeLater(
new Runnable()
{
public void run()
{
// display player's mark
idField.setText( "You are player \"" + myMark + "\"" );
} // end method run
} // end anonymous inner class
); // end call to SwingUtilities.invokeLater
myTurn = ( myMark.equals( X_MARK ) ); // determine if client's turn
playAgain.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
output.format("New Game\n");
output.flush();
// loop over the rows in the board
for ( int row = 0; row < board.length; row++ )
{
// loop over the columns in the board
for ( int column = 0; column < board[ row ].length;column++ )
{
// create square
setMark(board[row][column], "");
} // end inner for
} // end outer for
}
});
// receive messages sent to client and output them
while ( true )
{
if ( input.hasNextLine() )
processMessage( input.nextLine() );
} // end while
} // end method run
// process messages received by client
private void processMessage( String message )
{
// valid move occurred
if ( message.equals( "Valid move." ) )
{
displayMessage( "Valid move, please wait.\n" );
setMark( currentSquare, myMark ); // set mark in square
} // end if
else if ( message.equals( "Invalid move, try again" ) )
{
displayMessage( message + "\n" ); // display invalid move
myTurn = true; // still this client's turn
} // end else if
else if ( message.equals( "Opponent moved" ) )
{
int location = input.nextInt(); // get move location
input.nextLine(); // skip newline after int location
int row = location / 3; // calculate row
int column = location % 3; // calculate column
setMark( board[ row ][ column ],
( myMark.equals( X_MARK ) ? O_MARK : X_MARK ) ); // mark move
displayMessage( "Opponent moved. Your turn.\n" );
myTurn = true; // now this client's turn
} // end else if
else if(message.equals("Game over. Players Tied")){
int location = input.nextInt(); // get move location
input.nextLine(); // skip newline after int location
int row = location / 3; // calculate row
int column = location % 3; // calculate column
setMark( board[ row ][ column ],
( myMark.equals( X_MARK ) ? O_MARK : X_MARK ) ); // mark move
displayMessage("Game over. Players tied");
playAgain.setEnabled(true);
}//else if
else if(message.equals("Game over, you lose")){
int location = input.nextInt(); // get move location
input.nextLine(); // skip newline after int location
int row = location / 3; // calculate row
int column = location % 3; // calculate column
setMark( board[ row ][ column ],
( myMark.equals( X_MARK ) ? O_MARK : X_MARK ) ); // mark move
displayMessage("Game over, you lose.");
playAgain.setEnabled(true);
}//else if
else if(message.equals("Game over. You win!")){
displayMessage("Game over. You win!");
playAgain.setEnabled(true);
}else if(message.equals("New Game")){
displayMessage("\nNew game requested");
// loop over the rows in the board
for ( int row = 0; row < board.length; row++ )
{
// loop over the columns in the board
for ( int column = 0; column < board[ row ].length; column++ )
{
// create square
setMark(board[row][column], "");
} // end inner for
} // end outer for
playAgain.setEnabled(false);
}
else
displayMessage( message + "\n" ); // display the message
} // end method processMessage
// manipulate displayArea in event-dispatch thread
private void displayMessage( final String messageToDisplay )
{
SwingUtilities.invokeLater(
new Runnable()
{
public void run()
{
displayArea.append( messageToDisplay ); // updates output
} // end method run
} // end inner class
); // end call to SwingUtilities.invokeLater
} // end method displayMessage
// utility method to set mark on board in event-dispatch thread
private void setMark( final Square squareToMark, final String mark )
{
SwingUtilities.invokeLater(
new Runnable()
{
public void run()
{
squareToMark.setMark( mark ); // set mark in square
} // end method run
} // end anonymous inner class
); // end call to SwingUtilities.invokeLater
} // end method setMark
// send message to server indicating clicked square
public void sendClickedSquare( int location )
{
// if it is my turn
if ( myTurn )
{
output.format( "%d\n", location ); // send location to server
output.flush();
myTurn = false; // not my turn any more
} // end if
} // end method sendClickedSquare
// set current Square
public void setCurrentSquare( Square square )
{
currentSquare = square; // set current square to argument
} // end method setCurrentSquare
// private inner class for the squares on the board
private class Square extends JPanel
{
private String mark; // mark to be drawn in this square
private int location; // location of square
public Square( String squareMark, int squareLocation )
{
mark = squareMark; // set mark for this square
location = squareLocation; // set location of this square
addMouseListener(
new MouseAdapter()
{
public void mouseReleased( MouseEvent e )
{
setCurrentSquare( Square.this ); // set current
// send location of this square
sendClickedSquare( getSquareLocation() );
} // end method mouseReleased
} // end anonymous inner class
); // end call to addMouseListener
} // end Square constructor
// return preferred size of Square
public Dimension getPreferredSize()
{
return new Dimension( 30, 30 ); // return preferred size
} // end method getPreferredSize
// return minimum size of Square
public Dimension getMinimumSize()
{
return getPreferredSize(); // return preferred size
} // end method getMinimumSize
// set mark for Square
public void setMark( String newMark )
{
mark = newMark; // set mark of square
repaint(); // repaint square
} // end method setMark
// return Square location
public int getSquareLocation()
{
return location; // return location of square
} // end method getSquareLocation
// draw Square
public void paintComponent( Graphics g )
{
super.paintComponent( g );
g.drawRect( 0, 0, 29, 29 ); // draw square
g.drawString( mark, 11, 20 ); // draw mark
} // end method paintComponent
} // end inner-class Square
} // end class TicTacToeClient
the server code
// Fig. 27.13: TicTacToeServer.java
// Server side of client/server Tic-Tac-Toe program.
import java.awt.BorderLayout;
import java.net.ServerSocket;
import java.net.Socket;
import java.io.IOException;
import java.util.Formatter;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.Condition;
import javax.swing.JFrame;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
public class TicTacToeServer extends JFrame
{
private String[] board = new String[ 9 ]; // tic-tac-toe board
private JTextArea outputArea; // for outputting moves
private Player[] players; // array of Players
private ServerSocket server; // server socket to connect with clients
private int currentPlayer; // keeps track of player with current move
private final static int PLAYER_X = 0; // constant for first player
private final static int PLAYER_O = 1; // constant for second player
private final static String[] MARKS = { "X", "O" }; // array of marks
private ExecutorService runGame; // will run players
private Lock gameLock; // to lock game for synchronization
private Condition otherPlayerConnected; // to wait for other player
private Condition otherPlayerTurn; // to wait for other player's turn
// set up tic-tac-toe server and GUI that displays messages
public TicTacToeServer()
{
super( "Tic-Tac-Toe Server" ); // set title of window
// create ExecutorService with a thread for each player
runGame = Executors.newFixedThreadPool( 2 );
gameLock = new ReentrantLock(); // create lock for game
// condition variable for both players being connected
otherPlayerConnected = gameLock.newCondition();
// condition variable for the other player's turn
otherPlayerTurn = gameLock.newCondition();
for ( int i = 0; i < 9; i++ )
board[ i ] = ""; // create tic-tac-toe board
players = new Player[ 2 ]; // create array of players
currentPlayer = PLAYER_X; // set current player to first player
try
{
server = new ServerSocket( 12345, 2 ); // set up ServerSocket
} // end try
catch ( IOException ioException )
{
ioException.printStackTrace();
System.exit( 1 );
} // end catch
outputArea = new JTextArea(); // create JTextArea for output
add( outputArea, BorderLayout.CENTER );
outputArea.setText( "Server awaiting connections\n" );
setSize( 300, 300 ); // set size of window
setVisible( true ); // show window
} // end TicTacToeServer constructor
// wait for two connections so game can be played
public void execute()
{
// wait for each client to connect
for ( int i = 0; i < players.length; i++ )
{
try // wait for connection, create Player, start runnable
{
players[ i ] = new Player( server.accept(), i );
runGame.execute( players[ i ] ); // execute player runnable
} // end try
catch ( IOException ioException )
{
ioException.printStackTrace();
System.exit( 1 );
} // end catch
} // end for
gameLock.lock(); // lock game to signal player X's thread
try
{
players[ PLAYER_X ].setSuspended( false ); // resume player X
otherPlayerConnected.signal(); // wake up player X's thread
} // end try
finally
{
gameLock.unlock(); // unlock game after signalling player X
} // end finally
} // end method execute
// display message in outputArea
private void displayMessage( final String messageToDisplay )
{
// display message from event-dispatch thread of execution
SwingUtilities.invokeLater(
new Runnable()
{
public void run() // updates outputArea
{
outputArea.append( messageToDisplay ); // add message
} // end method run
} // end inner class
); // end call to SwingUtilities.invokeLater
} // end method displayMessage
// determine if move is valid
public boolean validateAndMove( int location, int player )
{
// while not current player, must wait for turn
while ( player != currentPlayer )
{
gameLock.lock(); // lock game to wait for other player to go
try
{
otherPlayerTurn.await(); // wait for player's turn
} // end try
catch ( InterruptedException exception )
{
exception.printStackTrace();
} // end catch
finally
{
gameLock.unlock(); // unlock game after waiting
} // end finally
} // end while
// if location not occupied, make move
if ( !isOccupied( location ) )
{
board[ location ] = MARKS[ currentPlayer ]; // set move on board
currentPlayer = 1- currentPlayer; // change player
// let new current player know that move occurred
players[ currentPlayer ].otherPlayerMoved( location );
gameLock.lock(); // lock game to signal other player to go
try
{
if(!isWin() && !isTie())
otherPlayerTurn.signal(); // signal other player to continue
} // end try
finally
{
if(!isWin() && !isTie())
gameLock.unlock(); // unlock game after signaling
} // end finally
return true; // notify player that move was valid
} // end if
else // move was not valid
return false; // notify player that move was invalid
} // end method validateAndMove
// determine whether location is occupied
public boolean isOccupied( int location )
{
if ( board[ location ].equals( MARKS[ PLAYER_X ] ) ||
board [ location ].equals( MARKS[ PLAYER_O ] ) )
return true; // location is occupied
else
return false; // location is not occupied
} // end method isOccupied
// place code in this method to determine whether game over
public boolean isWin()
{
boolean isGameOver = false;
if(!board[0].equals("") && board[0].equals(board[1])
&& board[1].equals(board[2]))
isGameOver = true;
if(!board[3].equals("") && board[3].equals(board[4])
&& board[4].equals(board[5]))
isGameOver = true;
if(!board[6].equals("") && board[6].equals(board[7])
&& board[7].equals(board[8]))
isGameOver = true;
if(!board[0].equals("") && board[0].equals(board[3])
&& board[3].equals(board[6]))
isGameOver = true;
if(!board[1].equals("") && board[1].equals(board[4])
&& board[4].equals(board[7]))
isGameOver = true;
if(!board[2].equals("") && board[2].equals(board[5])
&& board[5].equals(board[8]))
isGameOver = true;
if(!board[0].equals("") && board[0].equals(board[4])
&& board[4].equals(board[8]))
isGameOver = true;
if(!board[2].equals("") && board[2].equals(board[4])
&& board[4].equals(board[6]))
isGameOver = true;
return isGameOver; // this is left as an exercise
} // end method isWin
public boolean isTie(){
boolean isTie = false;
if(!board[0].equals("") && !board[1].equals("")
&& !board[2].equals("") && !board[3].equals("") && !board[4].equals("")
&& !board[5].equals("") && !board[6].equals("")
&& !board[7].equals("") && !board[8].equals(""))
isTie = true;
return isTie;
}//end method is Tie
// private inner class Player manages each Player as a runnable
private class Player implements Runnable
{
private Socket connection; // connection to client
private Scanner input; // input from client
private Formatter output; // output to client
private int playerNumber; // tracks which player this is
private String mark; // mark for this player
private boolean suspended = true; // whether thread is suspended
// set up Player thread
public Player( Socket socket, int number )
{
playerNumber = number; // store this player's number
mark = MARKS[ playerNumber ]; // specify player's mark
connection = socket; // store socket for client
try // obtain streams from Socket
{
input = new Scanner( connection.getInputStream() );
output = new Formatter( connection.getOutputStream() );
} // end try
catch ( IOException ioException )
{
ioException.printStackTrace();
System.exit( 1 );
} // end catch
} // end Player constructor
// send message that other player moved
public void otherPlayerMoved( int location )
{
if (!isTie() && !isWin()) {
output.format("Opponent moved\n");
output.format("%d\n", location); // send location of move
output.flush(); // flush output
}else if(isWin()){
output.format("Game over, you lose\n");
output.format("%d\n", location);
output.flush();
}else if( isTie()){
output.format("Game over. Players Tied\n");
output.format("%d\n", location);
output.flush();
}
} // end method otherPlayerMoved
// control thread's execution
public void run()
{
// send client its mark (X or O), process messages from client
try
{
displayMessage( "Player " + mark + " connected\n" );
output.format( "%s\n", mark ); // send player's mark
output.flush(); // flush output
// if player X, wait for another player to arrive
if ( playerNumber == PLAYER_X )
{
output.format( "%s\n%s", "Player X connected",
"Waiting for another player\n" );
output.flush(); // flush output
gameLock.lock(); // lock game to wait for second player
try
{
while( suspended )
{
otherPlayerConnected.await(); // wait for player O
} // end while
} // end try
catch ( InterruptedException exception )
{
exception.printStackTrace();
} // end catch
finally
{
gameLock.unlock(); // unlock game after second player
} // end finally
// send message that other player connected
output.format( "Other player connected. Your move.\n" );
output.flush(); // flush output
} // end if
else
{
output.format( "Player O connected, please wait\n" );
output.flush(); // flush output
} // end else
// while game not over
while ( true )
{
int location = 9; // initialize move location
if ( input.hasNextInt() )
location = input.nextInt(); // get move location
if ( input.hasNextLine() )
processMessage( input.nextLine() );
// check for valid move
if ( validateAndMove( location, playerNumber ) )
{
displayMessage( "\nlocation: " + location );
output.format( "Valid move.\n" ); // notify client
output.flush(); // flush output
if (isWin()){
output.format("Game over. You win!\n");
//output.format("%d\n", location);
output.flush();
}else if (isTie()){
output.format("Game over. Players Tied\n");
output.format("%d\n", location);
output.flush();
}
} // end if
else // move was invalid
{
output.format( "Invalid move, try again\n" );
output.flush(); // flush output
} // end else
} // end while
} // end try
finally
{
try
{
connection.close(); // close connection to client
} // end try
catch ( IOException ioException )
{
ioException.printStackTrace();
System.exit( 1 );
} // end catch
} // end finally
} // end method run
// set whether or not thread is suspended
public void setSuspended( boolean status )
{
suspended = status; // set value of suspended
} // end method setSuspended
public void processMessage(String message){
if ( message.equals( "New Game" ) )
{
displayMessage( "\nPlayer requested new game" );
for(int i=0; i<board.length; i++){
board[i] = "";
}
output.format("New Game\n");
output.flush();
} // end if
}
} // end class Player
} // end class TicTacToeServer
Your clients will need to listen to the server. Ideally, the action messages sent by the clients would be independent of the status messages sent by the server. This decoupling allows more robust scenarios than the "each client takes a turn" one you are doing. Specifically, the server would send status messages to all the clients, and the clients would update their state based on those messages, rather than their own actions.
Related
I have a created a simple tic tac toe GUI GAME. I want to extend it by changing what is displayed on the buttons from just text "X" and "O" to fancy graphic "X" and "O" (by providing jpg or png files) and also add sounds using .wav files.
This is my code for my game: (It works perfectly.. I just need help with the extensions.. Thanks)
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class TicTacToeGUI implements ActionListener
{
//Class constants
private static final int WINDOW_WIDTH = 300;
private static final int WINDOW_HEIGHT = 300;
private static final int TEXT_WIDTH = 30;
private static final String PLAYER_X = "X"; // player using "X"
private static final String PLAYER_O = "O"; // player using "O"
private static final String EMPTY = ""; // empty cell
private static final String TIE = "T"; // game ended in a tie
private String player; // current player (PLAYER_X or PLAYER_O)
private String winner; // winner: PLAYER_X, PLAYER_O, TIE, EMPTY = in progress
private int numFreeSquares; // number of squares still free
private JMenuItem resetItem; // reset board
private JMenuItem quitItem; // quit
private JLabel gameText; // current message
private JButton board[][]; // 3x3 array of JButtons
private JFrame window = new JFrame("TIC-TAC-TOE");
/**
* Constructs a new Tic-Tac-Toe GUI board
*/
public TicTacToeGUI()
{
setUpGUI(); // set up GUI
setFields(); // set up other fields
}
/**
* Set up the non-GUI fields
*
*/
private void setFields()
{
winner = EMPTY;
numFreeSquares = 9;
player = PLAYER_X;
}
/**
* reset the game so we can start again.
*
*/
private void resetGame()
{
// reset board
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
board[i][j].setText(EMPTY);
board[i][j].setEnabled(true);
}
}
gameText.setText("Game in Progress: X's turn");
// reset other fields
setFields();
}
/**
* Action Performed (from actionListener Interface).
* (This method is executed when a button is selected.)
*
* #param the action event
*/
public void actionPerformed(ActionEvent e)
{
// see if it's a menu item
if(e.getSource() instanceof JMenuItem)
{
JMenuItem select = (JMenuItem) e.getSource();
if (select==resetItem)
{
resetGame();// reset
return;
}
System.exit(0); // must be quit
}
// it must be a button
JButton chose = (JButton) e.getSource(); // set chose to the button clicked
chose.setText(player); // set its text to the player's mark
chose.setEnabled(false); // disable button (can't choose it now)
numFreeSquares--;
//see if game is over
if(haveWinner(chose))
{
winner = player; // must be the player who just went
}
else if(numFreeSquares==0)
{
winner = TIE; // board is full so it's a tie
}
// if have winner stop the game
if (winner!=EMPTY)
{
disableAll(); // disable all buttons
// print winner
String s = "Game over: ";
if (winner == PLAYER_X)
{
s += "X wins";
}
else if (winner == PLAYER_O)
{
s += "O wins";
}
else if (winner == TIE)
{
s += "Tied game";
}
gameText.setText(s);
}
else
{
// change to other player (game continues)
if (player==PLAYER_X)
{
player=PLAYER_O;
gameText.setText("Game in progress: O's turn");
}
else
{
player=PLAYER_X;
gameText.setText("Game in progress: X's turn");
}
}
}
/**
* Returns true if filling the given square gives us a winner, and false
* otherwise.
*
* #param Square just filled
*
* #return true if we have a winner, false otherwise
*/
private boolean haveWinner(JButton c)
{
// unless at least 5 squares have been filled, we don't need to go any further
// (the earliest we can have a winner is after player X's 3rd move).
if(numFreeSquares>4)
{
return false;
}
// find the square that was selected
int row=0, col=0;
outerloop: // a label to allow us to break out of both loops
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
if (c==board[i][j])
{
// object identity
row = i;
col = j; // row, col represent the chosen square
break outerloop; // break out of both loops
}
}
}
// check row "row"
if( board[row][0].getText().equals(board[row][1].getText()) && board[row][0].getText().equals(board[row][2].getText()) )
{
return true;
}
// check column "col"
if (board[0][col].getText().equals(board[1][col].getText()) &&board[0][col].getText().equals(board[2][col].getText()) )
{
return true;
}
// if row=col check one diagonal
if (row == col)
{
if( board[0][0].getText().equals(board[1][1].getText()) && board[0][0].getText().equals(board[2][2].getText()) )
{
return true;
}
}
// if row=2-col check other diagonal
if (row == 2-col)
{
if( board[0][2].getText().equals(board[1][1].getText()) && board[0][2].getText().equals(board[2][0].getText()) )
{
return true;
}
}
// no winner yet
return false;
}
/**
* Disables all buttons (game over)
*/
private void disableAll()
{
if (numFreeSquares==0)
{
return; // nothing to do
}
int i, j;
for (i = 0; i < 3; i++) {
for (j = 0; j < 3; j++) {
board[i][j].setEnabled(false);
}
}
}
/**
* Set up the GUI
*
*/
private void setUpGUI()
{
// for control keys
final int SHORTCUT_MASK = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
window.setSize(WINDOW_WIDTH,WINDOW_HEIGHT);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// set up the menu bar and menu
JMenuBar menubar = new JMenuBar();
window.setJMenuBar(menubar); // add menu bar to our frame
JMenu fileMenu = new JMenu("Game"); // create a menu called "Game"
menubar.add(fileMenu); // and add to our menu bar
resetItem = new JMenuItem("Reset"); // create a menu item called "Reset"
fileMenu.add(resetItem); // and add to our menu (can also use ctrl-R:)
resetItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, SHORTCUT_MASK));
resetItem.addActionListener(this);
quitItem = new JMenuItem("Quit"); // create a menu item called "Quit"
fileMenu.add(quitItem); // and add to our menu (can also use ctrl-Q:)
quitItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, SHORTCUT_MASK));
quitItem.addActionListener(this);
window.getContentPane().setLayout(new BorderLayout()); // default so not required
JPanel gamePanel = new JPanel();
gamePanel.setLayout(new GridLayout(3, 3));
window.getContentPane().add(gamePanel, BorderLayout.CENTER);
gameText = new JLabel("Game in Progress: X's turn");
window.getContentPane().add(gameText, BorderLayout.SOUTH);
// create JButtons, add to window, and action listener
board = new JButton[3][3];
Font font = new Font("Dialog", Font.BOLD, 24);
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
board[i][j] = new JButton(EMPTY);
board[i][j].setFont(font);
gamePanel.add(board[i][j]);
board[i][j].addActionListener(this);
}
}
window.setVisible(true);
}
}
To assign an image to a JButton you can either use constructor or a method below:
JButton myButton = new JButton(new ImageIcon("D:\\icon.png"));
setIcon(new ImageIcon("D:\\icon.png"));
Of course provide your own file path.
To play some .wav sounds you can use this:
import java.io.File;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
public class MusicPlayer {
private Clip clip;
public void play() {
try {
AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(new File("D:\\sound.wav").getAbsoluteFile());
clip = AudioSystem.getClip();
clip.open(audioInputStream);
clip.start();
}
catch(Exception ex) {
System.out.println("Error with playing sound.");
ex.printStackTrace();
}
}
}
I am working on an Virtual-Reality client for a Java API
I am not able to read the second command (1 byte ord) from the Java Server-Socket.
This is my java code:
package de.e_nexus.vr.server;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
import de.e_nexus.vr.server.codes.Client2ServerCode;
import de.e_nexus.vr.server.listeners.VRClientRequestAppInfo;
import de.e_nexus.vr.server.listeners.VRClientStatusListener;
import de.e_nexus.vr.server.listeners.VRExceptionListener;
import de.e_nexus.vr.server.mesh.Mesh;
import de.e_nexus.vr.server.mesh.MeshOutputStream;
public class VRServer extends ServerSocket {
private static final Charset LATIN1;
static {
LATIN1 = Charset.forName("latin1");
}
private final Set<VRClientStatusListener> statusListeners = new LinkedHashSet<VRClientStatusListener>();
private final Set<VRExceptionListener> exceptionListeners = new LinkedHashSet<VRExceptionListener>();
private final Set<VRClientRequestAppInfo> infoListeners = new LinkedHashSet<VRClientRequestAppInfo>();
private final Set<Mesh> toSend = new LinkedHashSet<Mesh>();
private class Worker extends Thread {
private boolean running = true;
public void run() {
try {
setSoTimeout(50);
} catch (SocketException e) {
e.printStackTrace();
}
while (running) {
cycle();
}
T = null;
};
}
private Worker T;
public VRServer() throws IOException {
super(8779);
}
public void addVRClientStatusListener(VRClientStatusListener vrClientStatusListener) {
statusListeners.add(vrClientStatusListener);
}
public void addVRExceptionListener(VRExceptionListener listener) {
exceptionListeners.add(listener);
}
public void start() {
if (T == null) {
T = new Worker();
T.start();
}
}
public void stop() {
T.running = false;
}
public boolean isStopping() {
return T != null && !T.running;
}
protected void cycle() {
try {
Socket s = accept();
log("Connected");
InputStream in = s.getInputStream();
OutputStream out = s.getOutputStream();
int read = in.read();
if (read == -1) {
log("Closed unexpectedly "+in.available());
} else {
Client2ServerCode code = Client2ServerCode.read(read);
log("Read " + code + " ord(" + read + ")");
switch (code) {
case GET_APP_INFO:
StringBuilder sb = new StringBuilder();
for (VRClientRequestAppInfo vrClientRequestAppInfo : infoListeners) {
sb.append(vrClientRequestAppInfo.getLatin1Title());
}
System.out.println(sb);
outLenString(out, sb.toString());
break;
case GET_INCOMING_MESH:
int count = Math.min(toSend.size(), 100);
out.write(count);
System.out.println("wrote incomming mesah count");
for (int i = 0; i < count; i++) {
Iterator<Mesh> iterator = toSend.iterator();
Mesh mesh = iterator.next();
iterator.remove();
ByteArrayOutputStream buff = new ByteArrayOutputStream();
MeshOutputStream mos = new MeshOutputStream(buff);
mos.writeMesh(mesh);
mos.flush();
outLenString(out, buff.size() + "");
out.write(buff.toByteArray());
}
out.flush();
default:
break;
}
System.out.println("Thanks");
}
s.close();
} catch (Exception e) {
notifyExceptionInCycle(e);
}
}
private void log(String string) {
System.out.println(string);
}
private void outLenString(OutputStream out, String string) throws IOException {
byte[] b = string.getBytes(LATIN1);
int length = b.length;
out.write(length);
out.write(b);
out.flush();
}
private void notifyExceptionInCycle(Exception e) {
boolean handled = false;
for (VRExceptionListener vrExceptionListener : exceptionListeners) {
try {
vrExceptionListener.handle(e);
handled = true;
} catch (Exception ex) {
ex.addSuppressed(e);
ex.printStackTrace();
}
}
if (!handled) {
e.printStackTrace();
}
}
public Thread getThread() {
return T;
}
public void addInfoListener(VRClientRequestAppInfo l) {
infoListeners.add(l);
}
public void addMesh(Mesh m) {
toSend.add(m);
}
}
The Client2ServerCode is this:
package de.e_nexus.vr.server.codes;
public enum Client2ServerCode {
GET_APP_INFO, GET_INCOMING_MESH;
public static Client2ServerCode read(int read) {
for (Client2ServerCode c : values()) {
if (c.ordinal() == read) {
return c;
}
}
return null;
}
}
And this is my AGK Basic code
//******************************************************
// AGKVR Demo 1 - Castle Basic
//******************************************************
//This demo application will help you get to know AGKVR
SetErrorMode(0)
SetWindowTitle('VR')
//Load the AGKVR plugin
#import_plugin AGKVR
GET_APP_INFO as integer = 0
sock as integer
sock = ConnectSocket('127.0.0.1',8779,1000)
if sock = 0
message ('Timeout while connecting!')
end
endif
sleep(200)
if GetSocketConnected (sock) <> 1
message ('Not connected!')
end
endif
rem connected!
SendSocketByte(sock, GET_APP_INFO)
FlushSocket(sock)
if GetSocketConnected (sock) <> 1
message ('Could not request for app-info from server!')
end
endif
readTitle(sock)
DeleteSocket(sock)
sock = ConnectSocket('127.0.0.1',8779,1000)
if sock = 0
message ('Timeout while connecting!')
end
endif
readMesh(sock)
DeleteSocket(sock)
// <---------------------- below this is old example code, you can ignore it
//Init App
SetSyncRate(0, 0)
SetWindowSize( 1024, 768, 0 )
SetScissor(0, 0, 0, 0)
//Hand Model ID numbers
RightHandModel as integer = 100
LeftHandModel as integer = 101
HandModelImg as integer = 100
//Generate Mipmaps for nicer looking textures
SetGenerateMipmaps( 1 )
//Create Skybox
SetSkyBoxHorizonColor( 200, 200, 255 )
SetSkyBoxHorizonSize( 10, 2 )
SetSkyBoxSkyColor( 50, 50, 255)
SetSkyBoxSunColor( 255,255,255 )
SetSkyBoxVisible( 1 )
SetSkyBoxSunSize( 1, 3.0 )
SetSkyBoxSunVisible( 1 )
//Load Hand Models
//---------------------------
//Right Hand
LoadObjectWithChildren(RightHandModel,"RHand.FBX")
SetObjectScale(RightHandModel,1.15,1.15,1.15)
SetObjectPosition(RightHandModel,0.0,0.0,-0.07)
SetObjectRotation(RightHandModel,0.0,180.0,90.0)
FixObjectPivot(RightHandModel)
SetObjectAnimationSpeed(RightHandModel,20)
LoadImage(HandModelImg,"Hand.png")
SetObjectImage(RightHandModel,HandModelImg,0)
SetObjectVisible(RightHandModel,0)
SetObjectCollisionMode(RightHandModel,0)
//Left Hand
LoadObjectWithChildren(LeftHandModel,"LHand.FBX")
SetObjectScale(LeftHandModel,1.15,1.15,1.15)
SetObjectPosition(LeftHandModel,0.0,0.0,-0.07)
SetObjectRotation(LeftHandModel,0.0,180.0,270.0)
FixObjectPivot(LeftHandModel)
SetObjectAnimationSpeed(LeftHandModel,20)
SetObjectImage(LeftHandModel,HandModelImg,0)
SetObjectVisible(LeftHandModel,0)
SetObjectCollisionMode(LeftHandModel,0)
//Call function to load world models
LoadWorld( )
//Set the Camera Range in AGKVR
//It is necessary to use this command for setting the camera's range instead of the standard AGK SetCameraRange command
AGKVR.SetCameraRange( 0.01, 1000.0 )
//Initialiaze AGKVR
//------------------------------------------------
// The parameters are the Right and Left Eye image ID's that will be used to render to the HMD
InitError As Integer
RightEyeImg As Integer = 500
LeftEyeImg As Integer = 501
InitError = AGKVR.Init( RightEyeImg, LeftEyeImg )
// InitError = 0: SUCCESS!
// InitError = 1: Unable to init VR runtime
// InitError = 2: Compositor initialization failed.
//This command will lock the player's heading direction
//to follow the turn angle of the HMD. This would be common in FPS games, where
//you want the forward moving direction's turn angle to change based on where the
//player is looking. LockPlayerTurn is ON(1) by default
AGKVR.LockPlayerTurn( 1 )
//This command will lock the player's heading direction (forward on the Z axis)
//to follow the pitch angle of the HMD. This would be useful in a freeflight style game
//where you want the forward moving direction's pitch angle to change based on where the
//player is looking. LockPlayerPitch is OFF(0) by default
AGKVR.LockPlayerPitch( 0 )
CreateVector3(1,2,3.4)
do
//Get AGKVR's Player container's current Position, which will be used later for collision purposes:
PlayerPosX as float
PlayerPosY as float
PlayerPosZ as float
PlayerPosX = AGKVR.GetPlayerX(): `
PlayerPosY = AGKVR.GetPlayerY()
PlayerPosZ = AGKVR.GetPlayerZ()
//Control Player Turn Angle with Left Controller Joystick
valx as float
valz as float
valx = AGKVR.LeftController_JoyX( )
AGKVR.RotatePlayerLocalY( valx )
//Control Player Movement based on Right Controller Joystick
valx = AGKVR.RightController_JoyX( )
valz = AGKVR.RightController_JoyY( )
AGKVR.MovePlayerLocalZ( valz*0.06 )
AGKVR.MovePlayerLocalX( valx*0.06 )
//Move with the Keyboard
if GetRawKeyState(87) = 1
AGKVR.MovePlayerLocalZ( 0.1 )
endif
if GetRawKeyState(83) = 1
AGKVR.MovePlayerLocalZ( -0.1 )
endif
if GetRawKeyState(65) = 1
AGKVR.MovePlayerLocalX( -0.1 )
endif
if GetRawKeyState(68) = 1
AGKVR.MovePlayerLocalX( 0.1 )
endif
//Collision
New_PlayerPosX as float
New_PlayerPosY as float
New_PlayerPosZ as float
//Get the Player's ground position now that any movement has occured.
New_PlayerPosX = AGKVR.GetPlayerX()
New_PlayerPosY = AGKVR.GetPlayerY()
New_PlayerPosZ = AGKVR.GetPlayerZ()
//Check for collision from previous position to new position (add the radius to the Y position)
if ObjectSphereSlide(0,PlayerPosX,PlayerPosY+0.5,PlayerPosZ,New_PlayerPosX,New_PlayerPosY+0.5,New_PlayerPosZ,0.5)>0
//Get the collision point (Subtract the collision radius from the Y position)
New_PlayerPosX = GetObjectRayCastSlideX(0)
New_PlayerPosY = GetObjectRayCastSlideY(0)-0.5
New_PlayerPosZ = GetObjectRayCastSlideZ(0)
//Set the player position to the collision point
AGKVR.SetPlayerPosition( New_PlayerPosX, New_PlayerPosY, New_PlayerPosZ )
endif
//Control Hand animation based on Trigger feedback
Rvalue as float
Lvalue as float
Rvalue = AGKVR.RightController_Trigger( )
Lvalue = AGKVR.LeftController_Trigger( )
SetObjectAnimationFrame(RightHandModel, GetObjectAnimationName( RightHandModel, 1 ), GetObjectAnimationDuration(RightHandModel,GetObjectAnimationName( RightHandModel, 1 ))*Rvalue,0)
SetObjectAnimationFrame(LeftHandModel, GetObjectAnimationName( LeftHandModel, 1 ), GetObjectAnimationDuration(LeftHandModel,GetObjectAnimationName( LeftHandModel, 1 ))*Lvalue,0)
//Now that the player's position and orientation has been modified,
//the UpdatePlayer command is called to update all the components of
//AGKVR's player framework
AGKVR.UpdatePlayer( )
//Position Hand Objects
if AGKVR.RightControllerFound( ) = 1
SetObjectPosition( RightHandModel, AGKVR.GetRightHandX(), AGKVR.GetRightHandY(), AGKVR.GetRightHandZ())
SetObjectRotation( RightHandModel, AGKVR.GetRightHandAngleX(), AGKVR.GetRightHandAngleY(), AGKVR.GetRightHandAngleZ())
SetObjectVisible( RightHandModel, 1 )
else
SetObjectVisible( RightHandModel, 0 )
endif
if AGKVR.LeftControllerFound( ) = 1
SetObjectPosition( LeftHandModel, AGKVR.GetLeftHandX(), AGKVR.GetLeftHandY(), AGKVR.GetLeftHandZ())
SetObjectRotation( LeftHandModel, AGKVR.GetLeftHandAngleX(), AGKVR.GetLeftHandAngleY(), AGKVR.GetLeftHandAngleZ())
SetObjectVisible( LeftHandModel, 1 )
else
SetObjectVisible( LeftHandModel, 0 )
endif
//This command renders to the HMD.
AGKVR.Render( )
//The camera's position and rotation will determine what is displayed on the monitor, not the HMD
SetCameraPosition( 1, AGKVR.GetHMDX(), AGKVR.GetHMDY(), AGKVR.GetHMDZ())
SetCameraRotation( 1, AGKVR.GetHMDAngleX(), AGKVR.GetHMDAngleY(), AGKVR.GetHMDAngleZ())
Print( ScreenFPS() )
//Display some controller feedbacks on the monitor
if AGKVR.RightController_Grip() = 1
Print( "Right Grip Pressed" )
endif
if AGKVR.LeftController_Grip() = 1
Print( "Left Grip Pressed" )
endif
if AGKVR.LeftController_Button1() = 1
Print( "Left Button 1 Pressed" )
endif
if AGKVR.RightController_Button1() = 1
Print( "Right Button 1 Pressed" )
endif
if AGKVR.LeftController_Button2() = 1
Print( "Left Button 2 Pressed" )
endif
if AGKVR.RightController_Button2() = 1
Print( "Right Button 2 Pressed" )
endif
//Sync to update the monitor display (the HMD display is updated through AGKVR's Render Command
//so this Sync is not necessary to for rendering to the HMD)
Sync()
loop
function readTitle(s)
count as integer
count=0
sleep (5)
while count < 100
if GetSocketBytesAvailable(s) > 0
exit
endif
sleep(10)
inc count
endwhile
if count = 100
message ('Timeout while read title!')
end
endif
appTitleLength as integer
appTitleLength = GetSocketByte(s)
txt as string
txt=''
for i = 0 to appTitleLength
txt = txt + chr(GetSocketByte(s))
next i
SetWindowTitle(txt)
endfunction
function readMesh(s)
if SendSocketByte(s, 1) = 1
message('Could not place command to buffer')
end
endif
if FlushSocket(s) = 1
message('Could not send the buffered command to the server')
end
endif
count as integer
count = 0
message('flushed')
sleep (5)
while count < 100
if GetSocketBytesAvailable(s) > 0
exit
endif
sleep(4)
inc count
endwhile
if count = 100
message ('Timeout while read incomming mesh count!')
end
endif
// in count is the mesh size stored.
for i = 0 to count
lenSize as integer
count = 0
while count < 100
if GetSocketBytesAvailable(s) > 0
exit
endif
sleep(5)
inc count
endwhile
if count = 100
message('Timeout while read size of incomming mesh memblock.')
end
endif
lenSize = GetSocketByte(s)
lenTxt as String
lenTxt=''
for j = 0 to lenSize
lenTxt=lenTxt + chr(GetSocketByte(s))
next j
lenSize = val(lenTxt)
count = 0
while count < 100
if GetSocketBytesAvailable(s) = lenSize
exit
endif
sleep(5)
inc count
endwhile
if count = 100
message('Timeout while read incomming mesh memblock.')
end
endif
blk as integer
blk = CreateMemblock(lenSize)
for j = 0 to lenSize
SetMemblockByte(blk, j, GetSocketByte(s))
next j
CreateObjectFromMeshMemblock(blk)
next i
endfunction
function LoadWorld( )
scaleval as float = 0.037
LoadObject(1,"Castle.fbx")
SetObjectScale(1,scaleval,scaleval,scaleval)
LoadImage(1,"banquet hall_D.png")
LoadImage(2,"portcullis_D.png")
LoadImage(3,"stable_D.png")
LoadImage(4,"well_D.png")
LoadImage(5,"wall.png")
LoadImage(6,"tower.png")
LoadImage(7,"kings apartment_D.png")
SetObjectMeshImage( 1, 1, 2, 0 )
SetObjectMeshImage( 1, 2, 5, 0 )
SetObjectMeshImage( 1, 4, 6, 0 )
SetObjectMeshImage( 1, 5, 7, 0 )
SetObjectMeshImage( 1, 6, 1, 0 )
SetObjectMeshImage( 1, 7, 3, 0 )
SetObjectMeshImage( 1, 8, 4, 0 )
SetObjectMeshImage( 1, 9, 1, 0 )
//Field
LoadImage(50,"TerrainGrass.png")
LoadObject(50,"field.fbx")
SetObjectRotation(50,0,0,0)
SetObjectScale(50,5,5,5)
SetObjectImage(50,50,0)
SetObjectMeshUVScale( 50, 1, 0, 100, 100 )
SetImageWrapU(50,1)
SetImageWrapV(50,1)
endfunction
The App-info is read successfully but Java does not recieve the first byte of the second command, why not?
The socket is not allowed as parameter. I put the open socket inside the functions and it works.
I want to use the ExecutorService to run a series of the same Runnable/Callable tasks. I've searched around for a tutorial or an example, but nothing that involves actually setting the value of an existing Runnable/Callable object and then using submit() to send that object back into the ExecutorService.
Basically, here's what I want to do:
Get a list of servers.
Iterate thru the list of servers, calling InetAddress.getByName(host) to grab data on each host.
Collect that data into Server beans for storage in a database.
So, right now, with 10,000(+) servers, it takes forever. So, my thought was to use the ExecutorService to manage a pool of threads. What I can't seem to figure out is how to detect when one thread is finished so I can grab the data. Then I need to get the next server in the list, place it into the Task and then submit() back to the ExecutorService.
That said, what I've read so far seems to point to the following, ExecutorService, submit(), Callable, Future.
So, as a psuedo-process:
Get list of servers.
Set up ExecutorService with numThreads number of threads
Iterate numThreads and create numThreads WorkerTask() objects.
Submit() WorkerTask() to ExecutorService for processing.
Detect when a WorkerTask() has finished, grab the Callable (Future) result.
Get the next server.
Set the server value into the WorkerTask() <-- How? This is elusive ...
Submit() the WorkerTask() (with the new value) back to the ExecutorService.
Iterate again.
ExecutorService.shutdown()...
So, a tutorial or example of this would be great...especially an example of placing a new value into a WorkerTask(). Also, I'd appreciate any thoughts on this proposed solution? Is this bad, good, or if there is another way, I'm open.
02/09/2014 - Edit and Add
Hi, So the following is a first cut at this. But to answer some question being posed:
- I've solved the issue of placing new data in a Worker and resubmitting to the ExecutorService...see the code snippet.
- I've also solved the issue of "get the stuff"...I simply cast the Future() results to the Worker class...see the code snippet.
- Finally, while I could just allocate every server to a Worker() and a Future(), I'm concerned that the current 10,000 will grow and memory will become an issue.
That said, here's a first attempt, and this works pretty well. Runs much faster and only uses the getNumbnerThreads() Worker and Future objects:
public List<ServerBean> lookupHostIps ( List<ServerBean> theServerList ) {
//ServerBean serverDto = null;
ServerBean ipDto = null;
List<ServerBean> theResults = new ArrayList<ServerBean>();
List<HostLookupWorker> theWorkers = new ArrayList<HostLookupWorker>( getNumberThreads() );
List<Future<HostLookupWorker>> theFutures = new ArrayList<Future<HostLookupWorker>>( getNumberThreads() );
ExecutorService executor = Executors.newFixedThreadPool ( getNumberThreads() );
// WORKERS : Create the workers...prime them with a server
// bean...
//
for (int j = 0; j < getNumberThreads(); j++) {
//for (int j = 0; j < theServerList.size(); j++) {
theWorkers.add ( new HostLookupWorker( theServerList.get(j) ) );
Future<HostLookupWorker> theFuture = executor.submit ( theWorkers.get ( j ) );
theFutures.add ( j, theFuture );
}
int lloopItems = getNumberThreads(); /* loops thru all servers */
//int lloopThreads = 0; /* loops thru threads */
int lidxThread = 0; /* what thread is ready */
//int lidxFuture = 0; /* what future is ready */
boolean lblnNext = false; /* is a thread done/ready */
int lidxWorkers = 0; /* tracks the futures */
while ( lloopItems < theServerList.size() ) {
// READY : Is one of the threads ready for more work?
if ( lblnNext ) {
// VALUE : Grab the thread by index and set the next
// server value.
theWorkers.get ( lidxThread ).setBean ( theServerList.get(lloopItems) );
getLog().debug ( "Thread [" + lidxThread + "] Assigned Host ["+theServerList.get(lloopItems).getServerName ()+"] " );
// FUTURE : Package a new Future<HostLookupWorker>
// and submit it to the thread pool.
Future<HostLookupWorker> theFuture = executor.submit ( theWorkers.get ( lidxThread ) );
theFutures.add ( lidxThread, theFuture );
lblnNext = false; /* reset to allow for another thread */
lloopItems++; /* increment the main loop counter */
}
while ( !(lblnNext) ) {
try {
if ( theFutures.get(lidxWorkers).get() != null ) {
// GET THE STUFF : Grab the results from the Future...
HostLookupWorker ltheItem = theFutures.get(lidxWorkers).get();
if ( ltheItem.getValue () != null ) {
if (!ltheItem.getValue ().contains("Cannot find host")){
ipDto = new ServerBean ();
ipDto.setServerId ( ltheItem.getBean ().getServerId() );
ipDto.setServerName ( ltheItem.getBean ().getServerName() );
ipDto.setIpAddress ( ltheItem.getValue () );
theResults.add(ipDto);
}
lidxThread = lidxWorkers; /* this thread is ready for more work */
lblnNext = true; /* flag the upper condition to assign new work */
getLog().debug ( "Thread [" + lidxThread + "] Host ["+ltheItem.getHost ()+"] has IP ["+ltheItem.getValue()+"]" );
}
}
else {
getLog().debug ( "Thread [" + lidxThread + "] NULL" );
}
lidxWorkers++; /* next worker/future */
if ( lidxWorkers >= getNumberThreads() ) {
lidxWorkers = 0;
}
}
catch(ExecutionException e){
getLog().error ( e );
}
catch(InterruptedException e){
getLog().error ( e );
}
}
}
executor.shutdown ();
return theResults;
}
Here's the Worker/Thread class :
import java.net.*;
import java.util.concurrent.Callable;
import com.lmig.cdbatch.dto.ServerBean;
public class HostLookupWorker implements Callable {
private InetAddress node = null;
private String value = null;
private boolean busy = false;
private ServerBean bean = null;
public HostLookupWorker () {
this.busy = false;
}
// public HostLookupWorker ( String theHost ) {
// this.busy = false;
// this.host = theHost;
// }
public HostLookupWorker ( ServerBean theItem ) {
this.busy = false;
this.bean = theItem;
//this.host = theItem.getServerName ().trim ();
}
public String lookup ( String host ) {
if ( host != null ) {
// get the bytes of the IP address
try {
this.node = InetAddress.getByName ( host );
}
catch ( UnknownHostException ex ) {
this.value = "Not Found [" + getHost() + "]";
return "Not Found [" + host + "]";
}
if ( isHostname(host) ) {
getBean().setIpAddress ( node.getHostAddress() );
return node.getHostAddress();
}
else { // this is an IP address
//return node.getHostName();
return host;
}
}
return host;
} // end lookup
public boolean isHostname(String host) {
// Is this an IPv6 address?
if (host.indexOf(':') != -1)
return false;
char[] ca = host.toCharArray();
// if we see a character that is neither a digit nor a period
// then host is probably a hostname
for (int i = 0; i < ca.length; i++) {
if (!Character.isDigit(ca[i])) {
if (ca[i] != '.')
return true;
}
}
// Everything was either a digit or a period
// so host looks like an IPv4 address in dotted quad format
return false;
} // end isHostName
// public void run() {
// value = lookup ( getHost() );
//
// }
public Object call() throws Exception {
Thread.sleep ( 10000 );
this.busy = true;
this.value = lookup ( getHost() );
this.busy = false;
return this;
}
public String getHost() {
return getBean().getServerName ().trim ();
}
public void setHost(String host) {
getBean().setServerName ( host.trim () );
}
public InetAddress getNode() {
return node;
}
public void setNode(InetAddress node) {
this.node = node;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public boolean isBusy() {
return busy;
}
public void setBusy(boolean busy) {
this.busy = busy;
}
public ServerBean getBean() {
return bean;
}
public void setBean(ServerBean bean) {
this.bean = bean;
}
}
So, to summarize:
- The process does work, and works fast.
- I need to fix the code a little, as there are getNumberThreads() - 1 Futures left unprocessed when the larger while () loop finally finishes...
So, what I'm struggling with now is how to detect when a thread is finished...I've seen multiple examples, one testing for Future() != null, others testing for Future() == null. So which one is right?
I think the best aproach in this case would be create a task for every server since they will be executed by the threads in the pull and then use tge future objects to retrieve server information returned by the task.
I am writing an tip calculator app in java applet with GUI, my question is how I make sure the error message will pop up if users enter letter instead of number
it is my first time asking question, please be easy on me! Thanks!!!
import objectdraw.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
// Typing in the text field and hitting return adds text to text area.
// Clicking on button erases the text area.
public class TextApplet extends Controller implements ActionListener
{
private static final int ROWS = 1; // rows in TextArea
private static final int COLS = 10; // cols in text field & area
private String amount;
private float number;
private JTextField inField, output; // Input field
private JButton clear, calc;
// button to clear output
public void begin()
{
Container contentPane = getContentPane();
JPanel topPanel = new JPanel(); // prepare text field & label
JLabel inLabel = new JLabel("Bill Cost: ");
inField = new JTextField(COLS);
inField.addActionListener(this);
JLabel topTitle = new JLabel("Tip Calculator", JLabel.CENTER);
JPanel combinePanel = new JPanel();
combinePanel.add ( inLabel );
combinePanel.add ( inField );
JPanel combinePanel1 = new JPanel();
combinePanel1.add ( topTitle );
topPanel.add ( combinePanel1 );
topPanel.add ( combinePanel );
topPanel.setLayout ( new GridLayout ( 3,1) );
contentPane.add(topPanel,BorderLayout.NORTH);
JPanel centerPanel = new JPanel(); // prepare text area & label
JLabel outLabel = new JLabel("Bill + Tip:");
output = new JTextField(COLS);
output.setEditable(false); // Prevent user from wrting in output
centerPanel.add(outLabel);
centerPanel.add(output);
contentPane.add(centerPanel,BorderLayout.CENTER);
JPanel bottomPanel = new JPanel();
// create button
clear = new JButton(" Clear ");
calc = new JButton("Calculate");
calc.addActionListener(this);
clear.addActionListener(this);
bottomPanel.add(calc);
bottomPanel.add(clear);
contentPane.add(bottomPanel,BorderLayout.SOUTH);
validate();
}
// add text to area if user hits return, else erase text area
public void actionPerformed(ActionEvent evt)
{
if (evt.getSource() == calc )
{
amount = inField.getText();
number = ( Float.parseFloat( amount ) );
number = (15*number/100);
output.setText ( Float.toString ( number ) + '$' );
}
else if (evt.getSource() == clear )
{
output.setText("$");
inField.setText("");
}
}
}
There are any number of ways you might achieve this, you could use
An InputVerifier
A JFormattedTextField
A JSpinner
Or a DocumentFilter and examples
Take a look at javax.swing.InputVerifier. That can be easily attached to a JTextField
JTextField inputField = new JTextField();
inputField.setInputVerifier(new NumericInputVerifier());
private class NumericInputVerifier extends InputVerifier
{
#Override
public boolean verify(JComponent input)
{
if (((JTextField) input).getText().matches("[0-9]+"))
{
return true;
}
else
{
JOptionPane.showMessageDialog(input, "Only numbers are allowed", "Warning", JOptionPane.WARNING_MESSAGE);
return false;
}
}
}
A complete example can be found here.
Edit Added an example of how to use InputVerifier to limit to numeric input. You'll want to double check the regex, but the basic idea is there...
Use a JFormattedTextField or a DocumentFilter. Then the user won't even be able to enter a non-numeric digit. See:
How to Use Formatted Text Fields
Implementing a Document Filter
For the document filter you will need to check each chraacter as it is entered to make sure it is a digit.
It is always better to do simple edits like that as the user types, rather than wait until you click on a button to do processing.
Hello Friend I will give a suggestion
please add validation when call actionPerformed method
public void actionPerformed(ActionEvent evt)
{
if (evt.getSource() == calc )
{
if(validate()){
amount = inField.getText();
number = ( Float.parseFloat( amount ) );
number = (15*number/100);
output.setText ( Float.toString ( number ) + '$' );
}
else{
// show message for inter valid number or any other
}
}
else if (evt.getSource() == clear )
{
output.setText("$");
inField.setText("");
}
}
boolean validate(){
try{
amount = inField.getText();
number = ( Float.parseFloat( amount ) );
return true;
}
catch(Exception e){
return false;
}
}
If you try to call Float.parseFloat on a String that cannot be converted to a float, it will throw a NumberFormatException. You need to catch this exception.
try {
number = ( Float.parseFloat( amount ) );
number = (15*number/100);
output.setText ( Float.toString ( number ) + '$' );
} catch(NumberFormatException e) {
//code to show error message here
}
Well considering, you'd have to turn the string into an integer to do the math, you could do this:
try {
int number = Ineger.parseInt(inField.getText());
} catch (NumberFormatException e) {
//SHOW WARNING
}
if (Label1.getText().matches("[0-9]+"))
// does Label1 containts numbers.
{
// do math
}
else
{
// warning
Lavbel1.setText("");
}
I am working on a client/server Tic-Tac-Toe game that consists of one server, and a client that consists of two threads. The entire program includes a TicTacToeServer class, TicTacToeService class, and TicTacToeClientPanel (which is the GUI and the client put together).
The main problem I am facing is within the client class itself. I launch two windows of the GUI/client (for the two different players), and am able to place one marker (X) on the first player. After this, the threads seem to halt, and I am unable to continue playing the game.
If I attempt to put client 1's (player 1) thread to sleep, it sleeps for its given allotment, but client 2's (player 2) thread never begins.
Is there any way that I can alternate between these two threads and go through my program, dependent on which player's turn it is?
import java.awt.*; //Color and GridLayout
import java.awt.event.*;
import java.io.*; //DataInputStream & DataOutputStream
import java.net.Socket;
import java.util.Scanner;
import javax.swing.*; //JPanel & JPanel
import javax.swing.border.LineBorder;
/**
* This is the Main Panel for the TicTacToe Client.
* It uses a displayBoard of Cell objects to display the TicTacToe board
* #author Professor Myers
*
*/
public class TicTacToeClientPanel extends JPanel implements Runnable {
//instance variables and constants
private Cell displayBoard[][] = new Cell[3][3];
private Scanner fromServer;
private PrintWriter out;
private Boolean myTurn, waiting, inputReady;
private Thread thread;
private char mySymbol;
private int rowSelected, columnSelected;
private JLabel statusLabel, playerInfo;
public static final int PLAYER1 = 1, PLAYER2 = 2;
public TicTacToeClientPanel()
{
//give initial values to instance variables
mySymbol = ' ';
myTurn = false;
JPanel sub1 = new JPanel();
playerInfo = new JLabel("");
statusLabel = new JLabel("");
sub1.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
//initialize Cells in board array and add to display
for (int y = 0; y < 3; y++)
{
for (int x = 0; x < 3; x++)
{
displayBoard[y][x] = new Cell(x+1, y+1);
c.gridx = y;
c.gridy = x;
c.fill = GridBagConstraints.BOTH;
sub1.add(displayBoard[y][x], c);
}
}
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
add(new JPanel().add(playerInfo));
add(sub1);
add(new JPanel().add(statusLabel));
connectToServer();
}
private void connectToServer()
{
try
{
//create socket
//set up Scanner and PrintWriter
Socket s = new Socket("localhost", 8880);
InputStream instream = s.getInputStream();
OutputStream outstream = s.getOutputStream();
fromServer = new Scanner(instream);
out = new PrintWriter(outstream);
}
catch (Exception e)
{
System.err.println(e);
}
//start the thread
thread = new Thread(this);
thread.start();
}
public void run()
{
int otherPRow, otherPColumn;
try {
int player = Integer.parseInt(fromServer.nextLine()); //Begin the game
int message = 0;
//set up symbol
//keep track of who's turn it is
//Display the player number and symbol (JLabel)
//Display the status of the player (who's turn is it)
if (player == PLAYER1) {
mySymbol = 'X';
playerInfo.setText("Player 1 with symbol \'X\'");
statusLabel.setText("My turn");
myTurn = true; //player1 goes first
message = Integer.parseInt(fromServer.nextLine());
}
else if (player == PLAYER2) {
mySymbol = 'O';
playerInfo.setText("Player 2 with symbol \'O\'");
statusLabel.setText("Waiting for Player 1 to move");
//what to do with waiting?
myTurn = false;
while (!myTurn) {
if (fromServer.hasNextLine()) {
System.out.println("ITS HAPPENING");
myTurn = true;
thread.setPriority(thread.MAX_PRIORITY);
}
}
}
while(message != 1 && message != 2 && message != 3) //CHANGE TO GAME NOT OVER
{
if(player == PLAYER1)
{
//wait for user to select a cell - sleep for awhile
//"write" the row and column to server
//"read" from the server - perform the appropriate action
//this code is only reached if server passes 5 or 4 to the first player
if (myTurn) {
waiting = true;
while(waiting) {
Thread.sleep(1000);
} //thread sleeps until something is clicked
}
//if this cell value is not empty (WRITE)
if (displayBoard[rowSelected-1][columnSelected-1].getSymbol() != ' ') {
System.out.println("Success");
out.println(rowSelected + '\n' + columnSelected);
out.flush();
statusLabel.setText("Waiting for Player 2 to move");
}
waiting = true;
while (waiting)
Thread.currentThread().sleep(1000);
//READ from server
message = Integer.parseInt(fromServer.nextLine());
if (message == 1) {
statusLabel.setText("I Won! (X)");
return;
}
else if (message == 2) {
//update from player 2's turn
otherPRow = Integer.parseInt(fromServer.nextLine());
otherPColumn = Integer.parseInt(fromServer.nextLine());
displayBoard[otherPRow-1][otherPColumn-1].setSymbol('O');
statusLabel.setText("Player 2 has won (O)");
return;
}
else if (message == 3) {
//update from player 2's turn
otherPRow = Integer.parseInt(fromServer.nextLine());
otherPColumn = Integer.parseInt(fromServer.nextLine());
displayBoard[otherPRow-1][otherPColumn-1].setSymbol('O');
statusLabel.setText("Game is over, no winner");
return;
}
else if (message == 4) { //traverses back to beginning of loop
otherPRow = Integer.parseInt(fromServer.nextLine());
otherPColumn = Integer.parseInt(fromServer.nextLine()); //What kind does it send? normal or +1?
displayBoard[otherPRow-1][otherPColumn-1].setSymbol('O');
statusLabel.setText("My turn");
myTurn = true;
}
}
else if(player == PLAYER2)
{
//"read" from the server - perform the appropriate action
//wait for the user to select a cell - sleep for a while
//"write" the row and column to server
myTurn = true;
statusLabel.setText("My turn");
message = Integer.parseInt(fromServer.nextLine());
System.out.println(message);
//player1 has won or game is full
if (message == 1 || message == 3) {
otherPRow = Integer.parseInt(fromServer.nextLine());
otherPColumn = Integer.parseInt(fromServer.nextLine());
displayBoard[otherPRow-1][otherPColumn-1].setSymbol('X');
if (message == 1) {
statusLabel.setText("Player 1 (X) won");
return;
}
else {
statusLabel.setText("Game is over, no winner");
return;
}
}
else if (message == 2) { //player2 has won
statusLabel.setText("I won! (O)");
return;
}
else if (message == 4) {
otherPRow = Integer.parseInt(fromServer.nextLine());
otherPColumn = Integer.parseInt(fromServer.nextLine());
displayBoard[otherPRow-1][otherPColumn-1].setSymbol('X');
//SLEEP for user input
statusLabel.setText("My turn");
myTurn = true;
waiting = true;
while (waiting) {
Thread.sleep(1000);
}
//WRITE to server
char s = displayBoard[rowSelected-1][columnSelected-1].getSymbol();
if (s != ' ' && s != 'X' && waiting == false) {
out.println(rowSelected + '\n' + columnSelected);
out.flush();
}
statusLabel.setText("Waiting for Player 1 to move");
if (!myTurn)
Thread.currentThread().sleep(10000);
}
}
}
}
catch (Exception e)
{
}
}
public class Cell extends JPanel
{
int row;
int column;
private char symbol;
public Cell(int r, int c)
{
row = r;
column = c;
symbol = ' ';
setBorder(new LineBorder(Color.black,1));
setPreferredSize(new Dimension(100,150));
addMouseListener(new ClickListener());
}
public void setSymbol(char c)
{
symbol = c;
repaint();
}
public char getSymbol()
{
return symbol;
}
protected void paintComponent (Graphics g)
{
super.paintComponent(g);
if(symbol == 'X')
{
g.drawLine(10, 10, getWidth()-10, getHeight()-10);
g.drawLine(getWidth()-10, 10, 10, getHeight()-10);
}
else if(symbol == 'O')
{
g.drawOval(10, 10, getWidth()-10, getHeight()-20);
}
}
private class ClickListener extends MouseAdapter
{
public void mouseClicked(MouseEvent e)
{
System.out.println("Clicked: " + row + " " + column);
if(symbol == ' ' && myTurn)
{
setSymbol(mySymbol);
myTurn = false;
rowSelected = row;
columnSelected = column;
statusLabel.setText("Waiting for the other player to move");
waiting = false;
}
}
}
}
public static void main (String[] args)
{
JFrame frame = new JFrame();
frame.setBounds(0, 0, 1000, 1200);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
TicTacToeClientPanel ttt = new TicTacToeClientPanel();
frame.getContentPane().add(ttt);
frame.pack();
frame.setVisible(true);
}
}
I quickly looked at your code. One tip: the variable waiting is not volatile. Therefore, there is no guarantee that the following loop will ever end:
waiting = true;
while(waiting) {
Thread.sleep(1000);
} //thread sleeps until something is clicked
On a mouse click waiting is set to false. That happens in a different thread. Because the variable waiting is not volatile, the JVM is allowed to optimize the above loop to:
while (true) {
Thread.sleep(1000);
}
Try making waiting volatile. That forces each write to this variable to become visible by other threads. If the variable is not volatile, then each thread may keep its own local copy of this variable.