-
Notifications
You must be signed in to change notification settings - Fork 0
High level architecture and design for an Architectury port
Here are some, hopefully non-controversial, suggestions on how we should think about a new port.
-
To facilitate supporting both Fabric and Forge, we should keep to the common ground as much as possible. That means no reliance on Forge events, or Fabric API, if possible. In practice, this means writing and maintaining our own set of mixins.
-
To make this as future-proof as possible we should strive to:
- Minimize the number of mixins to the bare minimum.
- Select our mixin spots with care.
- Never ever do any hard logic in the mixin code, but just pass control on to our intermediate layer.
- The intermediate layer will abstract the functionality to a usable API. Somewhat like how Fabric API/Forge bus events works, but we will own and control the API ourselves.
- The mixin classes should only call the intermediate layer, and go no deeper into the product code.
- Only the intermediate layer should know about the mixins. All other classes access this functionality through the intermediate layer.
-
A lot of the current code mixes handling input from Wynncraft with building and maintaining a model with sending output to Wynncraft with modifying the UI or with interacting with the Minecraft client. We need to separate those, so they become easier to maintain, reuse and update to future versions.
-
As I see it, we have two conceptual models we need to handle: The Wynncraft world, and the Minecraft client/UI. The litmus test for determining if it's the one or the other is that if Wynncraft updates, we should update our Wynncraft model, but if Minecraft updates, we should update the Minecraft model. These need to be cleanly separated or we will get into a support matrix hell. For the rest of the document, I'll assume we have
com.wynntils.wc
for the Wynncraft model, andcom.wynntils.mc
for the minecraft model. (These names is perhaps too similar, I'm open for other suggestions.com.wynntils.model
andcom.wynntils.client
was my first choice, maybe that's better.) -
The Minecraft model will need to handle input and output and a lot that's neither. It will need to handle stuff like adding key bindings, a play button to the in-game menu, how to draw overlays, how to send spells (or at least how to send fake mouse presses; the actual spell order is strictly a Wynncraft model knowledge), etc.
-
The Wynncraft model will need to handle input from Wynncraft (which typically comes either as Minecraft packages or text strings), parse/handle this input, and build a local model. In general, I believe we have been too bad at caching the data we have successfully parsed. I propose that we make the caching compulsary by designing it into the model. So let's say if we want to check if we are riding on a horse. Whenever we mount a horse, our Wynncraft model will detect this happening, and update the local model to set
isRidingHorse
totrue
. (And when dismounting, resetting it tofalse
.) If some part of the code, perhaps code that wants to spawn the horse (or maybe new functionality that warns you if you try to cast spells while riding), then they will query the model, and just check the localisRidingHorse
variable.
Apart from understanding the Wynncraft world in Wynncraft terms (are you on the hub? Are you tracking a quest? Is this an NPC dialogue?), we also need to be able to affect Wynncraft, e.g. by sending chat commands (/toggle music, /ping) or raw packages.
This all means that the Wynncraft model breaks down in three parts:
- Parsing (input)
- Storing/caching
- Changing (output)
We currently have a quite okay way of enabling/disabling individual features. I believe this is good, almost essential, for Wynntils. With that said, I think it could be worth to spend a bit of effort to make sure we:
- Has found the right place to check if a feature is enabled or disabled
- Really has a proper way of splitting down the functionality.
At the very least, I believe the current structure of "modules", and putting almost all functionality into "overlays", serves little purpose, and only confuses the code base and the settings UI. My suggestion is basically starting with a flat set of booleans to enable/disable certain funtionality, and then we can work with finding a logical way of grouping these in the Settings UI.