Inventory Item inheritance Serialization

I’m trying to get my game working with multiplayer. In order to do that I need to sync some objects storing Items over the Network. The problem is in the serialization of different inherited classes. I’m not sure how I can best differentiate between inherited classes and serialize its extra values. My current (simplified) setup:

public class Inventory {
    Item[] items = new Item[20];
    //add, remove methods etc.
}
public class Item {
    public int itemID;
    public float weight;
    public float damage;
    public float Quality;

    public static byte[] Serialize(Item item) {
        byte[] bytes = new byte[];
        //write the values of the item to a byte array
        return bytes;
    }

    public static object Deserialize( byte[] bytes ) {
        Item item = new Item();
        item.damage = ..//read from byte array here and assign the damage value, same for other values
        return item;
    }
    //other stuff  methods
}
public class Weapon : Item {
    public float maxGivenDamage;
    public float damageVariance;
    public DamageType typeOfDamage;
    //other stuff  methods
}

Now the problem is, how can I make difference between the base class and derrived classes, and (de)serialize it’s values. I’m not talking about the syntax side of this, I could just make a long list of if-statements considering every class that inherits Item, but I think that would be a messy way of doing it. Does someone know a better aproach to this?

what i do currently (and i am not the biggest pro):

i have one big savegame class where i store everything as Lists, ints, whatever. whenever something changes i save the whole savegame.
my gameobjects are created on load from a script and they link their data values to the savegame class object. this way the gameobjects are always linked correctly to the data structure but the data is seperated from the gameobject basically.

i am not sure if i explained that very will, i hope this helps at least a little bit.

I don’t think that will be a good way of doing that, especially when networking. Also, this is not really the problem here. The problem is how can I easily serialize and deserialize different values in different derrived classes. So in my example, How would I go best about recognizing the extra values stored in the Weapon class, indicate those values being inputted into the networking stream, and deserialize this data on the other side, making the new right class and assigning the right values.

Should I store a byte for the type of item, and then create a very long if-else statement block to check for the right Item type, create it and assign the values after that or is there a better way to do this?

I’m speaking in terms of polymorphism. How would I go about overriding a serialization method for example? I have some static methods in the base class to (try) handle (de)serialization right now, but if I could override a method that would be better. Then the next question arrises, how can I call the right method on the right derrived class from a plain data stream?

I don’t have my code in front of me, but I take a different approach. It’s been working well so far.

Basically, I have different datapackets that I used to communicate between the clients and server. Each datapacket communicates a specific thing from the server to the client, or from the client to the server. For example, I have
SC_AddItemToContainer → Tells all clients that an item is being added to a container.

So, let’s say that the server has added a sword to a container. The container is your inventory.
All items have a unique id, an integer. This sword’s happens to be 1235. The container happens to be you (your inventory), your unique is as 45.

I have a big ServerMessenger class, with a method like this:

public void ClientsAddItemToContainer(int itemId, int containerId)
{
    SC_AddItemToContainer dataPacket = new SC_AddItemToContainer(itemId, containterId);
    SocketComm.SendToAll(dataPacket); // This sends the datapackets to everyone.
}

Ok, sorry…I know this is sorta long winded…I’m getting there :slight_smile:

The SC_AddItemToContainer datapacket inherets from the DataPacket class. The datapacket class has a Serialize and DeSerialize method that the subclasses override.

The SC_AddItemToContainer datapacket subclass implements it like this:

public void Serialize(BinaryWriter writer)
{
    writer.write(_ItemId);
    writer.write(_ContainerId);
}

public void DeSerialize(BinaryReader reader)
{
    _ItemId = reader.ReadInt32();
    _ContainerId = reader.ReadInt32();
}

So basically…each datapacket knows how to serialize and deserialize its content. I don’t use reflection.

I think this works ok because…I don’t use polymorphism for game items, in my project. Neither my server OR client have any clue what a sword is. There is no sword class, and an IronSword class that inherits from a sword class, and a FancyIronSword class that inherits from the IronSword class. At the core, I have an Entity class. This holds things that are common to all items:

