SimpleUpdate of headless server steals focus from a jframe

Hi all,

I’ve created a headless server in JME3, but I wanted some kind of easy control panel to control the server.
In that control panel (swing jframe) I have a listbox and I can select an item from that listbox.
But whenever the SimpleUpdate runs, the control panel looses focus, and the item in the listbox isn’t really active anymore. (which toggles the enable of a button on my control panel)

Any idea how I can make the simpleupdate not steal the focus?
PS: no other canvas or GUI in the server.

Kind regards

I did something similar a few months ago. For me it worked, but I have to say that I did not use a listbox. I could show you what I did, but I can’t promise anything. Of course in the following there might be classes which are created by me (ignore that, please).

The following is the ServerMain:

package server;

import com.jme3.app.SimpleApplication;
import com.jme3.math.Vector3f;
import com.jme3.network.ConnectionListener;
import com.jme3.network.HostedConnection;
import com.jme3.network.Message;
import com.jme3.network.Network;
import com.jme3.network.Server;
import com.jme3.renderer.RenderManager;
import com.jme3.system.JmeContext;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import mygame.messages.PlayerListMessage;
import server.messages.CloseConnectionMessage;
import server.messages.CommandMessage;

/**
 * Main class of the server
 *
 * @author Domenic
 */
public class ServerMain extends SimpleApplication implements ConnectionListener {

	private ServerGui serverGui;
	private Server server;
	private ServerManager serverManager; // you don't need this
	private ServerGameAppState gameAppState; // you don't need this

	public static void main(String[] args) {
		Utils.initSerializers();

		ServerMain app = new ServerMain();
		app.start(JmeContext.Type.Headless);
	}

	@Override
	public void simpleInitApp() {        
                serverGui = new ServerGui(this);
	}

	@Override
	public void simpleUpdate(float tpf) {
	}

	@Override
	public void simpleRender(RenderManager rm) {
	}

         // called from gui
	public void startServer() {
//        System.out.println("hier");
//        String ip = serverGui.getIpAdress();
		int port = serverGui.getPort();
		try {
			if (port > 0 && ((server == null) || (server != null && !server.isRunning()))) {

			    gameAppState = new ServerGameAppState();
			    serverManager = new ServerManager();

				server = Network.createServer(port);
				server.addMessageListener(new ServerListener(server, serverManager));
				server.addConnectionListener(this);
				server.start();

				serverGui.setStatusIcon(true);
				serverGui.makeShowUp("Server started");
				initAppStates();
			} else if (server != null && server.isRunning()) {
				serverGui.makeShowUp("There is already a server running!");
			} else {
				serverGui.makeShowUp("No Server started");
			}
		} catch (IOException ex) {
			Logger.getLogger(ServerMain.class.getName()).log(Level.SEVERE, null, ex);
			serverGui.setStatusIcon(false);
			serverGui.makeShowUp("Could not start server!");
		}
	}

	public Server getServer() {
		return server;
	}

	public void stopServer() {
		if (server != null && server.isRunning()) {
			server.close();
			serverGui.setStatusIcon(false);
			serverGui.makeShowUp("Server stopped");
			removeAppStates();
		} else {
			serverGui.makeShowUp("No server available");
		}
	}

	private void initAppStates() {
		stateManager.attach(gameAppState);
		stateManager.attach(serverManager);
	}
	
	private void removeAppStates() {
		stateManager.detach(gameAppState);
		stateManager.detach(serverManager);
	}

	public void sendMessage(Message m) {
		server.broadcast(m);
	}

	@Override
	public void destroy() {
		if (server != null && server.isRunning()) {
			server.close();
			removeAppStates();
		}
		super.destroy();
	}

	@Override
	public void connectionAdded(Server server, HostedConnection conn) {
	}

	@Override
	public void connectionRemoved(Server server, HostedConnection conn) {
	}
}

The following is the ServerGui:

package server;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JTextField;

/**
 * Builds up the gui
 *
 * @author Domenic
 */
public class ServerGui extends JFrame {
	
	private JButton startButton;
	private JButton stopButton;
	private JTextField portField;
	private JTextField ipField;
	private JLabel ipLabel;
	private JLabel portLabel;
	private JLabel imageLabel;
	private Icon onIcon;
	private Icon offIcon;
	
	private String title = "FPS Server";
	private int width = 450;
	private int height = 200;
	
	private ServerMain serverMain;
	
