/** * Copyright (C) 2017 Jan Schäfer (jansch@users.sourceforge.net) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jskat.control.iss; import java.util.HashMap; import java.util.List; import java.util.Map; import org.jskat.control.JSkatEventBus; import org.jskat.control.JSkatMaster; import org.jskat.control.command.iss.IssConnectCommand; import org.jskat.control.command.iss.IssDisconnectCommand; import org.jskat.control.command.iss.IssReadyToPlayCommand; import org.jskat.control.command.iss.IssResignCommand; import org.jskat.control.command.iss.IssShowCardsCommand; import org.jskat.control.command.iss.IssTableSeatChangeCommand; import org.jskat.control.command.iss.IssToggleTalkEnabledCommand; import org.jskat.control.command.table.CreateTableCommand; import org.jskat.control.command.table.ShowCardsCommand; import org.jskat.control.event.iss.IssDisconnectedEvent; import org.jskat.control.event.skatgame.GameFinishEvent; import org.jskat.control.event.table.ActivePlayerChangedEvent; import org.jskat.control.event.table.TableGameMoveEvent; import org.jskat.data.GameAnnouncement; import org.jskat.data.JSkatApplicationData; import org.jskat.data.JSkatViewType; import org.jskat.data.SkatGameData; import org.jskat.data.SkatGameData.GameState; import org.jskat.data.Trick; import org.jskat.data.iss.ChatMessage; import org.jskat.data.iss.GameStartInformation; import org.jskat.data.iss.MoveInformation; import org.jskat.data.iss.MovePlayer; import org.jskat.data.iss.MoveType; import org.jskat.data.iss.TablePanelStatus; import org.jskat.gui.JSkatView; import org.jskat.util.Card; import org.jskat.util.JSkatResourceBundle; import org.jskat.util.Player; import org.jskat.util.SkatConstants; import org.jskat.util.rule.SkatRuleFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.eventbus.Subscribe; /** * Controls all ISS related actions */ public class IssController { private static Logger log = LoggerFactory.getLogger(IssController.class); private final JSkatResourceBundle strings = JSkatResourceBundle.INSTANCE; private final JSkatEventBus eventBus = JSkatEventBus.INSTANCE; private final JSkatApplicationData appData = JSkatApplicationData.INSTANCE; private final JSkatMaster jskat; private JSkatView view; private IssConnector issConnector; private String login; private String password; private MessageGenerator issMsg; private OutputChannel issOut; private final Map<String, SkatGameData> gameData; /** * Constructor * * @param jskatMaster * JSkat master */ public IssController(JSkatMaster jskatMaster) { this.jskat = jskatMaster; this.gameData = new HashMap<String, SkatGameData>(); this.eventBus.register(this); } /** * Sets the view (MVC) * * @param newView * View */ public void setView(final JSkatView newView) { this.view = newView; } /** * Disconnects from ISS. * * @param command * ISS disconnect command */ @Subscribe public void disconnectFromIssOn(final IssDisconnectCommand command) { closeConnectionIfOpen(); } /** * Connection to ISS was lost. * * @param event * ISS disconnected event */ @Subscribe public void closeConnectionOn(final IssDisconnectedEvent event) { closeConnectionIfOpen(); } private void closeConnectionIfOpen() { if (this.issConnector != null && this.issConnector.isConnected()) { log.debug("connection to ISS still open"); //$NON-NLS-1$ this.issConnector.closeConnection(); } } /** * Shows the login panel for ISS */ public void showISSLoginPanel() { this.view.showISSLogin(); } @Subscribe public void establishConnectionOn(final IssConnectCommand command) { log.debug("connectToISS"); //$NON-NLS-1$ if (this.issConnector == null) { this.issConnector = new StreamConnector(); // issConnector = new WebSocketConnector(); } log.debug("connector created"); //$NON-NLS-1$ this.login = command.loginCredentials.getLoginName(); this.password = command.loginCredentials.getPassword(); if (this.issConnector != null && !this.issConnector.isConnected()) { this.issConnector.setConnectionData(this.login, this.password); final boolean isConnected = this.issConnector .establishConnection(this); if (isConnected) { log.debug("Connection to ISS established: " + this.issConnector.isConnected()); //$NON-NLS-1$ this.issMsg = new MessageGenerator(this.login); this.issOut = this.issConnector.getOutputChannel(); sendToIss(this.login); // sendToIss(issMsg.getLoginAndPasswordMessage(password)); } } } private void sendToIss(final String message) { this.issOut.sendMessage(message); } /** * Updates ISS player list * * @param playerName * Player name * @param language * Language * @param gamesPlayed * Games played * @param strength * Play strength */ public void updateISSPlayerList(final String playerName, final String language, final long gamesPlayed, final double strength) { this.jskat.updateISSPlayer(playerName, language, gamesPlayed, strength); } /** * Removes a player from the ISS player list * * @param playerName * Player name */ public void removeISSPlayerFromList(final String playerName) { this.jskat.removeISSPlayer(playerName); } /** * Updates ISS table list * * @param tableName * Table name * @param maxPlayers * Maximum number of players * @param gamesPlayed * Games played * @param player1 * Player 1 (? for free seat) * @param player2 * Player 2 (? for free seat) * @param player3 * Player 3 (? for free seat) */ public void updateISSTableList(final String tableName, final int maxPlayers, final long gamesPlayed, final String player1, final String player2, final String player3) { this.view.updateISSLobbyTableList(tableName, maxPlayers, gamesPlayed, player1, player2, player3); } /** * Removes a table from the ISS table list * * @param tableName * Table name */ public void removeISSTableFromList(final String tableName) { this.view.removeFromISSLobbyTableList(tableName); } /** * Sends the password to the ISS<br> * only used until protocol version 14 */ @Deprecated public void sendPassword() { sendToIss(this.password); } /** * Sends a chat message to the ISS * * @param message * Chat message */ public void sendChatMessage(final ChatMessage message) { sendToIss(this.issMsg.getChatMessage(message)); } /** * Adds a chat message to a chat * * @param messageType * Chat message type * @param params * Chat message */ public void addChatMessage(final ChatMessageType messageType, final List<String> params) { switch (messageType) { case LOBBY: addLobbyChatMessage(params); break; case TABLE: addTableChatMessage(params); break; case USER: // TODO implement it break; } } void addLobbyChatMessage(final List<String> params) { log.debug("addLobbyChatMessage"); //$NON-NLS-1$ final StringBuffer message = new StringBuffer(); // first the sender of the message message.append(params.get(0)).append(": "); //$NON-NLS-1$ // then the text for (int i = 1; i < params.size(); i++) { message.append(params.get(i)).append(' '); } final ChatMessage chatMessage = new ChatMessage("Lobby", //$NON-NLS-1$ message.toString()); this.view.appendISSChatMessage(ChatMessageType.LOBBY, chatMessage); } void addTableChatMessage(final List<String> params) { log.debug("addTableChatMessage"); // first the table for the message final String tableName = params.get(0); final StringBuffer message = new StringBuffer(); // second the sender of the message message.append(params.get(1)).append(": "); //$NON-NLS-1$ // then the text for (int i = 2; i < params.size(); i++) { message.append(params.get(i)).append(' '); } final ChatMessage chatMessage = new ChatMessage(tableName, message.toString()); this.view.appendISSChatMessage(ChatMessageType.TABLE, chatMessage); } /** * Requests the creation of a new table on the ISS */ public void requestTableCreation() { sendToIss(MessageGenerator.getTableCreationMessage()); } /** * Creates a local representation of an ISS table * * @param tableName * Table name * @param creator * Table creator * @param maxPlayers * Maximum number of players */ public void createTable(final String tableName, final String creator, final int maxPlayers) { this.eventBus.post(new CreateTableCommand(JSkatViewType.ISS_TABLE, tableName)); this.jskat.setActiveTable(JSkatViewType.ISS_TABLE, tableName); } /** * Joins a table on ISS * * @param tableName * Table name */ public void joinTable(final String tableName) { sendToIss(MessageGenerator.getJoinTableMessage(tableName)); } /** * Observes a table on ISS * * @param tableName * Table name */ public void observeTable(final String tableName) { sendToIss(MessageGenerator.getObserveTableMessage(tableName)); } /** * Leaves a table on ISS * * @param tableName * Table name */ public void leaveTable(final String tableName) { sendToIss(this.issMsg.getLeaveTableMessage(tableName)); } /** * Updates a local representation of an ISS table * * @param tableName * Table name * @param status * New table status */ public void updateISSTableState(final String tableName, final TablePanelStatus status) { this.view.updateISSTable(tableName, status); } /** * Updates a local representation of an ISS table * * @param tableName * Table name * @param status * New game status */ public void updateISSGame(final String tableName, final GameStartInformation status) { this.view.updateISSTable(tableName, this.appData.getIssLoginName(), status); this.gameData.put(tableName, createSkatGameData(status)); } private SkatGameData createSkatGameData(final GameStartInformation status) { final SkatGameData result = new SkatGameData(); result.setGameState(GameState.GAME_START); for (final Player player : Player.values()) { result.setPlayerName(player, status.getPlayerName(player)); } return result; } /** * Starts a game on a local representation of an ISS table * * @param tableName * Table name */ public void startGame(final String tableName) { this.view.startGame(tableName); } /** * Updates a move on a local representation of an ISS table * * @param tableName * Table name * @param moveInformation * Move information */ public void updateMove(final String tableName, final MoveInformation moveInformation) { final SkatGameData currGame = this.gameData.get(tableName); updateGameData(currGame, moveInformation); this.view.updateISSMove(tableName, currGame, moveInformation); // TODO (jan 19.11.2010) extract this into separate methods if (MoveType.DEAL.equals(moveInformation.getType())) { JSkatEventBus.INSTANCE.post(new ActivePlayerChangedEvent(tableName, Player.MIDDLEHAND)); } else if (MoveType.BID.equals(moveInformation.getType()) || MoveType.HOLD_BID.equals(moveInformation.getType()) || MoveType.PASS.equals(moveInformation.getType())) { if (MoveType.BID.equals(moveInformation.getType())) { if (MovePlayer.MIDDLEHAND.equals(moveInformation .getMovePlayer())) { JSkatEventBus.INSTANCE.post(new ActivePlayerChangedEvent( tableName, Player.FOREHAND)); } else if (MovePlayer.REARHAND.equals(moveInformation .getMovePlayer())) { if (!currGame.isPlayerPass(Player.FOREHAND)) { JSkatEventBus.INSTANCE .post(new ActivePlayerChangedEvent(tableName, Player.FOREHAND)); } else { JSkatEventBus.INSTANCE .post(new ActivePlayerChangedEvent(tableName, Player.MIDDLEHAND)); } } } else if (MoveType.HOLD_BID.equals(moveInformation.getType())) { if (MovePlayer.FOREHAND.equals(moveInformation.getMovePlayer())) { if (!currGame.isPlayerPass(Player.MIDDLEHAND)) { JSkatEventBus.INSTANCE .post(new ActivePlayerChangedEvent(tableName, Player.MIDDLEHAND)); } else { JSkatEventBus.INSTANCE .post(new ActivePlayerChangedEvent(tableName, Player.REARHAND)); } } else if (MovePlayer.MIDDLEHAND.equals(moveInformation .getMovePlayer())) { JSkatEventBus.INSTANCE.post(new ActivePlayerChangedEvent( tableName, Player.REARHAND)); } } else if (MoveType.PASS.equals(moveInformation.getType())) { if (MovePlayer.FOREHAND.equals(moveInformation.getMovePlayer()) || MovePlayer.MIDDLEHAND.equals(moveInformation .getMovePlayer())) { JSkatEventBus.INSTANCE.post(new ActivePlayerChangedEvent( tableName, Player.REARHAND)); } else if (MovePlayer.REARHAND.equals(moveInformation .getMovePlayer())) { if (!currGame.isPlayerPass(Player.FOREHAND)) { JSkatEventBus.INSTANCE .post(new ActivePlayerChangedEvent(tableName, Player.FOREHAND)); } else { JSkatEventBus.INSTANCE .post(new ActivePlayerChangedEvent(tableName, Player.MIDDLEHAND)); } } } if (isBiddingFinished(currGame)) { this.view.setDeclarer(tableName, currGame.getDeclarer()); this.view.setGameState(tableName, GameState.PICKING_UP_SKAT); } } else if (MoveType.GAME_ANNOUNCEMENT.equals(moveInformation.getType())) { JSkatEventBus.INSTANCE.post(new ActivePlayerChangedEvent(tableName, Player.FOREHAND)); } else if (MoveType.CARD_PLAY.equals(moveInformation.getType())) { // handle trick playing final Trick trick = currGame.getCurrentTrick(); if (trick.getThirdCard() != null) { final Player trickWinner = SkatRuleFactory.getSkatRules( currGame.getGameType()).calculateTrickWinner( currGame.getGameType(), trick); trick.setTrickWinner(trickWinner); currGame.addTrick(new Trick(currGame.getTricks().size(), trick .getTrickWinner())); JSkatEventBus.INSTANCE.post(new ActivePlayerChangedEvent( tableName, currGame.getCurrentTrick().getForeHand())); } else if (trick.getSecondCard() != null) { JSkatEventBus.INSTANCE.post(new ActivePlayerChangedEvent( tableName, trick.getForeHand().getRightNeighbor())); } else if (trick.getFirstCard() != null) { JSkatEventBus.INSTANCE.post(new ActivePlayerChangedEvent( tableName, trick.getForeHand().getLeftNeighbor())); } } } private boolean isBiddingFinished(final SkatGameData currGame) { boolean result = false; if (currGame.getNumberOfPasses() == 2) { for (final Player currPlayer : Player.values()) { if (!currGame.isPlayerPass(currPlayer)) { if (currGame.getMaxPlayerBid(currPlayer) > 0) { result = true; currGame.setDeclarer(currPlayer); } } } } return result; } private void updateGameData(final SkatGameData currGame, final MoveInformation moveInformation) { final Player movePlayer = moveInformation.getPlayer(); switch (moveInformation.getType()) { case DEAL: currGame.setGameState(GameState.DEALING); break; case BID: currGame.setGameState(GameState.BIDDING); currGame.addPlayerBid(movePlayer, moveInformation.getBidValue()); break; case HOLD_BID: currGame.setGameState(GameState.BIDDING); currGame.addPlayerBid(movePlayer, currGame.getMaxBidValue()); break; case PASS: currGame.setGameState(GameState.BIDDING); currGame.setPlayerPass(movePlayer, true); break; case SKAT_REQUEST: currGame.setGameState(GameState.DISCARDING); break; case PICK_UP_SKAT: currGame.setGameState(GameState.DISCARDING); break; case GAME_ANNOUNCEMENT: currGame.setGameState(GameState.DECLARING); currGame.setAnnouncement(moveInformation.getGameAnnouncement()); currGame.addTrick(new Trick(0, Player.FOREHAND)); break; case CARD_PLAY: currGame.setGameState(GameState.TRICK_PLAYING); currGame.addTrickCard(moveInformation.getCard()); break; case RESIGN: case TIME_OUT: currGame.setGameState(GameState.PRELIMINARY_GAME_END); break; } } /** * Handles end of a game * * @param tableName * Table name * @param newGameData * Game data */ public void endGame(final String tableName, final SkatGameData newGameData) { view.setGameState(tableName, GameState.GAME_OVER); // FIXME: merge event and command eventBus.post(new TableGameMoveEvent(tableName, new GameFinishEvent( newGameData.getGameSummary()))); eventBus.post(new ShowCardsCommand(tableName, newGameData .getCardsAfterDiscard())); gameData.put(tableName, newGameData); } /** * Shows a message from ISS. * * @param message * Message */ public void showMessage(final String message) { this.view.showMessage(this.strings.getString("iss_message"), message); //$NON-NLS-1$ } /** * Shows an error message from ISS. * * @param message * Message */ public void showErrorMessage(final String message) { this.view.showErrorMessage( this.strings.getString("iss_message"), message); //$NON-NLS-1$ } /** * Invites a player on ISS to play at a table on ISS. * * @param tableName * Table name * @param invitee * Invited player */ public void invitePlayer(final String tableName, final String invitee) { sendToIss(this.issMsg.getInvitePlayerMessage(tableName, invitee)); } /** * Sends ready to play signal to ISS * * @param command * ISS ready to play command */ @Subscribe public void sendReadyToPlayOn(final IssReadyToPlayCommand command) { sendToIss(this.issMsg.getReadyMessage(this.appData.getActiveTable())); } /** * Send talk enabled signal to ISS * * @param command * ISS toggle talk enabled command */ @Subscribe public void sendToggleTalkEnabledOn( final IssToggleTalkEnabledCommand command) { sendToIss(this.issMsg.getTalkEnabledMessage(this.appData .getActiveTable())); } /** * Sends a resign signal to ISS * * @param command * ISS resign command */ @Subscribe public void sendResignOn(final IssResignCommand command) { sendToIss(this.issMsg.getResignMessage(this.appData.getActiveTable())); } /** * Sends a show cards signal to ISS * * @param command * ISS show cards command */ @Subscribe public void sendShowCardsOn(final IssShowCardsCommand command) { sendToIss(this.issMsg .getShowCardsMessage(this.appData.getActiveTable())); } /** * Send table seat change singal to ISS * * @param command * ISS table seat change command */ @Subscribe public void sendTableSeatChangeOn(final IssTableSeatChangeCommand command) { sendToIss(this.issMsg.getTableSeatChangeMessage(this.appData .getActiveTable())); } /** * Send pass bid move to ISS * * @param tableName * Table name */ public void sendPassBidMove(final String tableName) { sendToIss(this.issMsg.getPassMoveMessage(tableName)); } /** * Send hold bid move to ISS * * @param tableName * Table name */ public void sendHoldBidMove(final String tableName) { sendToIss(this.issMsg.getHoldBidMoveMessage(tableName)); } /** * Send bid move to ISS * * @param tableName * Table name */ public void sendBidMove(final String tableName) { sendToIss(this.issMsg .getBidMoveMessage( tableName, SkatConstants.getNextBidValue(this.gameData.get( tableName).getMaxBidValue()))); } /** * Send pick up skat move to ISS * * @param tableName * Table name */ public void sendPickUpSkatMove(final String tableName) { sendToIss(this.issMsg.getPickUpSkatMoveMessage(tableName)); } /** * Send game announcement to ISS * * @param tableName * Table name * @param gameAnnouncement * Game announcement */ public void sendGameAnnouncementMove(final String tableName, final GameAnnouncement gameAnnouncement) { sendToIss(this.issMsg.getGameAnnouncementMoveMessage(tableName, gameAnnouncement)); } /** * Send card move to ISS * * @param tableName * Table name * @param nextCard * Card */ public void sendCardMove(final String tableName, final Card nextCard) { sendToIss(this.issMsg.getCardMoveMessage(tableName, nextCard)); } /** * Handle an invitation from another player * * @param invitor * Invitor * @param tableName * Table name * @param invitationTicket * Invitation ticket */ public void handleInvitation(final String invitor, final String tableName, final String invitationTicket) { if (this.view.showISSTableInvitation(invitor, tableName)) { sendToIss(MessageGenerator.getInvitationAcceptedMessage(tableName, invitationTicket)); } } /** * Updates a chat message on an ISS table * * @param tableName * Table name * @param message * Chat message */ public void updateISSTableChatMessage(final String tableName, final ChatMessage message) { // FIXME (jan 30.01.2011) tableName not needed here? this.view.appendISSChatMessage(ChatMessageType.TABLE, message); } /** * Checks whether a table is joined or not * * @param tableName * Table name * @return TRUE iff the table is joined */ public boolean isTableJoined(final String tableName) { return this.appData.isTableJoined(tableName); } }