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.