Crossfire JXClient, Trunk  R20561
DefaultCrossfireServerConnectionTest.java
Go to the documentation of this file.
1 /*
2  * This file is part of JXClient, the Fullscreen Java Crossfire Client.
3  *
4  * JXClient is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * JXClient is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with JXClient; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17  *
18  * Copyright (C) 2005-2008 Yann Chachkoff.
19  * Copyright (C) 2006-2011 Andreas Kirschbaum.
20  */
21 
22 package com.realtime.crossfire.jxclient.server.crossfire;
23 
24 import java.io.EOFException;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.io.OutputStream;
28 import java.net.ServerSocket;
29 import java.net.Socket;
30 import java.nio.charset.Charset;
31 import java.util.concurrent.Semaphore;
32 import org.jetbrains.annotations.NotNull;
33 import org.jetbrains.annotations.Nullable;
34 import org.junit.Assert;
35 import org.junit.Test;
36 
42 
46  @Nullable
47  private Semaphore sem;
48 
55  @Test(timeout = 30000)
56  public void testNegotiateNumLookObjects1() throws InterruptedException, IOException {
57  sem = new Semaphore(0);
58  final Model model = new Model();
59  final DefaultCrossfireServerConnection connection = new DefaultCrossfireServerConnection(model, null, "version");
60  final int port = startServer();
61  connection.start();
62  try {
63  connection.connect("localhost", port);
64  connection.setPreferredNumLookObjects(10);
65  assert sem != null;
66  sem.acquire();
68  Assert.assertEquals(10, connection.getCurrentNumLookObjects());
69  connection.setPreferredNumLookObjects(11);
70  connection.setPreferredNumLookObjects(12);
71  connection.setPreferredNumLookObjects(13);
72  connection.setPreferredNumLookObjects(14);
74  Assert.assertEquals(14, connection.getCurrentNumLookObjects());
75  } finally {
76  connection.stop();
77  }
78  }
79 
85  private int startServer() throws IOException {
86  //noinspection SocketOpenedButNotSafelyClosed
87  final ServerSocket server = new ServerSocket(0);
88  final Thread thread = new Thread(new Runnable() {
89 
93  @NotNull
94  private final Charset charset = Charset.forName("ISO-8859-1");
95 
99  @Override
100  public void run() {
101  try {
102  try (final Socket client = acceptClient(server)) {
103  final InputStream in = getInputStream(client);
104  final OutputStream out = getOutputStream(client);
105  while (true) {
106  final byte[] data = readPacket(in);
107  if (data == null) {
108  break;
109  }
110  int paramsIndex;
111  for (paramsIndex = 0; paramsIndex < data.length; paramsIndex++) {
112  if (data[paramsIndex] == (byte)' ') {
113  break;
114  }
115  }
116  final String cmd = new String(data, 0, paramsIndex, charset);
117  if (paramsIndex < data.length && data[paramsIndex] == (byte)' ') {
118  paramsIndex++;
119  }
120  switch (cmd) {
121  case "version":
122  writeString(out, "version 1 1 info");
123  break;
124 
125  case "setup":
126  processSetup(out, new String(data, paramsIndex, data.length-paramsIndex, charset));
127  break;
128 
129  case "requestinfo":
130  processRequestinfo(out, new String(data, paramsIndex, data.length-paramsIndex, charset));
131  break;
132 
133  case "toggleextendedtext":
134  // ignore
135  break;
136 
137  case "addme":
138  processAddme(out);
139  break;
140 
141  default:
142  Assert.fail("received unexpected command: "+cmd);
143  break;
144  }
145  }
146  }
147  } catch (final IOException ex) {
148  Assert.fail(ex.getMessage());
149  throw new AssertionError(ex);
150  }
151  }
152 
159  @Nullable
160  private byte[] readPacket(@NotNull final InputStream in) throws EOFException {
161  final int tmp;
162  try {
163  tmp = readByte(in);
164  } catch (final EOFException ignored) {
165  return null;
166  }
167  final int packetLen = tmp*0x100+readByte(in);
168  final byte[] data = new byte[packetLen];
169  try {
170  int pos = 0;
171  while (pos < data.length) {
172  final int len = in.read(data, pos, data.length-pos);
173  if (len == -1) {
174  throw new EOFException("unexpected end of file reached");
175  }
176  pos += len;
177  }
178  } catch (final IOException ex) {
179  Assert.fail(ex.getMessage());
180  throw new AssertionError(ex);
181  }
182  return data;
183  }
184 
191  private void processSetup(@NotNull final OutputStream out, @NotNull final String params) throws IOException {
192  final String[] params2 = params.split(" ", -1);
193  Assert.assertEquals(0, params2.length%2);
194  final StringBuilder sb = new StringBuilder("setup");
195  for (int i = 0; i < params2.length; i += 2) {
196  final String key = params2[i];
197  final String value = params2[i+1];
198  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")) {
199  sb.append(" ").append(key).append(" ").append(value);
200  } else {
201  sb.append(" ").append(key).append(" FALSE");
202  }
203  }
204  writeString(out, sb.toString());
205  }
206 
213  private void processRequestinfo(@NotNull final OutputStream out, @NotNull final String params) throws IOException {
214  if (params.equals("exp_table")) {
215  writeBytes(out, new byte[] {
216  'r',
217  'e',
218  'p',
219  'l',
220  'y',
221  'i',
222  'n',
223  'f',
224  'o',
225  ' ',
226  'e',
227  'x',
228  'p',
229  '_',
230  't',
231  'a',
232  'b',
233  'l',
234  'e',
235  ' ',
236  0,
237  1,
238  });
239  } else {
240  // ignore
241  }
242  }
243 
249  private void processAddme(@NotNull final OutputStream out) throws IOException {
250  writeString(out, "query 0 What is your name?");
251  writeString(out, "addme_success");
252  assert sem != null;
253  sem.release();
254  }
255 
262  private int readByte(@NotNull final InputStream in) throws EOFException {
263  final int ch;
264  try {
265  ch = in.read();
266  } catch (final IOException ex) {
267  Assert.fail(ex.getMessage());
268  throw new AssertionError(ex);
269  }
270  if (ch == -1) {
271  throw new EOFException("EOF");
272  }
273  return ch;
274  }
275 
282  private void writeString(@NotNull final OutputStream out, @NotNull final String s) throws IOException {
283  writeBytes(out, s.getBytes(charset));
284  }
285 
292  private void writeBytes(@NotNull final OutputStream out, @NotNull final byte[] b) throws IOException {
293  final int len = b.length;
294  out.write(len/0x100);
295  out.write(len);
296  out.write(b);
297  }
298 
299  });
300  thread.start();
301  return server.getLocalPort();
302  }
303 
309  @NotNull
310  private static Socket acceptClient(@NotNull final ServerSocket server) {
311  final Socket client;
312  try {
313  //noinspection SocketOpenedButNotSafelyClosed
314  client = server.accept();
315  } catch (final IOException ex) {
316  Assert.fail(ex.getMessage());
317  throw new AssertionError(ex);
318  }
319  return client;
320  }
321 
327  @NotNull
328  private static InputStream getInputStream(@NotNull final Socket socket) {
329  final InputStream in;
330  try {
331  in = socket.getInputStream();
332  } catch (final IOException ex) {
333  Assert.fail(ex.getMessage());
334  throw new AssertionError(ex);
335  }
336  return in;
337  }
338 
344  @NotNull
345  private static OutputStream getOutputStream(@NotNull final Socket socket) {
346  final OutputStream out;
347  try {
348  out = socket.getOutputStream();
349  } catch (final IOException ex) {
350  Assert.fail(ex.getMessage());
351  throw new AssertionError(ex);
352  }
353  return out;
354  }
355 
356 }
void setPreferredNumLookObjects(final int preferredNumLookObjects)
Sets the maximum number of objects in the ground view.Must not be called in connected state...
static OutputStream getOutputStream(@NotNull final Socket socket)
Returns the OutputStream of a Socket.
Combines all model classes that are updated.
Definition: Model.java:44
static InputStream getInputStream(@NotNull final Socket socket)
Returns the InputStream of a Socket.
static Socket acceptClient(@NotNull final ServerSocket server)
Accepts a single client from a ServerSocket.
int getCurrentNumLookObjects()
Returns the currently negotiated setup value of "num_look_objects".
void testNegotiateNumLookObjects1()
Checks that DefaultCrossfireServerConnection#setPreferredNumLookObjects(int) queues multiple updates...
void connect(@NotNull final String hostname, final int port)
Attempts to connect the client to a server.the hostname to connect to the port to connect to ...