API
hammy275 opened this issue ยท 9 comments
With #272 , we'll have a really good base to build an API off of for code-driven immersives.
Note that ImmersiveMC will not be switching to semver if I implement this. Instead, I'll make breaking changes between major Minecraft versions if needed, and leave it be otherwise.
Main reason to make an API here is for easier support to add compatibility with other mods.
All immersives that aren't JSON-driven after #272 should use the API to make sure it's good to go.
API Planning
This comment will effectively serve as a "document" describing the plans of the API. It will be edited as plans change and evolve. It's written relatively formally, mainly to serve as a guide to myself as I implement the API in-code.
Issues/Ideas with the Plan as Currently Written
Solved Things not Described In Detail Here
RelativeHitboxInfo
andRelativeHitboxInfoBuilder
.- A lot of other things
- Not exposing the ImmersiveMC implementation of the WorldStorage system, and marking the whole thing as beta. Most API implementors should be using
Container
s anyway, which don't require it. - Not exposing ImmersiveMC's built-in Immersives due to instability on their implementation combined with requiring to expose a lot of the internal workings of them (such as the network implementations) due to parameterization on the classes.
Goals/Scope
- Have a functioning API allowing other mods to easily add ImmersiveMC support with stability between ImmersiveMC versions and Minecraft versions.
- The goal is to implement an API only for block-based Immersives. Trackers make more sense as part of Vivecraft's official API, and Item Immersives will, if implemented, be implemented at a later date.
- Rewrite ImmersiveMC to act as a consumer of the API. If it works for ImmersiveMC, it should work for anyone creating Immersives.
Provide documentation on the ImmersiveMC wiki for creating Immersives with the API.Moved to #410.
Interfaces
The API will be built around a few interfaces, which will be exposed in the package com.hammy275.immersivemc.api
. Implementations of these interfaces that needs to be done by ImmersiveMC are an implementation detail, and can change at any time. The "contract" created by these interfaces constitute the API. Interfaces requiring an implementation by ImmersiveMC contain a static instance()
method, which retrieves an instance of that interface. These names are not final as-of writing.
ImmersiveMCRegistration
(implementation provided by ImmersiveMC): Used for registeringImmersiveHandler
s. Also used to registerImmersiveChecker
s for client-only Immersives or Immersives that don't need anImmersiveHandler
.ImmersiveMCClientRegistration
(implementation provided by ImmersiveMC): Used for registeringImmersive
s.ImmersiveInfo
(implementations made by API implementors): An interface that holds info for an Immersive for an individual block in the world. ReplacesAbstractImmersiveInfo
as the representation of an Immersive info, withAbstractImmersiveInfo
now implementing this interface.- Functions required to be implemented by implementors.
setSlotHovered()
: A function that tells that info that a given slot is being hovered over.isSlotHovered()
: Renamed fromslotHovered()
.getAllHitboxes()
andhasHitboxes()
.getBlockPosition()
- Functions required to be implemented by implementors.
Immersive
(implementations made by API implementors): An interface that acts as the base for all client-side portions of an Immersive. This will replaceAbstractImmersive
internally as the representation of an Immersive, andAbstractImmersive
will instead implement this interface. Will heavily use a generic type that's required to be a form ofImmersiveInfo
. It assumes that an Immersive author wants hitboxes, and potentially item guides, hooking into ImmersiveMC with required implementations for both.- Functions required to be implemented by implementors.
clientAuthoritative()
getHandler()
: Must be nonnull.shouldRender()
render()
:renderTick()
is useless, remove it.tick()
: This is where actual ticking happens after all checks have passed. Code should be refactored accordingly.buildInfo()
: Constructs anImmersiveInfo
from a given block position.getTrackedObjects()
handleHitboxInteract()
: Rename ofhandleRightClick()
, and will now return anint
that determines the number of ticks of cooldown before an Immersive can be interacted with again. If a negative number is returned, no meaningful right-click was performed. If 0 is returned, a meaningful right-click was performed, but no cooldown should happen.configScreenInfo()
: An optionalImmersiveConfigScreenInfo
to handle showing the ability to enable/disable the Immersive on ImmersiveMC's config screen. Can be null, which will not show this information.shouldDisableRightClicksWhenInteractionsDisabled()
: ReplacesshouldBlockClickIfEnabled()
.isVROnly()
- Optional functions.
globalTick()
: Defaults to an empty implementation.
- Functions required to be implemented by implementors.
ImmersiveBuilder
(implementation provided by ImmersiveMC): A new interface that has the same "contract" as theImmersiveBuilder
class currently gives. The currentImmersiveBuilder
will be renamed, likely toImmersiveBuilderImpl
.ImmersiveHandler
(implementations made by API implementors): The interface as it currently exists in ImmersiveMC's codebase.ImmersiveRenderHelpers
(implementation provided by ImmersiveMC): Will be the new location for all helper functions currently inAbstractImmersive
(renderItem
, etc.).WorldStorages
(implementation provided by ImmersiveMC): Will be remade into an interface, with the implementation code being placed into aWorldStorageImpl
.BoundingBox
(implementations provided by ImmersiveMC).ImmersiveConfigScreenInfo
(implementation made by API implementors): The information used to populate ImmersiveMC's config screen if wanted.NetworkStorage
(implementation made by API implementors): Now part of the API.HitboxInfo
andHitboxInfoFactory
(implementation provided by ImmersiveMC). Not to be confused with theHitboxInfo
already in the codebase, this stores all data about a hitbox in anImmersiveInfo
, such as theBoundingBox
for detection, whether it's a trigger hitbox, etc.HitboxInfoFactory
is needed since we only have interfaces to work with.BuiltImmersiveInfo
(implementation provided by ImmersiveMC): The original class version of this will become an implementation provided by ImmersiveMC. This is the info worked with when building an Immersive usingImmersiveBuilder
.
As a convenience, interfaces implemented by ImmersiveMC might also provide static functions, which simply run the same functions on the instance of the interface.
Convenience Classes
Although at its core, the API is built on interfaces, some convenience classes will be exposed to make development easier. These will be moved to the api
package, and must have the portions that are and aren't covered by the API mentioned in docstrings, with visibility changed to match. For exmaple, fields that aren't covered by the API should be made private
, to signify that implementors shouldn't be interacting with them. The following classes will be exposed for convenience:
AbstractImmersive
. This file will definitely need to be cleaned up to be ready to be exposed in the API, but otherwise, is good to go to provide a ready-to-use implementation of anImmersive
.AbstractImmersiveInfo
: Also needs to be cleaned up, but will be great to build off of.OBB
and its associated classes.
Registration
Mods should be able to register the client information (ImmersiveMCClientRegistration
) and the common information (ImmersiveMCRegistration
) in any order (common than client or client than common).
For servers, the moment the common registration happens, we can perform any actions ImmersiveMC needs internally. Client registrations for Immersives done on the server are only required to not crash, and may perform any behaviors otherwise.
For clients, when a client or common registration happens, we check if the other registration has occurred for that Immersive. If so, perform actions internally as needed. If not, these internal actions are performed when the other registration is done. These internal actions will also ensure both a client and common registration occur for any Immersive, such as by adding to a list of ResourceLocation
s when one registration happens, than removing when the other does. On world join, all Immersives that were partially registered in such a manner are logged to chat, but do not impede game functionality otherwise. This mainly serves to make sure mods are unlikely to release with partially-implemented Immersives in this manner without causing crashes or upsetting gameplay otherwise if this were to happen. An exception to this is client-only Immersives, which will not be checked for in common, and if registered to common, are only required to not crash, like when registering client Immersives on a dedicated server.
Immersives will not be able to be "un-registered", and multiple Immersives cannot share the same ID. Registering an Immersive with the same ID as another Immersive will cause a crash. Furthermore, Immersives cannot be registered while a world is loaded on the server, or when in-game on the client. A crash will occur if this happens. Other than this, Immersives can be registered at anytime.
Despite using the term "registration" throughout this, Immersives will not use a registry. It's overkill for ImmersiveMC, and might cause issues when someone with ImmersiveMC joins a server without it (or vice-versa).
The server and client, when both using ImmersiveMC, must have the same Immersives registered in common. The check for this will be imperfect but simple: whenever an immersive is registered, a variable (call it runningHash
for now) will be XOR'd with the ResourceLocation
of the Immersive. When joining a server, the runningHash
is sent from the server to the client, along with the number of Immersives registered on both sides. If either or both of these values mismatch, the client disconnects due to the mismatch, as there is guaranteed to be one. Otherwise, it's very likely that both sides have the same Immersives, so we allow play to proceed. This is intentionally not enforced for the Immersives registered to the client, as client-only Immersives are supported, and thus, aren't known nor needed to be known to the server.
Backwards Compatibility (and Breaking It) and Versioning
Since ImmersiveMC doesn't use semantic versioning, and there are no plans to switch to it (similarly to Minecraft itself), we need to maintain backwards compatibility as much as possible. In semantic versioning terms, upgrades to the "minor" version and the "patch" version are okay under any circumstances, however "major" version changes should be minimal. Barring exceptional circumstances, they should only take place when at least one of the following happen:
- Minecraft forces the change in an update. For example, the move to JOML in 1.19.3 causing the removal of several math-related objects to be replaced with new ones.
- A major, code-breaking update to Minecraft happens that will likely already affect many modders. Examples of this include 1.19.0, 1.19.3, 1.19.4, 1.20.0, 1.20.2, 1.20.3, 1.20.5, etc.
To draw a clear line, backwards compatibility is defined as behavior mentioned in the docstrings for classes and interfaces covered by the API. If something is not documented there, it is not part of the API. For example, if an API method throws an exception, but the docstring does not mention such an exception, the exception being thrown is not part of the API, and may be changed at any time without warning. Such an exception is effectively a bug or some form of undefined behavior.
To prevent confusion between multiple version numbers, and to effectively force myself to keep API compatibility strong, there are no current plans to expose an API version number.
Immersive Priorities
Since I plan to add support for many mods in ImmersiveMC, if another mod dev wants to add support for something in their mod that's already in ImmersiveMC, their implementation should have priority. There won't be a priority system, that would be overkill. Instead, the register methods will check if the namespace of the Immersive's ResourceLocation
is ImmersiveMC's mod ID. If so, the implementation is placed at the end of the list of Immersives. Otherwise, it's placed at the beginning.
With the work done during the 1.5.0 dev-cycle, I think it only makes sense at this point to target this for 1.5.0.
To prevent having to refactor everything before making the API, and having to transition all the Immersives in one go, I'll make an adapter that takes an Immersive
implementation, and make it work with the current AbstractImmersive
system. This will entail:
- Writing the API as planned.
- Write an
ImmersiveAPIAdapter
thatextends AbstractImmersive
(and an equivalent for theInfo
s), that takes in anImmersive
implementation and exposes it in a wayAbstractImmersive
understands. - Port all of ImmersiveMC's Immersives to the API.
- Remove the adaptors and refactor accordingly.
- Settle on the final names and organization of all API classes, interfaces, etc.
During the above process, any gaps in the API will make themselves apparent, and can be filled in.
9f35d9c contains part of step 1 as mentioned above, with the flaws of the API implementation as of this comment. ImmersiveRenderHelpers
and below have not been implemented, and static versions of methods implemented by ImmersiveMC have also not been added.
With how the JSON is planned to be built, the API may end up simply being the JSON format
The API should actually come BEFORE #272 , especially considering some immersives won't be implementable in JSON.