ImmersiveMC

ImmersiveMC

683k Downloads

API

hammy275 opened this issue ยท 9 comments

commented

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.

commented

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 and RelativeHitboxInfoBuilder.
  • 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 Containers 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 registering ImmersiveHandlers. Also used to register ImmersiveCheckers for client-only Immersives or Immersives that don't need an ImmersiveHandler.
  • ImmersiveMCClientRegistration (implementation provided by ImmersiveMC): Used for registering Immersives.
  • ImmersiveInfo (implementations made by API implementors): An interface that holds info for an Immersive for an individual block in the world. Replaces AbstractImmersiveInfo as the representation of an Immersive info, with AbstractImmersiveInfo 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 from slotHovered().
      • getAllHitboxes() and hasHitboxes().
      • getBlockPosition()
  • Immersive (implementations made by API implementors): An interface that acts as the base for all client-side portions of an Immersive. This will replace AbstractImmersive internally as the representation of an Immersive, and AbstractImmersive will instead implement this interface. Will heavily use a generic type that's required to be a form of ImmersiveInfo. 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 an ImmersiveInfo from a given block position.
      • getTrackedObjects()
      • handleHitboxInteract(): Rename of handleRightClick(), and will now return an int 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 optional ImmersiveConfigScreenInfo 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(): Replaces shouldBlockClickIfEnabled().
      • isVROnly()
    • Optional functions.
      • globalTick(): Defaults to an empty implementation.
  • ImmersiveBuilder (implementation provided by ImmersiveMC): A new interface that has the same "contract" as the ImmersiveBuilder class currently gives. The current ImmersiveBuilder will be renamed, likely to ImmersiveBuilderImpl.
  • 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 in AbstractImmersive (renderItem, etc.).
  • WorldStorages (implementation provided by ImmersiveMC): Will be remade into an interface, with the implementation code being placed into a WorldStorageImpl.
  • 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 and HitboxInfoFactory (implementation provided by ImmersiveMC). Not to be confused with the HitboxInfo already in the codebase, this stores all data about a hitbox in an ImmersiveInfo, such as the BoundingBox 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 using ImmersiveBuilder.

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 an Immersive.
  • 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 ResourceLocations 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.

commented

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.

commented

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:

  1. Writing the API as planned.
  2. Write an ImmersiveAPIAdapter that extends AbstractImmersive (and an equivalent for the Infos), that takes in an Immersive implementation and exposes it in a way AbstractImmersive understands.
  3. Port all of ImmersiveMC's Immersives to the API.
  4. Remove the adaptors and refactor accordingly.
  5. 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.

commented

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.

commented

With how the JSON is planned to be built, the API may end up simply being the JSON format

commented

The API should actually come BEFORE #272 , especially considering some immersives won't be implementable in JSON.

commented

Tracking for porting of API to currently supported versions. 1.21 will come with the up-port of 1.20.4 when the rest of beta 2 is done.

commented

Accidentally left some commented code in ChestInfo, need to remove that.

commented

Finally done!