Crossfire JXClient, Trunk
TestCrossfireServer.java
Go to the documentation of this file.
1 package com.realtime.crossfire.jxclient.server.crossfire;
2 
3 import java.io.EOFException;
4 import java.io.IOException;
5 import java.io.InputStream;
6 import java.io.OutputStream;
7 import java.net.ServerSocket;
8 import java.net.Socket;
9 import java.net.SocketException;
10 import java.nio.charset.Charset;
11 import java.nio.charset.StandardCharsets;
12 import java.util.concurrent.Semaphore;
13 import org.jetbrains.annotations.NotNull;
14 import org.jetbrains.annotations.Nullable;
15 import org.junit.Assert;
16 
20 public class TestCrossfireServer {
21 
25  @NotNull
26  private final Charset charset = StandardCharsets.ISO_8859_1;
27 
31  @NotNull
32  private final Semaphore sem = new Semaphore(0);
33 
37  @NotNull
38  @SuppressWarnings({"resource", "IOResourceOpenedButNotSafelyClosed", "SocketOpenedButNotSafelyClosed"})
39  private final ServerSocket server = new ServerSocket(0);
40 
44  @NotNull
45  private final Thread thread = new Thread(this::run);
46 
50  @NotNull
51  private final Object sync = new Object();
52 
56  @Nullable
57  private Socket client;
58 
63  public TestCrossfireServer() throws IOException {
64  }
65 
69  public void start() {
70  thread.start();
71  }
72 
78  public void stop() throws InterruptedException, IOException {
79  thread.interrupt();
80  synchronized (sync) {
81  server.close();
82  if (client != null) {
83  client.close();
84  }
85  }
86  thread.join();
87  }
88 
93  public int getLocalPort() {
94  return server.getLocalPort();
95  }
96 
101  public void waitForCharacterLogin() throws InterruptedException {
102  sem.acquire();
103  }
104 
108  private void run() {
109  try {
110  final Socket tmp = acceptClient(server);
111  synchronized (sync) {
112  client = tmp;
113  }
114  try {
115  final InputStream in = getInputStream(tmp);
116  while (true) {
117  final byte[] data = readPacket(in);
118  if (data == null) {
119  break;
120  }
121  int paramsIndex;
122  for (paramsIndex = 0; paramsIndex < data.length; paramsIndex++) {
123  if (data[paramsIndex] == (byte)' ') {
124  break;
125  }
126  }
127  final String cmd = new String(data, 0, paramsIndex, charset);
128  if (paramsIndex < data.length && data[paramsIndex] == (byte)' ') {
129  paramsIndex++;
130  }
131  switch (cmd) {
132  case "version":
133  writeString("version 1 1 info");
134  break;
135 
136  case "setup":
137  processSetup(new String(data, paramsIndex, data.length-paramsIndex, charset));
138  break;
139 
140  case "requestinfo":
141  processRequestinfo(new String(data, paramsIndex, data.length-paramsIndex, charset));
142  break;
143 
144  case "toggleextendedtext":
145  // ignore
146  break;
147 
148  case "addme":
149  processAddme();
150  break;
151 
152  default:
153  Assert.fail("received unexpected command: "+cmd);
154  break;
155  }
156  }
157  } finally {
158  synchronized (sync) {
159  client = null;
160  }
161  //noinspection ThrowFromFinallyBlock
162  tmp.close();
163  }
164  } catch (final IOException ex) {
165  Assert.fail(ex.getMessage());
166  throw new AssertionError(ex);
167  }
168  }
169 
176  private static byte @Nullable [] readPacket(@NotNull final InputStream in) throws EOFException {
177  final int tmp;
178  try {
179  tmp = readByte(in);
180  } catch (final EOFException ignored) {
181  return null;
182  }
183  final int packetLen = tmp*0x100+readByte(in);
184  final byte[] data = new byte[packetLen];
185  try {
186  int pos = 0;
187  while (pos < data.length) {
188  final int len = in.read(data, pos, data.length-pos);
189  if (len == -1) {
190  throw new EOFException("unexpected end of file reached");
191  }
192  pos += len;
193  }
194  } catch (final IOException ex) {
195  Assert.fail(ex.getMessage());
196  throw new AssertionError(ex);
197  }
198  return data;
199  }
200 
206  private void processSetup(@NotNull final String params) throws IOException {
207  final String[] params2 = params.split(" ", -1);
208  Assert.assertEquals(0, params2.length%2);
209  final StringBuilder sb = new StringBuilder("setup");
210  for (int i = 0; i < params2.length; i += 2) {
211  final String key = params2[i];
212  final String value = params2[i+1];
213  if (key.equals("map2cmd") || key.equals("newmapcmd") || key.equals("facecache") || key.equals("extendedTextInfos") || key.equals("itemcmd") || key.equals("spellmon") || key.equals("tick") || key.equals("num_look_objects") || key.equals("mapsize")) {
214  sb.append(" ").append(key).append(" ").append(value);
215  } else {
216  sb.append(" ").append(key).append(" FALSE");
217  }
218  }
219  writeString(sb.toString());
220  }
221 
227  private void processRequestinfo(@NotNull final String params) throws IOException {
228  switch (params) {
229  case "exp_table":
230  writeBytes("replyinfo exp_table \0\1".getBytes(StandardCharsets.US_ASCII));
231  break;
232 
233  case "skill_info 1":
234  writeBytes("replyinfo skill_info ".getBytes(StandardCharsets.US_ASCII));
235  break;
236 
237  case "skill_extra 1":
238  writeBytes("replyinfo skill_extra ".getBytes(StandardCharsets.US_ASCII));
239  break;
240 
241  case "knowledge_info":
242  writeBytes("replyinfo knowledge_info ".getBytes(StandardCharsets.US_ASCII));
243  break;
244 
245  case "image_info":
246  writeBytes("replyinfo image_info 0\n0\n".getBytes(StandardCharsets.US_ASCII));
247  break;
248 
249  case "startingmap":
250  writeBytes("replyinfo startingmap ".getBytes(StandardCharsets.US_ASCII));
251  break;
252 
253  case "race_list":
254  writeBytes("replyinfo race_list ".getBytes(StandardCharsets.US_ASCII));
255  break;
256 
257  case "class_list":
258  writeBytes("replyinfo class_list ".getBytes(StandardCharsets.US_ASCII));
259  break;
260 
261  case "newcharinfo":
262  writeBytes("replyinfo newcharinfo ".getBytes(StandardCharsets.US_ASCII));
263  break;
264 
265  default:
266  Assert.fail("requestinfo "+params+" not implemented");
267  break;
268  }
269  }
270 
275  private void processAddme() throws IOException {
276  writeString("query 0 What is your name?");
277  writeString("addme_success");
278  assert sem != null;
279  sem.release();
280  }
281 
288  private static int readByte(@NotNull final InputStream in) throws EOFException {
289  final int ch;
290  try {
291  ch = in.read();
292  } catch (final SocketException ex) {
293  final String message = ex.getMessage();
294  if (!message.equals("Socket closed")) {
295  Assert.fail(message);
296  throw new AssertionError(ex);
297  }
298  final EOFException ex2 = new EOFException("EOF");
299  ex2.initCause(ex);
300  throw ex2;
301  } catch (final IOException ex) {
302  Assert.fail(ex.getMessage());
303  throw new AssertionError(ex);
304  }
305  if (ch == -1) {
306  throw new EOFException("EOF");
307  }
308  return ch;
309  }
310 
316  public void writeString(@NotNull final String s) throws IOException {
317  writeBytes(s.getBytes(charset));
318  }
319 
325  public void writeBytes(final byte @NotNull [] b) throws IOException {
326  final OutputStream out = getOutputStream();
327  final int len = b.length;
328  out.write(len/0x100);
329  out.write(len);
330  out.write(b);
331  }
332 
338  @NotNull
339  private static Socket acceptClient(@NotNull final ServerSocket server) {
340  final Socket client;
341  try {
342  //noinspection SocketOpenedButNotSafelyClosed
343  client = server.accept();
344  } catch (final IOException ex) {
345  Assert.fail(ex.getMessage());
346  throw new AssertionError(ex);
347  }
348  return client;
349  }
350 
356  @NotNull
357  private static InputStream getInputStream(@NotNull final Socket socket) {
358  final InputStream in;
359  try {
360  in = socket.getInputStream();
361  } catch (final IOException ex) {
362  Assert.fail(ex.getMessage());
363  throw new AssertionError(ex);
364  }
365  return in;
366  }
367 
372  @NotNull
373  private OutputStream getOutputStream() {
374  final Socket tmp;
375  synchronized (sync) {
376  tmp = client;
377  }
378  if (tmp == null) {
379  Assert.fail("no client connection");
380  }
381 
382  final OutputStream out;
383  try {
384  out = tmp.getOutputStream();
385  } catch (final IOException ex) {
386  Assert.fail(ex.getMessage());
387  throw new AssertionError(ex);
388  }
389  return out;
390  }
391 
392 }
com.realtime.crossfire.jxclient.server.crossfire.TestCrossfireServer.getLocalPort
int getLocalPort()
Definition: TestCrossfireServer.java:93
com.realtime.crossfire.jxclient.server.crossfire.TestCrossfireServer.getInputStream
static InputStream getInputStream(@NotNull final Socket socket)
Definition: TestCrossfireServer.java:357
com.realtime.crossfire.jxclient.server.crossfire.TestCrossfireServer.processSetup
void processSetup(@NotNull final String params)
Definition: TestCrossfireServer.java:206
com.realtime.crossfire.jxclient.server.crossfire.TestCrossfireServer.client
Socket client
Definition: TestCrossfireServer.java:57
com.realtime.crossfire.jxclient.server.crossfire.TestCrossfireServer.charset
final Charset charset
Definition: TestCrossfireServer.java:26
com.realtime.crossfire.jxclient.server.crossfire.TestCrossfireServer.TestCrossfireServer
TestCrossfireServer()
Definition: TestCrossfireServer.java:63
com.realtime.crossfire.jxclient.server.crossfire.TestCrossfireServer.stop
void stop()
Definition: TestCrossfireServer.java:78
com.realtime.crossfire.jxclient.server.crossfire.TestCrossfireServer.thread
final Thread thread
Definition: TestCrossfireServer.java:45
com.realtime.crossfire.jxclient.server.crossfire.TestCrossfireServer.sync
final Object sync
Definition: TestCrossfireServer.java:51
com.realtime.crossfire.jxclient.server.crossfire.TestCrossfireServer.acceptClient
static Socket acceptClient(@NotNull final ServerSocket server)
Definition: TestCrossfireServer.java:339
com.realtime.crossfire.jxclient.server.crossfire.TestCrossfireServer.server
final ServerSocket server
Definition: TestCrossfireServer.java:39
com.realtime.crossfire.jxclient.server.crossfire.TestCrossfireServer.start
void start()
Definition: TestCrossfireServer.java:69
com.realtime.crossfire.jxclient.server.crossfire.TestCrossfireServer.readPacket
static byte[] readPacket(@NotNull final InputStream in)
Definition: TestCrossfireServer.java:176
com.realtime.crossfire.jxclient.server.crossfire.TestCrossfireServer.readByte
static int readByte(@NotNull final InputStream in)
Definition: TestCrossfireServer.java:288
com.realtime.crossfire.jxclient.server.crossfire.TestCrossfireServer.getOutputStream
OutputStream getOutputStream()
Definition: TestCrossfireServer.java:373
com.realtime.crossfire.jxclient.server.crossfire.TestCrossfireServer.writeBytes
void writeBytes(final byte @NotNull[] b)
Definition: TestCrossfireServer.java:325
com.realtime.crossfire.jxclient.server.crossfire.TestCrossfireServer.waitForCharacterLogin
void waitForCharacterLogin()
Definition: TestCrossfireServer.java:101
com.realtime.crossfire.jxclient.server.crossfire.TestCrossfireServer.run
void run()
Definition: TestCrossfireServer.java:108
com.realtime.crossfire.jxclient.server.crossfire.TestCrossfireServer.processAddme
void processAddme()
Definition: TestCrossfireServer.java:275
com.realtime.crossfire.jxclient.server.crossfire.TestCrossfireServer.writeString
void writeString(@NotNull final String s)
Definition: TestCrossfireServer.java:316
com.realtime.crossfire.jxclient.server.crossfire.TestCrossfireServer
Definition: TestCrossfireServer.java:20
com.realtime.crossfire.jxclient.server.crossfire.TestCrossfireServer.processRequestinfo
void processRequestinfo(@NotNull final String params)
Definition: TestCrossfireServer.java:227
com.realtime.crossfire.jxclient.server.crossfire.TestCrossfireServer.sem
final Semaphore sem
Definition: TestCrossfireServer.java:32