Package net.yura.lobby.client

Source Code of net.yura.lobby.client.LobbyCom

/*   
    (C) 2007-2012 yura.net
   This file is part of Lobby.

    Lobby is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; version 3 of the License

    Lobby is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
package net.yura.lobby.client;

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.security.KeyFactory;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.crypto.Cipher;
import net.yura.lobby.gen.ProtoLobby;
import net.yura.lobby.model.Game;
import net.yura.lobby.model.GameType;
import net.yura.lobby.model.Message;
import net.yura.lobby.model.Player;
import net.yura.lobby.util.ByteBufferInputStream;
import net.yura.mobile.util.StringUtil;

public class LobbyCom implements Connection, ProtoAccess.ObjectProvider {

      protected static final Logger logger = Logger.getLogger(LobbyCom.class.getName());
   
    private static final int PROTOCOL_VERSION = 1;

    private LobbyClient myclient;
    private AndroidLobbyClient androidClient;
   
    private String username;
    private String password;
    private RSAPublicKey pubKey;
    private Cipher encrypt;

    private Map gameTypeMap;
    private String tempRegName;
    private String tempRegPass;

    private ProtoAccess access = new ProtoAccess(this);

    //SocketClient client;
    TcpClient client;
   
    private final String uuid,appName,appVersion;
   
    public LobbyCom(String uuid,String appName,String appVersion) {
        this.uuid = uuid;
        this.appName = appName;
        this.appVersion = appVersion;
        try {
            // for android we NEED "RSA/ECB/PKCS1Padding" as "RSA" does not work
            // when the server tries to decrypt it, it throws a BadPaddingException
            encrypt = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        gameTypeMap = new HashMap();
    }
  
    void connected() {
        Map hello = new HashMap();
        hello.put("version", new Integer(PROTOCOL_VERSION) );
        hello.put("uuid", uuid );
        hello.put("appName", appName);
        hello.put("appVersion", appVersion);
        hello.put("locale", Locale.getDefault().toString() );
        send(ProtoLobby.REQUEST_HELLO, hello );
    }
    void disconnected() {
        // TODO maybe need to clear other things too
        gameTypeMap.clear();
        myclient.disconnected();
    }

    public Object getObjectId(Object object) {
        if (object instanceof GameType) {
            return new Integer( ((GameType)object).getId() );
        }
        throw new IllegalArgumentException("unknown object: "+object);
    }
    public Object getObjetById(Object id, Class clas) {
        if (clas == GameType.class) {
            return gameTypeMap.get(id);
        }
        throw new IllegalArgumentException("unknown class: "+clas);
    }
  
    final List sendQueue = new java.util.Vector();
    Thread writeThread;

    void send(String command,Object param) {

       if (writeThread==null) {
            writeThread = new Thread() {
                public void run() {
                    try {
                        while ( !Thread.interrupted() ) {
                            Message message;
                            synchronized (sendQueue) {
                                while (sendQueue.isEmpty()) {
                                    sendQueue.wait();
                                }
                                message = (Message)sendQueue.remove(0);
                            }

                            try {
//                                int size = access.computeAnonymousObjectSize(message);
//                                ByteArrayOutputStream bytes = new ByteArrayOutputStream(size);
//                                access.save(bytes, message);
//                                client.sendToNetwork(bytes.toByteArray());

                                TcpClient cl = client;
                                if (cl!=null) {
                                    int size = access.computeAnonymousObjectSize(message);
                                    ByteArrayOutputStream bytes = new ByteArrayOutputStream(size+4);
                                    new DataOutputStream(bytes).writeInt(size);
                                    access.save(bytes, message);
                                    cl.send( ByteBuffer.wrap( bytes.toByteArray() ) );
                                }
                            }
                            catch (InterruptedException in) {
                                throw in; // do not want to catch this
                            }
                            catch (Exception ex) {
                                Level level = (ex instanceof IOException && "not connected".equals(ex.getMessage())) ||
                                          (ex instanceof SocketException && "Broken pipe".equals(ex.getMessage())) ||
                                          (ex instanceof SocketException && "sendto failed: EPIPE (Broken pipe)".equals(ex.getMessage()))
                                        ?Level.INFO:Level.WARNING;
                                logger.log(level,"could not send "+message,ex);
                            }
                        }
                    }
                    catch (InterruptedException ex) { }
                }
            };
            writeThread.start();
       }
      
       System.out.println("sending message "+command+" "+param);
      
       Message message = new Message();
       message.setCommand(command);
       message.setParam(param);

       if (wait!=0) {
            message.setWait(new Integer(wait));
       }
      
       synchronized (sendQueue) {
            sendQueue.add(message);
            sendQueue.notify();
       }
    }

    private int wait;
    public void setWait(int wait) {
        this.wait = wait;
    }

    private boolean canEncrypt() {
        return encrypt != null;
    }
  
    private byte[] encrypt(String name) {
       try {
            return encrypt.doFinal( name.getBytes( "UTF-8" ) );
       }
       catch (Exception ex) {
           throw new RuntimeException(ex);
       }
    }

    private void serverPublicKey(byte[] message) {
  // this method is called when the server wants to give the client a new public key
      if (canEncrypt()) {
         RSAPublicKey previousKey = pubKey;
         try {
            pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(message));
            encrypt.init(Cipher.ENCRYPT_MODE, pubKey);
         }
   catch (Exception e) {
            e.printStackTrace();
            pubKey = previousKey;
            return;
         }
      }
    }

    void messageFromServer(Message msg) {
        String command = msg.getCommand();
        Object param = msg.getParam();

        System.out.println("got message "+command+" "+param);
      
        try {
       
            if (ProtoLobby.COMMAND_KEY.equals(command)) {
                Map map = (Map)param;
                String newHost = (String)map.get("newHost");
                if (newHost!=null) {
                    int index = newHost.indexOf(':');
                    String server = index<0?newHost:newHost.substring(0, index);
                    int port = index<0?client.getPort():Integer.parseInt( newHost.substring(index+1) );
                   
                    disconnect();
                    connect(server, port);
                }
                else {
                    String guestName = (String)map.get("username");
                    Integer userType = (Integer)map.get("usertype");
                    serverPublicKey( (byte[])map.get("key") );

                    String u=username,p=password;
                    username = guestName;
                    myclient.setUsername(guestName, userType.intValue());

                    myclient.connecting("Handshake Complete");
                    myclient.connecting("You are now connected");
                    myclient.connected();

                    if (u!=null && p!=null) {
                        login(u, p);
                    }
                }
            }
            else if (ProtoLobby.COMMAND_ALL_GAMETYPES.equals(command)) {
                List gametypes = (List)param;
                for (int c=0;c<gametypes.size();c++) {
                    GameType gt = (GameType)gametypes.get(c);
                    gameTypeMap.put( new Integer(gt.getId()), gt);
                }
                myclient.addGameType(gametypes);
            }
            else if (ProtoLobby.COMMAND_ADD_OR_UPDATE_GAME.equals(command)) {
                myclient.addOrUpdateGame( (Game)param );
            }
            else if (ProtoLobby.COMMAND_REMOVE_GAME.equals(command)) {
                Map map = (Map)param;
                myclient.removeGame( ((Integer)map.get("game_id")).intValue() );
            }
            else if (ProtoLobby.COMMAND_GAME_MESSAGE.equals(command)) {
                Map map = (Map)param;
                myclient.messageForGame( ((Integer)map.get("game_id")).intValue(), map.get("message"));
            }
            else if (ProtoLobby.COMMAND_LOGIN_OK.equals(command)) {
                Map map = (Map)param;
                Integer userType = (Integer)map.get("userType");
                String newName = (String)map.get("username");
                username = newName==null?tempRegName:newName;
                password = tempRegPass;
                myclient.setUsername(username, userType.intValue() );
            }
            else if (ProtoLobby.COMMAND_LOGIN_ERROR.equals(command)) {
                myclient.error( "login error: "+param );
            }
            else if (ProtoLobby.COMMAND_SERVER_ERROR.equals(command)) {
                myclient.error( "server error: "+param );
            }
            else if (ProtoLobby.COMMAND_LOGOUT_OK.equals(command)) {
                Map map = (Map)param;
                String guestUser = (String)map.get("username");
                username = guestUser;
                password = null;
                myclient.setUsername( guestUser , Player.PLAYER_GUEST );
            }
            else if (ProtoLobby.COMMAND_RENAME_PLAYER.equals(command)) {
                Map map = (Map)param;
                myclient.renamePlayer((String)map.get("oldName"), (String)map.get("newName"), ((Integer)map.get("userType")).intValue());
            }
            else if (ProtoLobby.COMMAND_CHAT_MESSAGE.equals(command)) {
                Map map = (Map)param;
                String message = (String)map.get("message");
                String sender = (String)map.get("sender");
                Integer room_id = (Integer)map.get("room_id");
                if (room_id!=null) {
                    myclient.incomingChat( room_id.intValue(), sender , message);
                }
                else {
                    myclient.incomingChat(sender,message);
                }
            }
            else if (ProtoLobby.COMMAND_PLAYER_ADDED.equals(command)) {
                Map map = (Map)param;
                Player player = (Player)map.get("player");
                Integer room_id = (Integer)map.get("room_id");
                if (room_id==null) {
                    myclient.addPlayer(player);
                }
                else {
                    myclient.addPlayer( room_id.intValue() , player );
                }
            }
            else if (ProtoLobby.COMMAND_PLAYER_REMOVE.equals(command)) {
                Map map = (Map)param;
                String playerName = (String)map.get("playerName");
                Integer room_id = (Integer)map.get("room_id");
                if (room_id==null) {
                    myclient.removePlayer(playerName);
                }
                else {
                    myclient.removePlayer( room_id.intValue() , playerName );
                }
            }
            else if (ProtoLobby.COMMAND_GAME_STARTED.equals(command)) {
                Map map = (Map)param;
                myclient.gameStarted( ((Integer)map.get("game_id")).intValue() );
            }
            else if (ProtoLobby.COMMAND_ANDROID_REGISTER_DONE.equals(command)) {
                androidClient.registerDone();
            }
            else if (ProtoLobby.COMMAND_ANDROID_UNREGISTER_DONE.equals(command)) {
                androidClient.unregisterDone();
            }
            else {
                myclient.error("unknown command "+command+" "+param);
            }
        }
        catch (Exception ex) {
            System.err.println("error handling command "+msg+" error: "+ex);
            ex.printStackTrace();
        }
    }
  
    //##########################################################################
    //######################### Connection interface ###########################
    //##########################################################################

    public void addEventListener(LobbyClient lc) {
        myclient = lc;
    }

    public void addAndroidEventListener(AndroidLobbyClient lc) {
        androidClient = lc;
    }
   
    public void connect(String sev, int p) {
//        client = new SocketClient() {
//            void log(String msg) {
//                myclient.connecting(msg);
//            }
//            void connected() {
//                LobbyCom.this.connected();
//            }
//            void disconnected() {
//                LobbyCom.this.disconnected();
//            }
//            void gotMessage(byte[] bytes) {
//                ByteArrayInputStream messageBytes = new ByteArrayInputStream( bytes );
//                Message message = (Message)access.load(messageBytes, bytes.length);
//                messageFromServer(message);
//            }
//        };
//        client.connect(sev, p);
        client = new TcpClient() {
//            ByteInputStream data = new ByteInputStream();
            int size = -1;
            protected void onRead(final ByteBuffer buf) throws Exception {
//                byte[] bytes = new byte[ buf.remaining() ];
//                buf.get(bytes);
//                data.addBytes(bytes);
                ByteBufferInputStream data = new ByteBufferInputStream(buf);
                while (true) {
                    if (size==-1 && data.available() >= 4) {
                        size = data.readInt();
                    }
                    else if (size>=0 && data.available() >= size) {
                        Message message = (Message)access.load(data, size);
                        size = -1;
                        messageFromServer(message);
                    }
                    else {
                        break;
                    }
                }
            }
            protected void onConnected() throws Exception {
                connected();
            }
            protected void onDisconnected() {
                disconnected();
                size = -1; // reset the read size var
            }
        };
        client.setAddress(sev, p);
        client.start();
    }

    public void disconnect() {
        //client.disconnect();
        client.stop();
        client = null;
       
        if (writeThread!=null) {
            writeThread.interrupt();
            writeThread=null;
        }
    }

    public void setNick(String name) {
        name = checkUsername(name);
        if (name.equals(username)) return; // this is already our name, do nothing
        tempRegName = name;
        Map login = new HashMap();
        login.put("username", canEncrypt()? (Object) encrypt(name):(Object) name );
        send( ProtoLobby.REQUEST_SET_NICK , login );
    }
    /**
     * we do not like double spaces, or single spaces at the start or end of a username
     */
    public static String checkUsername(String name) {
        name = name.trim();
        while (name.indexOf("  ")>=0) {
            name = StringUtil.replaceAll(name, "  ", " ");
        }
        return name;
    }

    public void login(String name, String pass) {
  if (!canEncrypt()) {
    myclient.error("Please update your java to version 1.5 or higher to be able to register and login \nhttp://java.sun.com/javase/downloads/");
  }
  else {
    tempRegName = name;
    tempRegPass = pass;

                Map login = new HashMap();
                login.put("username",  encrypt(name) );
                login.put("password",  encrypt(pass) );
               
    send( ProtoLobby.REQUEST_LOGIN , login );
  }
    }
    public void register(String username, String email, String password) {
  if (!canEncrypt()) {
    myclient.error("Please update your java to version 1.5 or higher to be able to register and login \nhttp://java.sun.com/javase/downloads/");
  }
  else {
    tempRegName = username;
    tempRegPass = password;
               
                Map register = new HashMap();
                register.put("username",  encrypt(username) );
                register.put("password",  encrypt(password) );
                register.put("email",  encrypt(email) );
               
                send( ProtoLobby.REQUEST_REGISTER , register);
  }
    }
    public void logout() {
        send( ProtoLobby.REQUEST_LOGOUT , null );
    }

    public void getGameTypes() {
        send( ProtoLobby.REQUEST_ALL_GAMETYPES , null );
    }
    public void getGames(GameType gameType) {
        Map map = new HashMap();
        map.put("game_type_id", new Integer(gameType.getId()) );
        send( ProtoLobby.REQUEST_GET_GAMES , map );
    }


    public void createNewGame(Game game) {
        send( ProtoLobby.REQUEST_CREATE_NEW_GAME , game );
    }
    public void joinGame(int game_id) {
        Map map = new HashMap();
        map.put("game_id", new Integer(game_id) );
        send( ProtoLobby.REQUEST_JOIN_GAME , map );
    }
    public void leaveGame(int game_id) {
        Map map = new HashMap();
        map.put("game_id", new Integer(game_id) );
        send( ProtoLobby.REQUEST_LEAVE_GAME , map );
    }


    public void playGame(int gameId) {
        Map map = new HashMap();
        map.put("game_id", new Integer(gameId) );
        send( ProtoLobby.REQUEST_PLAY_GAME, map );
    }
    public void sendGameMessage(int game_id, Object message) {
        Map map = new HashMap();
        map.put("game_id", new Integer(game_id) );
        map.put("message", message );
        send( ProtoLobby.REQUEST_GAME_MESSAGE, map );
    }
    public void closeGame(int game_id) {
        Map map = new HashMap();
        map.put("game_id", new Integer(game_id) );
        send( ProtoLobby.REQUEST_CLOSE_GAME , map );
    }

    public void sendChat(int roomid, String message) {
        Map map = new HashMap();
        if (roomid>=0) {
            map.put("room_id", new Integer(roomid) );
        }
        map.put("message", message );
        send( ProtoLobby.COMMAND_CHAT_MESSAGE , map );
    }


    public void androidRegister(String registrationId) {
        send( ProtoLobby.REQUEST_ANDROID_REGISTER , registrationId );
    }
    public void androidUnregister(String registrationId) {
        send( ProtoLobby.REQUEST_ANDROID_UNREGISTER , registrationId );
    }

    @Override
    public void delGame(int gameId) {
        Map map = new HashMap();
        map.put("game_id", new Integer(gameId) );
        send( ProtoLobby.REQUEST_DEL_GAME , map );
    }

    @Override
    public void sendAdminCommand(String command, Map params) {
        send(command, params);
    }
   
}
TOP

Related Classes of net.yura.lobby.client.LobbyCom

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.