/**
Creep Smash, a multiplayer towerdefence game
created as a project at the Hochschule fuer
Technik Stuttgart (University of Applied Science)
http://www.hft-stuttgart.de
Copyright (C) 2008 by
* Andreas Wittig
* Bernd Hietler
* Christoph Fritz
* Fabian Kessel
* Levin Fritz
* Nikolaj Langner
* Philipp Schulte-Hubbert
* Robert Rapczynski
* Ron Trautsch
* Sven Supper
http://creepsmash.sf.net/
This program 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 de.creepsmash.server;
import java.util.ConcurrentModificationException;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.apache.log4j.Logger;
import java.util.Hashtable;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import de.creepsmash.common.IConstants;
import de.creepsmash.common.IConstants.ResponseType;
import de.creepsmash.common.messages.server.CreateGameResponseMessage;
import de.creepsmash.common.messages.server.GameDescription;
import de.creepsmash.common.messages.server.GamesMessage;
import de.creepsmash.common.messages.server.JoinGameResponseMessage;
import de.creepsmash.common.messages.server.MessageMessage;
import de.creepsmash.common.messages.server.PlayerQuitMessage;
import de.creepsmash.common.messages.server.PlayersMessage;
import de.creepsmash.common.messages.server.ServerMessage;
import de.creepsmash.server.game.Game;
import de.creepsmash.server.game.PlayerInGame;
import de.creepsmash.server.game.TerminatedGameState;
import de.creepsmash.server.model.BlackList;
import de.creepsmash.server.model.Player;
/**
* The lobby manages games and clients who are authenticated but not currently
* playing.
*/
public class Lobby implements Game.GameObserver {
private List<Client> clients;
private List<Game> games;
private int nextGameId;
private static Logger logger = Logger.getLogger(Game.class);
/**
* Create a new lobby with zero clients and zero games.
*/
public Lobby() {
this.clients = new LinkedList<Client>();
this.games = new LinkedList<Game>();
this.nextGameId = 1;
}
/**
* Send a message to all clients in the lobby.
* @param message the message to send.
*/
private synchronized void sendAll(ServerMessage message) {
if (message == null) {
throw new IllegalArgumentException("'message' was null");
}
for (Client client : this.clients) {
client.send(message);
}
}
/**
* Creates a GAMES message with the current list of games.
* @return the message
*/
private synchronized GamesMessage gamesMessage() {
Set<GameDescription> gameDescriptions = new HashSet<GameDescription>();
try {
for (Game game : this.games) {
String GamePlayer4 = "";
String GamePlayer3 = "";
String GamePlayer2 = "";
String GamePlayer1 = "";
List<String> Players = game.getPlayerNames();
int count = 1;
for (String p : Players) {
if (count == 1) {
GamePlayer1 = p;
}else if (count == 2) {
GamePlayer2 = p;
}else if (count == 3) {
GamePlayer3 = p;
}else if (count == 4) {
GamePlayer4 = p;
}
count++;
}
gameDescriptions.add(new GameDescription(game.getGameId(),
"[" + ((game.getCurrentPlayers() > 0) ? game.getPlayerScore()/game.getCurrentPlayers() : 0) + "]" + game.getGameName(),
game.getMapId(),
game.getMaxPlayers(),
game.getCurrentPlayers(),
game.getMaxEloPoints(),
game.getMinEloPoints(),
((game.getPasswort().length() > 0) ? "yes" : "no"),
GamePlayer1,
GamePlayer2,
GamePlayer3,
GamePlayer4,
game.getState()));
}
} catch (ConcurrentModificationException cme) {
return this.gamesMessage();
}
return new GamesMessage(gameDescriptions);
}
/**
* Creates a PLAYERS message with the current list of clients.
* @return the message
*/
private synchronized PlayersMessage playersMessage() {
Hashtable<String, Integer> playerNames = new Hashtable<String, Integer>();
try {
for (Client client : this.clients) {
if (client.check()) {
Player player =
AuthenticationService.getPlayer(client.getUserName());
playerNames.put(client.getUserName(), player.getElopoints() - 500);
} else {
logger.info("PlayersMessage/leaveLobby: " + client.getUserName());
this.leaveLobby(client);
}
}
} catch (ConcurrentModificationException cme) {
return this.playersMessage();
}
return new PlayersMessage(playerNames);
}
/**
* Find the game with the given id.
* @param gameId the gameId to look for.
* @return the game, of null if it could not be found.
*/
private synchronized Game findGame(int gameId) {
for (Game game : this.games) {
if (game.getGameId() == gameId) {
return game;
}
}
return null;
}
/**
* Find the game with the given name.
* @param gameName the name to look for.
* @return the game, or null if it could not be found.
*/
private synchronized Game findGame(String gameName) {
for (Game game : this.games) {
if (gameName.equals(game.getGameName())) {
return game;
}
}
return null;
}
/**
* Find the Palyer and Kick/Ban from Server
* @param player the name to look for.
* @param adminClient Modarator Client
* @param banUser the youser account
* @param banUserAndMac the youser account and add MAC to the Blacklist
* @return boolena
* @author Contex
*/
public synchronized boolean kickClient(Player player,Client adminClient, boolean banUser, boolean banUserAndMac) {
boolean kik = false;
for (Game game : this.games) {
for (PlayerInGame Player : game.getClients()) {
if (Player.getClient().getUserName().equals(player.getName())) {
logger.info("Kick Player inGame: "
+ Player.getClient().getUserName());
game
.sendAll(new MessageMessage("System",
"<span style=\"color:red;\">"
+ Player.getClient().getUserName()
+ " were kicked by <b>"
+ adminClient.getUserName()
+ "</b></span>"));
game.sendAll(new MessageMessage("System", Player
.getClient().getUserName()
+ " has left..."));
game.removeClient(Player.getClient());
if (game.getClients().isEmpty()) {
game.shutdown();
gameTerminated(game);
} else {
game.sendAll(new PlayerQuitMessage(player.getName(),
"Kick", Player.getClient().getClientID()));
game.gamePlayersChanged();
}
Player.getClient().disconnect();
gamesMessage();
adminClient
.send(new MessageMessage("System",
"<span style=\"color:red;\">"
+ Player.getClient().getUserName()
+ " were kicked by <b>"
+ adminClient.getUserName()
+ "</b></span>"));
kik = true;
break;
}
}
if (kik == true)
break;
}
if (kik == false) {
for (Client client : this.clients) {
if (client.getUserName().equals(player.getName())) {
logger.info("Kick Player inLobby: " + client.getUserName());
sendAll(new MessageMessage("System",
"<span style=\"color:red;\">"
+ client.getUserName()
+ " were kicked by <b>"
+ adminClient.getUserName() + "</b></span>"));
sendAll(new MessageMessage("System", client.getUserName()
+ " has left..."));
client.disconnect();
kik = true;
break;
}
}
}
if (kik == false) {
adminClient.send(new MessageMessage("System",
"<span style=\"color:red;\"> " + player.getName()
+ " User not Online !</span>"));
}
if (banUser == true) {
player.setBlocked(true);
try {
EntityManager entityManager =
PersistenceManager.getInstance().getEntityManager();
EntityTransaction entityTransaction = entityManager
.getTransaction();
entityTransaction.begin();
entityManager.merge(player);
entityManager.flush();
entityTransaction.commit();
adminClient.send(new MessageMessage("System","<span style=\"color:red;\">"
+ player.getName()
+ " (Account) has been banned !</span>"));
logger.debug("Block for User " + player.getName() + " / "
+ player.getMac() + " saved.");
} catch (Throwable t) {
logger.error("error while saving block for User "
+ player.getName() + " / " + player.getMac() + " ", t);
}
}
if (banUserAndMac == true) {
adminClient.send(new MessageMessage("System","<span style=\"color:red;\">"
+ player.getName()
+ " (Mac) has been blacklisted !</span>"));
try {
EntityManager entityManager = PersistenceManager
.getInstance().getEntityManager();
EntityTransaction entityTransaction = entityManager
.getTransaction();
entityTransaction.begin();
BlackList blacklist = new BlackList();
blacklist.setData(player.getMac());
entityManager.persist(blacklist);
entityManager.flush();
entityTransaction.commit();
logger.debug("Block for MAC " + player.getName() + " / "
+ player.getMac() + " saved.");
} catch (Throwable t) {
logger.error("error while saving block for MAC "
+ player.getName() + " / " + player.getMac() + " ", t);
}
}
return kik;
}
/**
* Find the Palyer and unBan and Remove from Blacklist
* @param player the name to look for.
* @param adminClient Modarator Client
* @return boolena
* @author Contex
*/
public synchronized boolean unBanClient(Player player,Client adminClient) {
boolean Retrun = false;
try {
EntityManager entityManager =
PersistenceManager.getInstance().getEntityManager();
EntityTransaction entityTransaction = entityManager.getTransaction();
entityTransaction.begin();
BlackList blacklist = entityManager.find(BlackList.class, player.getMac());
if (blacklist != null) {
entityManager.remove(blacklist);
adminClient.send(new MessageMessage("System","<span style=\"color:red;\">"
+ player.getName()
+ " (Mac) has been unbanned !</span>"));
Retrun = true;
}
entityTransaction.commit();
} catch (Throwable t) {
logger.error("error while remove block for User "
+ player.getName() + " / " + player.getMac() + " ", t);
}
try {
player.setBlocked(false);
EntityManager entityManager =
PersistenceManager.getInstance().getEntityManager();
EntityTransaction entityTransaction = entityManager
.getTransaction();
entityTransaction.begin();
entityManager.merge(player);
entityManager.flush();
entityTransaction.commit();
adminClient.send(new MessageMessage("System","<span style=\"color:red;\">"
+ player.getName()
+ " (User) has been unbanned !</span>"));
logger.debug("undBlock for User " + player.getName() + " / "
+ player.getMac() + " removed.");
} catch (Throwable t) {
logger.error("error while saving undBlock for User "
+ player.getName() + " / " + player.getMac() + " ", t);
}
return Retrun;
}
/**
* Join the lobby.
* @param newClient the client who wants to join the lobby.
*/
public synchronized void newClient(Client newClient) {
if (newClient == null) {
throw new IllegalArgumentException("'newClient' was null");
}
this.clients.add(newClient);
newClient.send(gamesMessage());
sendAll(playersMessage());
logger.info("new client joined the lobby: " + newClient);
}
/**
* Create a new game.
* @param sender the client who wants to create a new game. Must not be null.
* @param gameName the game's name. Must not be null.
* @param mapId the id of the map.
* @param maxPlayers the max number of players in the game. Must be at least
* 2.
* @return the game, or null if game creation failed.
*/
public synchronized Game createGame(Client sender, String gameName, int mapId,
int maxPlayers,String Passwort,int MaxEloPoints,int MinEloPoints) {
if (sender == null) {
throw new IllegalArgumentException("'client' was null");
}
if (gameName == null) {
throw new IllegalArgumentException("'gameName' was null");
}
if (maxPlayers < 2) {
throw new IllegalArgumentException(
"'maxPlayers' must be >= 2 (was: " + maxPlayers + ")");
}
if (findGame(gameName) != null) {
logger.info("client tried to create game '" + gameName
+ "' but a game with the same name exists already");
sender.send(new CreateGameResponseMessage(ResponseType.failed));
return null;
}
Game game =
new Game(this.nextGameId++, gameName, mapId, maxPlayers, sender, Passwort, MaxEloPoints, MinEloPoints);
logger.info("new game: " + game);
this.games.add(game);
game.addObserver(this);
sender.send(new CreateGameResponseMessage(ResponseType.ok));
this.clients.remove(sender);
game.newClient(sender);
sendAll(playersMessage());
return game;
}
/**
* Join a game.
* @param sender the client who wants to join the game. Must not be null.
* @param gameId the game's id.
* @return the game, or null if the game cannot be found or the client
* can't join.
*/
public synchronized Game joinGame(Client sender, int gameId, String Passwort) {
if (sender == null) {
throw new IllegalArgumentException("'client' was null");
}
Game game = findGame(gameId);
if (game == null) {
sender.send(new JoinGameResponseMessage(ResponseType.failed));
return null;
}
synchronized (game) {
int clientElopoints = AuthenticationService.getPlayer(
sender.getUserName()).getElopoints() - 500;
if ((clientElopoints < game.getMinEloPoints() && game.getMinEloPoints() != 0)
|| (clientElopoints > game.getMaxEloPoints() && game.getMaxEloPoints() != 0)) {
sender.send(new JoinGameResponseMessage(ResponseType.failed));
return null;
}
if (game.getPasswort().length() > 0) {
if (!game.getPasswort().equals(Passwort)) {
sender
.send(new JoinGameResponseMessage(
ResponseType.failed));
return null;
}
}
if ((game.canClientJoin()) && (game.check4Multi(sender.getIPAddress(), sender.getMACAddress()))) {
sender.send(new JoinGameResponseMessage(ResponseType.ok));
this.clients.remove(sender);
game.newClient(sender);
} else {
sender.send(new JoinGameResponseMessage(ResponseType.failed));
return null;
}
}
sendAll(playersMessage());
return game;
}
/**
* Send a chat message.
* @param sender the client sending the message. Must not be null.
* @param message the chat message. Must not be null.
*/
public synchronized void sendMessage(Client sender, String message) {
if (sender == null) {
throw new IllegalArgumentException("'sender' was null");
}
if (message == null) {
throw new IllegalArgumentException("'message' was null");
}
sendAll(new MessageMessage(sender.getUserName(), message));
}
/**
* Sends PLAYERS and GAMES messages to a client.
* @param sender the client to send the messages to. Must not be null.
*/
public synchronized void refresh(Client sender) {
if (sender == null) {
throw new IllegalArgumentException("'sender' was null");
}
sender.send(playersMessage());
sender.send(gamesMessage());
}
/**
* Allows a client to leave the lobby.
* @param client the client. Must not be null.
*/
public synchronized void leaveLobby(Client client) {
if (client == null) {
throw new IllegalArgumentException("'client' was null");
}
if (!this.clients.contains(client)) {
return;
}
this.clients.remove(client);
sendAll(playersMessage());
logger.info("client left the lobby: " + client);
}
/**
* Shutdown the lobby and all games.
*/
public synchronized void shutdown() {
logger.info("shutting down the lobby");
for (Game game : this.games) {
game.shutdown();
}
this.games = null;
this.clients = null;
}
/**
* A game's state changed.
* @param game the game that changed.
*/
public void gameStateChanged(Game game) {
sendAll(gamesMessage());
}
/**
* A game's players changed.
* @param game the game that changed.
*/
public void gamePlayersChanged(Game game) {
sendAll(gamesMessage());
}
/**
* A game ended.
* @param game the game that changed.
*/
public void gameEnded(Game game) {
sendAll(gamesMessage());
}
/**
* A game terminated.
* @param game the game that changed.
*/
public synchronized void gameTerminated(Game game) {
this.games.remove(game);
sendAll(gamesMessage());
}
}