/** * 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.ArrayList; import java.util.Iterator; import java.util.List; import java.util.StringTokenizer; import org.jskat.control.JSkatEventBus; import org.jskat.control.event.iss.IssConnectedEvent; import org.jskat.control.event.iss.IssDisconnectedEvent; import org.jskat.control.event.table.TableRemovedEvent; import org.jskat.data.JSkatViewType; import org.jskat.data.SkatGameData; import org.jskat.data.iss.MoveInformation; import org.jskat.util.JSkatResourceBundle; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Handles messages from ISS */ public class MessageHandler extends Thread { private static Logger log = LoggerFactory.getLogger(MessageHandler.class); private StreamConnector connect; private final IssController issControl; private final JSkatResourceBundle strings; private final List<String> messageList; private JSkatEventBus eventBus = JSkatEventBus.INSTANCE; private final static int protocolVersion = 14; /** * Constructor * * @param conn * Connection to ISS * @param controller * ISS controller for JSkat */ public MessageHandler(final StreamConnector conn, final IssController controller) { this.connect = conn; this.issControl = controller; this.strings = JSkatResourceBundle.INSTANCE; this.messageList = new ArrayList<String>(); } public MessageHandler(final IssController controller) { this.issControl = controller; this.strings = JSkatResourceBundle.INSTANCE; this.messageList = new ArrayList<String>(); } /** * {@inheritDoc} */ @Override public void run() { while (true) { if (this.messageList.size() > 0) { final String message = getNextMessage(); handleMessage(message); } else { try { Thread.sleep(100); } catch (final InterruptedException e) { log.warn("Thread.sleep() was interrupted"); //$NON-NLS-1$ } } } } synchronized void addMessage(final String newMessage) { this.messageList.add(newMessage); } private synchronized String getNextMessage() { return this.messageList.remove(0); } void handleMessage(final String message) { if (message == null) { this.eventBus.post(new IssDisconnectedEvent()); } else { final StringTokenizer tokenizer = new StringTokenizer(message); // get // first // command final String first = tokenizer.nextToken(); // get all parameters final List<String> params = new ArrayList<String>(); while (tokenizer.hasMoreTokens()) { params.add(tokenizer.nextToken()); } try { handleMessage(first, params); } catch (final Exception except) { log.error("Error in parsing ISS protocoll", except); //$NON-NLS-1$ this.issControl.showErrorMessage(this.strings .getString("iss_error_parsing_iss_protocol")); //$NON-NLS-1$ } } } void handleMessage(final String first, final List<String> params) { final MessageType type = MessageType.getByString(first); if (MessageType.UNKNOWN.equals(type)) { log.error("UNHANDLED MESSAGE: " + first + params.toString()); //$NON-NLS-1$ } } else { // FIXME (jansch 30.05.2011) put message into a queue try { handleMessageObsolete(type, params); } catch (final Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } } void handleMessageObsolete(final MessageType type, final List<String> params) throws Exception { switch (type) { case PASSWORD: handlePasswordMessage(); break; case WELCOME: handleWelcomeMessage(params); break; case VERSION: handleVersionMessage(params); break; case CLIENTS: handleClientListMessage(params); break; case TABLES: handleTableListMessage(params); break; case CREATE: handleTableCreateMessage(params); break; case INVITE: handleTableInvitationMessage(params); break; case TABLE: handleTableUpdateMessage(params); break; case DESTROY: handleTableDestroyMessage(params); break; case ERROR: handleErrorMessage(params); break; case TEXT: handleTextMessage(params); break; case YELL: handleLobbyChatMessage(params); break; } } void handleLobbyChatMessage(final List<String> params) { this.issControl.addChatMessage(ChatMessageType.LOBBY, params); } void handlePasswordMessage() { this.issControl.sendPassword(); } void handleTextMessage(final List<String> params) { // FIXME show it to the user log.error(params.toString()); } void handleErrorMessage(final List<String> params) { log.error(params.toString()); this.issControl.showErrorMessage(getI18ErrorString(getErrorString(params))); } private String getErrorString(final List<String> params) { for (final String param : params) { if (param.startsWith("_")) { //$NON-NLS-1$ return param; } } return params.toString(); } private String getI18ErrorString(final String errorString) { if ("_id_pw_mismatch".equals(errorString)) { //$NON-NLS-1$ return this.strings.getString("iss_login_password_wrong"); //$NON-NLS-1$ } else if ("_not_your_turn".equals(errorString)) { //$NON-NLS-1$ return this.strings.getString("iss_not_your_turn"); //$NON-NLS-1$ } else if ("_invalid_move_colon".equals(errorString)) { //$NON-NLS-1$ return this.strings.getString("iss_invalid_move_colon"); //$NON-NLS-1$ } return errorString; } void handleTableCreateMessage(final List<String> params) { log.debug("table creation message"); //$NON-NLS-1$ final String tableName = params.get(0); final String creator = params.get(1); final int seats = Integer.parseInt(params.get(2)); this.issControl.createTable(tableName, creator, seats); } void handleTableDestroyMessage(final List<String> params) { log.debug("table destroy message"); //$NON-NLS-1$ final String tableName = params.get(0); eventBus.post(new TableRemovedEvent(tableName, JSkatViewType.ISS_TABLE)); } void handleTableInvitationMessage(final List<String> params) { log.debug("table destroy message"); //$NON-NLS-1$ final String invitor = params.get(0); final String tableName = params.get(1); final String invitationTicket = params.get(2); this.issControl.handleInvitation(invitor, tableName, invitationTicket); } /** * table .1 bar state 3 bar xskat xskat:2 . bar . 0 0 0 0 0 0 1 0 xskat $ 0 * 0 0 0 0 0 1 1 xskat:2 $ 0 0 0 0 0 0 1 1 . . 0 0 0 0 0 0 0 0 false */ void handleTableUpdateMessage(final List<String> params) { log.debug("table update message"); //$NON-NLS-1$ final String tableName = params.get(0); if (this.issControl.isTableJoined(tableName)) { // FIXME (jan 18.11.2010) is this the name of the creator or the // login name of the current player? final String creator = params.get(1); final String actionCommand = params.get(2); final List<String> detailParams = params.subList(3, params.size()); if (actionCommand.equals("error")) { //$NON-NLS-1$ handleErrorMessage(params.subList(3, params.size())); } else if (actionCommand.equals("state")) { //$NON-NLS-1$ this.issControl.updateISSTableState(tableName, MessageParser.getTableStatus(creator, detailParams)); } else if (actionCommand.equals("start")) { //$NON-NLS-1$ this.issControl .updateISSGame(tableName, MessageParser .getGameStartStatus(creator, detailParams)); } else if (actionCommand.equals("go")) { //$NON-NLS-1$ this.issControl.startGame(tableName); } else if (actionCommand.equals("play")) { //$NON-NLS-1$ final MoveInformation moveInfo = MessageParser .getMoveInformation(detailParams); MessageParser.parsePlayerTimes(detailParams, moveInfo); this.issControl.updateMove(tableName, moveInfo); } else if (actionCommand.equals("tell")) { //$NON-NLS-1$ this.issControl.updateISSTableChatMessage(tableName, MessageParser .getTableChatMessage(tableName, detailParams)); } else if (actionCommand.equals("end")) { //$NON-NLS-1$ this.issControl.endGame(tableName, getGameInformation(detailParams)); } else { log.debug("unhandled action command: " + actionCommand + " for table " + tableName); //$NON-NLS-1$ //$NON-NLS-2$ } } } private SkatGameData getGameInformation(final List<String> params) { // first glue alle params back together final String gameResult = glueParams(params); return MessageParser.parseGameSummary(gameResult); } private String glueParams(final List<String> params) { String result = new String(); final Iterator<String> paramIterator = params.iterator(); while (paramIterator.hasNext()) { if (result.length() > 0) { result += " "; //$NON-NLS-1$ } result += paramIterator.next(); } return result; } /** * Handles a client list message * * @param params * parameters */ void handleClientListMessage(final List<String> params) { final String plusMinus = params.remove(0); if (plusMinus.equals("+")) { //$NON-NLS-1$ updateClientList(params); } else if (plusMinus.equals("-")) { //$NON-NLS-1$ removeClientFromList(params); } } /** * Adds or updates a client on the client list * * @param params * Player information */ void updateClientList(final List<String> params) { final String playerName = params.get(0); final String language = params.get(2); final long gamesPlayed = Long.parseLong(params.get(3)); final double strength = Double.parseDouble(params.get(4)); this.issControl.updateISSPlayerList(playerName, language, gamesPlayed, strength); } /** * Removes a client from the client list * * @param params * Player information */ void removeClientFromList(final List<String> params) { this.issControl.removeISSPlayerFromList(params.get(0)); } /** * Handles the welcome message and checks the protocol version * * @param params * Welcome information */ void handleWelcomeMessage(final List<String> params) { final String login = params.get(0); final double issProtocolVersion = Double.parseDouble(params.get(params .size() - 1)); log.debug("ISS version: " + issProtocolVersion); //$NON-NLS-1$ log.debug("local version: " + protocolVersion); //$NON-NLS-1$ if ((int) issProtocolVersion != protocolVersion) { // TODO handle this in JSkatMaster log.error("Wrong protocol version!!!"); //$NON-NLS-1$ log.error("iss version: " + issProtocolVersion); //$NON-NLS-1$ log.error("local version: " + protocolVersion); //$NON-NLS-1$ } eventBus.post(new IssConnectedEvent(login)); } /** * Handles the version message and checks the protocol version * * @param params * Welcome information */ void handleVersionMessage(final List<String> params) { log.debug("ISS version: " + params.get(0)); } /** * Handles a table list message * * @param params * Table information */ void handleTableListMessage(final List<String> params) { final String plusMinus = params.remove(0); if (plusMinus.equals("+")) { //$NON-NLS-1$ updateTableList(params); } else if (plusMinus.equals("-")) { //$NON-NLS-1$ removeTableFromList(params); } } /** * Adds or updates a table on the table list * * @param params * Table information */ void updateTableList(final List<String> params) { final String tableName = params.get(0); final int maxPlayers = Integer.parseInt(params.get(1)); final long gamesPlayed = Long.parseLong(params.get(2)); final String player1 = params.get(3); final String player2 = params.get(4); final String player3 = params.get(5); this.issControl.updateISSTableList(tableName, maxPlayers, gamesPlayed, player1, player2, player3); } /** * Removes a table from the table list * * @param params * Table information */ void removeTableFromList(final List<String> params) { this.issControl.removeISSTableFromList(params.get(0)); } }