Position
OwnerId
Health
Quality
Weight
Damage
and so on
Nothing really inherit from Entity. Everything in the game is pretty much an entity, and can be communicated the same way.
All the properties of items are stored in a database, and loaded at server startup.

So how to different items do different things, if their data is so generic? Simple…abilities are assigned to items, and THOSE have specific features. That way I can assign a Chop that goes to all axes (the axe family). Or, I can make specific items have very unique abilities. The Glowing Sword of Goblin Fire may actually shoot a fireball, if the fireball ability is assigned to that specific type of sword.

Client selects “Fireball” ability assigned to a wand, and attacks.

The server knows about all abilites (drop is a transfer from a container to the world). Lead is a rope ability, an entity should follow another entity. Chop is a hatchet ability, causes damage on the target. So ability validation and determining the result actually happens on the server. Each ability has an effect assigned to it, the effect decides things like animation played, sound, result, etc.

What animations/effects to perform for abilities are determined by the client, because all abilities have an EFFECT id assigned.
So, the Fire wand may have a Small Fireball ability assigned. When players select that, the client sends a request to the server to perform that ability. At the same time, the client starts playing the animation.
Meanwhile, the server determines the outcome of the ability (sad goblin gets hit for 100 dmg) and queues up the results to happen 2 seconds in the future (it takes 2 secs for it to reach sad goblin, based on speed).
When those 2 seconds pass, server sends out a SC_ReduceEntityHealth damagepacket, with the goblin id attached. Or if its dead, server tells the clients to kill the poor goblin.

Generally…I stay away from polymorphism in a situation like this unless I really have to. For my project…the server doesn’t have a sword class, or a shield class.
Instead, an iron sword is just an entity with weight, value, quality, a unique id, an owner. It adds 0 armor when equipped. It can be equipped in the hands slots.
It has these abilities assigned to it:
Thrust
Slash

The Thrust ability does 5 physical damage, melee distance.
The Thrust effect is a combination of the ability, player, sword and effect stats. The player will play a Thrust animation, and reduce the target’s health by X amount as a result.

The server queues up the effect to happen based on how long it should take to perform (or hit its target). The server can compensate for latency in this. So if a fireball should hit your goblin 5 seconds (ok, its a slow fireball) from now, and player latency is 1 second, the server queues it up to happen 4 seconds from now instead.

I’m still working on this system, so it’s not 100% complete.

Well, I hope that big rambling post was somewhat useful! Good luck on your project, I know several of us have been working on the same type of thing.

One other thing…having a datapacket handle what gets serializes is fine for a few values, like this:

public void Serialize(BinaryWriter writer)

{

    writer.write(_ItemId);
    writer.write(_ContainerId);

}

 

public void DeSerialize(BinaryReader reader)

{

    _ItemId = reader.ReadInt32();

    _ContainerId = reader.ReadInt32();

}

But if you are serializing complex objects in several datapackets, make the objects serialize themselves.

SC_SendContainerItems(int containerId, List containerItems)

Where someone has opened a container, and the server needs to send a list of items in the container and the container id. You wouldn’t put the serializing of the all of the entity’s properties in the list in this datapacket, because it would look like this:

This is pseudocode, by the way.

public void Serialize(Binary writer)
{
	writer.write(_ContainerId); // this is fine

	writer.write(_containerItems.Count); // Let the client know how many are coming
	foreach(Entity entity in _containerItems)
	{
		writer.write(entity.Id); // This would work but...but is any datapacket sending entity information going to have to do this over and over?
		writer.write(entity.Name); // You would have to update this EACH time the entity class got more properties
		writer.write(entity.Quality); // you would have to update it in every datapacket that serializes entities :(
										// So if you had 20 different datapackets that transmit entity data you would be updating them a lot ugh.
										// Granted, most communication just sends the ID of the entity to update, instead of all of its data.
										
	}
}

