package com.esir.sr.sweetsnake.server;
import java.rmi.RemoteException;
import java.util.LinkedList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.esir.sr.sweetsnake.api.IClientCallback;
import com.esir.sr.sweetsnake.api.IServer;
import com.esir.sr.sweetsnake.api.IServerForAdmin;
import com.esir.sr.sweetsnake.api.IGuiForServer;
import com.esir.sr.sweetsnake.dto.GameRequestDTO;
import com.esir.sr.sweetsnake.dto.GameSessionDTO;
import com.esir.sr.sweetsnake.dto.PlayerDTO;
import com.esir.sr.sweetsnake.enumeration.PlayerStatus;
import com.esir.sr.sweetsnake.exception.GameRequestNotFoundException;
import com.esir.sr.sweetsnake.exception.GameSessionNotFoundException;
import com.esir.sr.sweetsnake.exception.MaximumNumberOfPlayersException;
import com.esir.sr.sweetsnake.exception.PlayerNotAvailableException;
import com.esir.sr.sweetsnake.exception.PlayerNotFoundException;
import com.esir.sr.sweetsnake.exception.UnableToConnectException;
import com.esir.sr.sweetsnake.exception.UnauthorizedActionException;
import com.esir.sr.sweetsnake.factory.DtoConverterFactory;
import com.esir.sr.sweetsnake.provider.BeanProvider;
import com.esir.sr.sweetsnake.registry.GameRequestsRegistry;
import com.esir.sr.sweetsnake.registry.GameSessionsRegistry;
import com.esir.sr.sweetsnake.registry.PlayersRegistry;
import com.esir.sr.sweetsnake.session.GameRequest;
import com.esir.sr.sweetsnake.session.GameSession;
import com.esir.sr.sweetsnake.session.Player;
/**
*
* @author Herminaƫl Rougier
* @author Damien Jouanno
*
*/
@Component
public class Server implements IServer, IServerForAdmin
{
/**********************************************************************************************
* [BLOCK] STATIC FIELDS
**********************************************************************************************/
/** The logger */
private static final Logger log = LoggerFactory.getLogger(Server.class);
/**********************************************************************************************
* [BLOCK] FIELDS
**********************************************************************************************/
/** The bean provider */
@Autowired
private BeanProvider beanProvider;
/** The players registry */
@Autowired
private PlayersRegistry playersRegistry;
/** The requests registry */
@Autowired
private GameRequestsRegistry requestsRegistry;
/** The sessions registry */
@Autowired
private GameSessionsRegistry sessionsRegistry;
/** The server GUI */
@Autowired
private IGuiForServer gui;
/**********************************************************************************************
* [BLOCK] CONSTRUCTOR & INIT
**********************************************************************************************/
/**
*
*/
protected Server() {
super();
}
/**
*
*/
@PostConstruct
protected void init() {
log.info("Initializating the Server");
new Timer().schedule(new TimerTask() {
@Override
public void run() {
gui.serverStarted();
}
}, 200);
}
/**
*
*/
@PreDestroy
protected void destroy() {
log.info("Destroying the Server");
for (final String playerId : playersRegistry.getPlayersName()) {
try {
disconnect(playersRegistry.get(playerId).getCallback());
} catch (final PlayerNotFoundException e) {
log.error(e.getMessage(), e);
}
}
}
/**********************************************************************************************
* [BLOCK] PRIVATE METHODS
**********************************************************************************************/
/**
*
* @param client
* @return
*/
private String retrieveClientName(final IClientCallback client) {
try {
return client.getName().trim();
} catch (final RemoteException e) {
log.error(e.getMessage(), e);
}
return new String();
}
/**
*
* @param name
* @return
*/
private boolean validClientName(final String name) {
return name.matches("^\\w+$");
}
/**
*
* @param client
* @return
*/
private List<PlayerDTO> getPlayersList(final IClientCallback client) {
final String clientName = client == null ? "" : retrieveClientName(client);
final List<PlayerDTO> playersList = new LinkedList<PlayerDTO>();
for (final String player : playersRegistry.getPlayersName()) {
if (!clientName.equals(player)) {
try {
final PlayerDTO playerDTO = DtoConverterFactory.convertPlayer(playersRegistry.get(player));
playersList.add(playerDTO);
} catch (final PlayerNotFoundException e) {
log.error(e.getMessage(), e);
}
}
}
return playersList;
}
/**
*
* @return
*/
private List<GameRequestDTO> getRequestsList() {
final List<GameRequestDTO> requestsList = new LinkedList<GameRequestDTO>();
for (final String id : requestsRegistry.getRequestsId()) {
try {
final GameRequestDTO requestDto = DtoConverterFactory.convertGameRequest(requestsRegistry.get(id));
requestsList.add(requestDto);
} catch (final GameRequestNotFoundException e) {
log.error(e.getMessage(), e);
}
}
return requestsList;
}
/**
*
* @return
*/
private List<GameSessionDTO> getSessionsList() {
final List<GameSessionDTO> sessionsList = new LinkedList<GameSessionDTO>();
for (final String id : sessionsRegistry.getSessionsId()) {
try {
final GameSessionDTO sessionDto = DtoConverterFactory.convertGameSession(sessionsRegistry.get(id));
sessionsList.add(sessionDto);
} catch (final GameSessionNotFoundException e) {
log.error(e.getMessage(), e);
}
}
return sessionsList;
}
/**********************************************************************************************
* [BLOCK] PUBLIC SERVER IMPLEMENTED METHODS
**********************************************************************************************/
/*
* (non-Javadoc)
*
* @see com.esir.sr.sweetsnake.api.IServer#connect(com.esir.sr.sweetsnake.api.IClientCallback)
*/
@Override
public void connect(final IClientCallback client) throws UnableToConnectException {
final String clientName = retrieveClientName(client);
// bad username
if (clientName == null || !validClientName(clientName)) {
log.warn("Invalid username {}, must be alphanumeric only", clientName);
throw new UnableToConnectException("invalid username, must be alphanumeric only");
}
// username already taken
if (playersRegistry.contains(clientName)) {
log.warn("Username {} is already taken", clientName);
throw new UnableToConnectException("username " + clientName + " is already taken");
}
// creating player
final Player player = beanProvider.getPrototype(Player.class, client);
playersRegistry.add(player);
try {
client.connected();
} catch (final RemoteException e) {
log.error(e.getMessage(), e);
}
sendRefreshPlayersList();
sendRefreshSessionsList();
log.info("New client with username {} has connected", clientName);
}
/*
* (non-Javadoc)
*
* @see com.esir.sr.sweetsnake.api.IServer#disconnect(com.esir.sr.sweetsnake.api.IClientCallback)
*/
@Override
public void disconnect(final IClientCallback client) {
try {
final String clientName = retrieveClientName(client);
final Player player = playersRegistry.get(clientName);
// cancel sent requests
for (final String requestId : player.getSentRequestsIds()) {
final GameRequest request = requestsRegistry.get(requestId);
request.cancel();
requestsRegistry.remove(requestId);
}
// deny received requests
if (player.getReceivedRequestId() != null) {
final GameRequest request = requestsRegistry.get(player.getReceivedRequestId());
request.deny();
requestsRegistry.remove(request.getId());
}
// leave current session
if (player.getGameSessionId() != null) {
sessionsRegistry.get(player.getGameSessionId()).leaveGame(client, true);
}
// removing player from registry
playersRegistry.remove(clientName);
client.disconnected();
sendRefreshPlayersList();
sendRefreshSessionsList();
sendRefreshRequestsList();
log.info("Player {} has disconnected", clientName);
} catch (final PlayerNotFoundException | GameRequestNotFoundException | GameSessionNotFoundException | RemoteException e) {
log.error(e.getMessage(), e);
}
}
/*
* (non-Javadoc)
*
* @see com.esir.sr.sweetsnake.api.IServer#sendRequest(com.esir.sr.sweetsnake.api.IClientCallback,
* com.esir.sr.sweetsnake.dto.PlayerDTO)
*/
@Override
public void sendRequest(final IClientCallback client, final PlayerDTO playerDto) throws PlayerNotFoundException, PlayerNotAvailableException, GameSessionNotFoundException {
final Player player1 = playersRegistry.get(retrieveClientName(client)), player2 = playersRegistry.get(playerDto.getName());
// player is not available to play
if (player2.getStatus() != PlayerStatus.AVAILABLE) {
throw new PlayerNotAvailableException("player is not available");
}
final GameSession gameSession = sessionsRegistry.get(player1.getGameSessionId());
gameSession.addFictivePlayer(player2);
// creating request
final GameRequest request = beanProvider.getPrototype(GameRequest.class, gameSession.getId(), player1, player2);
requestsRegistry.add(request);
sendRefreshPlayersList();
sendRefreshSessionsList();
sendRefreshRequestsList();
log.info("Game request between {} and {} is pending", player1.getName(), player2.getName());
}
/*
* (non-Javadoc)
*
* @see com.esir.sr.sweetsnake.api.IServer#acceptRequest(com.esir.sr.sweetsnake.api.IClientCallback,
* com.esir.sr.sweetsnake.dto.GameRequestDTO)
*/
@Override
public void acceptRequest(final IClientCallback client, final GameRequestDTO requestDto) throws GameRequestNotFoundException, GameSessionNotFoundException, MaximumNumberOfPlayersException {
final GameRequest request = requestsRegistry.get(requestDto.getId());
// request no more available
if (!request.getRequestedPlayer().getName().equals(retrieveClientName(client))) {
log.warn("No matching request with id {}", request.getId());
throw new GameRequestNotFoundException("no matching request");
}
// removing request
requestsRegistry.remove(requestDto.getId());
// retrieving session & player
final GameSession gameSession = sessionsRegistry.get(requestDto.getSessionId());
final Player requestedPlayer = request.getRequestedPlayer();
gameSession.addPlayer(requestedPlayer);
sendRefreshPlayersList();
sendRefreshSessionsList();
sendRefreshRequestsList();
log.info("Player {} joined the session {}", requestedPlayer.getName(), gameSession.getId());
}
/*
* (non-Javadoc)
*
* @see com.esir.sr.sweetsnake.api.IServer#denyRequest(com.esir.sr.sweetsnake.api.IClientCallback,
* com.esir.sr.sweetsnake.dto.GameRequestDTO)
*/
@Override
public void denyRequest(final IClientCallback client, final GameRequestDTO requestDto) throws GameRequestNotFoundException {
final GameRequest request = requestsRegistry.get(requestDto.getId());
final Player requestedPlayer = request.getRequestedPlayer();
// request no more available
if (!requestedPlayer.getName().equals(retrieveClientName(client))) {
log.warn("No matching request with id {}", request.getId());
throw new GameRequestNotFoundException("no matching request");
}
// deny session and check session status
if (sessionsRegistry.contains(request.getSessionid())) {
try {
final GameSession gameSession = sessionsRegistry.get(request.getSessionid());
gameSession.denied(requestedPlayer);
} catch (final GameSessionNotFoundException e) {
log.error(e.getMessage(), e);
}
}
// deny and remove request
request.deny();
requestsRegistry.remove(requestDto.getId());
sendRefreshPlayersList();
sendRefreshSessionsList();
sendRefreshRequestsList();
log.info("Request {} denied by {}", request.getId(), request.getRequestedPlayer().getName());
}
/*
* (non-Javadoc)
*
* @see com.esir.sr.sweetsnake.api.IServer#cancelRequest(com.esir.sr.sweetsnake.api.IClientCallback,
* com.esir.sr.sweetsnake.dto.GameRequestDTO)
*/
@Override
public void cancelRequest(final IClientCallback client, final GameRequestDTO requestDTO) throws GameRequestNotFoundException {
final GameRequest request = requestsRegistry.get(requestDTO.getId());
// cancel and remove the request
request.cancel();
requestsRegistry.remove(request.getId());
sendRefreshPlayersList();
sendRefreshSessionsList();
sendRefreshRequestsList();
log.info("Request {} has been canceled by {}", request.getId(), request.getRequestingPlayer().getName());
}
/*
* (non-Javadoc)
*
* @see com.esir.sr.sweetsnake.api.IServer#createSession(com.esir.sr.sweetsnake.api.IClientCallback)
*/
@Override
public void createSession(final IClientCallback client) throws UnauthorizedActionException {
try {
final Player player = playersRegistry.get(retrieveClientName(client));
if (player.getGameSessionId() != null) {
log.warn("Player {} tried to create a game session while already in another one", player.getName());
throw new UnauthorizedActionException("you already are in a game session");
}
final GameSession gameSession = beanProvider.getPrototype(GameSession.class);
try {
gameSession.addPlayer(player);
} catch (final MaximumNumberOfPlayersException e) {
log.error(e.getMessage(), e);
}
sessionsRegistry.add(gameSession);
sendRefreshPlayersList();
sendRefreshSessionsList();
log.info("Game session {} has been created by {}", gameSession.getId(), player.getName());
} catch (final PlayerNotFoundException e) {
log.error(e.getMessage(), e);
}
}
/*
* (non-Javadoc)
*
* @see com.esir.sr.sweetsnake.api.IServer#joinSession(com.esir.sr.sweetsnake.api.IClientCallback,
* com.esir.sr.sweetsnake.dto.GameSessionDTO)
*/
@Override
public void joinSession(final IClientCallback client, final GameSessionDTO sessionDTO) throws GameSessionNotFoundException, MaximumNumberOfPlayersException {
final GameSession session = sessionsRegistry.get(sessionDTO.getId());
try {
final Player player = playersRegistry.get(retrieveClientName(client));
session.addPlayer(player);
sendRefreshPlayersList();
sendRefreshSessionsList();
log.info("Session {} has been joined by {}", session.getId(), player.getName());
} catch (final PlayerNotFoundException e) {
log.error(e.getMessage(), e);
}
}
/**********************************************************************************************
* [BLOCK] PUBLIC SERVER ADMIN IMPLEMENTED METHODS
**********************************************************************************************/
/*
* (non-Javadoc)
*
* @see com.esir.sr.sweetsnake.api.IServerAdmin#startServer()
*/
@Override
public void startServer() {
// TODO Auto-generated method stub
}
/*
* (non-Javadoc)
*
* @see com.esir.sr.sweetsnake.api.IServerAdmin#stopServer()
*/
@Override
public void stopServer() {
// TODO Auto-generated method stub
}
/*
* (non-Javadoc)
*
* @see com.esir.sr.sweetsnake.api.IServerAdmin#kickPlayer(com.esir.sr.sweetsnake.dto.PlayerDTO)
*/
@Override
public void kickPlayer(final PlayerDTO player) {
// TODO Auto-generated method stub
}
/*
* (non-Javadoc)
*
* @see com.esir.sr.sweetsnake.api.IServerAdmin#stopSession(com.esir.sr.sweetsnake.dto.GameSessionDTO)
*/
@Override
public void stopSession(final GameSessionDTO session) {
// TODO Auto-generated method stub
}
/*
* (non-Javadoc)
*
* @see com.esir.sr.sweetsnake.api.IServerAdmin#removeSession(com.esir.sr.sweetsnake.dto.GameSessionDTO)
*/
@Override
public void removeSession(final GameSessionDTO session) {
// TODO Auto-generated method stub
}
/*
* (non-Javadoc)
*
* @see com.esir.sr.sweetsnake.api.IServerAdmin#removeRequest(com.esir.sr.sweetsnake.dto.GameRequestDTO)
*/
@Override
public void removeRequest(final GameRequestDTO request) {
// TODO Auto-generated method stub
}
/**********************************************************************************************
* [BLOCK] PUBLIC METHODS
**********************************************************************************************/
/**
*
*/
public void sendRefreshPlayersList() {
new Timer().schedule(new TimerTask() {
@Override
public void run() {
for (final String playerName : playersRegistry.getPlayersName()) {
try {
final Player player = playersRegistry.get(playerName);
final IClientCallback callback = player.getCallback();
callback.refreshPlayersList(getPlayersList(callback));
} catch (RemoteException | PlayerNotFoundException e) {
log.error(e.getMessage(), e);
}
}
gui.refreshPlayers(getPlayersList(null));
}
}, 100);
}
/**
*
*/
public void sendRefreshSessionsList() {
new Timer().schedule(new TimerTask() {
@Override
public void run() {
final List<GameSessionDTO> sessionsList = getSessionsList();
for (final String playerName : playersRegistry.getPlayersName()) {
try {
final Player player = playersRegistry.get(playerName);
player.getCallback().refreshSessionsList(sessionsList);
} catch (RemoteException | PlayerNotFoundException e) {
log.error(e.getMessage(), e);
}
}
gui.refreshSessions(sessionsList);
}
}, 100);
}
/**
*
*/
public void sendRefreshRequestsList() {
new Timer().schedule(new TimerTask() {
@Override
public void run() {
final List<GameRequestDTO> requestsList = getRequestsList();
gui.refreshRequests(requestsList);
}
}, 100);
}
}