/*
*
* Copyright (c) 2000-2007 by Rodney Kinney
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License (LGPL) as published by the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, copies are available
* at http://www.opensource.org.
*/
/*
* Copyright (c) 2003 by Rodney Kinney. All rights reserved.
* Date: May 9, 2003
*/
package VASSAL.chat.node;
import java.io.IOException;
import java.net.Socket;
import java.util.Iterator;
import java.util.Properties;
import VASSAL.tools.SequenceEncoder;
/**
* Node representing a single player.
* A leaf node in a hierarchical server.
* Reads and writes directly to a socket
* {@link #getInfo} returns an encoded {@link java.util.Properties} object with real name, profile, etc.
*/
public class PlayerNode extends Node implements SocketWatcher {
private SocketHandler input;
protected String id;
protected String info;
private AsynchronousServerNode server;
private static ConnectionLimiter connLimiter = new ConnectionLimiter();
public PlayerNode(Socket socket, AsynchronousServerNode server) throws IOException {
super(null,null,null);
this.server = server;
this.input = new BufferedSocketHandler(socket,this);
input.start();
}
public String getId() {
return id;
}
public boolean isLeaf() {
return true;
}
public void send(String msg) {
input.writeLine(msg);
}
// Always update IP on client info in case client 'forgets' their IP
public String getInfo() {
String ip = input.sock.getInetAddress().getHostAddress();
return info + (ip.length() > 0 ? "|ip=" + ip : "");
}
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof PlayerNode)) return false;
final PlayerNode player = (PlayerNode) o;
if (!id.equals(player.id)) return false;
return true;
}
public int hashCode() {
return id.hashCode();
}
public void handleMessage(String line) {
String[] info;
Properties p;
String cmd;
if ((info = Protocol.decodeRegisterCommand(line)) != null) {
id = info[0];
this.info = info[2];
server.registerNode(info[1],this);
}
else if ((info = Protocol.decodeJoinCommand(line)) != null) {
// Requests to move to a locked room must be accompanied by the rooms 'password' which
// is the owner of the room. This allows 'Invited' clients to join Locked rooms.
// Rooms are reused, so clients are allowed to enter an empty locked room without a password.
final SequenceEncoder.Decoder sd = new SequenceEncoder.Decoder(info[0], '/');
sd.nextToken("");
final String joinRoomName = sd.nextToken("");
final Node room = server.getModule(this).getDescendant(joinRoomName);
if (room != null) {
final boolean locked = "true".equals(room.getInfoProperty(NodeRoom.LOCKED));
if (locked && room.getChildren().length > 0) {
final String owner = room.getInfoProperty(NodeRoom.OWNER);
if (info.length < 2 || !owner.equals(info[1])) {
return;
}
}
}
server.move(this,info[0]);
}
else if ((info = Protocol.decodeForwardCommand(line)) != null) {
server.forward(info[0],info[1]);
}
else if ((info = Protocol.decodeStatsCommand(line)) != null) {
this.info = info[0];
server.updateInfo(this);
}
else if ((info = Protocol.decodeKickCommand(line)) != null) {
server.kick(this, info[0]);
}
else if ((p = Protocol.decodeRoomsInfo(line)) != null) {
// FIXME: Use Properties.stringPropertyNames() in 1.6+.
for (Iterator<Object> it = p.keySet().iterator(); it.hasNext();) {
String roomName = (String) it.next();
Node target = server.getModule(this).getDescendant(roomName);
if (target != null) {
target.setInfo(p.getProperty(roomName));
server.updateInfo(target);
}
}
}
else if ((cmd = Protocol.decodeLoginCommand(line)) != null) {
connLimiter.register(cmd,input);
}
}
public void socketClosed(SocketHandler handler) {
server.disconnect(this);
}
}