package net.yura.lobby.server;
import java.io.File;
import java.lang.management.ManagementFactory;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.logging.Level;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import net.yura.lobby.client.LobbyCom;
import net.yura.lobby.client.ProtoAccess;
import net.yura.lobby.database.Database;
import net.yura.lobby.database.GameRoom;
import net.yura.lobby.database.GameTypeRoom;
import net.yura.lobby.database.User;
import net.yura.lobby.database.impl.JPADatabase;
import net.yura.lobby.gen.ProtoLobby;
import net.yura.lobby.model.Game;
import net.yura.lobby.model.GameType;
import net.yura.lobby.model.Message;
import net.yura.lobby.model.Player;
/**
* @author Yura Mamyrin
*/
public class GameLobby implements LobbyServerMXBean {
final Database database;
// this is loaded at the start and never changes
private final Map<Integer,GameType> classLoaders = new HashMap();
private final ConcurrentMap<Integer,Collection<LobbySession>> gameTypeRooms = new ConcurrentHashMap();
private final Map<Integer,ServerGame> runningGames = new ConcurrentHashMap();
private boolean doAndroidSend;
private boolean saveOnShutdown=true;
private String welcomeMessage,newHost;
final SocketServer server;
public GameLobby(SocketServer sserver) {
this.server = sserver;
try {
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
mbs.registerMBean(this, new ObjectName("net.yura.lobby:type=LobbyServer") );
}
catch (Exception ex) {
throw new RuntimeException(ex);
}
//database = new net.yura.lobby.database.impl.MemoryDatabase();
database = new JPADatabase();
TurnBasedGame.scheduler.pause();
loadFromDB();
TurnBasedGame.scheduler.resume();
// add shutdown hook only if we manage to create a DB
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
if (saveOnShutdown) {
saveToDB();
}
}
});
doAndroidSend = true;
}
public void loadFromDB() {
Collection<Integer> gameIds = new ArrayList();
try {
database.startTransaction();
Collection<GameTypeRoom> list = database.getGameTypes();
for (GameTypeRoom gtr:list) {
classLoaders.put(gtr.getId(), gtr.getClassLoader() );
}
Collection<GameRoom> games = database.getGameRooms();
for (GameRoom game:games) {
gameIds.add( game.getId() );
}
}
finally {
database.endTransaction();
}
int count=0;
for (int gameId:gameIds) {
count++;
LobbyServer.logger.info(count+"/"+gameIds.size()+" loading "+gameId);
long start = System.currentTimeMillis();
long db1;
GameRoom game;
byte[] data;
try {
database.startTransaction();
game = database.getGame(gameId);
db1 = System.currentTimeMillis();
data = game.getGameData();
}
finally {
database.endTransaction();
}
long db2 = System.currentTimeMillis();
if (data!=null) {
try {
ServerGame sg = createServerGame(game, null);
sg.loadGame(data);
}
catch (Exception ex) {
LobbyServer.logger.log(Level.WARNING, "failed to load game from DB "+game, ex);
}
}
long load = System.currentTimeMillis();
LobbyServer.logger.info(count+"/"+gameIds.size()+" done "+gameId+" took GET_GAME:"+(db1-start)+" GET_DATA:"+(db2-db1)+
(data == null ? " no data" : " DATA:"+data.length+" LOAD:"+(load-db2)));
}
}
@Override
public void saveToDB() {
LobbyServer.logger.info("going to save games to DB");
int priority = Thread.currentThread().getPriority();
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
Collection<ServerGame> games = new ArrayList( runningGames.values() );
int count = 0;
for (ServerGame game:games) {
count++;
try {
if (game.isFinished()) {
LobbyServer.logger.info(count+"/"+games.size()+" removing "+game.getId());
removeGame( game.getId() );
}
else {
LobbyServer.logger.info(count+"/"+games.size()+" saving "+game.getId());
saveGame( game.getId() );
}
}
catch (Exception ex) {
LobbyServer.logger.log(Level.WARNING, "failed to save/remove game to DB "+game.getId(), ex);
}
}
Thread.currentThread().setPriority(priority);
LobbyServer.logger.info("DONE saving games to DB");
}
@Override
public void saveGame(int id) {
try {
database.startTransaction();
GameRoom gameRoom = getGame(id);
ServerGame serverGame = runningGames.get(id);
if (serverGame!=null) {
gameRoom.setGameData( serverGame.saveGameState() );
}
database.saveGame(gameRoom);
}
finally {
database.endTransaction();
}
}
public void connected(LobbySession session,String uuid,byte[] key) {
if (newHost!=null) {
Map responce = new HashMap();
responce.put("newHost", newHost );
send( session,ProtoAccess.COMMAND_KEY,responce );
}
else {
try {
database.startTransaction();
// just in case the client does not have one
//if (uuid!=null) { uuid = UUID.randomUUID().toString(); }
User client = database.getUUIDGuestName(uuid);
if (client==null) {
client = new User();
client.setUid(uuid);
client.setType(Player.PLAYER_GUEST);
database.saveUser(client);
}
Map responce = new HashMap();
responce.put("key", key );
responce.put("username", client.getName() );
responce.put("usertype", client.getType() );
session.setUser( client.getName() );
session.setClientUID( uuid );
send( session,ProtoAccess.COMMAND_KEY,responce );
if (welcomeMessage!=null) {
Map map = new HashMap();
map.put("message", welcomeMessage);
send(session, ProtoAccess.COMMAND_CHAT_MESSAGE, map);
}
}
finally {
database.endTransaction();
}
}
}
public void disconnected(LobbySession session) {
try {
if (session.currentGameType!=-1) {
getPlayers(session.currentGameType).remove(session);
session.currentGameType=-1;
}
// make copy of list to avoid ConcurrentModificationException
List<ServerGame> games = new ArrayList( session.playingOrWatchingGame );
for (ServerGame gm: games) {
try {
// can throw game not found exception, if a game has been killed from jmx but user was in it
stopPlayOrWatch(session,gm.getId());
}
catch (Exception ex) {
LobbyServer.logger.log(Level.WARNING, "error stopping play of game "+session+" "+gm.getId(), ex);
}
}
//if (ch.getPlayerType() == Player.PLAYER_GUEST && ch.getGames().isEmpty()) {
// TODO delete this guest account
//}
}
catch (Exception ex) {
LobbyServer.logger.log(Level.WARNING, "error in disconnected "+session, ex);
}
}
public void setNick(LobbySession session, String username) {
try {
setNick(session.getUsername(), username);
}
catch (IllegalArgumentException ex) {
send( session,ProtoAccess.COMMAND_LOGIN_ERROR, String.valueOf( ex.getMessage() ) );
}
}
@Override
public void setNick(String oldname, String username) {
User user;
try {
database.startTransaction();
// maybe this should not "fix" the errors but throw an exception!
// then again we send the username back to the client, so is prob ok
username = LobbyCom.checkUsername(username);
if (oldname.equals(username) || (oldname.toLowerCase().startsWith("guest") && username.length()==0)) {
// should never happen
throw new IllegalArgumentException("new name same as old name: "+oldname);
}
if (username.toLowerCase().startsWith("guest")) {
throw new IllegalArgumentException("can not start your nick with guest: "+username );
}
if (trimToLetters(username.toLowerCase()).endsWith("bot")) {
throw new IllegalArgumentException("can not end your nick with bot: "+username );
}
if (username.toLowerCase().endsWith("resigned")) {
throw new IllegalArgumentException("can not end your nick with resigned: "+username );
}
if (username.toLowerCase().equals("null")) {
throw new IllegalArgumentException("you can not call yourself: "+username );
}
if (username.indexOf('\n') >= 0 || username.indexOf('\r') >= 0 || username.indexOf('\t') >= 0) {
throw new IllegalArgumentException("you can not have a that symbol in your name: "+username );
}
if (username.length()!=0 && username.length() < 4) {
throw new IllegalArgumentException("username must be at least 4 chars: "+username);
}
if (username.length()!=0 && username.length() > 45) { // 45 is length of field in JPADB
throw new IllegalArgumentException("username is too long, must be less then 45 chars: "+username);
}
if (username.length()!=0 && database.getUser(username)!=null) {
// error: an account is already registered with this name
// error: or a guest has joined a game with this name
// error: a guest is connected with this name
throw new IllegalArgumentException("this user is already in the database: "+username);
}
user = database.getUser(oldname);
if (user==null) {
throw new IllegalArgumentException("user with name \""+oldname+"\" not found");
}
user.setName(username.length()==0?null:username);
database.saveUser(user);
}
finally {
database.endTransaction();
}
// everything went ok with renaming, now we need to send out 3 commands
// 1) COMMAND_LOGIN_OK/COMMAND_LOGOUT_OK
// 2) COMMAND_ADD_OR_UPDATE_GAME and COMMAND_RENAME_PLAYER
// and we need to make sure it is in this order otherise the client game action buttons may get wrong option
Map responce = new HashMap();
responce.put("username", user.getName() ); // username sent server->client not encrypted
responce.put("userType", user.getType() );
Collection<LobbySession> sessions = server.getLobbySession(oldname);
for (LobbySession session:sessions) {
session.setUser(user.getName());
// TODO what exactly is the difference between these 2 commands???
if (username.length()==0) {
send( session,ProtoAccess.COMMAND_LOGOUT_OK, responce );
}
else {
send( session,ProtoAccess.COMMAND_LOGIN_OK, responce );
}
}
// these are the games that the user is currently in or is watching
Set<LobbySession> others = new HashSet();
// this is the games that the user has joined
for (GameRoom gm: user.getGames() ) {
ServerGame serverGame = runningGames.get(gm.getId());
if (serverGame!=null) {
serverGame.midgameLogin( oldname, user.getName() );
others.addAll( serverGame.getAllClients() );
}
gameChanged(gm);
}
for (LobbySession session:sessions) for (ServerGame gm: session.playingOrWatchingGame ) {
// put together a list of everyone watching any of this users games
others.addAll( gm.getAllClients() );
}
Map send = new HashMap();
send.put("oldName", oldname );
send.put("newName", user.getName() );
send.put("userType", user.getType() );
for (LobbySession other: others) {
// we need to send this even to ourselves as we need to be renamed there too
send(other, ProtoAccess.COMMAND_RENAME_PLAYER, send);
}
}
public static String trimToLetters(String in) {
int start = 0;
int end = in.length();
for (int c=0;c<in.length();c++) {
if (!Character.isLetter(in.charAt(c))) {
start = c+1;
}
else {
break;
}
}
if (start != end) {
for (int c=in.length()-1;c>=0;c--) {
if (!Character.isLetter(in.charAt(c))) {
end = c;
}
else {
break;
}
}
}
return in.substring(start, end);
}
public void sendGameTypes(LobbySession session) {
if (newHost!=null) {
// should never normally get here, unless its a old version of the app
throw new IllegalStateException("please update your app");
}
try {
database.startTransaction();
Collection<GameTypeRoom> c = database.getGameTypes();
List<GameType> gametypes = new ArrayList();
for (GameTypeRoom gtr:c) {
gametypes.add( gtr.getGameType() );
}
Collections.sort(gametypes);
send( session,ProtoAccess.COMMAND_ALL_GAMETYPES,gametypes );
}
finally {
database.endTransaction();
}
}
@Override
public int getNumberRunningGames() {
return runningGames.size();
}
@Override
public void androidSend(String username,String msg, Integer gameId) {
LobbyServer.logger.info("AndroidSend to "+doAndroidSend+" "+username+" "+msg+" "+gameId);
if (doAndroidSend) {
AndroidGCM.send( this, database, username, msg, gameId);
}
}
@Override
public void sendChatRoomMessage(int game_id, String message) {
sendChatMessage(game_id, null, message);
}
@Override
public void sendSystemMessage(String message) {
sendChatMessage(null, null, message);
}
@Override
public void kickUser(String name) {
Collection<LobbySession> sessions = server.getLobbySession(name);
LobbyServer.logger.log(Level.INFO, "going to kick \""+name+"\" = "+sessions);
for (LobbySession s:sessions) {
server.kick(s);
}
}
@Override
public void setSaveOnShutdown(boolean b) {
saveOnShutdown = b;
}
@Override
public boolean getSaveOnShutdown() {
return saveOnShutdown;
}
@Override
public void setDoAndroidSend(boolean b) {
doAndroidSend = b;
}
@Override
public boolean getDoAndroidSend() {
return doAndroidSend;
}
@Override
public void setWelcomeMessage(String msg) {
welcomeMessage="".equals(msg)?null:msg;
}
@Override
public String getWelcomeMessage() {
return welcomeMessage;
}
@Override
public void setNewHost(String host) {
newHost="".equals(host)?null:host;
}
@Override
public String getNewHost() {
return newHost;
}
@Override
public Map<String, Integer> getClientVersions() {
Map<String, Integer> result = new HashMap();
Collection<LobbySession> towho = server.getLobbySession(null);
for (LobbySession name: towho ) {
String version = name.getClientVersion();
Integer count = result.get(version);
result.put(version, count==null?1:count+1);
}
return result;
}
@Override
public void resignFromAllGames(String username) {
List<Integer> games = new ArrayList();
try {
database.startTransaction();
User user = database.getUser(username);
for (GameRoom game:user.getGames()) {
games.add(game.getId());
}
}
finally {
database.endTransaction();
}
for (int game:games) {
try {
leaveGame(username, game);
}
catch(Exception ex) {
LobbyServer.logger.log(Level.WARNING, "resignFromAllGames error leaving game "+game, ex);
}
}
}
public void sendChatMessage(Integer game_id,String sender,String message) {
Collection<LobbySession> towho;
if (game_id==null) {
towho = server.getLobbySession(null);
}
else {
ServerGame sg = runningGames.get( game_id );
if (sg==null) {
throw new IllegalArgumentException("can not send "+sender+":\""+message+"\" can not find server game with id: "+game_id);
}
towho = sg.getAllClients();
}
Map map = new HashMap();
if (game_id!=null) {
map.put("room_id", game_id );
}
if (sender!=null) {
map.put("sender", sender);
}
map.put("message", message);
for (LobbySession name: towho ) {
send(name, ProtoAccess.COMMAND_CHAT_MESSAGE, map);
}
}
private User getUser(LobbySession session) {
User user = database.getUUIDGuestName( session.getClientUID() );
if (user==null) {
throw new IllegalArgumentException("no user for "+session.getClientUID()+" "+session.getUsername());
}
return user;
}
private GameRoom getGame(int id) {
GameRoom gameRoom = database.getGame( id );
if (gameRoom==null) {
throw new IllegalArgumentException("can not find game with id: "+id);
}
return gameRoom;
}
public GameRoom createGame(Game game) {
if (newHost!=null) {
// should never normally get here, unless its a old version of the app
throw new IllegalStateException("please update your app");
}
try {
database.startTransaction();
GameRoom gameRoom = new GameRoom(game);
GameTypeRoom gameTypeRoom = database.getGameType( game.getType().getId() );
gameTypeRoom.createNewGame(gameRoom);
database.saveGame(gameRoom);
return gameRoom;
}
finally {
database.endTransaction();
}
}
public void setCurrentGameType(LobbySession session,int game_type_id) {
try {
database.startTransaction();
// remove from current one
if (session.currentGameType!=-1) {
getPlayers(session.currentGameType).remove(session);
}
getPlayers(game_type_id).add(session);
session.currentGameType = game_type_id;
List<GameRoom> games = new ArrayList(database.getGameType(game_type_id).getGameRooms());
Collections.sort(games);
for (GameRoom gr: games) {
sendGame(session, gr.getGame());
}
}
finally {
database.endTransaction();
}
}
/**
* give me all the sessions that are watching this gameType
*/
private Collection<LobbySession> getPlayers(int gameTypeId) {
// add to new list
Collection<LobbySession> players = gameTypeRooms.get(gameTypeId);
if (players==null) {
// we need to use something Concurrent or we will get ConcurrentModificationException
gameTypeRooms.putIfAbsent(gameTypeId, new ConcurrentSkipListSet() );
players = gameTypeRooms.get(gameTypeId);
}
return players;
}
void gameChanged(GameRoom gr) {
Collection<LobbySession> players = getPlayers(gr.getGameTypeRoom().getId());
for (LobbySession session:players) {
sendGame(session,gr.getGame());
}
}
private void sendGame(LobbySession session, Game game) {
ServerGame sgame = runningGames.get(game.getId());
if (sgame!=null) {
game.setWhosTurn( sgame.getWhosTurn() );
game.setInGame( sgame.getAllClients().size() );
}
send( session , ProtoAccess.COMMAND_ADD_OR_UPDATE_GAME , game );
}
@Override
public void joinGame(String username,int id) {
GameRoom game = joinGame(username, id, null);
gameChanged(game);
}
public GameRoom joinGame(String username,int id, LobbySession creator) {
ServerGame serverGame = runningGames.get(id);
GameRoom game;
if (serverGame!=null) {
serverGame.playerJoined( username ); // TODO case may be wrong!!
}
try {
database.startTransaction();
game = getGame( id );
User user = database.getUser(username);
// as this game is already started and serverGame.playerJoined was successfull
// we need to increase the number of available places in this game
// to allow this new player to be added to it with game.joinGame
if (serverGame!=null) {
game.setMaxPlayers( game.getMaxPlayers()+1 );
}
game.joinGame( user );
database.saveGame(game);
}
finally {
database.endTransaction();
}
if (game.getNumOfPlayers() == game.getMaxPlayers() && serverGame==null) {
serverGame = createServerGame(game, creator);
Set<User> players = game.getUsers();
String[] playerNames = new String[players.size()];
Iterator<User> it = players.iterator();
for (int c=0;it.hasNext();c++) {
playerNames[c] = it.next().getName();
}
serverGame.startGame(game.getOptions(), playerNames );
}
return game;
}
@Override
public void leaveGame(String username,int gameId) {
ServerGame serverGame = runningGames.get(gameId);
GameRoom game;
boolean gameRemoved=false;
// we want to remove the player from the DB first otherwise things can be happeing in 2 threads at the same time
// and this will cause potentially incorrect information to be sent to the client
// e.g. both here and game thread send updated Game to client, but the client gets the game with the removed player
// after the game without the removed player.
try {
database.startTransaction();
game = getGame( gameId );
if (game.getNumOfPlayers() == game.getMaxPlayers() && serverGame==null) {
throw new IllegalStateException("game has not been initialised yet");
}
User user = database.getUser(username);
username = user.getName(); // as the case may have not been exactly correct when this method was called
game.leaveGame( user );
if (serverGame==null) {
gameRemoved = destroyGameCheck(serverGame,gameId);
}
else {
game.setMaxPlayers( game.getMaxPlayers()-1 );
}
if (!gameRemoved) {
database.saveGame(game);
}
}
finally {
database.endTransaction();
}
if (!gameRemoved) {
// now that we have finished everything DB related, now call resign on the live object
if (serverGame!=null) {
serverGame.playerResigned( username ); // this may call gameFinished -> destroyGameCheck
}
gameChanged(game);
}
}
public void playOrWatch(LobbySession session,int game_id) {
try {
database.startTransaction();
ServerGame serverGame = runningGames.get( game_id );
if (serverGame==null) {
throw new IllegalArgumentException("can not find running game for id "+game_id);
}
if (serverGame.getAllClients().contains(session)) {
throw new IllegalStateException("session is already playing/watching this game "+game_id+" "+session);
}
if (!server.isConnected(session)) {
throw new IllegalStateException("session is not connected");
}
// this will now send the game object to the client
serverGame.clientEntered( session );
session.playingOrWatchingGame.add(serverGame);
// while we were having all this fun, we may have lost the client connection
if (!server.isConnected(session)) {
serverGame.clientLeaves( session );
session.playingOrWatchingGame.remove(serverGame);
throw new IllegalStateException("session is not connected, and we have had to remove it from the game");
}
Set<Player> users = getPlayersAndSpectators( serverGame.getAllClients() );
for (Player player:users) {
Map message = new HashMap();
message.put("room_id", serverGame.getId() );
message.put("player", player );
send( session ,ProtoAccess.COMMAND_PLAYER_ADDED, message );
}
// TODO what if the person joins is already there with another session?
// tell everyone else we have joined, except us, as we already know
Map message = new HashMap();
message.put("room_id", serverGame.getId() );
message.put("player", getUser(session) );
for (LobbySession user: serverGame.getAllClients() ) {
if (user != session) {
send( user,ProtoAccess.COMMAND_PLAYER_ADDED, message );
}
}
}
finally {
database.endTransaction();
}
}
public void stopPlayOrWatch(LobbySession session,int game_id) {
try {
database.startTransaction();
ServerGame serverGame = runningGames.get( game_id );
if (serverGame==null) {
throw new IllegalArgumentException("can not find running game for id "+game_id);
}
if (!serverGame.getAllClients().contains(session)) {
throw new IllegalStateException("session is not playing/watching this game "+game_id+" "+session);
}
// this will now send the game object to the client
serverGame.clientLeaves( session );
session.playingOrWatchingGame.remove(serverGame);
// TODO in the game, get the current players and remove all of them from our current session
// tell everyone else we have left
Map message = new HashMap();
message.put("room_id", serverGame.getId() );
message.put("playerName", session.getUsername() );
for (LobbySession user: serverGame.getAllClients() ) {
// TODO do not send to us
send( user,ProtoAccess.COMMAND_PLAYER_REMOVE, message );
}
destroyGameCheck(serverGame,serverGame.getId());
}
finally {
database.endTransaction();
}
}
public void messageForGame(int gameId,String username, Object message) {
ServerGame serverGame = runningGames.get( gameId );
serverGame.messageFromUser(username, message );
}
public Set<Player> getPlayersAndSpectators(Collection<LobbySession> sessions) {
Set<Player> set = new HashSet();
for (LobbySession session:sessions) {
set.add( getUser(session) );
}
return set;
}
private boolean destroyGameCheck(ServerGame serverGame,int id) {
// checks the game is not just been created
GameRoom game = getGame(id);
if ((serverGame==null || serverGame.getAllClients().isEmpty()) && ((serverGame!=null && serverGame.isFinished()) || game.getUsers().isEmpty())) {
removeGame(game);
return true;
}
return false;
}
private void removeGame(GameRoom gameRoom) {
int id = gameRoom.getId();
ServerGame serverGame = runningGames.get(id);
if (serverGame!=null) {
// TODO what if someone is watching this game, what do?
serverGame.destroyServerGame();
runningGames.remove( id );
}
int gameTypeId = gameRoom.getGameTypeRoom().getId();
gameRoom.getGameTypeRoom().getGameRooms().remove(gameRoom);
database.removeGame(gameRoom);
Map map = new HashMap();
map.put("game_id", id );
Collection<LobbySession> players = getPlayers(gameTypeId);
for (LobbySession session:players) {
send( session , ProtoAccess.COMMAND_REMOVE_GAME , map );
}
}
@Override
public void removeGame(int gameId) {
try {
database.startTransaction();
removeGame( getGame(gameId) );
}
finally {
database.endTransaction();
}
}
@Override
public boolean isFinished(int gameId) {
ServerGame serverGame = runningGames.get( gameId );
if (serverGame==null) {
throw new IllegalArgumentException("can not find running game for id "+gameId);
}
return serverGame.isFinished();
}
@Override
public int countFinishedGames() {
int count = 0;
for (ServerGame game:runningGames.values()) {
if (game.isFinished()) {
count++;
}
}
return count;
}
private ServerGame createServerGame(final GameRoom game, final LobbySession creator) {
try {
GameType gameType = classLoaders.get( game.getGameTypeRoom().getId() );
ClassLoader loader = gameType.getClassLoader( new File(".").toURI().toASCIIString() );
ServerGame serverGame = (ServerGame) Class.forName( gameType.getClassName(), true, loader ).newInstance();
serverGame.setId(game.getId());
serverGame.setTimeout(game.getTimeout());
runningGames.put(game.getId(), serverGame);
final String gameName = game.getName();
final ServerGame sg = serverGame;
serverGame.addServerGameListener(new ServerGameListener() {
@Override
public void messageFromGame(Object message, Collection<LobbySession> towho) {
Map map = new HashMap();
map.put("game_id", sg.getId() );
map.put("message", message);
for (LobbySession who:towho) {
send(who, ProtoAccess.COMMAND_GAME_MESSAGE, map);
}
}
@Override
public void gameStarted() {
if (creator != null) {
Map map = new HashMap();
map.put("game_id", sg.getId() );
send(creator, ProtoAccess.COMMAND_GAME_STARTED, map);
}
}
@Override
public void gameFinished() {
try {
database.startTransaction();
destroyGameCheck(sg,sg.getId());
}
finally {
database.endTransaction();
}
}
@Override
public void sendChatroomMessage(String message) {
sendChatRoomMessage(sg.getId(),message);
}
@Override
public void needInputFrom(String username) {
boolean doAndroidSend=false;
try {
database.startTransaction();
LobbySession found=null;
Game game = getGame( sg.getId() ).getGame();
Collection<LobbySession> sessions = sg.getAllClients();
for (LobbySession session:sessions) {
sendGame(session, game);
if (session.getUsername().equals(username)) {
found = session;
}
}
if (username!=null && found==null) {
Collection<LobbySession> session = server.getLobbySession(username);
if (!session.isEmpty()) {
for (LobbySession s:session) {
sendGame(s, game);
}
}
else {
// if they are not even connected
doAndroidSend = true;
}
}
}
finally {
database.endTransaction();
}
if (doAndroidSend) {
androidSend(username, "It is your go: "+gameName, sg.getId());
}
}
@Override
public void resignPlayer(String username) {
leaveGame(username, sg.getId());
}
@Override
public void logGameMove(String user, String move) { }
});
return serverGame;
}
catch (Exception ex) {
throw new RuntimeException(ex);
}
}
public void registerGCM(LobbySession session,String key) {
try {
database.startTransaction();
User user = getUser(session);
user.setAndroidId( key );
database.saveUser(user);
send(session, ProtoLobby.COMMAND_ANDROID_REGISTER_DONE, null);
}
finally {
database.endTransaction();
}
}
public void unregisterGCM(LobbySession session,String key) {
try {
database.startTransaction();
User user = getUser(session);
if (LobbyServer.equals(user.getAndroidId(), key)) {
user.setAndroidId(null);
database.saveUser(user);
send(session, ProtoLobby.COMMAND_ANDROID_UNREGISTER_DONE, null);
}
else {
throw new IllegalStateException("android "+user.getAndroidId()+" "+key);
}
}
finally {
database.endTransaction();
}
}
public GameType getGameType(int gameTypeId) {
return classLoaders.get( gameTypeId );
}
public void send(LobbySession session, String command, Object param) {
Message message = new Message();
message.setCommand(command);
message.setParam(param);
LobbyServer.logger.info("Sending "+session+" "+message);
server.send(session, message);
}
@Override
public void removeCurrentPlayerNotInGameGames() {
Collection<Integer> badGames = new ArrayList();
try {
database.startTransaction();
Collection<GameRoom> games = database.getGameRooms();
for (GameRoom game:games) {
ServerGame serverGame = runningGames.get(game.getId());
if (serverGame!=null) {
String whosTurn = serverGame.getWhosTurn();
if (whosTurn != null && !"".equals(whosTurn) && !game.hasUser(whosTurn)) {
LobbyServer.logger.log(Level.WARNING, "current player not in game \""+whosTurn+"\" players: "+game.getUsers());
badGames.add(serverGame.getId());
}
}
}
}
finally {
database.endTransaction();
}
for (int id : badGames) {
removeGame(id);
}
}
@Override
public void removeRunningGameNotFoundGames() {
Collection<Integer> badGames = new ArrayList();
try {
database.startTransaction();
Collection<GameRoom> games = database.getGameRooms();
for (GameRoom game:games) {
if (game.getNumOfPlayers() == game.getMaxPlayers() && runningGames.get(game.getId()) == null) {
LobbyServer.logger.log(Level.WARNING, "no running game found for game: "+game);
badGames.add( game.getId() );
}
}
}
finally {
database.endTransaction();
}
for (int id : badGames) {
removeGame(id);
}
}
}