/*
Copyright (C) 2011 monte
This file is part of PSP NetParty.
PSP NetParty is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package pspnetparty.lib.engine;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import pspnetparty.lib.CountDownSynchronizer;
import pspnetparty.lib.FileContentCache;
import pspnetparty.lib.ILogger;
import pspnetparty.lib.Utility;
import pspnetparty.lib.constants.AppConstants;
import pspnetparty.lib.constants.IServerRegistry;
import pspnetparty.lib.constants.ProtocolConstants;
import pspnetparty.lib.socket.AsyncTcpClient;
import pspnetparty.lib.socket.AsyncUdpClient;
import pspnetparty.lib.socket.IProtocol;
import pspnetparty.lib.socket.IProtocolDriver;
import pspnetparty.lib.socket.IProtocolMessageHandler;
import pspnetparty.lib.socket.IServer;
import pspnetparty.lib.socket.IServerListener;
import pspnetparty.lib.socket.ISocketConnection;
import pspnetparty.lib.socket.PacketData;
import pspnetparty.lib.socket.TextProtocolDriver;
public class RoomEngine {
private static final long ROOM_AGE_DELAY = 24 * 60 * 60 * 1000;
private static final long IDLE_TUNNEL_DELAY = 3 * 60 * 60 * 1000;
private static final long NOTIFY_DELAY = 15 * 60 * 1000;
private ILogger logger;
private AsyncTcpClient tcpClient;
private AsyncUdpClient udpClient;
private ConcurrentHashMap<String, Room> masterNameRoomMap;
private ConcurrentHashMap<InetSocketAddress, TunnelProtocolDriver> notYetLinkedTunnels;
private int maxRooms = 10;
private FileContentCache loginMessageFile = new FileContentCache();
private IServerRegistry serverNetwork;
private ConcurrentHashMap<RoomStatusProtocolDriver, Object> portalConnections;
private boolean isAcceptingPortal = true;
private ConcurrentHashMap<String, MyRoomProtocolDriver> myRoomEntries;
private CountDownSynchronizer countDownSynchronizer;
private ExecutorService executorService = Executors.newCachedThreadPool();
public RoomEngine(IServer tcpServer, IServer udpServer, ILogger logger, IServerRegistry net) throws IOException {
this.logger = logger;
masterNameRoomMap = new ConcurrentHashMap<String, Room>(20, 0.75f, 1);
notYetLinkedTunnels = new ConcurrentHashMap<InetSocketAddress, TunnelProtocolDriver>(30, 0.75f, 2);
myRoomEntries = new ConcurrentHashMap<String, RoomEngine.MyRoomProtocolDriver>();
portalConnections = new ConcurrentHashMap<RoomStatusProtocolDriver, Object>(20, 0.75f, 2);
tcpClient = new AsyncTcpClient(logger, 4000, 3000);
udpClient = new AsyncUdpClient(logger);
serverNetwork = net;
countDownSynchronizer = new CountDownSynchronizer(2);
IServerListener listener = new IServerListener() {
@Override
public void log(String message) {
RoomEngine.this.logger.log(message);
}
@Override
public void serverStartupFinished() {
if (countDownSynchronizer.countDown() == 0) {
initBackgroudThread();
countDownSynchronizer = new CountDownSynchronizer(2);
}
}
@Override
public void serverShutdownFinished() {
if (countDownSynchronizer.countDown() == 0) {
shutdown();
}
}
};
tcpServer.addServerListener(listener);
tcpServer.addProtocol(new RoomProtocol());
tcpServer.addProtocol(new RoomStatusProtocol());
tcpServer.addProtocol(new MyRoomProtocol());
TunnelProtocol tunnelProtocol = new TunnelProtocol();
tcpServer.addProtocol(tunnelProtocol);
udpServer.addServerListener(listener);
udpServer.addProtocol(tunnelProtocol);
}
private void initBackgroudThread() {
Thread cleanupThread = new Thread(new Runnable() {
@Override
public void run() {
try {
ByteBuffer bufferNotifyRoomOld = Utility.encode(ProtocolConstants.Room.NOTIFY_ROOM_AGE_OLD);
ByteBuffer bufferTunnelIdle = Utility.encode(ProtocolConstants.Room.NOTIFY_TUNNEL_COMMUNICATION_IDLE);
while (!executorService.isShutdown()) {
long deadline, notifyDeadline;
deadline = System.currentTimeMillis() - ROOM_AGE_DELAY;
notifyDeadline = deadline + NOTIFY_DELAY;
for (Entry<String, Room> e : masterNameRoomMap.entrySet()) {
Room room = e.getValue();
if (room.createdTime < deadline) {
destroyRoom(e.getKey());
} else if (!room.tooOldNotified && room.createdTime < notifyDeadline) {
room.tooOldNotified = true;
for (Entry<String, RoomProtocolDriver> e2 : room.playersByName.entrySet()) {
RoomProtocolDriver player = e2.getValue();
bufferNotifyRoomOld.position(0);
player.getConnection().send(bufferNotifyRoomOld);
}
}
}
deadline = System.currentTimeMillis() - IDLE_TUNNEL_DELAY;
notifyDeadline = deadline + NOTIFY_DELAY;
for (Entry<String, Room> e : masterNameRoomMap.entrySet()) {
Room room = e.getValue();
for (Entry<String, RoomProtocolDriver> e2 : room.playersByName.entrySet()) {
RoomProtocolDriver player = e2.getValue();
TunnelProtocolDriver tunnel = player.tunnel;
if (tunnel == null)
continue;
if (tunnel.lastTunnelTime < deadline) {
player.getConnection().disconnect();
} else if (tunnel.lastTunnelTime < notifyDeadline) {
if (tunnel.inactiveNotified)
continue;
tunnel.inactiveNotified = true;
bufferTunnelIdle.position(0);
player.getConnection().send(bufferTunnelIdle);
} else {
tunnel.inactiveNotified = false;
}
}
}
// clearAllMyRoomGhosts();
Thread.sleep(60000);
}
} catch (InterruptedException e) {
}
}
}, RoomEngine.class.getName() + " Cleanup");
cleanupThread.setDaemon(true);
cleanupThread.start();
}
private void shutdown() {
try {
executorService.shutdown();
} catch (RuntimeException e) {
logger.log(Utility.stackTraceToString(e));
}
}
public int getRoomCount() {
return masterNameRoomMap.size();
}
public int getMaxRooms() {
return maxRooms;
}
public void setMaxRooms(int maxRooms) {
if (maxRooms < 0)
return;
this.maxRooms = maxRooms;
StringBuilder sb = new StringBuilder();
appendRoomServerStatus(sb);
ByteBuffer buffer = Utility.encode(sb);
for (RoomStatusProtocolDriver client : portalConnections.keySet()) {
buffer.position(0);
client.getConnection().send(buffer);
}
}
public void setLoginMessageFile(String loginMessageFile) {
this.loginMessageFile.setFile(loginMessageFile);
}
public Set<InetSocketAddress> getPortalAddresses() {
HashSet<InetSocketAddress> addresses = new HashSet<InetSocketAddress>();
for (RoomStatusProtocolDriver driver : portalConnections.keySet()) {
addresses.add(driver.getConnection().getRemoteAddress());
}
return addresses;
}
public boolean isAcceptingPortal() {
return isAcceptingPortal;
}
public void setAcceptingPortal(boolean isAcceptingPortal) {
if (!isAcceptingPortal) {
for (RoomStatusProtocolDriver driver : portalConnections.keySet()) {
driver.getConnection().disconnect();
}
portalConnections.clear();
}
this.isAcceptingPortal = isAcceptingPortal;
}
public String allRoomsToString() {
StringBuilder sb = new StringBuilder();
for (Entry<String, Room> entry : masterNameRoomMap.entrySet()) {
Room room = entry.getValue();
sb.append(entry.getKey());
sb.append('\t').append(room.title);
sb.append('\t').append(room.playersByName.size()).append('/').append(room.maxPlayers);
sb.append('\t').append(room.password);
sb.append(AppConstants.NEW_LINE);
}
return sb.toString();
}
public String myRoomsToString() {
StringBuilder sb = new StringBuilder();
for (Entry<String, MyRoomProtocolDriver> entry : myRoomEntries.entrySet()) {
PlayRoom room = entry.getValue().myRoom;
sb.append(room.getRoomAddress());
sb.append('\t').append(room.getTitle());
sb.append('\t').append(room.getCurrentPlayers()).append('/').append(room.getMaxPlayers());
sb.append('\t').append(room.hasPassword() ? "鍵有" : "鍵無");
sb.append(AppConstants.NEW_LINE);
}
return sb.toString();
}
public void notifyAllPlayers(String message) {
ByteBuffer buffer = Utility.encode(ProtocolConstants.Room.NOTIFY_FROM_ADMIN + TextProtocolDriver.ARGUMENT_SEPARATOR + message);
for (Entry<String, Room> entry : masterNameRoomMap.entrySet()) {
Room room = entry.getValue();
for (Entry<String, RoomProtocolDriver> entry2 : room.playersByName.entrySet()) {
RoomProtocolDriver p = entry2.getValue();
buffer.position(0);
p.getConnection().send(buffer);
}
}
}
public boolean destroyRoom(String masterName) {
Room room = masterNameRoomMap.remove(masterName);
if (room == null)
return false;
for (Entry<String, RoomProtocolDriver> entry : room.playersByName.entrySet()) {
RoomProtocolDriver p = entry.getValue();
p.room = null;
p.name = "";
p.getConnection().send(Utility.encode(ProtocolConstants.Room.NOTIFY_ROOM_DELETED));
p.getConnection().disconnect();
}
StringBuilder sb = new StringBuilder();
sb.append(ProtocolConstants.RoomStatus.NOTIFY_ROOM_DELETED);
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(":").append(masterName);
ByteBuffer buffer = Utility.encode(sb);
for (RoomStatusProtocolDriver client : portalConnections.keySet()) {
buffer.position(0);
client.getConnection().send(buffer);
}
return true;
}
public void hirakeGoma(String masterName) {
Room room = masterNameRoomMap.get(masterName);
if (room == null)
return;
room.maxPlayers++;
}
public boolean destroyMyRoom(String roomAddress) {
MyRoomProtocolDriver driver = myRoomEntries.get(roomAddress);
if (driver == null)
return false;
ISocketConnection conn = driver.getConnection();
if (conn.isConnected()) {
conn.disconnect();
} else {
driver.dispose();
}
return true;
}
private void appendRoomServerStatus(StringBuilder sb) {
sb.append(ProtocolConstants.SERVER_STATUS);
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(maxRooms);
}
private static class Room {
private ConcurrentSkipListMap<String, RoomProtocolDriver> playersByName;
private HashMap<String, TunnelProtocolDriver> tunnelsByMacAddress;
private RoomProtocolDriver roomMaster;
private int maxPlayers = 4;
private String title;
private String password = "";
private String description = "";
private String remarks = "";
private long createdTime;
private boolean tooOldNotified = false;
private boolean isMacAdressBlackListEnabled = false;
private HashSet<String> macAddressWhiteList = new HashSet<String>();
private boolean isMacAdressWhiteListEnabled = false;
private HashSet<String> macAddressBlackList = new HashSet<String>();
private Room() {
playersByName = new ConcurrentSkipListMap<String, RoomProtocolDriver>();
tunnelsByMacAddress = new HashMap<String, TunnelProtocolDriver>();
createdTime = System.currentTimeMillis();
}
private void appendRoomInfoForParticipants(StringBuilder sb) {
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(roomMaster.name);
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(maxPlayers);
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(title);
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(password);
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(createdTime);
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(description);
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(remarks);
}
private void appendNotifyUserList(StringBuilder sb) {
sb.append(ProtocolConstants.Room.NOTIFY_USER_LIST);
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(roomMaster.name);
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(roomMaster.ssid);
for (Entry<String, RoomProtocolDriver> entry : playersByName.entrySet()) {
RoomProtocolDriver client = entry.getValue();
if (client == roomMaster)
continue;
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(client.name);
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(client.ssid);
}
}
private void appendNotifyRoomCreated(StringBuilder sb) {
sb.append(ProtocolConstants.RoomStatus.NOTIFY_ROOM_CREATED);
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(roomMaster != null ? roomMaster.name : "");
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(title);
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(playersByName.size());
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(maxPlayers);
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(!Utility.isEmpty(password) ? "Y" : "N");
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(createdTime);
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(description);
}
private boolean checkMacAddressFiltering(String mac) {
if (isMacAdressWhiteListEnabled && !macAddressWhiteList.isEmpty() && !macAddressWhiteList.contains(mac))
return true;
else if (isMacAdressBlackListEnabled && macAddressBlackList.contains(mac))
return true;
return false;
}
private boolean checkMacAddressFiltering(String mac1, String mac2) {
if (isMacAdressWhiteListEnabled && !macAddressWhiteList.isEmpty() && !macAddressWhiteList.contains(mac1)
&& !macAddressWhiteList.contains(mac2))
return true;
else if (isMacAdressBlackListEnabled && (macAddressBlackList.contains(mac1) || macAddressBlackList.contains(mac2)))
return true;
return false;
}
}
private void appendLoginMessage(StringBuilder sb) {
String loginMessage = loginMessageFile.getContent();
if (!Utility.isEmpty(loginMessage)) {
sb.append(TextProtocolDriver.MESSAGE_SEPARATOR);
sb.append(ProtocolConstants.Room.NOTIFY_FROM_ADMIN);
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(loginMessage);
}
}
private class RoomProtocol implements IProtocol {
@Override
public String getProtocol() {
return ProtocolConstants.PROTOCOL_ROOM;
}
@Override
public IProtocolDriver createDriver(ISocketConnection connection) {
RoomProtocolDriver client = new RoomProtocolDriver(connection);
return client;
}
@Override
public void log(String message) {
logger.log(message);
}
}
private class RoomProtocolDriver extends TextProtocolDriver {
private String name;
private Room room;
private TunnelProtocolDriver tunnel;
private String ssid = "";
private RoomProtocolDriver(ISocketConnection connection) {
super(connection, loginHandlers);
}
@Override
public void log(String message) {
logger.log(message);
}
@Override
public void connectionDisconnected() {
if (tunnel != null) {
tunnel = null;
}
Room room = this.room;
if (room == null)
return;
room.playersByName.remove(name);
if (this == room.roomMaster) {
masterNameRoomMap.remove(name);
RoomProtocolDriver newRoomMaster = null;
for (Entry<String, RoomProtocolDriver> entry : room.playersByName.entrySet()) {
String playerName = entry.getKey();
if (masterNameRoomMap.putIfAbsent(playerName, room) == null) {
newRoomMaster = entry.getValue();
room.roomMaster = newRoomMaster;
break;
}
}
if (newRoomMaster == null) {
for (Entry<String, RoomProtocolDriver> entry : room.playersByName.entrySet()) {
RoomProtocolDriver p = entry.getValue();
p.getConnection().send(Utility.encode(ProtocolConstants.Room.NOTIFY_ROOM_DELETED));
p.getConnection().disconnect();
}
room.playersByName.clear();
StringBuilder sb = new StringBuilder();
sb.append(ProtocolConstants.RoomStatus.NOTIFY_ROOM_DELETED);
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(":").append(name);
ByteBuffer buffer = Utility.encode(sb);
for (RoomStatusProtocolDriver portal : portalConnections.keySet()) {
buffer.position(0);
portal.getConnection().send(buffer);
}
} else {
StringBuilder sb = new StringBuilder();
sb.append(ProtocolConstants.Room.NOTIFY_USER_EXITED);
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(name);
sb.append(TextProtocolDriver.MESSAGE_SEPARATOR);
sb.append(ProtocolConstants.Room.NOTIFY_ROOM_UPDATED);
room.appendRoomInfoForParticipants(sb);
ByteBuffer buffer = Utility.encode(sb);
for (Entry<String, RoomProtocolDriver> entry : room.playersByName.entrySet()) {
RoomProtocolDriver p = entry.getValue();
buffer.position(0);
p.getConnection().send(buffer);
}
sb.delete(0, sb.length());
sb.append(ProtocolConstants.RoomStatus.NOTIFY_ROOM_DELETED);
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(":").append(name);
sb.append(TextProtocolDriver.MESSAGE_SEPARATOR);
room.appendNotifyRoomCreated(sb);
buffer = Utility.encode(sb);
for (RoomStatusProtocolDriver portal : portalConnections.keySet()) {
buffer.position(0);
portal.getConnection().send(buffer);
}
room.macAddressWhiteList.clear();
room.macAddressBlackList.clear();
room.isMacAdressWhiteListEnabled = false;
room.isMacAdressBlackListEnabled = false;
}
} else {
ByteBuffer buffer = Utility
.encode(ProtocolConstants.Room.NOTIFY_USER_EXITED + TextProtocolDriver.ARGUMENT_SEPARATOR + name);
for (Entry<String, RoomProtocolDriver> entry : room.playersByName.entrySet()) {
RoomProtocolDriver p = entry.getValue();
buffer.position(0);
p.getConnection().send(buffer);
}
StringBuilder sb = new StringBuilder();
sb.append(ProtocolConstants.RoomStatus.NOTIFY_ROOM_PLAYER_COUNT_CHANGED);
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(":").append(room.roomMaster.name);
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(room.playersByName.size());
buffer = Utility.encode(sb);
for (RoomStatusProtocolDriver client : portalConnections.keySet()) {
buffer.position(0);
client.getConnection().send(buffer);
}
}
}
@Override
public void errorProtocolNumber(String number) {
}
}
private final HashMap<String, IProtocolMessageHandler> loginHandlers;
private final HashMap<String, IProtocolMessageHandler> sessionHandlers;
{
loginHandlers = new HashMap<String, IProtocolMessageHandler>();
loginHandlers.put(ProtocolConstants.Room.COMMAND_ROOM_CREATE, new IProtocolMessageHandler() {
@Override
public boolean process(IProtocolDriver driver, String argument) {
RoomProtocolDriver player = (RoomProtocolDriver) driver;
// RC masterName maxPlayers title password description remarks
String[] tokens = argument.split(TextProtocolDriver.ARGUMENT_SEPARATOR, -1);
if (tokens.length != 6) {
player.getConnection().send(Utility.encode(ProtocolConstants.Room.ERROR_ROOM_CREATE_INVALID_DATA_ENTRY));
return false;
}
String password = tokens[3];
String name = tokens[0];
if (!Utility.isValidNameString(name)) {
player.getConnection().send(Utility.encode(ProtocolConstants.Room.ERROR_ROOM_CREATE_INVALID_DATA_ENTRY));
return false;
}
int maxPlayers;
try {
maxPlayers = Integer.parseInt(tokens[1]);
if (maxPlayers < 2 || maxPlayers > ProtocolConstants.Room.MAX_ROOM_PLAYERS) {
player.getConnection().send(Utility.encode(ProtocolConstants.Room.ERROR_ROOM_CREATE_INVALID_DATA_ENTRY));
return false;
}
} catch (NumberFormatException e) {
player.getConnection().send(Utility.encode(ProtocolConstants.Room.ERROR_ROOM_CREATE_INVALID_DATA_ENTRY));
return false;
}
String title = tokens[2];
if (title.length() == 0) {
player.getConnection().send(Utility.encode(ProtocolConstants.Room.ERROR_ROOM_CREATE_INVALID_DATA_ENTRY));
return false;
}
Room newRoom = null;
String errorMsg = null;
if (masterNameRoomMap.size() >= maxRooms) {
errorMsg = ProtocolConstants.Room.ERROR_ROOM_CREATE_BEYOND_LIMIT;
} else if (masterNameRoomMap.containsKey(name)) {
errorMsg = ProtocolConstants.Room.ERROR_ROOM_CREATE_DUPLICATED_NAME;
} else {
newRoom = new Room();
if (masterNameRoomMap.putIfAbsent(name, newRoom) != null) {
errorMsg = ProtocolConstants.Room.ERROR_ROOM_CREATE_DUPLICATED_NAME;
}
}
if (errorMsg != null) {
player.getConnection().send(Utility.encode(errorMsg));
return false;
}
newRoom.playersByName.put(name, player);
newRoom.roomMaster = player;
newRoom.title = title;
newRoom.maxPlayers = maxPlayers;
newRoom.password = password;
newRoom.description = tokens[4];
newRoom.remarks = tokens[5];
player.room = newRoom;
player.name = name;
player.setMessageHandlers(sessionHandlers);
StringBuilder sb = new StringBuilder();
sb.append(ProtocolConstants.Room.COMMAND_ROOM_CREATE);
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(newRoom.createdTime);
sb.append(TextProtocolDriver.MESSAGE_SEPARATOR);
appendLoginMessage(sb);
player.getConnection().send(Utility.encode(sb));
sb.delete(0, sb.length());
newRoom.appendNotifyRoomCreated(sb);
ByteBuffer buffer = Utility.encode(sb);
for (RoomStatusProtocolDriver stat : portalConnections.keySet()) {
buffer.position(0);
stat.getConnection().send(buffer);
}
return true;
}
});
loginHandlers.put(ProtocolConstants.Room.COMMAND_LOGIN, new IProtocolMessageHandler() {
@Override
public boolean process(IProtocolDriver driver, String argument) {
RoomProtocolDriver player = (RoomProtocolDriver) driver;
// LI loginName "masterName" password
String[] tokens = argument.split(TextProtocolDriver.ARGUMENT_SEPARATOR, -1);
if (tokens.length < 2)
return false;
String name = tokens[0];
if (!Utility.isValidNameString(name)) {
return false;
}
String masterName = tokens[1];
if (masterName.equals(name)) {
player.getConnection().send(Utility.encode(ProtocolConstants.Room.ERROR_LOGIN_DUPLICATED_NAME));
return false;
}
Room room = masterNameRoomMap.get(masterName);
if (room == null) {
player.getConnection().send(Utility.encode(ProtocolConstants.Room.ERROR_LOGIN_ROOM_NOT_EXIST));
return false;
}
String sentPassword = tokens.length == 3 ? tokens[2] : null;
if (!Utility.isEmpty(room.password)) {
if (sentPassword == null) {
player.getConnection().send(Utility.encode(ProtocolConstants.Room.NOTIFY_ROOM_PASSWORD_REQUIRED));
return true;
}
if (!room.password.equals(sentPassword)) {
player.getConnection().send(Utility.encode(ProtocolConstants.Room.ERROR_LOGIN_PASSWORD_FAIL));
return true;
}
}
if (room.playersByName.size() >= room.maxPlayers) {
// 最大人数を超えたので接続を拒否します
player.getConnection().send(Utility.encode(ProtocolConstants.Room.ERROR_LOGIN_BEYOND_CAPACITY));
return false;
}
if (room.playersByName.putIfAbsent(name, player) != null) {
// 同名のユーザーが存在するので接続を拒否します
player.getConnection().send(Utility.encode(ProtocolConstants.Room.ERROR_LOGIN_DUPLICATED_NAME));
return false;
}
player.setMessageHandlers(sessionHandlers);
player.name = name;
player.room = room;
ByteBuffer buffer = Utility.encode(ProtocolConstants.Room.NOTIFY_USER_ENTERED + TextProtocolDriver.ARGUMENT_SEPARATOR
+ name);
for (Entry<String, RoomProtocolDriver> entry : room.playersByName.entrySet()) {
RoomProtocolDriver p = entry.getValue();
if (p != player) {
buffer.position(0);
p.getConnection().send(buffer);
}
}
StringBuilder sb = new StringBuilder();
sb.append(ProtocolConstants.Room.COMMAND_LOGIN);
room.appendRoomInfoForParticipants(sb);
sb.append(TextProtocolDriver.MESSAGE_SEPARATOR);
room.appendNotifyUserList(sb);
appendLoginMessage(sb);
player.getConnection().send(Utility.encode(sb));
sb.delete(0, sb.length());
sb.append(ProtocolConstants.RoomStatus.NOTIFY_ROOM_PLAYER_COUNT_CHANGED);
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(":").append(masterName);
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(room.playersByName.size());
buffer = Utility.encode(sb);
for (RoomStatusProtocolDriver stat : portalConnections.keySet()) {
buffer.position(0);
stat.getConnection().send(buffer);
}
return true;
}
});
sessionHandlers = new HashMap<String, IProtocolMessageHandler>();
sessionHandlers.put(ProtocolConstants.Room.COMMAND_CHAT, new IProtocolMessageHandler() {
@Override
public boolean process(IProtocolDriver driver, String argument) {
RoomProtocolDriver player = (RoomProtocolDriver) driver;
Room room = player.room;
if (room == null)
return false;
StringBuilder sb = new StringBuilder();
sb.append(ProtocolConstants.Room.COMMAND_CHAT);
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(player.name);
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(argument);
ByteBuffer buffer = Utility.encode(sb);
for (Entry<String, RoomProtocolDriver> entry : room.playersByName.entrySet()) {
RoomProtocolDriver p = entry.getValue();
buffer.position(0);
p.getConnection().send(buffer);
}
return true;
}
});
sessionHandlers.put(ProtocolConstants.Room.COMMAND_PING, new IProtocolMessageHandler() {
@Override
public boolean process(IProtocolDriver driver, String argument) {
RoomProtocolDriver state = (RoomProtocolDriver) driver;
state.getConnection().send(
Utility.encode(ProtocolConstants.Room.COMMAND_PINGBACK + TextProtocolDriver.ARGUMENT_SEPARATOR + argument));
return true;
}
});
sessionHandlers.put(ProtocolConstants.Room.COMMAND_INFORM_PING, new IProtocolMessageHandler() {
@Override
public boolean process(IProtocolDriver driver, String argument) {
RoomProtocolDriver player = (RoomProtocolDriver) driver;
Room room = player.room;
if (room == null)
return false;
try {
Integer.parseInt(argument);
ByteBuffer buf = Utility.encode(ProtocolConstants.Room.COMMAND_INFORM_PING + TextProtocolDriver.ARGUMENT_SEPARATOR
+ player.name + TextProtocolDriver.ARGUMENT_SEPARATOR + argument);
for (Entry<String, RoomProtocolDriver> entry : room.playersByName.entrySet()) {
RoomProtocolDriver p = entry.getValue();
if (p != player) {
buf.position(0);
p.getConnection().send(buf);
}
}
} catch (NumberFormatException e) {
}
return true;
}
});
sessionHandlers.put(ProtocolConstants.Room.COMMAND_INFORM_TUNNEL_PORT, new IProtocolMessageHandler() {
@Override
public boolean process(IProtocolDriver driver, String argument) {
RoomProtocolDriver player = (RoomProtocolDriver) driver;
Room room = player.room;
if (room == null)
return false;
try {
int port = Integer.parseInt(argument);
InetSocketAddress remoteEP = new InetSocketAddress(player.getConnection().getRemoteAddress().getAddress(), port);
TunnelProtocolDriver tunnel = notYetLinkedTunnels.remove(remoteEP);
player.tunnel = tunnel;
if (tunnel != null) {
player.getConnection().send(Utility.encode(ProtocolConstants.Room.COMMAND_INFORM_TUNNEL_PORT));
tunnel.room = room;
tunnel.player = player;
tunnel.lastTunnelTime = System.currentTimeMillis();
}
} catch (NumberFormatException e) {
}
return true;
}
});
sessionHandlers.put(ProtocolConstants.Room.COMMAND_ROOM_UPDATE, new IProtocolMessageHandler() {
@Override
public boolean process(IProtocolDriver driver, String argument) {
RoomProtocolDriver player = (RoomProtocolDriver) driver;
Room room = player.room;
if (room == null || player != room.roomMaster)
return false;
// RU maxPlayers title password description remarks
String[] tokens = argument.split(TextProtocolDriver.ARGUMENT_SEPARATOR, -1);
if (tokens.length != 5) {
player.getConnection().send(Utility.encode(ProtocolConstants.Room.ERROR_ROOM_CREATE_INVALID_DATA_ENTRY));
return true;
}
String password = tokens[2];
room.maxPlayers = Math.min(Integer.parseInt(tokens[0]), ProtocolConstants.Room.MAX_ROOM_PLAYERS);
room.title = tokens[1];
room.password = password;
room.description = tokens[3];
room.remarks = tokens[4];
player.getConnection().send(Utility.encode(ProtocolConstants.Room.COMMAND_ROOM_UPDATE));
StringBuilder sb = new StringBuilder();
sb.append(ProtocolConstants.Room.NOTIFY_ROOM_UPDATED);
room.appendRoomInfoForParticipants(sb);
ByteBuffer buffer = Utility.encode(sb);
for (Entry<String, RoomProtocolDriver> entry : room.playersByName.entrySet()) {
RoomProtocolDriver p = entry.getValue();
if (p != player) {
buffer.position(0);
p.getConnection().send(buffer);
}
}
sb.delete(0, sb.length());
sb.append(ProtocolConstants.RoomStatus.NOTIFY_ROOM_UPDATED);
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(":").append(player.name);
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(room.title);
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(room.maxPlayers);
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(!Utility.isEmpty(password) ? "Y" : "N");
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(room.description);
buffer = Utility.encode(sb);
for (RoomStatusProtocolDriver stat : portalConnections.keySet()) {
buffer.position(0);
stat.getConnection().send(buffer);
}
return true;
}
});
sessionHandlers.put(ProtocolConstants.Room.COMMAND_ROOM_KICK_PLAYER, new IProtocolMessageHandler() {
@Override
public boolean process(IProtocolDriver driver, String argument) {
RoomProtocolDriver player = (RoomProtocolDriver) driver;
Room room = player.room;
if (room == null || player != room.roomMaster)
return false;
String name = argument;
if (Utility.equals(name, player.name))
return true;
RoomProtocolDriver kickedPlayer = null;
synchronized (room.playersByName) {
kickedPlayer = room.playersByName.remove(name);
if (kickedPlayer == null)
return true;
}
StringBuilder sb = new StringBuilder();
sb.append(ProtocolConstants.Room.NOTIFY_ROOM_PLAYER_KICKED);
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(name);
ByteBuffer buffer = Utility.encode(sb);
for (Entry<String, RoomProtocolDriver> entry : room.playersByName.entrySet()) {
RoomProtocolDriver p = entry.getValue();
buffer.position(0);
p.getConnection().send(buffer);
}
sb.delete(0, sb.length());
sb.append(ProtocolConstants.Room.NOTIFY_ROOM_PLAYER_KICKED);
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(name);
kickedPlayer.getConnection().send(Utility.encode(sb));
kickedPlayer.getConnection().disconnect();
sb.delete(0, sb.length());
sb.append(ProtocolConstants.RoomStatus.NOTIFY_ROOM_PLAYER_COUNT_CHANGED);
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(":").append(name);
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(room.playersByName.size());
buffer = Utility.encode(sb);
for (RoomStatusProtocolDriver stat : portalConnections.keySet()) {
buffer.position(0);
stat.getConnection().send(buffer);
}
return true;
}
});
sessionHandlers.put(ProtocolConstants.Room.COMMAND_ROOM_MASTER_TRANSFER, new IProtocolMessageHandler() {
@Override
public boolean process(IProtocolDriver driver, String argument) {
RoomProtocolDriver player = (RoomProtocolDriver) driver;
Room room = player.room;
if (room == null || player != room.roomMaster)
return false;
String name = argument;
if (Utility.equals(name, player.name))
return true;
RoomProtocolDriver newRoomMaster = room.playersByName.get(name);
if (newRoomMaster == null)
return true;
if (masterNameRoomMap.putIfAbsent(name, room) != null) {
player.getConnection().send(Utility.encode(ProtocolConstants.Room.ERROR_ROOM_TRANSFER_DUPLICATED_NAME));
return true;
}
masterNameRoomMap.remove(player.name);
room.roomMaster = newRoomMaster;
StringBuilder sb = new StringBuilder();
sb.append(ProtocolConstants.Room.NOTIFY_ROOM_UPDATED);
room.appendRoomInfoForParticipants(sb);
ByteBuffer buffer = Utility.encode(sb);
for (Entry<String, RoomProtocolDriver> entry : room.playersByName.entrySet()) {
RoomProtocolDriver p = entry.getValue();
buffer.position(0);
p.getConnection().send(buffer);
}
sb.delete(0, sb.length());
sb.append(ProtocolConstants.RoomStatus.NOTIFY_ROOM_DELETED);
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(":").append(player.name);
sb.append(TextProtocolDriver.MESSAGE_SEPARATOR);
room.appendNotifyRoomCreated(sb);
buffer = Utility.encode(sb);
for (RoomStatusProtocolDriver stat : portalConnections.keySet()) {
buffer.position(0);
stat.getConnection().send(buffer);
}
room.macAddressWhiteList.clear();
room.macAddressBlackList.clear();
room.isMacAdressWhiteListEnabled = false;
room.isMacAdressBlackListEnabled = false;
return true;
}
});
sessionHandlers.put(ProtocolConstants.Room.COMMAND_ROOM_DELETE, new IProtocolMessageHandler() {
@Override
public boolean process(IProtocolDriver driver, String argument) {
RoomProtocolDriver player = (RoomProtocolDriver) driver;
Room room = player.room;
if (room == null || player != room.roomMaster)
return false;
destroyRoom(player.name);
return true;
}
});
sessionHandlers.put(ProtocolConstants.Room.COMMAND_MAC_ADDRESS_PLAYER, new IProtocolMessageHandler() {
@Override
public boolean process(IProtocolDriver driver, String macAddress) {
RoomProtocolDriver player = (RoomProtocolDriver) driver;
Room room = player.room;
if (room == null)
return false;
TunnelProtocolDriver tunnelState = room.tunnelsByMacAddress.get(macAddress);
if (tunnelState == null)
return true;
try {
String name = tunnelState.player.name;
if (Utility.isEmpty(name))
return true;
StringBuilder sb = new StringBuilder();
sb.append(ProtocolConstants.Room.COMMAND_MAC_ADDRESS_PLAYER);
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(macAddress);
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(name);
player.getConnection().send(Utility.encode(sb));
} catch (NullPointerException e) {
}
return true;
}
});
sessionHandlers.put(ProtocolConstants.Room.COMMAND_INFORM_SSID, new IProtocolMessageHandler() {
@Override
public boolean process(IProtocolDriver driver, String argument) {
RoomProtocolDriver player = (RoomProtocolDriver) driver;
player.ssid = argument;
StringBuilder sb = new StringBuilder();
sb.append(ProtocolConstants.Room.NOTIFY_SSID_CHANGED);
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(player.name);
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(player.ssid);
ByteBuffer buffer = Utility.encode(sb);
for (Entry<String, RoomProtocolDriver> entry : player.room.playersByName.entrySet()) {
RoomProtocolDriver p = entry.getValue();
if (p != player) {
buffer.position(0);
p.getConnection().send(buffer);
}
}
return true;
}
});
sessionHandlers.put(ProtocolConstants.Room.COMMAND_WHITELIST_ENABLE, new IProtocolMessageHandler() {
@Override
public boolean process(IProtocolDriver driver, String argument) {
RoomProtocolDriver player = (RoomProtocolDriver) driver;
Room room = player.room;
if (room == null || player != room.roomMaster)
return false;
room.isMacAdressWhiteListEnabled = "Y".equals(argument);
return true;
}
});
sessionHandlers.put(ProtocolConstants.Room.COMMAND_WHITELIST_ADD, new IProtocolMessageHandler() {
@Override
public boolean process(IProtocolDriver driver, String args) {
RoomProtocolDriver player = (RoomProtocolDriver) driver;
Room room = player.room;
if (room == null || player != room.roomMaster)
return false;
for (String macAddress : args.split(TextProtocolDriver.ARGUMENT_SEPARATOR))
room.macAddressWhiteList.add(macAddress);
return true;
}
});
sessionHandlers.put(ProtocolConstants.Room.COMMAND_WHITELIST_REMOVE, new IProtocolMessageHandler() {
@Override
public boolean process(IProtocolDriver driver, String macAddress) {
RoomProtocolDriver player = (RoomProtocolDriver) driver;
Room room = player.room;
if (room == null || player != room.roomMaster)
return false;
room.macAddressWhiteList.remove(macAddress);
return true;
}
});
sessionHandlers.put(ProtocolConstants.Room.COMMAND_BLACKLIST_ENABLE, new IProtocolMessageHandler() {
@Override
public boolean process(IProtocolDriver driver, String argument) {
RoomProtocolDriver player = (RoomProtocolDriver) driver;
Room room = player.room;
if (room == null || player != room.roomMaster)
return false;
room.isMacAdressBlackListEnabled = "Y".equals(argument);
return true;
}
});
sessionHandlers.put(ProtocolConstants.Room.COMMAND_BLACKLIST_ADD, new IProtocolMessageHandler() {
@Override
public boolean process(IProtocolDriver driver, String args) {
RoomProtocolDriver player = (RoomProtocolDriver) driver;
Room room = player.room;
if (room == null || player != room.roomMaster)
return false;
for (String macAddress : args.split(TextProtocolDriver.ARGUMENT_SEPARATOR))
room.macAddressBlackList.add(macAddress);
return true;
}
});
sessionHandlers.put(ProtocolConstants.Room.COMMAND_BLACKLIST_REMOVE, new IProtocolMessageHandler() {
@Override
public boolean process(IProtocolDriver driver, String macAddress) {
RoomProtocolDriver player = (RoomProtocolDriver) driver;
Room room = player.room;
if (room == null || player != room.roomMaster)
return false;
room.macAddressBlackList.remove(macAddress);
return true;
}
});
}
private class TunnelProtocol implements IProtocol {
@Override
public String getProtocol() {
return ProtocolConstants.PROTOCOL_TUNNEL;
}
@Override
public IProtocolDriver createDriver(ISocketConnection connection) {
TunnelProtocolDriver tunnel = new TunnelProtocolDriver();
tunnel.connection = connection;
notYetLinkedTunnels.put(connection.getRemoteAddress(), tunnel);
return tunnel;
}
@Override
public void log(String message) {
logger.log(message);
}
}
private class TunnelProtocolDriver implements IProtocolDriver {
private ISocketConnection connection;
private Room room;
private RoomProtocolDriver player;
private long lastTunnelTime;
private boolean inactiveNotified = false;
private void updateTunnelTime() {
lastTunnelTime = System.currentTimeMillis();
}
@Override
public ISocketConnection getConnection() {
return connection;
}
@Override
public boolean process(PacketData data) {
ByteBuffer packet = data.getBuffer();
if (!Utility.isPspPacket(packet)) {
InetSocketAddress remoteAddress = connection.getRemoteAddress();
if (notYetLinkedTunnels.containsKey(remoteAddress)) {
connection.send(Utility.encode(Integer.toString(remoteAddress.getPort())));
}
return true;
}
Room room = this.room;
if (room == null)
return true;
RoomProtocolDriver srcPlayer = player;
if (srcPlayer == null)
return true;
boolean srcPlayerSsidIsNotEmpty = !Utility.isEmpty(srcPlayer.ssid);
updateTunnelTime();
String destMac = Utility.macAddressToString(packet, 0, false);
String srcMac = Utility.macAddressToString(packet, 6, false);
room.tunnelsByMacAddress.put(srcMac, this);
if (srcPlayer == room.roomMaster)
room.macAddressWhiteList.add(srcMac);
if (Utility.isMacBroadCastAddress(destMac)) {
if (room.checkMacAddressFiltering(srcMac))
return true;
for (Entry<String, RoomProtocolDriver> entry : room.playersByName.entrySet()) {
RoomProtocolDriver destPlayer = entry.getValue();
if (srcPlayer == destPlayer)
continue;
TunnelProtocolDriver destTunnel = destPlayer.tunnel;
if (destTunnel == null)
continue;
if (srcPlayerSsidIsNotEmpty && !Utility.isEmpty(destPlayer.ssid))
if (!srcPlayer.ssid.equals(destPlayer.ssid))
continue;
packet.position(0);
destTunnel.connection.send(packet);
}
} else {
if (room.checkMacAddressFiltering(srcMac, destMac))
return true;
TunnelProtocolDriver destTunnel = room.tunnelsByMacAddress.get(destMac);
if (destTunnel == null)
return true;
RoomProtocolDriver destPlayer = destTunnel.player;
if (destPlayer == null)
return true;
if (srcPlayerSsidIsNotEmpty && !Utility.isEmpty(destPlayer.ssid))
if (!srcPlayer.ssid.equals(destPlayer.ssid))
return true;
destTunnel.connection.send(packet);
}
return true;
}
@Override
public void connectionDisconnected() {
try {
player.tunnel = null;
player = null;
room = null;
} catch (NullPointerException e) {
InetSocketAddress address = connection.getRemoteAddress();
if (address != null)
notYetLinkedTunnels.remove(address);
}
}
@Override
public void errorProtocolNumber(String number) {
}
}
private void appendMyRoomCreated(PlayRoom room, StringBuilder sb) {
sb.append(ProtocolConstants.RoomStatus.NOTIFY_ROOM_CREATED);
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(room.getServerAddress());
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(room.getMasterName());
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(room.getTitle());
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(room.getCurrentPlayers());
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(room.getMaxPlayers());
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(room.hasPassword() ? "Y" : "N");
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(room.getCreatedTime());
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(room.getDescription());
}
private class RoomStatusProtocol implements IProtocol {
@Override
public String getProtocol() {
return ProtocolConstants.PROTOCOL_ROOM_STATUS;
}
@Override
public IProtocolDriver createDriver(ISocketConnection connection) {
if (!isAcceptingPortal)
return null;
serverNetwork.reload();
if (!serverNetwork.isValidPortalServer(connection.getRemoteAddress().getAddress()))
return null;
RoomStatusProtocolDriver driver = new RoomStatusProtocolDriver(connection);
portalConnections.put(driver, this);
logger.log("ポータルから接続されました: " + driver.address);
StringBuilder sb = new StringBuilder();
appendRoomServerStatus(sb);
if (!masterNameRoomMap.isEmpty()) {
for (Room room : masterNameRoomMap.values()) {
sb.append(TextProtocolDriver.MESSAGE_SEPARATOR);
room.appendNotifyRoomCreated(sb);
}
}
if (!myRoomEntries.isEmpty()) {
for (Entry<String, MyRoomProtocolDriver> entry : myRoomEntries.entrySet()) {
PlayRoom room = entry.getValue().myRoom;
sb.append(TextProtocolDriver.MESSAGE_SEPARATOR);
appendMyRoomCreated(room, sb);
}
}
connection.send(Utility.encode(sb));
return driver;
}
@Override
public void log(String message) {
logger.log(message);
}
}
private class RoomStatusProtocolDriver extends TextProtocolDriver {
private String address;
private RoomStatusProtocolDriver(ISocketConnection connection) {
super(connection, portalHandlers);
address = Utility.socketAddressToStringByIP(getConnection().getRemoteAddress());
}
@Override
public void log(String message) {
logger.log(message);
}
@Override
public void connectionDisconnected() {
portalConnections.remove(this);
logger.log("ポータルから切断されました: " + address);
}
@Override
public void errorProtocolNumber(String number) {
}
}
private final HashMap<String, IProtocolMessageHandler> portalHandlers;
{
portalHandlers = new HashMap<String, IProtocolMessageHandler>();
}
private class MyRoomProtocol implements IProtocol {
@Override
public String getProtocol() {
return ProtocolConstants.PROTOCOL_MY_ROOM_ENTRY;
}
@Override
public IProtocolDriver createDriver(ISocketConnection connection) {
MyRoomProtocolDriver driver = new MyRoomProtocolDriver(connection);
return driver;
}
@Override
public void log(String message) {
logger.log(message);
}
}
private class MyRoomProtocolDriver extends TextProtocolDriver {
private PlayRoom myRoom;
private MyRoomProtocolDriver(ISocketConnection connection) {
super(connection, myRoomEntryHandlers);
}
@Override
public void log(String message) {
logger.log(message);
}
@Override
public void connectionDisconnected() {
dispose();
}
private void dispose() {
if (myRoom == null)
return;
PlayRoom room = myRoom;
myRoom = null;
myRoomEntries.remove(room.getRoomAddress());
StringBuilder sb = new StringBuilder();
sb.append(ProtocolConstants.RoomStatus.NOTIFY_ROOM_DELETED);
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(room.getRoomAddress());
ByteBuffer buffer = Utility.encode(sb);
for (RoomStatusProtocolDriver driver : portalConnections.keySet()) {
buffer.position(0);
driver.getConnection().send(buffer);
}
}
@Override
public void errorProtocolNumber(String number) {
}
}
private final HashMap<String, IProtocolMessageHandler> myRoomEntryHandlers;
private final HashMap<String, IProtocolMessageHandler> myRoomHandlers;
{
myRoomEntryHandlers = new HashMap<String, IProtocolMessageHandler>();
myRoomEntryHandlers.put(ProtocolConstants.MyRoom.COMMAND_ENTRY, new IProtocolMessageHandler() {
private String makeHostName(String s, IProtocolDriver driver) {
return s.length() > 0 ? s : driver.getConnection().getRemoteAddress().getAddress().getHostAddress();
}
@Override
public boolean process(IProtocolDriver driver, String argument) {
MyRoomProtocolDriver myRoomClient = (MyRoomProtocolDriver) driver;
// R authCode hostName:port masterName title currentPlayers
// maxPlayers hasPassword created description
final String[] tokens = argument.split(TextProtocolDriver.ARGUMENT_SEPARATOR, -1);
if (tokens.length != 9)
return false;
try {
String authCode = tokens[0];
String[] address = tokens[1].split(":");
String hostname = makeHostName(address[0], myRoomClient);
int port = Integer.parseInt(address[1]);
String masterName = tokens[2];
String title = tokens[3];
int currentPlayers = Integer.parseInt(tokens[4]);
int maxPlayers = Integer.parseInt(tokens[5]);
boolean hasPassword = "Y".equals(tokens[6]);
long created = Long.parseLong(tokens[7]);
String description = tokens[8];
PlayRoom room = new PlayRoom(null, hostname + ":" + port, masterName, title, hasPassword, currentPlayers, maxPlayers,
created);
room.setDescription(description);
InetSocketAddress socketAddress = new InetSocketAddress(hostname, port);
return checkMyRoomEntry(myRoomClient, socketAddress, room, authCode);
} catch (NumberFormatException e) {
return false;
}
}
private boolean checkMyRoomEntry(final MyRoomProtocolDriver driver, final InetSocketAddress address, final PlayRoom room,
final String authCode) {
Runnable task = new Runnable() {
@Override
public void run() {
IProtocol tunnelProtocol = new IProtocol() {
@Override
public void log(String message) {
logger.log(message);
}
@Override
public String getProtocol() {
return ProtocolConstants.PROTOCOL_TUNNEL;
}
@Override
public IProtocolDriver createDriver(final ISocketConnection connection) {
return null;
}
};
try {
udpClient.connect(address, 5000, tunnelProtocol);
} catch (IOException e) {
driver.getConnection().send(Utility.encode(ProtocolConstants.MyRoom.ERROR_UDP_PORT_NOT_OPEN));
driver.getConnection().disconnect();
return;
}
IProtocol roomProtocol = new IProtocol() {
@Override
public void log(String message) {
logger.log(message);
}
@Override
public String getProtocol() {
return ProtocolConstants.PROTOCOL_ROOM;
}
@Override
public IProtocolDriver createDriver(final ISocketConnection connection) {
StringBuilder sb = new StringBuilder();
sb.append(ProtocolConstants.Room.COMMAND_CONFIRM_AUTH_CODE);
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(room.getMasterName());
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(authCode);
connection.send(Utility.encode(sb));
return new IProtocolDriver() {
private boolean success = false;
@Override
public ISocketConnection getConnection() {
return connection;
}
@Override
public boolean process(PacketData data) {
String message = data.getMessage();
if (ProtocolConstants.Room.COMMAND_CONFIRM_AUTH_CODE.equals(message)) {
success = true;
}
return false;
}
@Override
public void connectionDisconnected() {
if (success) {
driver.myRoom = room;
myRoomEntries.put(room.getRoomAddress(), driver);
StringBuilder sb = new StringBuilder();
appendMyRoomCreated(room, sb);
ByteBuffer buffer = Utility.encode(sb);
for (RoomStatusProtocolDriver portal : portalConnections.keySet()) {
buffer.position(0);
portal.getConnection().send(buffer);
}
driver.getConnection().send(Utility.encode(ProtocolConstants.MyRoom.COMMAND_ENTRY));
driver.setMessageHandlers(myRoomHandlers);
} else {
driver.getConnection().send(Utility.encode(ProtocolConstants.MyRoom.ERROR_INVALID_AUTH_CODE));
driver.getConnection().disconnect();
}
}
@Override
public void errorProtocolNumber(String number) {
}
};
}
};
try {
tcpClient.connect(address, 10000, roomProtocol);
} catch (IOException e) {
driver.getConnection().send(Utility.encode(ProtocolConstants.MyRoom.ERROR_TCP_PORT_NOT_OPEN));
driver.getConnection().disconnect();
return;
}
}
};
executorService.execute(task);
return true;
}
});
myRoomHandlers = new HashMap<String, IProtocolMessageHandler>();
myRoomHandlers.put(ProtocolConstants.MyRoom.COMMAND_UPDATE, new IProtocolMessageHandler() {
@Override
public boolean process(IProtocolDriver driver, String argument) {
MyRoomProtocolDriver myroom = (MyRoomProtocolDriver) driver;
if (myroom.myRoom == null)
return false;
// U title maxPlayers hasPassword description
String[] tokens = argument.split(TextProtocolDriver.ARGUMENT_SEPARATOR, -1);
if (tokens.length != 4)
return false;
try {
String title = tokens[0];
int maxPlayers = Integer.parseInt(tokens[1]);
boolean hasPassword = "Y".equals(tokens[2]);
String description = tokens[3];
PlayRoom room = myroom.myRoom;
room.setTitle(title);
room.setMaxPlayers(maxPlayers);
room.setHasPassword(hasPassword);
room.setDescription(description);
StringBuilder sb = new StringBuilder();
sb.append(ProtocolConstants.RoomStatus.NOTIFY_ROOM_UPDATED);
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(room.getRoomAddress());
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(room.getTitle());
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(room.getMaxPlayers());
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(room.hasPassword() ? "Y" : "N");
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(room.getDescription());
ByteBuffer buffer = Utility.encode(sb);
for (RoomStatusProtocolDriver stat : portalConnections.keySet()) {
buffer.position(0);
stat.getConnection().send(buffer);
}
return true;
} catch (NumberFormatException e) {
}
return false;
}
});
myRoomHandlers.put(ProtocolConstants.MyRoom.COMMAND_UPDATE_PLAYER_COUNT, new IProtocolMessageHandler() {
@Override
public boolean process(IProtocolDriver driver, String argument) {
MyRoomProtocolDriver state = (MyRoomProtocolDriver) driver;
PlayRoom room = state.myRoom;
if (room == null)
return false;
try { // C playerCount
int playerCount = Integer.parseInt(argument);
room.setCurrentPlayers(playerCount);
StringBuilder sb = new StringBuilder();
sb.append(ProtocolConstants.RoomStatus.NOTIFY_ROOM_PLAYER_COUNT_CHANGED);
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(room.getRoomAddress());
sb.append(TextProtocolDriver.ARGUMENT_SEPARATOR);
sb.append(playerCount);
ByteBuffer buffer = Utility.encode(sb);
for (RoomStatusProtocolDriver stat : portalConnections.keySet()) {
buffer.position(0);
stat.getConnection().send(buffer);
}
return true;
} catch (NumberFormatException e) {
}
return false;
}
});
}
}