I swear Pauper is still being developed, I swear it. I am the sole developer of the project so it just proceeds very slowly at times, especially right now since I've gotten addicted to a couple of video games lately and those are the only things I wanna spend my free time doing outside of IRL relationships

But I'm still working on it!
As stated before, Player files have completely been detached from Java's default serialization which is a big step in moving the source away from OOP in general. My end-goal with Pauper is to treat Entities in general as a series of components, regarding them as a set of data rather than Objects themselves. This will allow entities to have a "has-a" relationship with other classes rather than the "is-a" relationship that is necessary with inheritance.
I mentioned before that I am now using binary to save player classes rather than Java's default serialization; I am utilizing Reflection to achieve this:
Java:
try {
stream.writeByte(CURRENT_VERSION);
Field[] fields = getSerializableFields(player.getClass());
stream.writeShort(fields.length);
for (Field field : fields) {
try {
String name = field.getName();
Object value;
field.setAccessible(true);
value = field.get(player);
stream.writeString(name);
serializeField(stream, value);
} catch (Exception e) {
Logger.log("PlayerBinarySerializer",
"Error serializing field " + field.getName() + ": " + e.getMessage());
stream.writeByte(0);
}
}
byte[] data = stream.getBuffer();
return data.length > COMPRESSION_THRESHOLD ? compressInThread(data) : data;
}
private static Field[] getSerializableFields(Class<?> clazz) {
return FIELD_CACHE.computeIfAbsent(clazz, c ->
Arrays.stream(c.getDeclaredFields())
.filter(f -> !Modifier.isStatic(f.getModifiers()))
.filter(f -> !Modifier.isTransient(f.getModifiers()))
.filter(f -> !Modifier.isFinal(f.getModifiers()))
.toArray(Field[]::new)
);
}
While not downright completely removed from OOP -- Players are still very much treated as Objects -- this does allow me to break inheritance and default serialization in general without harming any Player-related data. Saving Player files this way lets me decouple their data away from the rest of the server, only having relays of information being shared from Player files and server.
This will allow me to create an ECS (Entity-Component-System) which will convert all entities (Players, NPCs, Objects) into a modular set of data overtime without causing any corruption as this system expands. This style of coding is called composition, which is superior to OOP in most aspects almost all of the time. Even in simple hierarchies where it only stems 2-3 levels deep, composition is almost always more friendly to CPU. This practice will allow me to create separate systems that can encompass any and all aspects of the server to be used in modularity. This will mean, for example, that I could apply health and health-bars to things like world objects; even tin ore rocks. They could walk around and have behavioral models since they are no longer tied to the "WorldObject" class or whatever.
Sure, this is certainly possible using OOP - but it would be necessary to duplicate code in order to achieve that, rather than using the already existing code that's been completely restricted through inheritance. It is also just bad practice to treat complicated things as Objects, such as Player classes, as that provides heaps of tax to hardware systems. When correctly utilizing composition, the server will only read what relevant data it should, whereas OOP will read the entire dataset every single time.
In game design, most developers think of structuring in terms like this:
This is essentially how Matrix is setup, with even one additional inheritance step before reaching the Player/NPC class. This makes the source wildly restrictive, fragile, and taxing on systems. If I'd like to damage a monster, the server must read the full Entity class which determines whether or not it's a real entity or a "world object." If it's determined to be a real entity - it'll ignore the WorldObject class. But now it must fully read both NPC and Player classes, all of their data from top to bottom. Once it has every single piece of data in the Actor classes, it will then read and execute relevant methods.
Now what if I wanted to create a combat dummy WorldObject that has HP and will despawn once that HP is depleted? I'll need to duplicate code from the other Entity classes in order to achieve that instead of relying on code that already exists.
Using composition will entirely wipe out these steps. If I'd like to damage an entity using composition, it only reads the relevant sets of data. Something like this:
Which, instead of limiting data to hierarchies, it will simply read relevant sets of data and apply logic correspondingly. This will apply to every single entity in the server: Players, NPCs, Objects, even world tiles and things alike. Fully converting the source over to composition would do
heaps in creating a very stable RSPS source, or even engine if you'd like to get so bold. OOP is incredibly inefficient for urgent tasks, like most things involving multiplayer games.
So, long-run goal will be to slowly convert Pauper from OOP to composition almost in its entirety. Again though, this is the LONG-term goal. There is already so much content within the Matrix source, this is not going to be a quick task and will be slowly developed overtime.