Skip to content

Instantly share code, notes, and snippets.

@jrenner
Created December 14, 2013 01:57
Show Gist options
  • Save jrenner/7954727 to your computer and use it in GitHub Desktop.
Save jrenner/7954727 to your computer and use it in GitHub Desktop.
LwjglHeadlessApplication test server
package com.example.libgdx.skeleton;
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Net;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.net.ServerSocket;
import com.badlogic.gdx.net.Socket;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef;
import com.badlogic.gdx.physics.box2d.CircleShape;
import com.badlogic.gdx.physics.box2d.FixtureDef;
import com.badlogic.gdx.physics.box2d.PolygonShape;
import com.badlogic.gdx.physics.box2d.World;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.GdxRuntimeException;
import com.badlogic.gdx.utils.StringBuilder;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.Arrays;
/** For testing of LwjglHeadlessApplication (headless server)
* @author Jon Renner */
public class Main extends ApplicationAdapter {
@Override
public void create () {
System.out.println("Starting headless test...");
new Thread(new HeadlessServer("LibGDX Headless Test")).start();
makeWorld();
}
private World world;
private static final Array<Body> bodies = new Array<Body>();
void makeWorld() {
world = new World(new Vector2(0, 0), true);
BodyDef bdef = new BodyDef();
CircleShape shape = new CircleShape();
shape.setRadius(1f);
FixtureDef fdef = new FixtureDef();
fdef.shape = shape;
bdef.type = BodyDef.BodyType.DynamicBody;
Vector2 pos = new Vector2();
for (int i = 0; i < 5; i++) {
Body body = world.createBody(bdef);
pos.set(MathUtils.random(-5f, 5f), MathUtils.random(-5f, 5f));
shape.setPosition(pos);
body.createFixture(fdef);
bodies.add(body);
}
}
private static final float TIME_STEP = 1/60f;
private static long time = 0;
@Override
public void render() {
if (System.currentTimeMillis() - time > 5000) {
time = System.currentTimeMillis();
HeadlessClient client = new HeadlessClient();
client.sendRequest(Request.Identify);
client.sendRequest(Request.NumClients);
client.sendRequest(Request.BodyData);
client.close();
}
synchronized (bodies) {
world.step(TIME_STEP, 8, 3);
Body randomBody = bodies.random();
randomBody.applyForceToCenter(10f, 0f, true);
}
}
private static final int DEFAULT_PORT = 11209;
private static final String DEFAULT_HOST = "localhost";
private static enum Request {
Identify((byte) 0x01), // get server name
NumClients((byte) 0x02), // get num of connected clients
Shutdown((byte) 0x03), // shutdown server
BodyData((byte) 0x04), // get box2d body info
;
private static byte prefix = 0x7D;
private byte code;
Request(byte code) {
this.code = code;
}
public byte getCode() {
return code;
}
public static Request getRequest(byte code) {
for (Request req : Request.values()) {
if (req.getCode() == code) {
return req;
}
}
return null;
}
}
class clientHandler implements Runnable {
private HeadlessServer server;
private Socket clientSock;
public clientHandler(Socket socket, HeadlessServer server) {
this.server = server;
this.clientSock = socket;
}
@Override
public void run() {
while (clientSock.isConnected()) {
server.incrementActiveClients();
InputStream inStream = clientSock.getInputStream();
PrintWriter writer = new PrintWriter(clientSock.getOutputStream());
byte[] buf = new byte[256];
try {
while (inStream.read(buf) != -1) {
Request request = checkBufferForRequest(buf);
if (request != null) {
handleRequest(request, writer);
}
}
clientSock.dispose();
} catch (IOException e) {
e.printStackTrace();
} finally {
server.decrementActiveClients();
}
}
}
private Request checkBufferForRequest(byte[] buf) {
int adjustedLength = buf.length - 1; // code is one byte for prefix and one byte for request
for (int i = 0; i < adjustedLength; i++) {
byte first = buf[i];
if (first == Request.prefix) {
// got prefix, next byte should be request code
byte second = buf[i + 1];
Request req = Request.getRequest(second);
if (req != null) {
return req;
}
}
}
return null;
}
private void handleRequest(Request request, PrintWriter clientWriter) {
switch (request) {
case Identify:
clientWriter.write("SERVER ID: " + server.identifier);
clientWriter.flush();
break;
case NumClients:
clientWriter.write("ACTIVE CLIENTS: " + server.getActiveClients());
clientWriter.flush();
break;
case Shutdown:
clientWriter.write("SHUTTING DOWN SERVER");
clientWriter.flush();
System.out.println("Shutdown ordered by client, exiting...");
System.exit(0);
break;
case BodyData:
StringBuilder sb = new StringBuilder();
sb.append("Box2D bodies:\n");
synchronized (bodies) {
int i = 0;
for (Body body : bodies) {
sb.append(" Body #").append(i++).append("\n");
sb.append(" Position: ").append(body.getPosition()).append("\n");
sb.append(" Velocity: ").append(body.getPosition()).append("\n");
sb.append(" Angle: ").append(body.getAngle()).append("\n");
}
}
clientWriter.write(sb.toString());
clientWriter.flush();
break;
default:
String msg = "unhandled request type: " + request.toString();
clientWriter.write(msg);
clientWriter.flush();
System.out.println(msg);
}
}
}
class HeadlessServer implements Runnable {
private String identifier;
private int activeClients;
public HeadlessServer(String identifier) {
this.identifier = identifier;
System.out.println("Created server: " + identifier);
}
@Override
public void run() {
// test a simple server
ServerSocket server = Gdx.net.newServerSocket(Net.Protocol.TCP, DEFAULT_PORT, null);
while (true) {
Socket clientSock = server.accept(null);
new Thread(new clientHandler(clientSock, this)).start();
}
}
private synchronized void incrementActiveClients() {
activeClients = activeClients + 1;
}
private synchronized void decrementActiveClients() {
activeClients = activeClients - 1;
}
private synchronized int getActiveClients() {
return activeClients;
}
}
class HeadlessClient {
private Socket serverSock;
public HeadlessClient() {
System.out.println("Creating new client...");
serverSock = Gdx.net.newClientSocket(Net.Protocol.TCP, DEFAULT_HOST, DEFAULT_PORT, null);
}
public void sendRequest(Request req) {
if (serverSock == null) {
throw new GdxRuntimeException("server socket unavailable");
}
OutputStream out = serverSock.getOutputStream();
byte[] msg = new byte[2];
msg[0] = Request.prefix;
msg[1] = req.getCode();
try {
out.write(msg);
out.flush();
} catch (IOException e) {
System.out.println("problem sending to server");
e.printStackTrace();
}
System.out.println("sent request to server: " + req);
byte[] buf = new byte[2048];
InputStream in = serverSock.getInputStream();
// we don't handle incoming data larger than the buffer because we don't care, this is just a test
try {
int n = in.read(buf);
if (n != -1) {
String serverMsg = new String(Arrays.copyOfRange(buf, 0, n));
System.out.println("Received from server: " + serverMsg);
}
} catch (IOException e) {
System.out.println("problem reading from server");
e.printStackTrace();
}
}
public void close() {
serverSock.dispose();
}
}
private static void sleep(long ms) {
try {
Thread.sleep(ms);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment