/*******************************************************************************
* Copyright (c) 2015, 2016
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*******************************************************************************/
package jsettlers.main;
import java.util.LinkedList;
import java.util.List;
import jsettlers.common.ai.EPlayerType;
import jsettlers.common.menu.ENetworkMessage;
import jsettlers.common.menu.IChatMessageListener;
import jsettlers.common.menu.IJoinPhaseMultiplayerGameConnector;
import jsettlers.common.menu.IJoiningGame;
import jsettlers.common.menu.IJoiningGameListener;
import jsettlers.common.menu.IMapDefinition;
import jsettlers.common.menu.IMultiplayerListener;
import jsettlers.common.menu.IMultiplayerPlayer;
import jsettlers.common.menu.IOpenMultiplayerGameInfo;
import jsettlers.common.player.ECivilisation;
import jsettlers.common.utils.collections.ChangingList;
import jsettlers.logic.map.loading.MapLoader;
import jsettlers.logic.map.loading.list.MapList;
import jsettlers.logic.player.PlayerSetting;
import jsettlers.main.datatypes.MultiplayerPlayer;
import jsettlers.network.NetworkConstants;
import jsettlers.network.client.interfaces.INetworkClient;
import jsettlers.network.client.receiver.IPacketReceiver;
import jsettlers.network.common.packets.ChatMessagePacket;
import jsettlers.network.common.packets.MapInfoPacket;
import jsettlers.network.common.packets.MatchInfoUpdatePacket;
import jsettlers.network.common.packets.MatchStartPacket;
import jsettlers.network.common.packets.PlayerInfoPacket;
import jsettlers.network.infrastructure.channel.reject.RejectPacket;
import jsettlers.network.server.match.EPlayerState;
/**
*
* @author Andreas Eberle
*
*/
public class MultiplayerGame {
private final AsyncNetworkClientConnector networkClientFactory;
private final ChangingList<IMultiplayerPlayer> playersList = new ChangingList<IMultiplayerPlayer>();
private INetworkClient networkClient;
private IJoiningGameListener joiningGameListener;
private IMultiplayerListener multiplayerListener;
private IChatMessageListener chatMessageListener;
private boolean iAmTheHost = false;
public MultiplayerGame(AsyncNetworkClientConnector networkClientFactory) {
this.networkClientFactory = networkClientFactory;
}
public IJoiningGame join(final String matchId) {
new Thread("joinGameThread") {
@Override
public void run() {
networkClient = networkClientFactory.getNetworkClient();
networkClient.joinMatch(matchId, generateMatchStartedListener(), generateMatchInfoUpdatedListener(), generateChatMessageReceiver());
}
}.start();
return generateJoiningGame();
}
public IJoiningGame openNewGame(final IOpenMultiplayerGameInfo gameInfo) {
iAmTheHost = true;
new Thread("openNewGameThread") {
@Override
public void run() {
networkClient = networkClientFactory.getNetworkClient();
IMapDefinition mapDefintion = gameInfo.getMapDefinition();
MapInfoPacket mapInfo = new MapInfoPacket(mapDefintion.getMapId(), mapDefintion.getMapName(), "", "", mapDefintion.getMaxPlayers());
networkClient.openNewMatch(gameInfo.getMatchName(), gameInfo.getMaxPlayers(), mapInfo, 4711L, generateMatchStartedListener(),
generateMatchInfoUpdatedListener(), generateChatMessageReceiver());
}
}.start();
return generateJoiningGame();
}
private IJoiningGame generateJoiningGame() {
return new IJoiningGame() {
@Override
public void setListener(IJoiningGameListener joiningGameListener) {
MultiplayerGame.this.joiningGameListener = joiningGameListener;
if (joiningGameListener != null && networkClient != null && networkClient.getState() == EPlayerState.IN_MATCH) {
joiningGameListener.gameJoined(generateJoinPhaseGameConnector());
}
}
@Override
public void abort() {
networkClient.leaveMatch();
}
};
}
private IPacketReceiver<ChatMessagePacket> generateChatMessageReceiver() {
return new IPacketReceiver<ChatMessagePacket>() {
@Override
public void receivePacket(ChatMessagePacket packet) {
if (chatMessageListener != null) {
chatMessageListener.chatMessageReceived(packet.getAuthorId(), packet.getMessage());
}
}
};
}
private IPacketReceiver<MatchStartPacket> generateMatchStartedListener() {
return new IPacketReceiver<MatchStartPacket>() {
@Override
public void receivePacket(MatchStartPacket packet) {
updatePlayersList(packet.getMatchInfo().getPlayers());
MapLoader mapLoader = MapList.getDefaultList().getMapById(packet.getMatchInfo().getMapInfo().getId());
long randomSeed = packet.getRandomSeed();
boolean[] availablePlayers = new boolean[mapLoader.getMaxPlayers()];
byte ownPlayerId = calculatePlayerInfos(availablePlayers);
PlayerSetting[] playerSettings = determinePlayerSettings(availablePlayers);
JSettlersGame game = new JSettlersGame(mapLoader, randomSeed, networkClient.getNetworkConnector(), ownPlayerId, playerSettings);
multiplayerListener.gameIsStarting(game.start());
}
};
}
private PlayerSetting[] determinePlayerSettings(boolean[] availablePlayers) {
PlayerSetting[] playerSettings = new PlayerSetting[availablePlayers.length];
byte i = 0;
for (; i < playersList.getItems().size(); i++) {
playerSettings[i] = new PlayerSetting(i);
}
EPlayerType aiType = iAmTheHost ? EPlayerType.AI_VERY_HARD : EPlayerType.HUMAN;
for (; i < availablePlayers.length; i++) {
playerSettings[i] = new PlayerSetting(aiType, ECivilisation.ROMAN, i);
}
return playerSettings;
}
byte calculatePlayerInfos(boolean[] availablePlayers) {
String myId = networkClient.getPlayerInfo().getId();
byte i = 0;
byte ownPlayerId = -1;
for (IMultiplayerPlayer currPlayer : playersList.getItems()) {
availablePlayers[i] = true;
if (currPlayer.getId().equals(myId)) {
ownPlayerId = i;
}
i++;
}
for (byte ii = i; ii < availablePlayers.length; ii++) {
availablePlayers[ii] = true;
}
if (ownPlayerId < 0) {
throw new RuntimeException("Wasn't able to find my id!");
} else {
return ownPlayerId;
}
}
private IPacketReceiver<MatchInfoUpdatePacket> generateMatchInfoUpdatedListener() {
return new IPacketReceiver<MatchInfoUpdatePacket>() {
@Override
public void receivePacket(MatchInfoUpdatePacket packet) {
if (joiningGameListener != null) {
joiningGameListener.gameJoined(generateJoinPhaseGameConnector());
joiningGameListener = null;
}
updatePlayersList(packet.getMatchInfo().getPlayers());
receiveSystemMessage(new MultiplayerPlayer(packet.getUpdatedPlayer()), getNetworkMessageById(packet.getUpdateReason()));
}
};
}
void updatePlayersList(PlayerInfoPacket[] playerInfoPackets) {
List<IMultiplayerPlayer> players = new LinkedList<IMultiplayerPlayer>();
for (PlayerInfoPacket playerInfoPacket : playerInfoPackets) {
players.add(new MultiplayerPlayer(playerInfoPacket));
}
playersList.setList(players);
}
private ENetworkMessage getNetworkMessageById(NetworkConstants.ENetworkMessage errorMessageId) {
switch (errorMessageId) {
case INVALID_STATE_ERROR:
return ENetworkMessage.INVALID_STATE_ERROR;
case NO_LISTENER_FOUND:
return ENetworkMessage.UNKNOWN_ERROR;
case NOT_ALL_PLAYERS_READY:
return ENetworkMessage.NOT_ALL_PLAYERS_READY;
case PLAYER_JOINED:
return ENetworkMessage.PLAYER_JOINED;
case PLAYER_LEFT:
return ENetworkMessage.PLAYER_LEFT;
case UNAUTHORIZED:
return ENetworkMessage.UNAUTHORIZED;
case READY_STATE_CHANGED:
return ENetworkMessage.READY_STATE_CHANGED;
case UNKNOWN_ERROR:
default:
return ENetworkMessage.UNKNOWN_ERROR;
}
}
void receiveSystemMessage(IMultiplayerPlayer author, ENetworkMessage networkMessage) {
if (chatMessageListener != null) {
chatMessageListener.systemMessageReceived(author, networkMessage);
}
}
private IJoinPhaseMultiplayerGameConnector generateJoinPhaseGameConnector() {
networkClient.registerRejectReceiver(new IPacketReceiver<RejectPacket>() {
@Override
public void receivePacket(RejectPacket packet) {
receiveSystemMessage(null, getNetworkMessageById(packet.getErrorMessageId()));
System.out.println("Received reject packet: rejectedKey: " + packet.getRejectedKey() + " messageid: " + packet.getErrorMessageId());
}
});
return new IJoinPhaseMultiplayerGameConnector() {
@Override
public void startGame() {
networkClient.startMatch();
}
@Override
public void setReady(boolean ready) {
networkClient.setReadyState(ready);
}
@Override
public void setMultiplayerListener(IMultiplayerListener multiplayerListener) {
MultiplayerGame.this.multiplayerListener = multiplayerListener;
}
@Override
public ChangingList<IMultiplayerPlayer> getPlayers() {
return playersList;
}
@Override
public void abort() {
networkClient.leaveMatch();
}
@Override
public void setChatListener(IChatMessageListener chatMessageListener) {
MultiplayerGame.this.chatMessageListener = chatMessageListener;
}
@Override
public void sendChatMessage(String chatMessage) {
networkClient.sendChatMessage(chatMessage);
}
};
}
}