package chat;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.util.ArrayList;
import javax.swing.JFrame;
import javax.swing.JLabel;
import com.esotericsoftware.kryonet.Connection;
import com.esotericsoftware.kryonet.Listener;
import com.esotericsoftware.kryonet.Server;
import chat.Network.ChatMessage;
import chat.Network.RegisterName;
import chat.Network.UpdateNames;
import com.esotericsoftware.minlog.Log;
public class ChatServer {
Server server;
public ChatServer () throws IOException {
server = new Server() {
protected Connection newConnection () {
// By providing our own connection implementation, we can store per
// connection state without a connection ID to state look up.
return new ChatConnection();
}
};
// For consistency, the classes to be sent over the network are
// registered by the same method for both the client and server.
Network.register(server);
server.addListener(new Listener() {
public void received (Connection c, Object object) {
// We know all connections for this server are actually ChatConnections.
ChatConnection connection = (ChatConnection)c;
if (object instanceof RegisterName) {
// Ignore the object if a client has already registered a name. This is
// impossible with our client, but a hacker could send messages at any time.
if (connection.name != null) return;
// Ignore the object if the name is invalid.
String name = ((RegisterName)object).name;
if (name == null) return;
name = name.trim();
if (name.length() == 0) return;
// Store the name on the connection.
connection.name = name;
// Send a "connected" message to everyone except the new client.
ChatMessage chatMessage = new ChatMessage();
chatMessage.text = name + " connected.";
server.sendToAllExceptTCP(connection.getID(), chatMessage);
// Send everyone a new list of connection names.
updateNames();
return;
}
if (object instanceof ChatMessage) {
// Ignore the object if a client tries to chat before registering a name.
if (connection.name == null) return;
ChatMessage chatMessage = (ChatMessage)object;
// Ignore the object if the chat message is invalid.
String message = chatMessage.text;
if (message == null) return;
message = message.trim();
if (message.length() == 0) return;
// Prepend the connection's name and send to everyone.
chatMessage.text = connection.name + ": " + message;
server.sendToAllTCP(chatMessage);
return;
}
}
public void disconnected (Connection c) {
ChatConnection connection = (ChatConnection)c;
if (connection.name != null) {
// Announce to everyone that someone (with a registered name) has left.
ChatMessage chatMessage = new ChatMessage();
chatMessage.text = connection.name + " disconnected.";
server.sendToAllTCP(chatMessage);
updateNames();
}
}
});
server.bind(Network.port);
server.start();
// Open a window to provide an easy way to stop the server.
JFrame frame = new JFrame("Chat Server");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.addWindowListener(new WindowAdapter() {
public void windowClosed (WindowEvent evt) {
server.stop();
}
});
frame.getContentPane().add(new JLabel("Close to stop the chat server."));
frame.setSize(320, 200);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
void updateNames () {
// Collect the names for each connection.
Connection[] connections = server.getConnections();
ArrayList names = new ArrayList(connections.length);
for (int i = connections.length - 1; i >= 0; i--) {
ChatConnection connection = (ChatConnection)connections[i];
names.add(connection.name);
}
// Send the names to everyone.
UpdateNames updateNames = new UpdateNames();
updateNames.names = (String[])names.toArray(new String[names.size()]);
server.sendToAllTCP(updateNames);
}
// This holds per connection state.
static class ChatConnection extends Connection {
public String name;
}
public static void main (String[] args) throws IOException {
Log.set(Log.LEVEL_DEBUG);
new ChatServer();
}
}