x x
x x

Reputation: 35

Pause and wait for button to be clicked, then continue

I am trying to build a simple tictactoe network game. I need the program to wait until a player makes a move and then continue. In my whileConnected() function at the bottom of my code, I have a while(true) cycle that is supposed to run forever and display a confirmation message when the button is pressed (which is signaled by the fact that the content of the String variable 'message' changes).

The problem is that even if the String message variable changes when the button is clicked, my whileConnected() function never realizes this and the if statement inside the function never evaluates to true. An identical if statement inside the ButtonListener class works fine and displays the desired confirmation message.

How can I solve this problem? I've read and read and I get the idea that I should use Threads (I read about them but I never used them before, that's why it's only a guess). Do I need threads? Can someone explain in short the principle that should be used for this specific problem? (how can I make the program pause until the button is clicked and then continue using relevant information created when the button was clicked). A code example would really lighten up my reading about threads - which is a really abstract topic when one is a beginner.

Below is my code, thanks in advance.

public class Test extends JFrame
{
    private Container contentPane;
    private JButton btn00;
    private static String message = "";

    private class ButtonListener implements ActionListener
    {

        @Override
        public void actionPerformed(ActionEvent e)
        {
            String buttonText = e.getActionCommand();

            if (buttonText.equals("My Button"))
            {
                message = "pressed";
                if (message != "")
                    System.out.println(message+"(executed by ButtonListener)");
            }           
        }

    }

    public Test()
    {
        this.contentPane = this.getContentPane();
        btn00 = new JButton("My Button");
        btn00.setSize(btn00.getPreferredSize());
        btn00.setLocation(20,20);
        ButtonListener listener = new ButtonListener();
        btn00.addActionListener(listener);

        // configure frame
        this.setSize(300,300);
        this.setDefaultCloseOperation(EXIT_ON_CLOSE);

        // make panel
        JPanel panel = new JPanel();
        panel.setSize(200,200);
        panel.setLocation(10,10);
        panel.add(btn00);
        this.contentPane.add(panel);        
    }

    public static void main(String[] args)
    {
        Test gui = new Test();
        gui.setVisible(true);
        // connected
        whileConnected();

    }

    private static void whileConnected()
    {
        System.out.println("message is at first empty: "+message);
        while (true)
        {
            // the if below never evaluates to true... why?

            if (message != "") // this is never true
                System.out.println(message+"(executed by whileConnected)");         
        }

    }

}

Upvotes: 0

Views: 8476

Answers (3)

MadProgrammer
MadProgrammer

Reputation: 347184

Swing, like most GUI frameworks, is an event driven environment. Basically this means, the application will wait for some kind of event to occur and then will trigger any registered listeners tha are waiting for notification for that event.

In your case, you would simply need to register a AdtionListener to the button. When the user clicks it (or activates via the keyboard), the actionPerformed method will be called and you can execute what ever code you need.

Check out How to write action listeners for more info.

Swing is also a single threaded framwework. That is, all interaction with the UI must be executed from within the context of the Event Dispatching Thread.

This also means, that any long running or blocking task MUST be executed from a separate thread so as not to block the EDT from processing incoming events and repaint request, which could make you application appear as if it has hung.

Check out Concurrency in Swing for more details

To my mind, your game protocol is going to need some way of tracking whose round it is. This would be achieved simply via the use if a virtual token.

The basic concept would be, unless the player has the token, they can't make a move. Once the player makes a move, that move and the token is sent to the other player.

Upvotes: 2

Ben Barden
Ben Barden

Reputation: 2111

If you're using swing, you're already using threads. Swing by its nature has a thread for I/O and a thread for back-end. You do indeed want to use threads here - among other things, putting a thread in wait is a lot cheaper than giving it an infinite loop to churn on.

Listeners are another application of threads, and I wouldn't be surprised if you could get most or all of what you want just by using well-constructed listeners. Alternately, there are these things called semaphores. Semaphores are a way for threads to handle timing - if a thread attempts to lock a semaphore that's already locked, it will wait until another thread unlocks it before continuing (and locking it again). In your case, you might try the following.

  • Have a button listener, a main function, and a locked semaphore.
  • Main function starts, does any initial behaviors, and attempts to grab semaphore. since the semaphore is already locked, it holds.
  • When the button listener fires, one of the things it does is unlock the semaphore.
  • As soon as the semaphore unlocks, the main function grabs it (thus locking it once more) and does whatever it's supposed to do. Eventually, it finishes that, and attempts to grab the (locked) semaphore again, thus waiting for the next time the button listener fires.
  • repeat.

Edit: To include and explain the actual accepted solution (from comments below).

Fix: add Thread.sleep(1000); to the inside of the while loop in the whileConnected function.

Explanation: The while loop is an infinite loop that contains nothing but an if statement. This if statement evaluates to false (and therefore does nothing further) for a really long time (at least as far as the computer is concerned). This acts in much the same way an electrical short does - there's nothing to slow it down, so the thread that runs that main function burns up a lot of computing resources doing nothing. This is bad. It is possible that some failsafe kicked in and killed the thread. It is possible that the thread failed or broke something in an uglier manner. In any case, including a sleep statement (in this case, sleeping for a second each loop, as it's measured in ms) prevents this, and thus allows the function to continue indefinitely.

Upvotes: 2

Causteau
Causteau

Reputation: 117

I think by definition the Game is paused until the button is pressed, as the gui will not be running the the main thread, it should be running in the event dispatching thread.

Swing certainly has a number of gotchas around threading and listeners that can result in some unpredictable behaviour when not implemented correctly.

Have you gone through the Java tutorials on oracle for Swing? Most relavent would be the examples in http://docs.oracle.com/javase/tutorial/uiswing/events/index.html for Listeners and http://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html for WorkerThreads

I've found with Swing the best thing is to download the examples here and try to expand on them

I agree with Ben Barden though, from my understanding of what you need, I think you can achieve what you need using listeners.

Upvotes: 0

Related Questions