aboutsummaryrefslogtreecommitdiff
path: root/src/ch/epfl/xblast/client/Client.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/ch/epfl/xblast/client/Client.java')
-rw-r--r--src/ch/epfl/xblast/client/Client.java360
1 files changed, 360 insertions, 0 deletions
diff --git a/src/ch/epfl/xblast/client/Client.java b/src/ch/epfl/xblast/client/Client.java
new file mode 100644
index 0000000..24ca727
--- /dev/null
+++ b/src/ch/epfl/xblast/client/Client.java
@@ -0,0 +1,360 @@
1package ch.epfl.xblast.client;
2
3import ch.epfl.xblast.Lists;
4import ch.epfl.xblast.PlayerAction;
5import ch.epfl.xblast.PlayerID;
6import ch.epfl.xblast.Time;
7import ch.epfl.xblast.server.Server;
8
9import javax.swing.*;
10import java.awt.*;
11import java.io.IOException;
12import java.lang.reflect.InvocationTargetException;
13import java.net.InetSocketAddress;
14import java.net.SocketAddress;
15import java.net.StandardProtocolFamily;
16import java.nio.ByteBuffer;
17import java.nio.channels.DatagramChannel;
18import java.util.*;
19import java.util.List;
20import java.util.function.Consumer;
21
22/**
23 * The Client class.
24 *
25 * @author Pacien TRAN-GIRARD (261948)
26 */
27public class Client {
28
29 /**
30 * Client's parameters.
31 */
32 private static final String DEFAULT_SERVER_HOST = "localhost";
33 private static final int DEFAULT_SERVER_PORT = Server.DEFAULT_PORT;
34 private static final int PACKET_MAX_SIZE = 1000;
35 private static final long REGISTER_PING_INTERVAL = 1 * Time.NS_PER_S; // ns
36
37 /**
38 * Communication channel.
39 */
40 private static class Channel {
41
42 /**
43 * Transforms a buffer to a list of bytes.
44 *
45 * @param buf given buffer
46 * @return the list of bytes built from the given buffer
47 */
48 private static List<Byte> bufferToList(ByteBuffer buf) {
49 List<Byte> l = new ArrayList<>(buf.remaining());
50
51 while (buf.hasRemaining())
52 l.add(buf.get());
53
54 return Collections.unmodifiableList(l);
55 }
56
57 /**
58 * Creates the UDP communication Channel.
59 *
60 * @return the UDP communication Channel
61 */
62 private static DatagramChannel openChannel() {
63 try {
64 return DatagramChannel.open(StandardProtocolFamily.INET);
65 } catch (IOException e) {
66 e.printStackTrace();
67 System.exit(1);
68 return null;
69 }
70 }
71
72 /**
73 * Parameters of the Channel.
74 */
75 private final SocketAddress serverAddr;
76 private final DatagramChannel channel;
77
78 /**
79 * Instantiates a new Channel.
80 *
81 * @param iface socket
82 */
83 Channel(InetSocketAddress iface) {
84 this.serverAddr = iface;
85 this.channel = openChannel();
86 }
87
88 /**
89 * Instantiates a new Channel.
90 *
91 * @param host hostname
92 * @param port port
93 */
94 Channel(String host, Integer port) {
95 this(new InetSocketAddress(
96 Optional.ofNullable(host).orElse(DEFAULT_SERVER_HOST),
97 Optional.ofNullable(port).orElse(DEFAULT_SERVER_PORT)));
98 }
99
100 /**
101 * Closes the Channel.
102 */
103 void closeChannel() {
104 try {
105 this.channel.close();
106 } catch (IOException e) {
107 e.printStackTrace();
108 }
109 }
110
111 /**
112 * Sends an action through the Channel.
113 *
114 * @param action action to send
115 */
116 void sendAction(PlayerAction action) {
117 this.sendByte(action.toByte());
118 }
119
120 /**
121 * Receives a GameState through the Channel.
122 *
123 * @param block wait for an incoming game state before returning
124 * @return the received GameState
125 */
126 List<Byte> receiveGameState(boolean block) {
127 Optional<List<Byte>> state;
128
129 do {
130 state = this.receive(block);
131 } while (!state.isPresent());
132
133 return state.get();
134 }
135
136 /**
137 * Sends a byte through the Channel.
138 *
139 * @param b byte to send
140 */
141 private void sendByte(byte b) {
142 this.send(ByteBuffer.wrap(new byte[]{b}));
143 }
144
145 /**
146 * Sends the content of a buffer.
147 *
148 * @param b buffer containing the bytes to send
149 */
150 private void send(ByteBuffer b) {
151 try {
152 this.channel.send(b, this.serverAddr);
153 } catch (IOException e) {
154 e.printStackTrace();
155 }
156 }
157
158 /**
159 * Receives data from the Channel.
160 *
161 * @param block wait for an incoming byte before returning
162 * @return the received data
163 */
164 private Optional<List<Byte>> receive(boolean block) {
165 try {
166 ByteBuffer buf = ByteBuffer.allocate(PACKET_MAX_SIZE);
167 this.channel.configureBlocking(block);
168 this.channel.receive(buf);
169 buf.flip();
170 return Optional.of(bufferToList(buf));
171 } catch (IOException e) {
172 e.printStackTrace();
173 return Optional.empty();
174 }
175 }
176
177 }
178
179 /**
180 * The Graphical User Interface.
181 */
182 private static class GUI {
183
184 /**
185 * Builds the window.
186 *
187 * @param content content of the window
188 * @return the frame
189 */
190 private static JFrame buildFrame(Container content) {
191 JFrame frame = new JFrame();
192 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
193 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
194 frame.setVisible(true);
195
196 frame.setContentPane(content);
197 frame.pack();
198 return frame;
199 }
200
201 /**
202 * Attaches the keyboard event handler to the component.
203 *
204 * @param comp component
205 * @param actionConsumer actionConsumer corresponding to the keyboardEventHandler
206 */
207 private static void attachKeyboardHandler(Component comp, Consumer<PlayerAction> actionConsumer) {
208 comp.addKeyListener(new KeyboardEventHandler(actionConsumer));
209 comp.requestFocusInWindow();
210 }
211
212 /**
213 * GUI's parameters.
214 */
215 private final XBlastComponent gameComponent;
216 private final Consumer<PlayerAction> actionConsumer;
217
218 /**
219 * Instantiates a new GUI.
220 *
221 * @param actionConsumer actionConsumer corresponding to the keyboardEventHandler
222 */
223 GUI(Consumer<PlayerAction> actionConsumer) {
224 this.gameComponent = new XBlastComponent();
225 this.actionConsumer = actionConsumer;
226 }
227
228 /**
229 * Builds and display the GUI.
230 */
231 public void display() {
232 buildFrame(this.gameComponent);
233 attachKeyboardHandler(this.gameComponent, this.actionConsumer);
234 }
235
236 /**
237 * Updates the displayed GameState.
238 *
239 * @param gs new GameState to be displayed
240 * @param p Player ID corresponding to the client
241 */
242 public void setGameState(GameState gs, PlayerID p) {