views:

1498

answers:

4

Leading on from my previous questions I am going to try and clarify one of the problems I am facing.

I am trying to create a multiplayer version of PacMan.

A simple overview of how i've made my application is that I have a server which has a ServerSocket which is constantly listening on a specific port. Once a client connects it creates a thread in which all the transferring of data takes place. It first listens for the Direction in which the client wants to move and then sends the updates back to the client. On the client side it's the reverse of this sending the direction and then waiting for the update to be received. This is done using ObjectOutput/Input Streams. There is also a game class which has all the game logic and this has the game loop which each looping calls each of the connected players serverThread.exchangeDatas() method.

My game works ok with one client connected but as soon as another client joins (locally is all that i've tested, but this should work right?) It crashed my game in the Server thread producing the following error java.io.StreamCorruptedException: invalid type code: 00.

Is the code that trips this exception

        Object o = in.readObject();
        if (o instanceof Direction)
        {
            // Get new location
            player.setDirection((Direction) o);
        //System.out.println("Got: " + (Direction)o + " new location: " + player.getNextLocation());
        }

        // Send gameState

        //System.out.println("Sending data");
        Data data = new Data(game.getGameState(), game.getScared(), game.getScareLeft(), player.getLives(), player.getScore());
        out.flush();
        out.writeObject(data);
        out.flush();
        out.reset();

Server code

ServerThread code (the one that crashes in the exchangeData method

Client Controller code

Game loop code - line 42 is what calls the exchange data method

Any help would be amazing with these.

http://www.mediafire.com/download.php?nwwqmzywfom

here's a download to both the client and the server (they are netbeans projects)

Sorry for my messy code =[

+1  A: 

Multithreading needs synchronisation. Are you using a synced stream (or syncing the access to the stream yourself)?

Update: The code sample given may very well be called from multiple threads. If multiple threads access the same stream, errors like the ones you are experiencing may very well occur.

I suggest having a look at http://java.sun.com/docs/books/tutorial/essential/concurrency/sync.html.

By marking a method/class synchronized, the runtime will make sure that this method/class will be active in no more than 1 thread at the same time. This is actually a vast and difficult topic, so take some time to learn the basics and make sure you understand the basic problem. :)

mafutrct
I've not done anything like that - do you have any tutorials? is this to stop the threads jumping in front of others?
Malachi
Yes, it stops 2 threads from doing something at once. That said, you aren't accessing your streams or sockets from multiple threads as far as I can see, so I don't think that's your problem.
MBCook
+2  A: 

This is hard to debug without the rest of the code, so I'm going to post my first through and play around a little. Why are you calling .reset() on your streams? That would move the read position in the stream back to the start, which isn't what you want I would think (never used ObjectInputStream).


OK, I took your code and hacked up some little stuff so it would compile, and it worked for me with the .reset(). It looks like what you were doing there is right.

I don't think I can help more without the rest of the code. Sorry.

PS: I assume that you have things Serializable, implementing Runnable, etc which the code you posted didn't have. But if you didn't have those things setup, then your code wouldn't compile in the first place.


After messing around with your code for 45 minutes, I can't get the error to happen. I had to hack a few things here and there to make it run (since it was missing a class or two and some of the code above) but I don't think I changed it (much). I'm not sure my changes would effect anything, it's a little hard to know.

I have two guesses as to what's happening. The first is going to be the .reset() thing again. If you haven't commented that out and see if it makes any difference.

So with that ignored, I'm going to take a different stab at what's going on here.

java.io.StreamCorruptedException: invalid type code: 00

I'm going to guess that the 00 might be a hint. My understanding is that there should be a signature byte being sent over (as proof that the object is what you say it is, so Strings don't get interpreted as doubles or PacManGameUIs) and obviously Java is unhappy because it's wrong.

But why 00? That's an interesting value. My guess (and this is a big guess) is that is the first byte of an integer (which is 4 bytes when sent with ObjectOutputStream.writeInt()). Perhaps you have an integer being sent that isn't being read before you call .readObject on the input stream? I'm not sure if that would cause the error, but it would be my best guess.

In a situation like this, there are two things to do. The first is to cut things down. Make two little class files that do nothing but start threads that communicate with each other in a cut down manner (without all the game management stuff) but using the same flow and logic. See if you can re-create the error there.

The other way (and this will be more instructive if you are daring) is to listen in on the conversation between the two program halves using something like WireShark or tcpdump. This will let you see the raw bytes you are sending across. While this can be confusing and hard to interpret, it should make figuring out if you are sending the wrong object easy. Sending an int will probably be around 4 bytes, but sending a large structure would take more. Through experimentation you should be able to figure it out. It's a pain, but it will let you know exactly what is being sent.

It may be hard to snoop on the loopback interface (when a computer talks to it's self), at least on Windows with WireShark (I don't remember it being that easy), so it's often easiest to do this with two computers (one as server, one as client) so you can easily peek into the packets.

Sorry again that I can't be more helpful.

MBCook
I've been stuck on this problem forever and I can't see what's not working. I'll upload the code for you to have a look at and see if you can compile it. My code is really bad by the way...http://www.mediafire.com/download.php?nwwqmzywfom
Malachi
It's really bugging me. I took your approach and just sent Strings too and from a client and a server with the logic being processed in a central class and the same errors occured. So it must be something to do with how i've implemented it. Thanks for your help. I'll bug my lecturer i think!
Malachi
+1  A: 

Maybe a bit off topic but javagaming.org has a section for 'OnLine Game Development & Networking'. You might want to check that as well.

Petteri Hietavirta
A: 

Can I just ask what you expect to happen if it were possible for a second client to join? You're not storing the reference of that Thread object, when another iteration occurs the object no longer has a reference therefore the JRE would destroy the object, would it not?

while (true)
{
    System.out.println("Waiting for conneciton on port " + port);
    Socket client = listener.accept();
    System.out.println("Accepted a connection from: " + client.getInetAddress());
    Thread t = new Thread(new PacManServerThread(client, game));
    t.start();
}

Surely you need to to do something like

myGenericArray.add(t);

at the end of your loop otherwise once the object loses scope in the loop it would be destroyed?

Kezzer
Threads are not garbage collected until they are finished, as far as I know
Mario Ortegón
Inside the pacmanserver thread class it calls a method that creates a new player and passes reference to the main game of the thread that called it. this.player = game.playerJoinsGame(this); is this wrong?
Malachi