/* * Copyright (c) 2011 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of 'jMonkeyEngine' nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package jme3test.network; import java.util.logging.Level; import java.util.logging.Logger; import com.jme3.network.*; import com.jme3.network.serializing.Serializable; import com.jme3.network.serializing.Serializer; import java.io.IOException; /** * A simple test chat server. When SM implements a set * of standard chat classes this can become a lot simpler. * * @version $Revision$ * @author Paul Speed */ public class TestChatServer { // Normally these and the initialized method would // be in shared constants or something. public static final String NAME = "Test Chat Server"; public static final int VERSION = 1; public static final int PORT = 5110; public static final int UDP_PORT = 5110; private Server server; private boolean isRunning; public TestChatServer() throws IOException { // Use this to test the client/server name version check this.server = Network.createServer(NAME, VERSION, PORT, UDP_PORT); // Initialize our own messages only after the server has been created. // It registers some additional messages with the serializer by default // that need to go before custom messages. initializeClasses(); ChatHandler handler = new ChatHandler(); server.addMessageListener(handler, ChatMessage.class); server.addConnectionListener(new ChatConnectionListener()); } public boolean isRunning() { return isRunning; } public synchronized void start() { if( isRunning ) { return; } server.start(); isRunning = true; } public synchronized void close() { if( !isRunning ) { return; } // Gracefully let any connections know that the server is // going down. Without this, their connections will simply // error out. for( HostedConnection conn : server.getConnections() ) { conn.close("Server is shutting down."); } try { Thread.sleep(1000); // wait a couple beats to let the messages go out } catch( InterruptedException e ) { e.printStackTrace(); } server.close(); isRunning = false; notifyAll(); } protected void runCommand( HostedConnection conn, String user, String command ) { if( "/shutdown".equals(command) ) { server.broadcast(new ChatMessage("server", "Server is shutting down.")); close(); } else if( "/help".equals(command) ) { StringBuilder sb = new StringBuilder(); sb.append("Chat commands:\n"); sb.append("/help - prints this message.\n"); sb.append("/shutdown - shuts down the server."); server.broadcast(new ChatMessage("server", sb.toString())); } } public static void initializeClasses() { // Doing it here means that the client code only needs to // call our initialize. Serializer.registerClass(ChatMessage.class); } public static void main(String... args) throws Exception { // Increate the logging level for networking... System.out.println("Setting logging to max"); Logger networkLog = Logger.getLogger("com.jme3.network"); networkLog.setLevel(Level.FINEST); // And we have to tell JUL's handler also // turn up logging in a very convoluted way Logger rootLog = Logger.getLogger(""); if( rootLog.getHandlers().length > 0 ) { rootLog.getHandlers()[0].setLevel(Level.FINEST); } TestChatServer chatServer = new TestChatServer(); chatServer.start(); System.out.println("Waiting for connections on port:" + PORT); // Keep running basically forever while( chatServer.isRunning ) { synchronized (chatServer) { chatServer.wait(); } } } private class ChatHandler implements MessageListener<HostedConnection> { public ChatHandler() { } @Override public void messageReceived(HostedConnection source, Message m) { if (m instanceof ChatMessage) { // Keep track of the name just in case we // want to know it for some other reason later and it's // a good example of session data ChatMessage cm = (ChatMessage)m; source.setAttribute("name", cm.getName()); // Check for a / command if( cm.message.startsWith("/") ) { runCommand(source, cm.name, cm.message); return; } System.out.println("Broadcasting:" + m + " reliable:" + m.isReliable()); // Just rebroadcast... the reliable flag will stay the // same so if it came in on UDP it will go out on that too source.getServer().broadcast(cm); } else { System.err.println("Received odd message:" + m); } } } private class ChatConnectionListener implements ConnectionListener { @Override public void connectionAdded( Server server, HostedConnection conn ) { System.out.println("connectionAdded(" + conn + ")"); } @Override public void connectionRemoved(Server server, HostedConnection conn) { System.out.println("connectionRemoved(" + conn + ")"); } } @Serializable public static class ChatMessage extends AbstractMessage { private String name; private String message; public ChatMessage() { } public ChatMessage(String name, String message) { setName(name); setMessage(message); } public void setName(String name) { this.name = name; } public String getName() { return name; } public void setMessage(String s) { this.message = s; } public String getMessage() { return message; } @Override public String toString() { return name + ":" + message; } } }