Instead, I would put a Serialize method in the Entity class, and let the entity handle its serialization. Then, you would serialize your entities like this:

The datapacket would do this:

public void Serialize(Binary writer)
{
	writer.write(_ContainerId); // this is fine

	writer.write(_containerItems.Count); // Let the client know how many are coming
	foreach(Entity entity in _containerItems)
	{
		entity.Serialize(writer); // Just give it the binary writer. The entity is responsible for serializing its properties. 
	}
}

In the Entity class, you would have this method:

public void Serialize(BinaryWriter writer)
{
	writer.Serialize(_Id);
	writer.Serialize(_Name);
	writer.Serialize(_Quality); // and so on. You have to serialize all properties...but at least only in one place.
}

So, all my complex objects that need to be serialize do the lifting themselves. The datapacket just asks the object to serialize (or deserialize), given the BinaryWriter or BinaryReader it wants them to use.

Again…hope this helps. Sorry if it doesn’t!
If this looks like a good idea…maybe it would be helpful to package up the whole datapacket/socket stuff I am doing, and just toss it out for community use and input.

Unfortunately, yes, that’s pretty much exactly what we do in our MMO.

We’re using byte[ ] in some cases and JSON in others. For JSON, you only need to supply the correct type to Deserialize(jsonString), although in our case we ended up needing to duplicate the converstion Switch() blocks in a few places (esoteric compatability solution). Obviously for byte[ ], it’s the regular old process of counting offsets, or using field IDs if you want to allow sparse population (which it sounds like you do), but again that is all manual, at least the way we do it. Super fast though and works everywhere.

I looked into bytewise instance serialization, which is one way of preserving type data (assuming a C# server), but the instances are enormous.

For what it’s worth, the seemingly elegant solution you’re looking for, something like Deserialize, would almost certainly require reflection. As ugly as big Switch() statements might be, they will be much faster and far more portable than any reflection-based approach.

Cheers,
-Adrian

Thanks for the input guys!

JC your posts always have alot of interesting approaches and knowledge, always enjoy reading them :slight_smile: Sounds like you have a complete different approach than I, nor Photon has.

@hausmaus Thanks, that was the main thing I was wondering. Thought there had to be a better way in doing this, but sounds like there isn’t really one.

I’m probably going to stick with the big switch statement for now, since the approach of jc pretty much requires a complete overhaul. I’ll try to cut down the number of derrived Item classes anyway and generalize some more things. (maybe using Interfaces is a solution? or would that be even worse?)

Thanks for considering those great walls of text, wasstraat65 :slight_smile: Sorry it didn’t really fit your needs, but good luck!

As a sidenote,

Today I discoverd that the way Photon handles it’s RPC’s, there are some problems with inheritance of classes (even if you specify the correct serialization etc.)

Example:

public class Item {
    //serialize and deserialize
}
public class Weapon : Item {
    //serialization and other custom stuff
}
public class Chest : Photon.MonoBehaviour {
    void Start() {
        Item item = new Item();
        photonView.RPC("AddItem", PhotonTargets.All, item );
    }

    public void AddItem(Item item) {
        //stuff here
    }
}

In the Chest script, everything works fine as long as you populate it with the base Item class, but as soon as you call the RPC with a Weapon type item, which inherits from Item, this go mad. Adding a AddItem method with a Weapon type as parameter would fix the problem, but that would mean that you need a seperate method for every single variation of the Item class, which is unmaintainable (especially when those methods are interface implementations)

So in the long run, I ended up doing something similar JC is doing: sending an ItemPacket as RPC parameter, which is just a plain class with a public Item variable. Then I changed the RPC method to conform to the ItemPacket input and handle the rest with ItemPacket.item and use that. This also required to let Photon know of the ItemPacket class to (de)serialize it, but in its serialization methods, I just pointed to the Item serializations.

Again, thanks for your input JC :smile: