package pdp.scrabble.multiplayer.impl;
import java.rmi.AlreadyBoundException;
import java.rmi.AccessException;
import java.rmi.NotBoundException;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.server.UnicastRemoteObject;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import pdp.scrabble.game.Bag;
import pdp.scrabble.game.Letter;
import pdp.scrabble.utility.Display;
import pdp.scrabble.multiplayer.Client;
import pdp.scrabble.multiplayer.Server;
import static pdp.scrabble.Factory.FACTORY;
import static pdp.scrabble.Language.getGameLang;
/**
*/
public class ServerImpl extends UnicastRemoteObject implements Server {
private static final long serialVersionUID = 1L;
/** List of all client connected */
private List<Client> clients = new ArrayList<Client>(4);
/** Reference to clients' name */
private HashMap<Client, String> clientsName = new HashMap<Client, String>(1);
/** Reference to clients' id */
private HashMap<Client, Integer> clientsID = new HashMap<Client, Integer>(1);
/** Ready state. */
private int playerReady = 0;
/** Player's id to play */
private int idTurn = 0;
/** Bag reference. */
private Bag bag = null;
/** Openened state. */
private boolean openServer = true;
/** Running state. */
private boolean runningServer = false;
/** Server url. */
private String url = null;
/** Thread check */
private Thread check = null;
/** Thread check state. */
private boolean threadCheck = false;
/** Langue of game. */
private String language = null;
public ServerImpl(int port, String language)
throws RemoteException, AlreadyBoundException {
super();
this.bag = FACTORY.createBag();
this.threadCheck = false;
this.language = language;
this.initialize(port);
}
private void initialize(int port)
throws RemoteException, AlreadyBoundException {
this.url = "rmi://localhost:" + port + "/Scrabble";
try {
LocateRegistry.createRegistry(port);
}
catch (Exception e) {
}
try {
Naming.rebind(this.url, this);
this.threadCheck = true;
this.check = new Thread() {
@Override
public void run() {
setName("Clients alive check");
while (threadCheck) {
Client toRemove = null;
try {
sleep(TIMEOUT);
}
catch (InterruptedException e1) {
//Display.error("Sleep", "Error");
}
for (Client client : clients) {
try {
client.ping();
}
catch (Exception e) {
toRemove = client;
}
}
if (toRemove != null) {
try {
disconnect(clientsID.get(toRemove));
}
catch (RemoteException ex) {
Display.error("Disconnect", "Error RMI");
}
}
}
}
};
this.check.start();
}
catch (MalformedURLException ex) {
Display.error("Server", "Bad URL !");
this.url = null;
}
}
@Override
public Client connect(String nick, Client client) throws RemoteException {
this.clients.add(client);
this.clientsName.put(client, nick);
this.clientsID.put(client, clientsID.size());
if (this.clients.size() >= 4) {
this.openServer = false;
}
this.sendMessage("<Game>", this.clientsName.get(client) + " "
+ getGameLang("connected."));
this.updatePlayerTable();
this.updateButton();
this.playerReady = 0;
return client;
}
@Override
public void disconnect(int id) throws RemoteException {
Client client = getClientById(id);
if (!this.runningServer) {
this.clients.remove(client);
this.sendMessage(
"<Game>", this.clientsName.get(client) + " "
+ getGameLang("disconnected."));
this.clientsID.remove(client);
this.removePlayerTable(id);
this.updateButton();
this.playerReady = 0;
this.openServer = true;
}
else {
this.clients.remove(client);
this.sendMessage(
"<Game>", this.clientsName.get(client) + " "
+ getGameLang("disconnected."));
this.setPlayertable(id, null, getGameLang("Disconnect"), -1);
}
}
@Override
public void endServer()
throws RemoteException, MalformedURLException, AccessException {
this.sendMessage(
"<Game>", "Server is closed,"
+ " Wait if server will come back or exit the Game");
this.setPlayertable(0, null, getGameLang("Disconnect"), -1);
UnicastRemoteObject.unexportObject(this, false);
this.clients.clear();
this.clientsID.clear();
this.clientsName.clear();
try {
Naming.unbind(this.url);
this.url = null;
}
catch (NotBoundException ex) {
Display.error("Server", "Error Url Server");
}
this.threadCheck = false;
}
@Override
public void endTurnMulti() throws RemoteException {
int idSrc = this.idTurn;
if (this.idTurn + 1 < this.clients.size()) {
this.idTurn++;
}
else {
this.idTurn = 0;
}
// Need to check if client disconnected while playing
if (!this.clientsID.containsValue(this.idTurn)) {
if (this.idTurn + 1 < this.clients.size()) {
this.idTurn++;
}
else {
this.idTurn = 0;
}
}
Client client = getClientById(this.idTurn);
String name = this.clientsName.get(client);
client.getTurn(name);
this.setPlayertable(idSrc, null, getGameLang("Thinking"), -1);
this.setPlayertable(this.idTurn, null, getGameLang("Playing"), -1);
this.sendMessage("<Game>", client.getName() + " " + getGameLang("turn."));
}
@Override
public void sendReadyMulti(int id) throws RemoteException {
this.playerReady++;
if (this.playerReady == this.clients.size()) {
this.openServer = false;
this.runningServer = true;
this.bag.initWithLanguage(this.language);
this.bag.fill();
//we put all client in state ready
for (Client client : this.clients) {
client.launchGameMulti();
}
}
}
@Override
public void sendMessage(String src, String msg) throws RemoteException {
for (Client client : this.clients) {
if (client != null) {
client.getMessage(src, msg);
}
}
}
@Override
public void sendLetter(Client client, Letter letter, int v, int h)
throws RemoteException {
for (Client cl : clients) {
if (client != cl) {
cl.getLetter(letter, v, h);
}
else {
//local client already have letters on his board
}
}
}
@Override
public Letter pickLetter() throws RemoteException {
return this.bag.getRandomLetter();
}
/** Return client with the player's id ID.
* @param ID player id.
* @return client corresponding to id.
*/
private Client getClientById(int ID) {
Client client = null;
for (Client cl : clients) {
if (clientsID.get(cl) == ID) {
client = cl;
}
}
return client;
}
@Override
public boolean isOpen() throws RemoteException {
return this.openServer;
}
@Override
public boolean existName(String name) throws RemoteException {
return this.clientsName.containsValue(name);
}
@Override
public int getWordValueServer(String str) throws RemoteException {
return this.bag.getWordValue(str);
}
@Override
public int getSizePlayers() throws RemoteException {
return this.clients.size();
}
@Override
public Letter exchangeLetter(Letter letter) throws RemoteException {
this.bag.addLetter(letter);
return this.bag.getRandomLetter();
}
@Override
public void sendStats(int turn, String name, int points, String words)
throws RemoteException {
for (Client client : this.clients) {
client.getStats(turn, name, points, words);
}
}
private void updatePlayerTable() throws RemoteException {
for (Client client : this.clients) {
client.clearTable();
for (Client user : this.clients) {
client.getAddPlayerTable(this.clientsName.get(user), getGameLang("Wait"), 0);
}
}
}
private void removePlayerTable(int id) throws RemoteException {
for (Client client : this.clients) {
client.getRemovePlayerTable(id);
}
}
@Override
public void setPlayertable(int id, String name, String status, int score)
throws RemoteException {
for (Client client : this.clients) {
if (client != null) {
client.getSetPlayerTable(id, name, status, score);
}
}
}
private void showDialog(String head, String message) throws RemoteException {
for (Client client : this.clients) {
client.showDialog(head, message);
}
}
@Override
public boolean ping() throws RemoteException {
return true;
}
private void updateButton() throws RemoteException {
for (Client client : this.clients) {
client.updateButton();
for (int i = 0; i < clients.size(); i++) {
client.getSetPlayerTable(i, null, getGameLang("Wait"), -1);
}
}
}
@Override
public String getLanguage() throws RemoteException{
return this.language;
}
}