Jump to content

Alternative to saving data other than Capabilities (Server Side)


Burchard36

Recommended Posts

So I have known java for a few years and have even made many MC plugins in the past and decided ill try my hand at modding (1.13.2).
Everything was going good until I wanted to store data more permanently (as of now I was just using EntityPlayerMP#getEntityData and storing data there just to test some features of my mod).

I looked for many hours through the internet and discovered there's Capabilities, but the documentation is very bad for the forge site overall (And most tutorials on youtube for it are outdated and the syntax's have changed) for people trying to learn how to mod. So I was wondering: Would it be considered "bad modding practice" to create my own DataStorage system? I've done this in the past before with my plugins where I did these steps:

1) When the player first joins the server, a data file with default value(s) is written in a file (data/players/<UUID>.json) if it doesn't exist, if it does goto step #2
2) The default data (or grabbed data from #1) is transformed against a PlayerData class (Using google GSON there's a method that allows converting JSON to a class if fields are correctly named, I cant remember the method name off the top of my head but I used it before)
3) The PlayerData instance is then loaded into a HashMap to be accessed again later without needing to read a file (aka cache)
4) Of course there is a Runnable running every 5 minutes or so that saves the PlayerData objects back to they're respective file (in spigot I did this Asynchronously), as well as server stop event the save method is called as well

This worked extremely well for me when using SpigotAPI, so would this type of system be performance demanding on a Forge Server or this considered "bad practice" because I don't want to learn Capabilities? I would assume not since the JSON files are being cached and only ever read from when a player joins the server (this could have issues if a lot (50-100+) of player joins a server at one time, but a queue system can be added if it becomes an issue later down the road, doubt this will ever occur though), not to mention if I use this system it would be far faster for me to setup rather than scouring the internet to try and figure out how capabilities work (not to mention I do want to update the mod to a later version later on, but I found the 1.13 API more resourceful in terms of tutorials on the internet, so I figured I would call it my "learning mod",)

And if anyone asks: The data I'm storing is just a collection of long's, int's, strings, and enums (that can be parsed back to a Enum class during Runtime), I don't have any need to store items for this mod, just raw data (Its for health, level, skill points, energy, money etc etc)

Edit: Please reply "Capabilities are easy" I have already explained above that the documentation was not helpful and that most tutorials are way outdated for what I'm doing. I'm simply asking if what I want to do now will have a major performance impact on the server side aspect

Edited by Burchard36
Added line
Link to comment
Share on other sites

  

5 minutes ago, DietmarKracht said:

Capabilities are not that hard to understand....

I don't want someone to come on this thread saying "they aren't hard to understand", the documentation is not great for them, it throws too much information at you at once, not to mention it doesn't fully explain everything you need to know to properly use them nor completely how you need to set everything up (There is a 1.10 video that perfectly explains everything but when I had tried the video there was multiple things changed between versions and I couldn't even get it to run/figure out how some of the things worked that got added)

I'm not the type to learn through just rushing through 20 paragraphs with out of context code that doesn't fully explain everything, I have my own learning pace and so does everyone else, and those docs are relatively useless to my learning curve

 

Link to comment
Share on other sites

So you are more the type of "give me some code i can copy and paste cuz i don't want to understand the concept thats going on here"?

 

The documentation on capabilities is perfectly fine. They function as the exact purpose you just described in your first post. So give it a try at least. I doubt you are under strict time constraints and need to finish porting your mod within the next week....

Link to comment
Share on other sites

1 hour ago, DietmarKracht said:

So you are more the type of "give me some code i can copy and paste cuz i don't want to understand the concept thats going on here"?

 

The documentation on capabilities is perfectly fine. They function as the exact purpose you just described in your first post. So give it a try at least. I doubt you are under strict time constraints and need to finish porting your mod within the next week....

No, I never asked for anyone to spoon-feed me, I am actually interested in learning things and being able to write them on my own hence why I was saying that the docs were confusing because they didn't actually teach anything, and inquired about classes they already had assumed you made without know what to extend and how to Override the methods and return correct values, and how to link everything together as one system, it just seemed really confusing to me and I couldn't learn anything from it

