Network Message Getting Garbled

Started by
3 comments, last by Shrodinger's Sloth 10 years, 8 months ago

8/15/13: I realize now I may have made this post too complex/messy and no one wants to take the time to understand what it is I'm saying. I will summarize everything right here:

/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\

I am sending an object containing an ArrayList over a network from a server to a client, but instead of getting the data sent, the client appears to be re-initializing the object.

/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\

After several days of testing, it has come to my attention that .. I'm stumped. My program is getting fairly complex so I'm going to try to post only the relevant information. This is a 2D networked game with a client/server model.

Ok, going to start with my "Area" object. Basically it is two things.. a 2d array of tiles (not the problem) and an ArrayList of entities (objects, players, npc's .. game objects).


import java.io.Serializable;
import java.util.ArrayList;

public class Area implements Serializable 
{
	protected static final long serialVersionUID = 1112122200L;
	private int size;
	private Tile[][] tileData;
	ArrayList<Entity> entities;
	
	public Area(int size)
	{
		this.size = size;
		entities = new ArrayList<Entity>();
		tileData = new Tile[size][size];
		
		for(int i = 0;i<size;i++)
		{
			for(int j = 0;j<size;j++)
			{
				tileData[i][j] = new Tile();
			}
		}
	}
	
	public int getSize()
	{
		return size;
	}
	
	public Tile getTile(int x, int y)
	{
		return tileData[x][y];
	}
	
	public ArrayList getEntities()
	{
		return entities;
	}
	
	public void setEntitits(ArrayList ents)
	{
		System.out.println("Before");
		for(int i = 0;i<entities.size();i++)
		{
			Entity ent = (Entity) entities.get(i);
			System.out.println("Name: "+ent.getName() +" X: " + ent.getX() + " Y: " + ent.getY());
		}
		this.entities = ents;
		System.out.println("After");
		for(int i = 0;i<entities.size();i++)
		{
			Entity ent = (Entity) entities.get(i);
			System.out.println("Name: "+ent.getName() +" X: " + ent.getX() + " Y: " + ent.getY());
		}
	}
	
	public Entity newEntity(int type)
	{
		Entity ent = null;
		
		switch(type)
		{
		case Entity.GAMEOBJECT:
			break;
		case Entity.NPC:
			break;
		case Entity.PLAYER:
			ent = new Player("Bob", 20, 20, 0);
			break;
		default:
		}
		if(ent!=null)
		{
			entities.add(ent);
		}
		return ent;
	}
	
	public void addEntity(Entity player) 
	{
		entities.add(player);
	}
	
	public void removeEntity(Entity entity)
	{
		entities.remove(entity);
	}


}

Then there's the Entity object (what fills the entities ArrayList of an area)


import java.io.Serializable;


public abstract class Entity implements Serializable 
{
	protected static final long serialVersionUID = 1112122200L;
	public static final int PLAYER = 0,
		    GAMEOBJECT = 1,
		    NPC = 2;
	private String name;
    private int x;
    private int y;
    private int imageURN;
    
	public Entity(String name,int x, int y, int imageURN)
	{
		this.name = name;
		this.x = x;
		this.y = y;
		this.imageURN = imageURN;
	}

// CUT OUT THE GETTERS AND SETTERS FOR EACH VARIABLE
}

I wasn't sure if this would be important too, it's pretty basic. Just the player object that extends the entity object.


import java.io.Serializable;

public class Player extends Entity implements Serializable 
{
	protected static final long serialVersionUID = 1112122200L;
	public Player(String name, int x, int y, int imageURN) 
	{
		super(name, x, y, imageURN);
		
	}
} 

The main server has a single "area" object which, by default, contains an array of default tiles and an empty ArrayList of entities.

When a player connects, the server creates a new player object VIA the area.addEntity method


Entity player = area.newEntity(Entity.PLAYER);
t.setPlayer(player);

This creates a new player entry with some defaults of name = "Bob", x = 20, y = 20, imageURN=0.

Ok, so now the area has an entity in it. The server then takes the area and sends it to the client. The client gets the area (having had nothing before) and draws the area accurately - the entity exists and has coordinates of 20,20.

Here comes the trouble: In the client, the player preses the down button. This sends a message to the server, "Move my (player) entity down". The server gets the message, gets the old coordinates, modifies them as needed, and sends then back to the client. Here is the server code handling this:


case networkMessage.MOVE:
int direction = m.getDirection();
switch(direction)
{
case 0:
   y = -1;
   x = 0;
   //System.out.println("Move UP: ("+x+","+y+")");
   break;
case 1:
   y = 0;
   x = 1;
   //System.out.println("Move RIGHT: ("+x+","+y+")");
   break;
case 2:
   y = 1;
   x = 0;
   //System.out.println("Move DOWN: ("+x+","+y+")");
   break;
case 3:
   y = 0;
   x = -1;
   //System.out.println("Move LEFT: ("+x+","+y+")");
   break;
default:
   y = 0;
   x = 0;
}
System.out.println("Coords were: "+player.getX()+","+player.getY());
Entity oldPlayer = player;
player.setX(player.getX()+x);
player.setY(player.getY()+y);
area.removeEntity(oldPlayer);
area.addEntity(player);
System.out.println("New coords: "+player.getX()+","+player.getY());
ArrayList entities = area.getEntities();
m = new networkMessage(networkMessage.AREADATA,0,0,entities);
for(int i = 0;i<entities.size();i++)
{
   Entity ent = (Entity) entities.get(i);
   System.out.println("Name: "+ent.getName() +" X: " + ent.getX() + " Y: " + ent.getY());
}
outToClient.writeObject(m);
break;

Yes.. I know it's messy as hell.. it got worse and worse the more I tried to debug it. I realize I am omitting the networkMesage object.. dont know if it's important. Just assigns values with a multitude of constructors and has a get for every value.

Also, please note I completely understand this is not the best way to handle movement and probably not the best way to send the response to the client. I'm taking an approach where I do things in an un-optimal way first and refine them later. Please don't judge me too harshly.

Ok, so the output here is


/127.0.0.1 Type: Move (4)
Coords were: 20,20
New coords: 20,21
Name: Bob X: 20 Y: 21

So.. it knows the old coordinates were 20,20.. it knows the new coordinates are 20,21. It sends the entities list back to the client.

Here's the client code for getting this entities list.


case networkMessage.AREADATA:
   for(int i = 0;i<m.getEntities().size();i++)
   {
      Entity ent = (Entity) m.getEntities().get(i);
      System.out.println("Name: "+ent.getName() +" X: " + ent.getX() + " Y: " + ent.getY());
   }
   area.setEntitits(m.getEntities());
   iDrawListener.redraw();
   break;

And that's it. The output here:


Name: Bob X: 20 Y: 20

So even though the server modified the entities list, the client still has the old list.

The only reason I can come up with for this is that the client is somehow .. creating a new entity instead of using the old one. It's creating a new entity and getting the default stuff. I have no idea how.. and before I make all my code look worse than it already does, I thought I'd try to get some help.

Some key points:

- I don't fully understand serialable. It's a way to reference an object as a serial to make it easier to transfer over a network, I guess.

- Object casting .. not 100% on it. Getting there. But when I do something like


Entity ent = ents.get(i); 

and Eclipse makes me change it to


Entity ent = (Entity) ents.get(i); 

I get a little lost.. and worried. I'm not sure why I need to cast an entity as an entity when it's already an entity. A part of me thinks this is the problem, but I don't know why.

Anyways, before I flood this post (too late), I'll leave it here. If this is too unclear, please let me know what I can do to make it clearer.

Thanks,

Jeremy

NOTE: I have left many lines of code out of this in an attempt to focus on what I believe to be important. If there are parts of the code that I should inclide, please let me know.

Advertisement

Hello,

I have not programmed in java in a long while but is it a problem that you do not specify the type of ArrayList for getEntities()?


 class Area
    public ArrayList getEntities()
    {
        return entities;
    }
Should be?
    public ArrayList<Entity> getEntities()
    {
        return entities;
    }

Also, if this is not part of the main problem, is it possible your client is reading the same initial data from the server?

I can't tell from the code but it's plausible you are rereading the initial state from the server and not the updated version.

Hope this helps.

It'll have perfect realistic physics, full dynamic lighting, and neural network AI, and it'll run at 0.00001 fps, and it'll be amazing!

Hello,

I have not programmed in java in a long while but is it a problem that you do not specify the type of ArrayList for getEntities()?


 class Area
    public ArrayList getEntities()
    {
        return entities;
    }
Should be?
    public ArrayList<Entity> getEntities()
    {
        return entities;
    }

Also, if this is not part of the main problem, is it possible your client is reading the same initial data from the server?

I can't tell from the code but it's plausible you are rereading the initial state from the server and not the updated version.

Hope this helps.

Thank you for reply. The first is a scenario I had not considered and seems very possible. The only part that makes me question it is that right before I send the data to the client, I use the same exact get methods on the server and confirm that it is sending the correct data. I will try this when I get home though.

As far as re-reading the initial state, I think that part is all set. I put lots of output in my client and can see that it is receiving the correct type of message and interpreting it correctly. There are two messages involved. The first is an initialization message that sends all the current data on the server when a client connects (contains everything in the area.. the tile data and the entities list) and I am certain it is only called once. The second is a smaller update message that just contains the entities and is the one that is not working.

Thinking about this further, I'm not even sure the first initialization message containing the full area is working correctly. It seems like the client is just getting an Area object with an entity in its ArrayList but is just re-initializing it instead of pulling the correct information. When I get home, I will put some output in the constructors to see if they're getting called again for some reason when the client gets the message.

Edit: Doing lots of Google searches to see if I can find any information. Many people seem to indicate that it is a really bad practice to send an object over a network and instead I should be sending primitive data types. I'm already sending my networkMessage over the network.. should I not do this? That seems to be really common and my code will be a complete mess if I can't use that.

As far as the area and entity data, I suppose I could break it down into several messages. If I give each entity a URN, I can ensure that synchronization goes smoothly.

It does concern me a little bit. If an entity has a URN, a name, an x coord, a y coord and an image URN.. it's 5 pieces of data, and this is just the basics (the entity object will get much more complex). This will get INCREDIBLY messy when the entity is complex and I need to break it all down to primitive types and re-construct it. Is that really the approach I should be taking?

@LostIdiot: No go on this. I did find something very interesting though by accident.

1) I connect one client, move around a bunch. I don't see the changes on the client but I can see the server is updating correctly.

2) I connect another client. The server sends the full map to the client and I can see the CORRECT position of the first client. I have no idea why. I even tried changing my server to re-send the entire map to the client every time someone moves but that doesn't work.

Somehow the initial send works and every subsequent send does not.

Edit: I ended up setting up a URN for each entity and sending "update" messages to change the position of a specific URN using 3 ints rather than sending over objects. It might make my work harder later but it works for now. I guess unless anyone else has any input on this, we can call this "solved".

Sorry to say I can't help any further here.

I've not touched networking at all yet. (My project plans are entirely antisocial.)

I'm glad you have a working solution, like you said not looking for optimal just working.

Maybe you can check out an opensource message passing API for some better ideas later.

It'll have perfect realistic physics, full dynamic lighting, and neural network AI, and it'll run at 0.00001 fps, and it'll be amazing!

This topic is closed to new replies.

Advertisement