aboutsummaryrefslogtreecommitdiff
path: root/src/ch/epfl/xblast/server/Server.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/ch/epfl/xblast/server/Server.java')
-rw-r--r--src/ch/epfl/xblast/server/Server.java341
1 files changed, 341 insertions, 0 deletions
diff --git a/src/ch/epfl/xblast/server/Server.java b/src/ch/epfl/xblast/server/Server.java
new file mode 100644
index 0000000..074b2a7
--- /dev/null
+++ b/src/ch/epfl/xblast/server/Server.java
@@ -0,0 +1,341 @@
1package ch.epfl.xblast.server;
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.painter.BoardPainter;
8
9import java.io.IOException;
10import java.net.InetSocketAddress;
11import java.net.SocketAddress;
12import java.net.StandardProtocolFamily;
13import java.nio.ByteBuffer;
14import java.nio.channels.DatagramChannel;
15import java.util.*;
16
17/**
18 * The Server class.
19 *
20 * @author Pacien TRAN-GIRARD (261948)
21 */
22public class Server {
23
24 /**
25 * Default parameters of the server.
26 */
27 private static final long REFRESH_RATE = (long) (50 * 1E6); // nanosecond
28 private static final int DEFAULT_EXPECTED_CLIENTS = PlayerID.values().length;
29 public static final int DEFAULT_PORT = 2016;
30
31 /**
32 * ID of an observer.
33 */
34 public static final byte OBSERVER = -1;
35
36 /**
37 * Prints a log message on the console.
38 *
39 * @param message message to be printed
40 */
41 private static void log(String message) {
42 System.out.println("[LOG] " + message);
43 }
44
45 /**
46 * A Channel.
47 */
48 private static class Channel {
49
50 /**
51 * Transforms a list of bytes to an array of bytes.
52 *
53 * @param l the given list of bytes
54 * @return the array built from the given list
55 */
56 private static byte[] toByteArray(List<Byte> l) {
57 byte[] arr = new byte[l.size()];
58
59 for (int i = 0; i < l.size(); ++i)
60 arr[i] = l.get(i);
61
62 return arr;
63 }
64
65 /**
66 * Creates the socket which will be listened to.
67 *
68 * @param host hostname
69 * @param port port to be listened
70 * @return the socket build from the given params
71 */
72 private static InetSocketAddress listeningInterface(String host, int port) {
73 if (Objects.isNull(host))
74 return new InetSocketAddress(port);
75 else
76 return new InetSocketAddress(host, port);
77 }
78
79 /**
80 * Open an UDP channel.
81 *
82 * @param iface listened socket
83 * @return a UDP channel
84 */
85 private static DatagramChannel openChannel(InetSocketAddress iface) {
86 try {
87 DatagramChannel chan = DatagramChannel.open(StandardProtocolFamily.INET);
88 chan.bind(iface);
89 return chan;
90 } catch (IOException e) {
91 e.printStackTrace();
92 System.exit(1);
93 return null;
94 }
95 }
96
97 /**
98 * The UDP communication channel.
99 */
100 private final DatagramChannel channel;
101
102 /**
103 * Instantiates a new Channel.
104 *
105 * @param iface listened socket
106 */
107 Channel(InetSocketAddress iface) {
108 this.channel = openChannel(iface);
109 }
110
111 /**
112 * Instantiates a new Channel.
113 *
114 * @param host hostname
115 * @param port port to be listened
116 */
117 Channel(String host, Integer port) {
118 this(listeningInterface(host, Optional.ofNullable(port).orElse(DEFAULT_PORT)));
119 }
120
121 /**
122 * Closes the channel.
123 */
124 void closeChannel() {
125 try {
126 this.channel.close();
127 } catch (IOException e) {
128 e.printStackTrace();
129 }
130 }
131
132 /**
133 * Accepts the registration of a new client.
134 *
135 * @param registrations number of expected registrations
136 * @return a list of the clients.
137 */
138 List<SocketAddress> acceptRegistrations(int registrations) {
139 List<SocketAddress> clients = new ArrayList<>(registrations);
140
141 while (clients.size() < registrations) {
142 SocketAddress client = this.acceptRegistration();
143 if (!clients.contains(client))
144 clients.add(client);
145 }
146
147 return Collections.unmodifiableList(clients);
148 }
149
150 /**
151 * Collects actions from the players.
152 *
153 * @return a list map containing the actions related to each player
154 */
155 Map<SocketAddress, PlayerAction> collectActions() {
156 Map<SocketAddress, PlayerAction> actions = new HashMap<>();
157 Optional<Map.Entry<SocketAddress, PlayerAction>> action;
158
159 while (true) {
160 action = this.receiveAction(false);
161 if (!action.isPresent()) break;
162 actions.put(action.get().getKey(), action.get().getValue());
163 }
164
165 return Collections.unmodifiableMap(actions);
166 }
167
168 /**
169 * Sends data through the socket.
170 *
171 * @param content data to be send
172 * @param dst recipient of the data
173 */
174 void send(List<Byte> content, SocketAddress dst) {
175 try {
176 ByteBuffer buf = ByteBuffer.wrap(toByteArray(content));
177 this.channel.send(buf, dst);
178 } catch (IOException e) {
179 e.printStackTrace();
180 }
181 }
182
183 /**
184 * Receives a Byte from the socket.
185 *
186 * @param block wait for an incoming byte before returning
187 * @return an Optional containing the received data
188 */
189 private Optional<Map.Entry<SocketAddress, Byte>> receiveByte(boolean block) {
190 try {
191 ByteBuffer buf = ByteBuffer.allocate(1);
192 this.channel.configureBlocking(block);
193 SocketAddress client = this.channel.receive(buf);
194
195 if (Objects.isNull(client) || buf.position() == 0)
196 throw new IOException();
197
198 return Optional.of(new AbstractMap.SimpleImmutableEntry<>(client, buf.get(0)));
199 } catch (IOException e) {
200 return Optional.empty();
201 }
202 }
203
204 /**
205 * Receives a player action from the socket.
206 *
207 * @param block wait for an incoming action before returning
208 * @return an Optional containing the received player action
209 */
210 private Optional<Map.Entry<SocketAddress, PlayerAction>> receiveAction(boolean block) {
211 try {
212 Map.Entry<SocketAddress, Byte> actionByte = this.receiveByte(block).get();
213 PlayerAction playerAction = PlayerAction.fromByte(actionByte.getValue());
214 return Optional.of(new AbstractMap.SimpleImmutableEntry<>(actionByte.getKey(), playerAction));
215 } catch (NoSuchElementException | IllegalArgumentException e) {
216 return Optional.empty();
217 }
218 }
219
220 /**
221 * Accepts an action from the socket.
222 *
223 * @return the accepted action
224 */
225 private Map.Entry<SocketAddress, PlayerAction> acceptAction() {
226 Optional<Map.Entry<SocketAddress, PlayerAction>> action;
227
228 do {
229 action = this.receiveAction(true);
230 } while (!action.isPresent());
231
232 return action.get();
233 }
234
235 /**
236 * Accepts the registration of a player.
237 *
238 * @return the address of the new player
239 */
240 private SocketAddress acceptRegistration() {
<