7. Rhythm Game : LoginUserList
이번 포스팅에서는
로그인이 성공한 다음 출력될
RoomPanel과 로그인한 유저의 목록을
띄울 수 있는 기능을 구현해보겠습니다.
===================================================================
Rhythm Game : LoginUserList
1. Rhythm Game : RoomPanel.java
로그인되고 출력될 화면에 나타날 컴포넌트들은 대략 6가지 정도일 것입니다.
1) 방만들기 / 빠른시작 - 게임방을 생성하여 특정 유저와 게임할 수 있도록 하게 해주는 기능을 구현해야 할 것입니다.
2) 방목록 - 실시간으로 생성되어있는 방의 정보를 띄우고 동시에 입장 가능한 방이라면 해당 방에 입장할 수 있어야합니다.
3) 로그아웃 / 게임종료 - 단순히 다른 계정으로 로그인하기 위한 로그아웃 기능이외에도 게임을 아예 종료하는 게임종료 버튼이 있어야합니다.
4) 내정보 - 로그인한 계정에 대한 정보를 출력해야합니다. (별도의 패널로 관리하였기 때문에 추후에 내정보를 불러오는 기능 구현할 때 다루겠습니다.)
5) 유저목록 - 계정이 생성되어 있는 모든 유저의 정보는 아니라도 실시간으로 로그인되어있는 유저의 목록을 띄워줍니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | package clientPanel; import java.awt.Font; ... public class RoomPanel extends JPanel { public JButton btCreateRoom; public JButton btLogOut; public List<JButton> btList; public JList userList; public PnInfoPanel pnInfo; public JTextField textField; public JButton btExit; public JButton btQuickStart; private Graphics screenGraphic; private Image panelImage; private Image selectedImage ; private Image backgroundImage = new ImageIcon(getClass().getResource("imge/roomImage.jpg")).getImage(); private JLabel lbInfoPicture; public RoomPanel(ClientUI c) { setBackground(new Color(51, 0, 0)); setSize(800, 800); setLayout(null); btList = new ArrayList<>(); // 방목록 버튼을 List로 관리 // 내정보 패널 생성 ========================================== pnInfo = c.pnInfo; add(pnInfo); pnInfo.setBounds(34, 62, 200, 139); // ========================================================== // 내정보에 들어갈 이미지 ===================================== lbInfoPicture = new JLabel(""); URL url3 = getClass().getResource("imge/mypsa4.png"); lbInfoPicture.setBounds(74, 23, 50, 50); lbInfoPicture.setIcon(new ImageIcon(url3)); pnInfo.add(lbInfoPicture); // =========================================================== // 유저목록을 띄울 리스트 ====================================== JScrollPane scrollPane = new JScrollPane(); scrollPane.setBounds(34, 211, 200, 548); add(scrollPane); userList = new JList(); userList.setCellRenderer(new DefaultListCellRenderer() { @Override public int getHorizontalAlignment() { // TODO Auto-generated method stub return CENTER; } }); scrollPane.setViewportView(userList); // =========================================================== // 방만들기 =================================================== btCreateRoom = new JButton(""); URL url2 = getClass().getResource("imge/CreateRoom.jpg"); btCreateRoom = new JButton(""); btCreateRoom.setBorder(new LineBorder(new Color(0, 0, 0))); btCreateRoom.setBorderPainted(false); btCreateRoom.setFocusPainted(false); btCreateRoom.setBackground(new Color(255, 0, 0, 0)); btCreateRoom.setIcon(new ImageIcon(url2)); btCreateRoom.setBounds(269, 62, 155, 71); add(btCreateRoom); // =========================================================== // 로그아웃 =================================================== btLogOut = new JButton("L O G O U T"); btLogOut.setFont(new Font("Tw Cen MT Condensed", Font.BOLD, 18)); btLogOut.setBackground(Color.RED); btLogOut.setBounds(608, 100, 149, 33); btLogOut.setFocusPainted(false); add(btLogOut); // =========================================================== // 게임종료 =================================================== btExit = new JButton("E X I T"); btExit.setBackground(Color.RED); btExit.setFont(new Font("Tw Cen MT Condensed", Font.BOLD, 18)); btExit.setBounds(608, 62, 149, 32); btExit.setFocusPainted(false); add(btExit); // ============================================================ // 빠른입장 ==================================================== URL url1 = getClass().getResource("imge/QuickStart.jpg"); btQuickStart = new JButton(""); btQuickStart.setBorder(new LineBorder(new Color(0, 0, 0))); btQuickStart.setBorderPainted(false); btQuickStart.setFocusPainted(false); btQuickStart.setBackground(new Color(255, 0, 0, 0)); btQuickStart.setBounds(430, 62, 149, 71); btQuickStart.setIcon(new ImageIcon(url1)); add(btQuickStart); // ============================================================ // 방목록 띄우기 위한 리스트 ==================================== JPanel panel = new JPanel(); panel.setBackground(Color.CYAN); panel.setBounds(269, 143, 488, 616); add(panel); panel.setPreferredSize(new Dimension(488, 464)); panel.setLayout(new GridLayout(0, 2, 0, 0)); for(int i=1;i<=8;i++) { JButton bt = new JButton(""); bt.setHorizontalAlignment(SwingConstants.CENTER); bt.setFont(new Font("나눔고딕코딩", Font.BOLD, 12)); bt.setEnabled(false); bt.setBorder(new BevelBorder(BevelBorder.RAISED, null, null, null, null)); bt.setFont((new Font("함초롬바탕", Font.BOLD | Font.ITALIC, 15))); panel.add(bt); btList.add(bt); } // ============================================================= } // 배경화면 그리기 =================================================== public void paint(Graphics g) { panelImage = createImage(this.getWidth(), this.getHeight()); screenGraphic= panelImage.getGraphics(); screenDraw(screenGraphic); g.drawImage(panelImage, 0, 0, null); } public void screenDraw(Graphics g) { g.drawImage(backgroundImage, 0, 0, null); paintComponents(g); this.repaint(); } // ================================================================== } | cs |
2. Rhythm Game : PersonalServer.java
유저 목록을 띄울 때의 시점은 3부분으로 나눌 수 있습니다.
1) 다른 사용자가 로그인했을 때 목록 갱신
2) 자신이 로그인했을 때 유저 목록 출력
3) 다른 사용자가 로그아웃했을 때 목록 갱신
유저목록을 출력하는 작업은 로그인이나 회원가입과 같이 특정 버튼을 눌렀을 때 실행되는 것이 아니라 로그인이 완료되고 RoomPanel로 넘어가는 시점에서 실행되는 작업입니다. 때문에 로그인이 완료되었을 때 유저목록을 서버로부터 받아오는 작업을 진행하는 방식으로 진행하면 됩니다.
간단히 말해 클라이언트가 서버에게 로그인 요청 데이터를 전송하고 서버에서 로그인에 처리를 할 때 로그인이 완료가 되었다면 UDP를 통해 로그인되어있는 모든 사용자에게 유저목록을 갱신하라고 신호를 주는 것입니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | package server; import java.io.IOException; ... public class PersonalServer extends Thread { ... private Account user; // 현재 계정 객체 저장 ... @Override public void run() { String[] command = null; while(socket.isConnected()) { ... switch(command[0]) { ... case "join": // 로그인 String result = accountPool.login(command[1], command[2], socket.getRemoteSocketAddress()); user = accountPool.getAccountMap().get(command[1]); sendToClient(result); if(result.equals("true")) { // 모든 유저에게 유저목록 갱신 요청 전송 sendAlramToAll("userListChange"); } break; } } } } | cs |
미리 정의해둔 sendAlramToAll() 메소드를 이용해 모든 유저에게 UDP로 유저목록 갱신하라는 신호를 주게 되는 것입니다.
3. Rhythm Game : ClientNetwork.java
이제 서버에서 UDP를 통해 유저목록을 갱신해야 한다는 신호를 클라이언트로 전송했기 때문에 클라이언트에서 해당 신호를 받았을 때의 동작을 처리하면 됩니다.
UDP를 통해 상황에 따라 다른 타입의 객체를 주고 받는 동작은 번거롭기 때문에 UDP를 통해서는 String 데이터만 전송하여 실제 갱신해야할 데이터를 전송하는 것이 아니라 갱신해야 한다는 신호만 주는 것입니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | package client; import java.io.IOException; ... public class ClientNetwork extends Thread { ... @Override public void run() { while (!ds.isClosed()) { DatagramPacket dp = new DatagramPacket(new byte[2048], 2048); try { ds.receive(dp); String data = new String(dp.getData(), 0, dp.getLength()); String[] str = data.split("#"); switch (str[0]) { case "userListChange": // 로그인 유저 목록 sendUserListRequest(); break; } } catch (IOException e) { System.out.println("dp failed .. " + e.toString()); ds.close(); break; } } } // 로그인 유저 목록과 내 정보 갱신 or 변경 public void sendUserListRequest() { Set<Account> resp = null; synchronized (oos) { try { oos.writeObject("get"); resp = (Set<Account>) ois.readObject(); // 방목록을 List에 뿌려주기 위한 작업 String[] ar = new String[resp.size()]; int i = 0; for (Account a : resp) { ar[i++] = a.toString(); } ui.pnRoom.userList.setListData(ar); } catch (Exception e) { System.out.println(e.toString()); } } } } | cs |
유치하게? 설명하자면 서버가 클라이언트에게 '너 유저목록 갱신해야해'라고 말해주는 것이 UDP이고, 그 말을 들은 클라이언트는 다시 서버에게 '알았어. TCP로 변경된 유저목록 보내줘'라고 동작하는 것입니다.
4. Rhythm Game : PersonalServer.java
클라이언트에서 TCP를 통해 서버에게 유저목록을 원한다는 요청이 들어왔을 때 서버에서 가지고 있는 현재 유저의 목록을 보내주면 됩니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | package server; import java.io.IOException; ... public class PersonalServer extends Thread { ... private Account user; // 현재 계정 객체 저장 ... @Override public void run() { String[] command = null; while(socket.isConnected()) { ... switch(command[0]) { ... case "get": // RoomPanel 유저 목록 resp = accountPool.getCurrentUser(); sendToClient(resp); break; } } } } | cs |
이렇게 설계한다면 생각보다 간단하게 로그인했을 때 유저의 목록을 가져올 수 있고, 또한 타 유저의 로그인에도 실시간으로 유저 목록을 갱신하고 출력할 수 있습니다.
다른 유저가 로그아웃했을 때도 로그인했을 때와 마찬가지로 처리해주면 됩니다. 자세한건 로그아웃할 때 다루어보겠습니다.
## 로그인되어있는 유저 목록 출력 기능에 대한 클래스 동작 흐름.
(로그인 성공시 Server) 로그인 처리가 완료되면 UDP를 통해 클라이언트에게 "userListChange" 데이터 전송
-> (Client) userListChange 데이터를 받고 유저 목록을 서버에게 요청하기 위해 서버로 "get" 데이터 전송
-> (Server) "get"에 대한 동작으로 현재 로그인한 유저 목록을 가지고 있는 currentUser 변수를 클라이언트에게 전송
-> (Client) 전달받은 데이터를 출력