However the guides provided by @diesieben07 were absolutely perfectly explained step by step (Which is how I prefer to learn things, I like wikis and tutorials to go down to the very nitty gritty and explain what every single thing does and make things very clear to the end user/developer)

 

1 hour ago, diesieben07 said:

Yes, because you will most likely get it wrong.

What exactly are you missing from the capability documentation? Note that there is also an article about them on the community wiki, which goes into a bit more detail: https://forge.gemwire.uk/wiki/Capabilities and https://forge.gemwire.uk/wiki/Capabilities/Attaching.

You are a LIFESAVER, I'm surprised this wasn't anywhere when I googled for it for hours. Finally managed to get my entire CapabilityManager using that guide it really explained loads more than the docs!

Here what I manager to put together with those guides:

ModCapabilites.java

Spoiler


public class ModCapabilities {

    @CapabilityInject(ICurrentHealth.class)
    public static Capability<ICurrentHealth> CURRENT_HEALTH = null;

    public ModCapabilities() {
        this.registerCapabilities();
    }

    public void registerCapabilities() {
        CapabilityManager.INSTANCE.register(
                ICurrentHealth.class,
                new CapabilityWorker<>(CURRENT_HEALTH),
                CurrentHealth::new
        );
    }

    private static class CapabilityWorker<S extends INBTBase, C extends INBTSerializable<S>> implements Capability.IStorage<C> {

        private final Capability<C> cap;

        public CapabilityWorker(final Capability<C> capability) {
            this.cap = capability;
        }

        @Nullable
        @Override
        public final INBTBase writeNBT(final Capability<C> capability,
                                 final C instance,
                                 final EnumFacing side) {
            if (this.cap != capability) return null; // Make sure were writing to the right Capability
            return instance.serializeNBT();
        }

        @Override
        @SuppressWarnings("unchecked") // No need to check S
        public final void readNBT(final Capability<C> capability,
                            final C instance,
                            final EnumFacing side,
                            final INBTBase nbtBase) {
            if (this.cap != capability) return; // Make sure were writing to the right Capability
            instance.deserializeNBT((S) nbtBase); // No need to check, always using NBT Compounds
        }
    }
}

 


CurrentHealth.java
 

Spoiler


public class CurrentHealth implements ICurrentHealth {

    private long health;

    public CurrentHealth() {
        this.health = 0;
    }

    @Override
    public long getHealth() {
        return this.health;
    }

    @Override
    public final NBTTagCompound serializeNBT() {
        final NBTTagCompound comp = new NBTTagCompound();
        comp.setLong(NBTTags.CURRENT_HEALTH.toString(), this.getHealth());
        return comp;
    }

    @Override
    public final void deserializeNBT(final NBTTagCompound nbt) {
        this.health = nbt.getLong(NBTTags.CURRENT_HEALTH.toString());
    }
}

 


And finally: ICurrentHealth.java
 

Spoiler


public interface ICurrentHealth extends INBTSerializable<NBTTagCompound> {

    long getHealth();
}

 


All that's left is just to attach it to the EntityPlayerMP but that was well explained on the docs at least so that's fairly easy and ill likely change ICurrentHealth and CurrentHealth to hold all stats of a player instead of just the health. (I didn't do type checks because ill be ONLY planning on storing NBT Compounds for data)

I did run into an issue while trying to figure out what the heck CapabilityWorker<S extends INBTBase, C extends INBTSerializable<S>> was, but I thought of it similarly to how I made ClassLoader system for SpigotMC to load external jars, seems just about the same thing (In regards to using ? extends Object)

Was actually really fun learning this, the docs made it seem more complicated than what it really was (It kept referencing psuedo classes I did not have and just had assumed I made those classes before I started making the system, and I had no idea how to create these classes), and again thank you a bunch for providing those guides they explained perfectly what I was missing, haven't tested this yet but I'm sure it will work with a few minor tweaks!

Edited by Burchard36
fixed a word spelling thingy mabob
Link to comment
Share on other sites

  • Guest locked this topic
Guest
This topic is now closed to further replies.

Announcements



×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.