Lemmmy / JRogue

Roguelike dungeon crawler written in Java
MIT License
3 stars 0 forks source link

Migrate to gson #103

Closed Lemmmy closed 5 years ago

Lemmmy commented 5 years ago

Migrating from org.json to gson. This will greatly simplify serialisation/deserialisation in the long run, so that we don't have to manually specify how every single thing is serialised and deserialised - we can simply add the @Expose annotation to it. For more complex types, we will have to write JsonSerializers, JsonDeserializers or TypeAdapters.

This has come with significant changes to the serialisation format. The most notable changes are that stores/subsystems will no longer be flattened:

// old
{
    "name": "Cool Dungeon",
    "turn": 10
}

// new
{
    "name": "Cool Dungeon",
    "turnSystem": {
        "turn": 10
    }
}

And that things such as entities, items, spells, etc now store an ID instead of their absolute class path:

// old
{
    "class": "jr.dungeon.entities.monsters.canines.MonsterJackal",
    "x": 5,
    "y": 40
}

// new
{
    "id": "monsterJackal",
    "x": 5,
    "y": 40
}

This is also a security improvement, as it prevents against injection attacks by whitelisting what can be used.

To allow this, a registry system has been added. Types such as Entities and Items can specify that they have a registry by annotating them with @HasRegistry, and optionally implementing Serialisable:

@HasRegistry
public class Entity implements Serialisable {

And everything that extends it has to add the @Registered annotation to specify its ID, for example:

@Registered(id="monsterJackal")
public class MonsterJackal extends MonsterCanine {

The rest is automatically handled.

As well as this, the old Persisting interface has been removed. Objects can no longer store raw persistent data in a JSONObject. We will have to find a new way to store animation data in the renderer; I think it would be better to store it renderer-side, rather than directly on the entity.

Due to the fact that gson TypeAdapters have no ability to pass in context, we can no longer resolve entity references by UUIDs at deserialisation time. To combat this, I have also created EntityReference. All references to entities will have to be replaced with this. It can be used similar to AtomicReferences:

private EntityReference<EntityLiving> targetEntity = new EntityReference<>(anEntity);

targetEntity.set(player);
targetEntity.get(dungeon); // returns player

The references store the UUID, and lazily get it and cache it when .get() is first called. Dungeon or Level can be passed in. If Dungeon is passed in, it will search all levels for the entity until it is found.

As a side note, I have only just realised that unserialise is grammatically incorrect, yet we use this term everywhere in the code. Considering most references to it are disappearing, I can probably fix that.

TODO