	public ServerGui(final ServerMain serverMain) {
		this.serverMain = serverMain;
		
		// init main frame
		setTitle(title);
		setSize(width, height);
		setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
		setLayout(null);
		setLocationRelativeTo(null);
		setResizable(false);
		
		// init buttons, labels and textFields
		ipLabel = new JLabel("IP-Adresse");
		ipLabel.setBounds(5, 15, 100, 25);
		add(ipLabel);
		
		portLabel = new JLabel("Port");
		portLabel.setBounds(5, 50, 100, 25);
		add(portLabel);
		
		ipField = new JTextField();
		ipField.setBounds(110, 15, 200, 25);
		ipField.setEnabled(false);
		ipField.setText("localhost");
		add(ipField);
		
		portField = new JTextField();
		portField.setBounds(110, 50, 200, 25);
		add(portField);
		
		startButton = new JButton("Start");
		startButton.setBounds(15, 100, 195, 40);
		startButton.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				serverMain.startServer();
			}
		});
		add(startButton);
		
		stopButton = new JButton("Stop");
		stopButton.setBounds(240, 100, 195, 40);
		stopButton.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				 serverMain.stopServer();
			}
		});
		add(stopButton);
		
		onIcon = new ImageIcon("assets/Interface/on.jpg");
		offIcon = new ImageIcon("assets/Interface/off.jpg");
		
		imageLabel = new JLabel();
		imageLabel.setIcon(offIcon);
		imageLabel.setBounds(350, 10, 80, 80);
		add(imageLabel);
				
		// set frame visible
		setVisible(true);  
	}  
	
	/**
	 * Get the ipAdress
	 * @return ip adress as string
	 */
	public String getIpAdress() {
		return ipField.getText();
	}
	
	/**
	 * Get the port from the port label
	 * @return an integer 
	 */
	public int getPort() {
		int port = -1;
		try {
			port = Integer.parseInt(portField.getText());
		} catch(Exception e) {
		}
	   return port;
	}
	/**
	 * Prints a message on the screen
	 * @param message 
	 */
	public void makeShowUp(String message) {
		if (message != null) {
			JOptionPane.showMessageDialog(null, message);
		}
	}
	
	/**
	 * Changes the status icon in gui
	 * If true, the check symbol is selected, else the X mark.
	 * @param status 
	 */
	public void setStatusIcon(boolean status) {
		if (status) {
			imageLabel.setIcon(onIcon);
			portField.setEnabled(false);
		} else {
			imageLabel.setIcon(offIcon);
			portField.setEnabled(true);
		}
	}

	@Override
	public void dispose() {
		super.dispose(); 
		serverMain.stop();
	}
}

By the way, I removed a few lines to make the hole thing look not that overwhelming, so if it doesn’t make any sense somewhere just ignore it :blush:

Gui looks like this:

It would be great if you tell me whether it helped you or not, because if not I could just remove this post again.

2 Likes

Why not keep this post? If next answer is “Did not help” then it did not help. ^^
Two of the next big things on my list are: A swing-based editor. A first simple gameserver. Have few experience with both of these. Your post will help me. :chimpanzee_smile:

1 Like

Cool, thank you :relaxed:

Not really sure what the headless application could be doing to steal focus but if you ever get far enough down the path that you want a more direct approach, (and if you don’t need asset manager on the server) you could drop the extends SimpleApplication and maybe take a look at my SiO2 library. It’s not well documented at the moment but the GameSystemManager is like an AppStateManager but without all of the rendering stuff.

You can see an example of it in this barebones client/server app setup:

It doesn’t have any rendering or UI related stuff at all so can’t possibly interfere with focus. I hope you do get an answer to your original question, though.

Hi Domenic,

Thank you for your example.
I’ve ran it (had to comment out a few lines) and that indeed seemed to work.
The big difference is that I noticed my focus got lost when something in the SimpleUpdate was executed.
So I changed your code a bit, but it seems I still keep focus.

Changes to the MainServer :: public void SimpleUpdate(float tpf) :

@Override
public void simpleUpdate(float tpf) {
        ctr += tpf;
        if(ctr > 10f) {
            updateCurTime();
            ctr = 0f;
        }
}

    private void updateCurTime() {
        long t = System.currentTimeMillis() / 1000;
        serverGui.setCurTime(""+t);
    }

This will set the value of a label in the serverGui every 10 seconds.
Changes to the serverGui:

public ServerGui(final ServerMain serverMain) {
	this.serverMain = serverMain;
	
	// init main frame
	setTitle(title);
	setSize(width, height);
	setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
	setLayout(null);
	setLocationRelativeTo(null);
	setResizable(false);
	
            JList list = new JList();
            list.setBounds(10,10,300,150);
            String[] items = {"test1", "test2", "test3", "test4", "test5"};
            list.setListData(items);
            add(list);
			
            timeLabel = new JLabel("CurTime");
            timeLabel.setBounds(350, 10, 80, 80);
            add(timeLabel);
            
	// set frame visible
	setVisible(true);  
}  

    public void setCurTime(String t) {
        timeLabel.setText(t);
    }

Now I have a list with 5 items.
When I select 1 of the items, and wait for the update, I still have the item selected!
That’s great, that’s what I need.

Now I have to figure out how to incorporate this into my game :smiley:

1 Like

I suggest you don’t update timeLabel in public void simpleUpdate(float). Instead, create some getter/setter methods in ServerMain, update timeLabel in ServerGui with a timer or thread.

WOW, I am glad it worked!

Hi Yan,

Thanks for that aditional suggestion.
Had to read it a twice, the first time I was like, what… I don’t want to update the timer everytime simpleupdate runs :smiley:
But what you are saying is to put the logic and timer in the serverGUI, and let it grab the current time from the ServerTime when needed.
Not a bad idea…
PS, as you might guess, It’s not about getting the time from the server, but more like the number of players, etc…

The important thing is not to update swing from a non-AWT thread. Badness awaits those who follow that path…

Keep the server standalone. You can always access server with public method like server.getStateManager().getState(StatsAppState.class).

ServerMain:

package server;

import com.jme3.app.SimpleApplication;
import com.jme3.app.StatsAppState;
import com.jme3.system.JmeContext.Type;

public class ServerMain extends SimpleApplication {
	
	public static void main(String[] args) {
		ServerMain server = new ServerMain();
		server.start(Type.Headless);
	}
	
	public ServerMain() {
		// setup your appStates. dont really need StatsAppState in a headless server.
		super(new StatsAppState());
	}

	@Override
	public void simpleInitApp() {
	}
}

ServerGUI:

package server;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.TextArea;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.Callable;

import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;

import com.jme3.app.StatsAppState;

/**
 * Simple Server GUI
 * @author yan
 *
 */
public class ServerGui extends JFrame {
	
	private String title = "Server GUI";
	
	private ServerMain server;
	private int width = 400;
	private int height = 300;
	
	private TextArea console;
	private JComboBox<String> combo;
	private JLabel timeLabel;
	
	private Timer timer;
	
	public ServerGui(final ServerMain server) {
		this.server = server;
		
		// init frame
		this.setTitle(title);
		this.setSize(width, height);
		this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
		this.addWindowListener(new WindowAdapter() {
			@Override
			public void windowClosed(WindowEvent e) {
				server.stop();
				timer.cancel();
			}
		});
		
		
		// init list
		combo = new JComboBox<String>();
		combo.addItem("A");
		combo.addItem("B");
		combo.addItem("C");
		combo.addItem("D");
		
		combo.addActionListener(listener);
		
		JPanel header = new JPanel(new FlowLayout(FlowLayout.LEFT));
		header.add(new JLabel("List:"));
		header.add(combo);
		
		// init console
		console = new TextArea();
		console.setBackground(Color.WHITE);
		console.setEditable(false);
		JScrollPane body = new JScrollPane();
		body.setViewportView(console);
		
		// redirect system out
		PrintStream printStream = new PrintStream(new MyOutputStream());
		System.setOut(printStream);
		System.setErr(printStream);
				
		// init timer
		timeLabel = new JLabel("Time: 0");
		add(timeLabel);
		timer = new Timer();
		timer.scheduleAtFixedRate(new TimerTask() {
			@Override
			public void run() {
				float fps = server.getTimer().getFrameRate();
				float time = server.getTimer().getTimeInSeconds();
				
				String text = String.format(" FPS:%.2f ServerTime:%.1fs", fps, time);
				timeLabel.setText(text);
			}
		}, 0, 1000);
		
		// border layout
		JPanel panel = new JPanel(new BorderLayout());
		panel.add(header, BorderLayout.NORTH);
		panel.add(body, BorderLayout.CENTER);
		panel.add(timeLabel, BorderLayout.SOUTH);
		this.setContentPane(panel);
		
		setLocationRelativeTo(null);
		this.setVisible(true);
	}
	
	public class MyOutputStream extends OutputStream {
		public void write(int arg0) throws IOException {
			// ignore
		}

		public void write(byte data[]) throws IOException {
			console.append(new String(data));
		}

		public void write(byte data[], int off, int len) throws IOException {
			console.append(new String(data, off, len));
			console.setCaretPosition(console.getText().length());
		}
	}
	
	private ActionListener listener = new ActionListener() {
		public void actionPerformed(final ActionEvent e) {
			// know what you choose
			System.out.println("You select: " + combo.getSelectedItem());
			
			// do something to your server
			server.enqueue(new Callable<Void>() {
				public Void call() {
					
					// example, not really need this in headless server.
					StatsAppState state = server.getStateManager().getState(StatsAppState.class);
					String text = state.getFpsText().getText();
					System.out.println(text);
					
					return null;
				}
			});
		}
	};
	
	public static void main(String[] args) {
		ServerMain server = new ServerMain();
		server.start(com.jme3.system.JmeContext.Type.Headless);
		
		new ServerGui(server);
	}

}
1 Like

Wow Yan,

Awesome!!!
That is exactly what I needed.
I can build forward on this one.

Just one more question.
It might be because I was drilled in the past to work with design patterns, but I’ve feel the need to make the ServerMain a singleton.

So I’ve got this:

private static ServerMain instance = null;

private ServerMain() {
    super(new StatsAppState());
}

public static ServerMain getInstance() {
    if(instance == null)
        instance = new ServerMain();
    return instance;
}

And from my other classes, I can then just call the server via the getInstance method, and never have to provide the server as a paramater into another class.

timer.scheduleAtFixedRate(new TimerTask() {
   float fps = ServerMain.getInstance().getTimer.getFrameRate();
   ....
}, 0, 1000);

Does that make any sense? Or is that bad JME3 practice?

That code cant really provide singleton, you should do it in this way.

private final static ServerMain server = new ServerMain();

public static ServerMain getServer() {
    return server;
}

But, as you can only have one app, no need singleton in fact.