Game network issues: remote commands only receieved when commands are being sent

I have a custom multithreaded client server networked game that is based on the simpleApplication jme3 game engine.



I’m not using the supplied networking code since this is a class project and my partner and I have to make our own.



The good news is, my partner and I have managed to create a two way communication link where our client side interactions with the objects are seen on the other side.



The bad news is, new messages are ONLY recieved when we are interacting with the objects ourselves (hence, hitting keys).



I tested this on the client input thread, new messages are only recieved on local user input, not as they come.



Here is the server code in question: The server basically creates a thread for each player connected (in this case only 2, and it isn’t very robust yet.



[java] do {

try {

message = (String) input.readObject();

System.out.println(“Thread” + count + ": " + message);

}

catch ( ClassNotFoundException e) {

System.out.println(“message error”);

}

catch( IOException e ) {

e.printStackTrace();

System.exit( 1 );

}

if(count == 1)

buffer.set(0, message);

else

buffer.set(1, message);



new_message = buffer.get(count);

//String new_message = message.replaceFirst(“1”, “0”);

System.out.println("Sending Back: " + new_message);

try {

output.writeObject(new_message);

}

catch( IOException e ) {

e.printStackTrace();

System.exit( 1 );

}

} while (!message.equals(“QUIT CONNECTION::EOF”));[/java]



My partner has a theory about this code, that the code stalls at the readObject if there is nothing to read. But we are completely lost at this point.



Any ideas?



ps…here is some of the pertinent code:



The buffer that is used in this excerpt.

[java]import java.lang.Exception.;

import java.util.concurrent.
;



public class ServerBuffer{

private final int CONCURRENCY = 2;

private final float LOAD = .75f;



private int count;

private ConcurrentHashMap <Integer, String>comm_buffer;



public ServerBuffer(int c){

count = 2*c;

comm_buffer =

new ConcurrentHashMap<Integer, String>(count, LOAD, CONCURRENCY);

}



public void set(int key, String value){

comm_buffer.put(key, value);

}



public String get(int key){

return comm_buffer.get(key);

}



}[/java]

There is nothing glaringly strange about the code provided. The problem must be elsewhere.



Really not enough information to diagnose the issue and it’s definitely not JME specific, really.



Some questions:

The first code block is run in a thread on the server?

Does the server redispatch the messages properly, ie: can you see them getting received and sent out again?

Is it only the clients that are not receiving the data? Or is it the server not sending it out?



As it is, it’s about like asking how long a piece of string is. If the client is where the problem is happening then that code will be relevant. If the server is where the problem is happening then something is missing from the above… what are the input and output objects, for example? Is the server a command line app or a JME app? If the latter, why?

I don’t know what your “input” variable is, but If you are using normal Java IO and not NIO, then its probably blocking, so yes, the code stalls and waits for a new message to be received.

@InShadow made me realize that I may have misread something.



When you say: “I tested this on the client input thread, new messages are only recieved on local user input, not as they come.”

i presumed you meant like keyboard and mouse input as “local user input”. If you mean something else then let us know.

Yes, when we’re sending commands via the inputListener.



InShadow, the input variable is a ObjectInputStream created by a java socket, which I imagine is the normal Java IO, what is this alternative or how can I fix the normal java IO?



The connection is initiated in the Main simpleApplication class, and the input and output streams are sent to the client input and output threads. The main class also sends a command to the client output which compiles 10 commands and sends it via a string packet to the server.



Here is the relevant code: First the update loop and connection methods of main, Client_Input, Client_Output, and finally the initial version of the server.

[java] @Override

public void simpleUpdate(float tpf) {

shipList.get(0).updating(tpf);

if (remote_test) {

shipList.get(1).updating(tpf);

//add new ships here

}

if (!remote_test) {

if(asteroid_test) {

for (int i = 0; i < 5; i++) {

if (Asteroids == null) {

targetLoc = new Vector3f(-20f + (float) generator.nextInt(40), 0,

-20f + (float) generator.nextInt(40));

tempAngle = (generator.nextFloat()) % FastMath.TWO_PI;

tempQ.fromAngles(0, tempAngle, 0);

Asteroids = new Asteroid(i, “”, projTarget, ’ ', targetLoc,

tempQ, shootables, .015f);

Asteroids.init();

}

else {

Asteroids.update();

if (!Asteroids.status())

Asteroids = null;

}

}

}

}

}

private void connectToServer() throws IOException {

client = new Socket(InetAddress.getByName(IP_ADDRESS), PORT);

}

private void getStreams() throws IOException {

output = new ObjectOutputStream(client.getOutputStream());

input = new ObjectInputStream(client.getInputStream());

}

private void closeConnection() {

try {

output.close();

input.close();

client.close();

}

catch (IOException e) {

System.out.println(“Close error”);

}

}[/java]

The shipList updating methods (in the case of those ships controlled remotely), calls a getPacket command which calls for a string packet via a buffer that also uses a concurrenthash map, it’s very much like the serverbuffer I already listed.

client_input

[java]public class Client_Input implements Runnable {

private ObjectInputStream inputc;

private ThreadBuffer buffer;

// private boolean stop = false;

public Client_Input(ObjectInputStream i, ThreadBuffer j) {

inputc = i;

buffer = j;

}

public void run() {

String message = “”;

do {

try {

message = (String) inputc.readObject();

System.out.println(“Input:” + message);

processMessage(message);

}

catch ( ClassNotFoundException e ) {

System.out.println(“Message error”);

}

catch ( IOException e ) {

System.out.println(“Read IO Error”);

System.exit(0); //how can I do this nicely?

}

} while (true);

}

private void processMessage(String input) {

Vector3f move = new Vector3f();

Vector3f rot = new Vector3f();

boolean fire;

Packet data = new Packet();

int id, count;

try {

StringTokenizer st = new StringTokenizer(input);

//id of ship

id = Integer.parseInt(st.nextToken());

//count of packet

count = Integer.parseInt(st.nextToken());

//x y z translation of input

move.x = Float.parseFloat(st.nextToken());

move.y = Float.parseFloat(st.nextToken());

move.z = Float.parseFloat(st.nextToken());

//x y z rotation of input

rot.x = Float.parseFloat(st.nextToken());

rot.y = Float.parseFloat(st.nextToken());

rot.z = Float.parseFloat(st.nextToken());

//fire

fire = Boolean.valueOf(st.nextToken());

data.id = 1;

data.count = count;

data.translate = move;

data.rotate = rot;

data.fire = fire;

// System.out.println("Client 1 - " + data);

buffer.set(id, data);

}

catch ( NullPointerException npe) {

System.out.println (“Null Pointer Process Message”);

System.out.println("Input: " + input);

}

}

}[/java]

client output

[java]public class Client_Output implements Runnable {

private int id;

private ObjectOutputStream output;

private int WAIT_SEND;

private float tx = 0, ty = 0, tz = 0, rx = 0, ry = 0, rz = 0;

private int transform_count = 0;

private boolean fire = false, no_input = true;

private ThreadBuffer buffer;

private Thread clientI;

private int count = 1;

public Client_Output (ObjectOutputStream o, ThreadBuffer input, int wait) {

id = 1;

output = o;

buffer = input;

WAIT_SEND = wait;

}

public void run() {

try {

output.flush();

} catch (IOException ex) {

Logger.getLogger(Client_Output.class.getName()).log(Level.SEVERE, null, ex);

}

}

public void packetData(char where, boolean b_input, float val) {

switch (where) {

case ‘X’: //move x

tx += val;

transform_count++;

break;

case ‘Y’: //move y

ty += val;

transform_count++;

break;

case ‘Z’: //move z

tz += val;

// System.out.println(transform_count + " - TZ: " + tz + " VAL: " + val);

transform_count++;

break;

case ‘x’: //rot x

rx += val;

transform_count++;

break;

case ‘y’: //rot y

ry += val;

transform_count++;

break;

case ‘z’: //rot z

rz += val;

transform_count++;

break;

case ‘f’: //fire

fire = b_input;

transform_count++;

break;

case ‘!’: //exit

transform_count = -1;

sendData(“QUIT CONNECTION::EOF”);

// closeConnection();

break;

case ‘?’: //bogus

sendData(“NADA”);

break;

default:

transform_count = -1;

System.out.println(“Default Case reached”);

sendData(“QUIT CONNECTION::EOF”);

// closeConnection();

break;

}

if (transform_count != -1) {

if (transform_count >= WAIT_SEND) {

sendData();

reset();

}

}

}

private void sendData() {

String message = id + " " + count + " " + tx + " " + ty + " " + tz + " " +

rx + " " + ry + " " + rz + " " + fire;

try {

// System.out.println(count + " :Data Rcvd Val: " + tz);

output.writeObject(message);

output.flush();

count++;

}

catch ( IOException e) {

System.out.println(“send error”);

}

}

private void sendData(String message) {

try {

output.writeObject(message);

output.flush();

}

catch ( IOException e) {

System.out.println(“send error”);

}

}

private void reset() {

tx = 0; ty = 0; tz = 0; rx = 0; ry = 0; rz = 0;

transform_count = 0;

fire = false;

}

}[/java]

And the server

[java]import java.net.;

import java.io.
;

public class Server{

private ServerSocket Sserver;

private Player players[];

private ServerBuffer ServBuff;

public Server(){

players = new Player [ 2 ];

//set up Server Socket

try{

Sserver = new ServerSocket (12345, 1);

}

catch ( IOException e ) {

e.printStackTrace();

System.exit( 1 );}

System.out.println(“WaitingForConnection”);

}

public void execute(){

ServBuff = new ServerBuffer(2);

for (int i = 0; i < players.length; i++ ) {

try {

players[ i ] = new Player( Sserver.accept(), i, ServBuff);

players[ i ].start();

}

catch( IOException e) {

e.printStackTrace();

System.exit( 1 );

}

}

}

public static void main ( String args[] ){

Server game = new Server();

game.execute();

}

}

class Player extends Thread{

private Socket connection;

private ObjectOutputStream output;

private ObjectInputStream input;

private int count;

private ServerBuffer buffer;

protected boolean threadSuspended = true;

// private Server control;

public Player ( Socket s, int num, ServerBuffer buf){

connection = s;

count = num;

buffer = buf;

try{

input = new ObjectInputStream( connection.getInputStream() );

output = new ObjectOutputStream( connection.getOutputStream() );

}

catch( IOException e ){

e.printStackTrace();

System.exit( 1 );

}

//control = g;

}

public void run(){

//boolean done = false;

String message = “”;

String new_message = “”;

System.out.println(“Connection successful”);

do {

try {

message = (String) input.readObject();

System.out.println(“Thread” + count + ": " + message);

}

catch ( ClassNotFoundException e) {

System.out.println(“message error”);

}

catch( IOException e ) {

e.printStackTrace();

System.exit( 1 );

}

if(count == 1)

buffer.set(0, message);

else

buffer.set(1, message);

new_message = buffer.get(count);

//String new_message = message.replaceFirst(“1”, “0”);

System.out.println("Sending Back: " + new_message);

try {

output.writeObject(new_message);

}

catch( IOException e ) {

e.printStackTrace();

System.exit( 1 );

}

} while (!message.equals(“QUIT CONNECTION::EOF”));

closeConnections();

}

private class ShipInfo {

public float tx = 0, ty = 0, tz = 0;

public float rx = 0, ry = 0, rz = 0;

public boolean fired = false;

@Override

public String toString() {

return "X: " + tx + " Y: " + ty + " Z: " + tz +

“rX: " + rx + " rY: " + ry + " rZ: " + rz +

" Fired:” + fired;

}

}

private void closeConnections() {

try {

output.close();

connection.close();

}

catch (IOException e){

System.out.println(“Close Error”);

}

}

}[/java]

Well, the only issue that I really see is that one thread handles the input and the output for a single connection. And maybe that’s ok since I don’t know how many clients you have. If you have more than one client, I don’t really see how client A’s message would get to client B but I do know that it would only be sent after client B receives a message.



Blocking IO is ok. The thread just waits until data is read… which is fine if you have multiple threads. Definitely stay away from NIO based networking if you can. The threading implications are very tricky and most people get it wrong and then wonder why their stuff randomly fails. Old Spider Monkey had the same issues before I rewrote it.



A typical server architecture where one client’s message gets broadcast to all of the other clients would have a queue per client and two threads per client. The reading thread just waits for a new message and when received just adds it to everyone’s queue. The writing thread just waits for data in the queue and sends it out.

Well, my partner made the modifications to Server (adding threads)…and it still hasn’t made any substatial changes. Client Input still only fires when something goes out. I’ll put it on the bottom of this message, but I wonder if it’s the point.



One last test when we threw in the towel tonight, I put a counter increment in the Client_Input above and a message before the readObject, after the readObject, and after the processMessage. It always stops before readObject when I stop sending input.



Is there any way to debug a multiplayer game line by line? Because I wonder if I have to do that to figure out exactly what is going on.



Could it be for some reason be the engine?



I am at a complete loss here.



Here is the updated server code, the other pieces of the client code remain unchanged outside of the added debug statements.



[java]import java.net.;

import java.io.
;



public class Server{

private ServerSocket Sserver;

private Player players[];

private ServerBuffer ServBuff;



public Server(){





players = new Player [ 2 ];



//set up Server Socket

try{

Sserver = new ServerSocket (12345, 1);

}

catch ( IOException e ) {

e.printStackTrace();

System.exit( 1 );}



System.out.println(“WaitingForConnection”);

}



public void execute(){

ServBuff = new ServerBuffer(2);

for (int i = 0; i < players.length; i++ ) {

try {

players[ i ] = new Player( Sserver.accept(), i, ServBuff);

players[ i ].start();

}

catch( IOException e) {

e.printStackTrace();

System.exit( 1 );

}

}



}



public static void main ( String args[] ){

Server game = new Server();

game.execute();

}

}

class Player extends Thread{

private Socket connection;

private ObjectOutputStream output;

private ObjectInputStream input;

private int count;

private ServerBuffer buffer;

protected boolean threadSuspended = true;

// private Server control;



public Player ( Socket s, int num, ServerBuffer buf){

connection = s;

count = num;

buffer = buf;





try{



input = new ObjectInputStream( connection.getInputStream() );

output = new ObjectOutputStream( connection.getOutputStream() );

}







catch( IOException e ){

e.printStackTrace();

System.exit( 1 );

}

//control = g;

}





public void run(){

//boolean done = false;

String message = “”;

getInput in;

writeOutput out;

String new_message = “”;

System.out.println(“Connection successful”);







do {

in = new getInput();

in.start();

message = in.getInput();

System.out.println(“Thread” + count + ": " + message);

out = new writeOutput();

out.start();

new_message = out.writeOutput();

System.out.println("Sending Back: " + new_message);

} while (!message.equals(“QUIT CONNECTION::EOF”));

closeConnections();



}

class getInput extends Thread{

public String getInput(){

String message = null;

try{

message = (String) input.readObject();

}

catch ( ClassNotFoundException e) {

System.out.println(“message error”);

}

catch( IOException e ) {

e.printStackTrace();

System.exit( 1 );

}

if(message.length()>0){

if(count == 1)

buffer.set(0, message);

else

buffer.set(1, message);

}

return message;

}

}

class writeOutput extends Thread{



public String writeOutput(){

String new_message = buffer.get(count);

//String new_message = message.replaceFirst(“1”, “0”);



try {

output.writeObject(new_message);

}

catch( IOException e ) {

e.printStackTrace();

System.exit( 1 );

}

return new_message;

}

}



private class ShipInfo {

public float tx = 0, ty = 0, tz = 0;

public float rx = 0, ry = 0, rz = 0;

public boolean fired = false;



@Override

public String toString() {

return "X: " + tx + " Y: " + ty + " Z: " + tz +

“rX: " + rx + " rY: " + ry + " rZ: " + rz +

" Fired:” + fired;

}

}







private void closeConnections() {

try {

output.close();

connection.close();

}

catch (IOException e){

System.out.println(“Close Error”);

}

}

}[/java]

I’m not sure I understand.



readObject() will block until it has data to return. Which is fine if all that thread is doing is reading. It will block until there is data and then dispatch it to something else.



If you architecture is supposed to be broadcasting the input from one player to all other players then you need two threads per player. There is no way around this. And I’ve described this in my last message how it would typically work if you need to try that.

I didn’t write this code (though my reading of it seems to suggest that is what it should do), but I believe that was the intent and well, at least been attempted to be implemented. But I guess we’re messing it up somehow.



We plan to just have this game be two player, the deadline approaches and we still have quite a bit more to accomplish with the server once this communication issue is fixed.



Perhaps the threads are pointing to the wrong places…because it’s extremely curious why it requires me to be sending something there to get something back. I guess it’s difficult to parse and gain the meaning when it’s not right in front of you. But man, it’s hard to debug with the flood of print statements that even come out of a short play session and I wonder if any other method of debugging a networked/multithreaded app is even possible



I think tomorrow, I"m going to suggest we put this code (and the I/O client code if necessary) into a box and make something new. I think my first experiment is to go back to something that did work, a simple server that just fires back the threads it receives. I think I’ll tweak it to randomly send different commands and see if that works, if it does, build from there, if not, it should be easier to debug to figure out how to make it work.



Thank you for you help and your patience, I’m sure once this is over I’ll look at your answers and plead with my partner to give the back of my head a few smacks to fix my dense skull. :stuck_out_tongue:

pegasusjf said:
because it's extremely curious why it requires me to be sending something there to get something back.


And I may be misunderstanding the problem and/or your description of what "sending something there" and "getting something back" means. Because your code says precisely that.

A Player waits forever to get a message and then sends it out. Then it waits forever to get another message and then sends it out.

But there is something I don't understand about the setup because I don't know how player 2 ever sees player 1's movement or whatever is going over the wire.

So, yeah, maybe take a step back. When you return to it maybe look at the interfaces inside of the new Spider Monkey... specifically the network.kernel classes and try to imagine how they might be implemented with a two thread (reader and writer) per client setup.

Meaning, I have to interact with the local ship, by sending commands via the inputListener that work on the local level ship object and also sends to the packetData in client_output which sends a packet to the server after 10 commands are added together.



For some reason, client input only recieves input packets from the server when a player is interacting (pressing movement keys or the fire button) on the client. The data I’m getting from client clearly shows this, because there is a discernable jump in the packet numbers between interactions. The debug info created by the server shows the data going through it and going back, but the client Input readObject gate opens when I’m transforming the local ship.



this is confusing, since the ship object tha represents the remote player is always being called to get a packet in the update loop. It’s supposed to be continually asking the threadBuffer for data (and IIRC, it always does, but nothing new).



The Client_Input thread only interacts with this when it sends data to the Threadbuffer to be read by the remote objects, and it always should be getting data because if I’m reading the server debug messages right there is almost always something going through unless the other player isn’t touching anything.



That is the most I can describe about the problem we’re having . If this gives you any further insight, I’ll be grateful.

Ok, I misunderstood from the direction the posts went. So presumably the server is working as desired.



And if you were to add a println before the readObject() you see that println and it blocks until the user does something? Hits a key?

[java]

try {

message = (String) inputc.readObject();

System.out.println("Input:" + message);

processMessage(message);

}

[/java]



Who calls these Runnables and how is the passed ObjectInputStream setup, ie: where does it come from?

pspeed said:
Ok, I misunderstood from the direction the posts went. So presumably the server is working as desired.

And if you were to add a println before the readObject() you see that println and it blocks until the user does something? Hits a key?
[java]
try {
message = (String) inputc.readObject();
System.out.println(&quot;Input:&quot; + message);
processMessage(message);
}
[/java]


Yes, I surrounded all those commands with printlns, it stops before the read until user input.


Who calls these Runnables and how is the passed ObjectInputStream setup, ie: where does it come from?

The socket and I/O streams are made in main (the simpleApplication class) as well as the Client Input and Client Output threads are created there. Client_Output is called directly via main when user input is made (in the inputListeners). Client_Input is called indirectly via the ThreadBuffer via Ship2D in the getPacket() method that is called in the updating method (which simply is set in the update loop in main) if the ship object is remotely controlled (has the isLocal bool set to false).

I think that's it, I believe I posted the entirity of the code if not in this thread, the previous thread and client hasn't changed much as far as network I/O goes.

Hope is kindled...Thank you.

I thought Client_Input was called from a separate thread?

“Client_Input is called indirectly via the ThreadBuffer via Ship2D in the getPacket()”



The code above should work presuming there is not some odd way that the ObjectInputStream is setup… but since I haven’t seen that code, I can’t comment.



Anyway, I guess the bottom line is that this isn’t really JME related or user input related… even though that’s what’s triggering it in your case. There is some contention between your input and output processing on the client. And that kind of thing is tough to debug from here especially having to jump between forum topics.



For example, I bet if you removed the key input processing and simply send an update every 5 seconds you would see your I/O stall every five seconds.

Here is Main, Hope that helps

[java]package mygame;

import com.jme3.app.SimpleApplication;

import com.jme3.material.Material;

import com.jme3.renderer.RenderManager;

import com.jme3.scene.;

import com.jme3.scene.shape.Box;

import com.jme3.math.
;

import com.jme3.input.KeyInput;

import com.jme3.input.controls.;

//import java.lang.Thread;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.
;

import java.io.;

import java.net.
;

public class Main extends SimpleApplication {

protected Spatial model;

protected Spatial model2;

//add more copies here

private Vector3f targetLoc;

private Random generator;

private final int INPUT_HOLD = 10;

private final int NUM_PLAYERS = 2; //change this to add more players

private final int GUESS_OBJECT_COUNT = 20;

private final float FORWARD_THRUST = .025f;

private final float BACK_THRUST = -.0125f;

private final String IP_ADDRESS = “131.230.133.28”; //“127.0.0.1”;//

// private final String IP_ADDRESS = “127.0.0.1”;//

private final int PORT = 12345;

private Node shipNode, shootables;

private Ship2D testShip;

private ArrayList<Ship2D> shipList = new ArrayList<Ship2D>(NUM_PLAYERS);

private boolean isRunning = true;

private Material projMain, projRem, projTarget;

private Client_Output client_O;

private Client_Input client_I;

private ThreadBuffer buffer;

private Socket client;

private ObjectInputStream input;

private ObjectOutputStream output;

private boolean remote_test = true;

private boolean asteroid_test = false;

private String debug_text = "n";

private Asteroid[] Asteroids;

private float tempAngle;

private Quaternion tempQ = new Quaternion();

// private float temp_add = 0;

public static void main( String[] args) {

Main app = new Main();

app.start();

}

@Override

public void simpleInitApp() {

//camera settings

cam.setParallelProjection(true);

cam.setLocation(new Vector3f(0f, 100f, 0f));

cam.lookAt(new Vector3f(0,0,0), Vector3f.UNIT_Y);

generator = new Random(System.nanoTime());

//node attachments

shipNode = new Node(“shipNode”);

shootables = new Node(“shootables”);

//load models

model = assetManager.loadModel(

“Models/fb_test_init.obj” );

if (remote_test) {

model2 = assetManager.loadModel(

“Models/fb_test_init.obj” );

//copy the above for each new declariation

}

Material main = new Material(

assetManager, “Common/MatDefs/Misc/Unshaded.j3md”);

main.setTexture(“ColorMap”,

assetManager.loadTexture(“Textures/fb_test_init_Default_color.jpg”));

Material remote = new Material(

assetManager, “Common/MatDefs/Misc/Unshaded.j3md”);

remote.setTexture(“ColorMap”,

assetManager.loadTexture(“Textures/fb_test_init_Default_remote_color.jpg”));

projMain = new Material(assetManager,

“Common/MatDefs/Misc/SolidColor.j3md”); // create a simple material

projMain.setColor(“m_Color”, ColorRGBA.Blue);

projRem = new Material(assetManager,

“Common/MatDefs/Misc/SolidColor.j3md”); // create a simple material

projRem.setColor(“m_Color”, ColorRGBA.Red);

projTarget = new Material(assetManager,

“Common/MatDefs/Misc/SolidColor.j3md”); // create a simple material

projTarget.setColor(“m_Color”, ColorRGBA.Green);

model.setMaterial(main);

if (remote_test) {

model2.setMaterial(remote);

//set materials here for new ships

}

//server connect

if (remote_test) {

try {

connectToServer();

getStreams();

} catch (IOException ex) {

System.out.println(“connect error”);

}

}

//other declariations

buffer = new ThreadBuffer(NUM_PLAYERS
GUESS_OBJECT_COUNT);

if (remote_test) {

client_O = new Client_Output(output, buffer, INPUT_HOLD);

client_I = new Client_Input(input, buffer);

}

shipList.add(new Ship2D(2,0,1,0,0,0,.25f, shipNode, shootables, model, projMain, 0, true, buffer));

if (remote_test) {

shipList.add(new Ship2D(-2,0,-1,0,0,0,.25f, shipNode, shootables, model2, projRem, 1, false, buffer));

//make new ships by copying the above line, change the id number and the translation

}

//local testing

if (!remote_test) {

if (asteroid_test)

Asteroids = new Asteroid[5];

}

testShip = shipList.get(0);

for (int i=0; i < shipList.size(); i++) {

shipList.get(i).start();

}

if (remote_test) {

ExecutorService threadExecutor = Executors.newCachedThreadPool();

threadExecutor.execute(client_O);

threadExecutor.execute(client_I);

threadExecutor.shutdown();

}

initKeys();

rootNode.attachChild(shootables);

rootNode.attachChild(shipNode);

}

private void initKeys() {

// You can map one or several inputs to one named action

inputManager.deleteMapping(“SIMPLEAPP_Exit”);

inputManager.addMapping(“Yaw_L”, new KeyTrigger(KeyInput.KEY_A));

inputManager.addMapping(“Yaw_R”, new KeyTrigger(KeyInput.KEY_D));

inputManager.addMapping(“Forward”, new KeyTrigger(KeyInput.KEY_W));

inputManager.addMapping(“Backward”, new KeyTrigger(KeyInput.KEY_S));

inputManager.addMapping(“Fire”, new KeyTrigger(KeyInput.KEY_SPACE));

inputManager.addMapping(“Move Bug”, new KeyTrigger(KeyInput.KEY_RCONTROL));

inputManager.addMapping(“Quit”, new KeyTrigger(KeyInput.KEY_ESCAPE));

// Add the names to the action listener.

inputManager.addListener(analogListener, new String[]{ “Roll_L”,

“Roll_R”, “Yaw_L”, “Yaw_R”, “Pitch_Up”, “Pitch_Down”, “Forward”,

“Backward”, “Quit”});

inputManager.addListener(actionListener, new String[] { “Fire”, “Move Bug” } );

inputManager.removeListener(flyCam);

}

private AnalogListener analogListener = new AnalogListener() {

public void onAnalog(String name, float value, float tpf) {

if (isRunning) {

if (name.equals(“Yaw_L”)) {

if (remote_test)

client_O.packetData(‘y’, false, value
speed);

testShip.transform(0, 0, 0, 0, value
speed, 0);

}

if (name.equals(“Yaw_R”)) {

if (remote_test)

client_O.packetData(‘y’, false, -value
speed);

testShip.transform(0, 0, 0, 0, -value
speed, 0);

}

if (name.equals(“Forward”)) {

if (remote_test)

client_O.packetData(‘Z’, false, FORWARD_THRUST
value
speed);

testShip.transform(0, 0, FORWARD_THRUST
value
speed, 0, 0, 0);

}

if (name.equals(“Backward”)) {

if (remote_test)

client_O.packetData(‘Z’, false, BACK_THRUST
value
speed);

testShip.transform(0, 0, BACK_THRUST
valuespeed, 0, 0, 0);

}

if (name.equals(“Quit”)) {

if (remote_test)

client_O.packetData(’!’, false, 0);

closeConnection();

stop();

}

}

}

};

private ActionListener actionListener = new ActionListener() {

public void onAction(String name, boolean keyPressed, float tpf) {

if (name.equals(“Fire”) && !keyPressed) {

if (remote_test)

client_O.packetData(‘f’, true, 0);

testShip.fire();

}

if (name.equals(“Move Bug”) && !keyPressed) {

if (remote_test)

System.out.printf("%s%s%sERROR Found!n%s%s%s",

debug_text,debug_text,debug_text,debug_text,debug_text,debug_text);

}

}

};



public void simpleUpdate(float tpf) {

shipList.get(0).updating(tpf);

if (remote_test) {

shipList.get(1).updating(tpf);

//add new ships here

}



// client_O.packetData(’?’, false, 0);

/


temp_add+=tpf;

if (temp_add >= (generator.nextFloat() % FastMath.PI)) {

client_O.packetData(‘Z’, false, FORWARD_THRUST*.05fspeed);

client_O.packetData(‘y’, false, -.05f
speed);

temp_add = 0;

}

*/

if (!remote_test) {

if(asteroid_test) {

for (int i = 0; i < 5; i++) {

if (Asteroids == null) {

targetLoc = new Vector3f(-20f + (float) generator.nextInt(40), 0,

-20f + (float) generator.nextInt(40));

tempAngle = (generator.nextFloat()) % FastMath.TWO_PI;

tempQ.fromAngles(0, tempAngle, 0);

Asteroids = new Asteroid(i, “”, projTarget, ’ ', targetLoc,

tempQ, shootables, .015f);

Asteroids.init();

}

else {

Asteroids.update();

if (!Asteroids.status())

Asteroids = null;

}

}

}

}

}



@Override

public void simpleRender(RenderManager rm) {

//TODO: add render code

}



private void connectToServer() throws IOException {

client = new Socket(InetAddress.getByName(IP_ADDRESS), PORT);

}



private void getStreams() throws IOException {

output = new ObjectOutputStream(client.getOutputStream());

input = new ObjectInputStream(client.getInputStream());

}



private void closeConnection() {

try {

output.close();

input.close();

client.close();

}

catch (IOException e) {

System.out.println(“Close error”);

}

}







}[/java]

Update: This works, it’s a expansion of my testing SimpleServer (which bounces the client packets back) to a server with an input and output thread where the output thread fires off a random command at the count.



Some minor questions:

  1. Is there a neater way to do timing?
  2. Is there a neater way to ensure everything dies and is collected properly. I kind of like C’s fork, wait/exit method of cleaning up processes. Though I imagine it would be much MUCH harder to have the same level of inter process communication.



    This is the first successful test.



    [java]/*TestServer
  • Jason Fairfield & Matt Coble
  • v - 0.25

    /





    import java.net.ServerSocket;

    import java.net.Socket;

    import java.io.
    ;

    import java.util.;

    import java.util.logging.Level;

    import java.util.logging.Logger;

    import java.util.concurrent.ExecutorService;

    import java.util.concurrent.Executors;



    public class TestServer {

    private ServerSocket server;

    private Socket connection;

    private ObjectOutputStream output;

    private ObjectInputStream input;

    private final int NUM_PLAYERS = 2;

    TestServerBuffer sBuffer;





    public void runServer() {

    try {

    server = new ServerSocket(12345, 1);



    while (true) {

    try {

    waitForConnections();

    getStreams();

    processConnections();

    }

    catch (EOFException e) {

    System.out.println("EOF Error");

    }

    finally

    {

    closeConnections();

    }

    }

    }

    catch (IOException ioe) {

    System.out.println("Server failed");

    }

    }



    private void waitForConnections() throws IOException

    {

    System.out.println("WaitingForConnection");

    connection = server.accept();

    }



    private void getStreams() throws IOException {

    output = new ObjectOutputStream( connection.getOutputStream());

    input = new ObjectInputStream( connection.getInputStream());

    }



    private void processConnections() throws IOException {

    System.out.println("Connection successful");

    sBuffer = new TestServerBuffer(NUM_PLAYERS);

    inputStream inputS = new inputStream(input, sBuffer, 1);

    outputStream outputS = new outputStream(output, sBuffer, 1);



    ExecutorService threadExecutor = Executors.newCachedThreadPool();



    threadExecutor.execute(outputS);

    threadExecutor.execute(inputS);

    threadExecutor.shutdown();



    do {



    } while (inputS.isRunning());

    outputS.setRunning(false);

    threadExecutor.shutdownNow();

    closeConnections();



    }



    private void closeConnections() {

    try {

    output.close();

    connection.close();

    }

    catch (IOException e){

    System.out.println("Close Error");

    }

    }



    public static void main(String[] args) {

    TestServer app = new TestServer();

    app.runServer();

    }



    private class inputStream implements Runnable {

    ObjectInputStream input;

    TestServerBuffer buffer;

    int player_key = 0;

    boolean running = true;

    String message;



    public inputStream (ObjectInputStream i, TestServerBuffer b, int pk) {

    input = i;

    buffer = b;

    player_key = pk;

    }



    public void run() {

    do {

    try {

    message = (String) input.readObject();

    System.out.printf("Input Thread: %sn", message);

    buffer.set(player_key, message);

    } catch (IOException ex) {

    Logger.getLogger(TestServer.class.getName()).log(Level.SEVERE, null, ex);

    } catch (ClassNotFoundException ex) {

    Logger.getLogger(TestServer.class.getName()).log(Level.SEVERE, null, ex);

    }

    } while (!message.equals("QUIT CONNECTION::EOF"));



    running = false;

    }



    public boolean isRunning() {

    return running;

    }

    }



    private class outputStream implements Runnable {

    ObjectOutputStream output;

    TestServerBuffer buffer;

    Random generator = new Random(System.nanoTime());

    int player_key = 0;

    private float timer = 0;

    private int counter = 100;

    boolean running = true;

    String message;



    public outputStream (ObjectOutputStream o, TestServerBuffer b, int pk) {

    output = o;

    buffer = b;

    player_key = pk;

    }



    public void run() {

    while (running) {

    timer += generator.nextFloat() % .6f;

    if (timer >= 350000) {

    message = makeCommand(generator.nextFloat() % .25f);

    System.out.println("Sending Back: " + message);

    try {

    output.writeObject(message);

    } catch (IOException ex) {

    Logger.getLogger(TestServer.class.getName()).log(Level.SEVERE, null, ex);

    }

    timer = 0;

    }

    }

    }



    private String makeCommand(float input) {

    int chooser = generator.nextInt(5);

    counter++;

    switch (chooser) {

    case 0: //turn left

    // System.out.println("Turning Left");

    return 1 + " " + counter + " 0 0 0" + " 0 " + input + " 0 " + "false";

    case 1: //turn right

    // System.out.println("Turning Right");

    return 1 + " " + counter + " 0 0 0" + " 0 " + -input + " 0 " + "false";

    case 2: //forward

    // System.out.println("Forward");

    return 1 + " " + counter + " 0 0 " + .025
    input + " 0 0 0 " + "false";

    case 3: //backward

    // System.out.println("Backward");

    return 1 + " " + counter + " 0 0 " + -.025*input + " 0 0 0 " + "false";

    case 4: //fire

    // System.out.println("FIRE!");

    return 1 + " " + counter + " 0 0 0" + " 0 0 0 " + "true";

    default:

    // System.out.println("Default");

    return 1 + " " + counter + " 0 0 0" + " 0 0 0 " + "false";

    }

    }



    public void setRunning(boolean i) {

    running = i;

    }



    }



    private class ShipInfo {

    public float tx = 0, ty = 0, tz = 0;

    public float rx = 0, ry = 0, rz = 0;

    public boolean fired = false;



    @Override

    public String toString() {

    return "X: " + tx + " Y: " + ty + " Z: " + tz +

    "rX: " + rx + " rY: " + ry + " rZ: " + rz +

    " Fired:" + fired;

    }

    }

    }[/java]