aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/ch/epfl/xblast/Time.java13
-rw-r--r--src/ch/epfl/xblast/client/Client.java188
-rw-r--r--src/ch/epfl/xblast/client/KeyboardEventHandler.java29
-rw-r--r--src/ch/epfl/xblast/server/GameState.java1
-rw-r--r--src/ch/epfl/xblast/server/Server.java11
-rw-r--r--test/ch/epfl/xblast/simulation/GraphicalSimulation.java19
6 files changed, 229 insertions, 32 deletions
diff --git a/src/ch/epfl/xblast/Time.java b/src/ch/epfl/xblast/Time.java
index 7c84257..c53f1ef 100644
--- a/src/ch/epfl/xblast/Time.java
+++ b/src/ch/epfl/xblast/Time.java
@@ -28,4 +28,17 @@ public interface Time {
28 */ 28 */
29 int NS_PER_S = 1000 * US_PER_S; 29 int NS_PER_S = 1000 * US_PER_S;
30 30
31 /**
32 * Pauses the current thread for the given duration.
33 *
34 * @param ns the sleep duration (ns)
35 */
36 static void sleep(long ns) {
37 try {
38 Thread.sleep((long) (ns / 1E6), (int) (ns % 1E6));
39 } catch (InterruptedException e) {
40 Thread.currentThread().interrupt();
41 }
42 }
43
31} 44}
diff --git a/src/ch/epfl/xblast/client/Client.java b/src/ch/epfl/xblast/client/Client.java
index dc08f1b..5b331f2 100644
--- a/src/ch/epfl/xblast/client/Client.java
+++ b/src/ch/epfl/xblast/client/Client.java
@@ -1,9 +1,25 @@
1package ch.epfl.xblast.client; 1package ch.epfl.xblast.client;
2 2
3import ch.epfl.xblast.Lists;
4import ch.epfl.xblast.PlayerAction;
5import ch.epfl.xblast.PlayerID;
6import ch.epfl.xblast.Time;
3import ch.epfl.xblast.server.Server; 7import ch.epfl.xblast.server.Server;
4 8
9import javax.swing.*;
10import java.awt.*;
11import java.io.IOException;
12import java.lang.reflect.InvocationTargetException;
5import java.net.InetSocketAddress; 13import java.net.InetSocketAddress;
14import java.net.SocketAddress;
15import java.net.StandardProtocolFamily;
16import java.nio.ByteBuffer;
17import java.nio.channels.DatagramChannel;
18import java.util.ArrayList;
19import java.util.Collections;
20import java.util.List;
6import java.util.Optional; 21import java.util.Optional;
22import java.util.function.Consumer;
7 23
8/** 24/**
9 * The Client class. 25 * The Client class.
@@ -14,17 +30,179 @@ public class Client {
14 30
15 private static final String DEFAULT_SERVER_HOST = "localhost"; 31 private static final String DEFAULT_SERVER_HOST = "localhost";
16 private static final int DEFAULT_SERVER_PORT = Server.DEFAULT_PORT; 32 private static final int DEFAULT_SERVER_PORT = Server.DEFAULT_PORT;
33 private static final int PACKET_MAX_SIZE = 1000;
34 private static final long REGISTER_PING_INTERVAL = 1 * Time.NS_PER_S; // ns
17 35
18 private final InetSocketAddress serverAddr; 36 private static class Channel {
37
38 private static List<Byte> bufferToList(ByteBuffer buf) {
39 List<Byte> l = new ArrayList<>(buf.remaining());
40
41 while (buf.hasRemaining())
42 l.add(buf.get());
43
44 return Collections.unmodifiableList(l);
45 }
46
47 private static DatagramChannel openChannel() {
48 try {
49 return DatagramChannel.open(StandardProtocolFamily.INET);
50 } catch (IOException e) {
51 e.printStackTrace();
52 System.exit(1);
53 return null;
54 }
55 }
56
57 private final SocketAddress serverAddr;
58 private final DatagramChannel channel;
59
60 Channel(InetSocketAddress iface) {
61 this.serverAddr = iface;
62 this.channel = openChannel();
63 }
64
65 Channel(String host, Integer port) {
66 this(new InetSocketAddress(
67 Optional.ofNullable(host).orElse(DEFAULT_SERVER_HOST),
68 Optional.ofNullable(port).orElse(DEFAULT_SERVER_PORT)));
69 }
70
71 void closeChannel() {
72 try {
73 this.channel.close();
74 } catch (IOException e) {
75 e.printStackTrace();
76 }
77 }
78
79 void sendAction(PlayerAction action) {
80 this.sendByte(action.toByte());
81 }
82
83 List<Byte> receiveGameState(boolean block) {
84 Optional<List<Byte>> state;
85
86 do {
87 state = this.receive(block);
88 } while (!state.isPresent());
89
90 return state.get();
91 }
92
93 private void sendByte(byte b) {
94 this.send(ByteBuffer.wrap(new byte[]{b}));
95 }
96
97 private void send(ByteBuffer b) {
98 try {
99 this.channel.send(b, this.serverAddr);
100 } catch (IOException e) {
101 e.printStackTrace();
102 }
103 }
104
105 private Optional<List<Byte>> receive(boolean block) {
106 try {
107 ByteBuffer buf = ByteBuffer.allocate(PACKET_MAX_SIZE);
108 this.channel.configureBlocking(block);
109 this.channel.receive(buf);
110 buf.flip();
111 return Optional.of(bufferToList(buf));
112 } catch (IOException e) {
113 e.printStackTrace();
114 return Optional.empty();
115 }
116 }
117
118 }
119
120 private static class GUI {
121
122 private static JFrame buildFrame(Container content) {
123 JFrame frame = new JFrame();
124 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
125 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
126 frame.setVisible(true);
127
128 frame.setContentPane(content);
129 frame.pack();
130 return frame;
131 }
132
133 private static void attachKeyboardHandler(Component comp, Consumer<PlayerAction> actionConsumer) {
134 comp.addKeyListener(new KeyboardEventHandler(actionConsumer));
135 comp.requestFocusInWindow();
136 }
137
138 private final XBlastComponent gameComponent;
139 private final Consumer<PlayerAction> actionConsumer;
140
141 GUI(Consumer<PlayerAction> actionConsumer) {
142 this.gameComponent = new XBlastComponent();
143 this.actionConsumer = actionConsumer;
144 }
145
146 public void display() {
147 buildFrame(this.gameComponent);
148 attachKeyboardHandler(this.gameComponent, this.actionConsumer);
149 }
150
151 public void setGameState(GameState gs, PlayerID p) {
152 this.gameComponent.setGameState(gs, p);
153 }
154
155 }
156
157 private final Channel channel;
158 private final GUI gui;
19 159
20 public Client(String host, Integer port) { 160 public Client(String host, Integer port) {
21 this.serverAddr = new InetSocketAddress( 161 this.channel = new Channel(host, port);
22 Optional.ofNullable(host).orElse(DEFAULT_SERVER_HOST), 162 this.gui = new GUI(this.channel::sendAction);
23 Optional.ofNullable(port).orElse(DEFAULT_SERVER_PORT));
24 } 163 }
25 164
26 public void run() { 165 public void run() {
27 // TODO 166 this.displayGUI();
167 this.establishConnection();
168 this.runGame();
169 this.channel.closeChannel();
170 }
171
172 private void displayGUI() {
173 try {
174 SwingUtilities.invokeAndWait(this.gui::display);
175 } catch (InterruptedException | InvocationTargetException e) {
176 e.printStackTrace();
177 System.exit(1);
178 }
179 }
180
181 private void runGame() {
182 while (true)
183 updateGameState();
184 }
185
186 private void establishConnection() {
187 Thread joinThread = new Thread(this::sendJoinRequest);
188 joinThread.start();
189 updateGameState();
190 joinThread.interrupt();
191 }
192
193 private void updateGameState() {
194 List<Byte> serializedGameState = this.channel.receiveGameState(true);
195 if (serializedGameState.size() < 1) return;
196 PlayerID player = PlayerID.fromByte(serializedGameState.get(0));
197 GameState gameState = GameStateDeserializer.deserialize(Lists.firstDropped(serializedGameState, 1));
198 this.gui.setGa