-
Notifications
You must be signed in to change notification settings - Fork 7
Platform Abstraction
The common module never imports Bukkit. Everything platform-specific reaches it through two
mechanisms: runtime providers (interfaces looked up in a registry) and compile-time stubs
(the "platoverride" pattern). This page is the reference for both, and for how to add a new seam of
your own.
For the big picture and the startup order, start at Architecture Overview.
EverNifeCore.getProviders() returns a single ECProviders instance. It wraps an ECBaseProvider,
a thread-safe (ConcurrentHashMap) map of Class<?> -> implementation:
public <T> T register(Class<T> providerType, T something); // register (last write wins, logs a warning if replacing)
public <T> T provide(Class<T> clazz); // look up (throws NoSuchElementException if absent)ECProviders exposes typed accessors over that map:
| Accessor | Provider interface | Registered by |
|---|---|---|
getPlatform() |
IPlatform |
McPlatform / HyPlatform
|
getECPluginExtractor() |
IECPluginExtractor |
McECPluginExtractor / HyECPluginExtractor
|
getEventDispatcher() |
ECEventDispatcher |
McECEventDispatcher / HyECEventDispatcher
|
EverNifeCore.getPlatform() is a shortcut for getProviders().getPlatform().
provide(...)throwsNoSuchElementExceptionwhen a provider is missing. That only happens if core code runs before the platform entry point has registered its providers - which is why each platform registers them as early as possible (Bukkit: the plugin's instance initializer; Hytale: the plugin constructor).
IPlatform (in common at
br.com.finalcraft.evernifecore.api.common.providers.platform.IPlatform) is the main contract the
core calls into. Its surface, grouped:
-
Identity -
getPlatformProviderId()(a stable tag,"minecraft"/"hytale", persisted inside account rows, so it must never change). -
Players -
getOnlinePlayers(),getPlayer(String),getPlayer(UUID)(returningFPlayer). -
Plugins -
isPluginLoaded(String). -
Commands -
registerCommand,unregisterCommand,makeConsoleExecuteCommand,makePlayerExecuteCommand. -
Listeners -
registerECListener,unregisterECListener. -
Placeholders -
isPAPIPresent,parse(FPlayer, String),createPlaceholderIntegration(...). -
Cosmetic output -
sendActionBarMessage,serverSupportsActionBar. -
Adapters -
getVecAdapter(),getChatAdapter(),createLogAdapterFor(...). -
Scheduling bridges -
runOnFirstTick(Runnable)(runs after every plugin has enabled) andrunOnMainThread(Runnable)(the bridge async storage callbacks use to touch game state safely). -
Lazy bootstrap hooks -
registerConfigTypes()andregisterArgParsers()(see below).
registerConfigTypes() and registerArgParsers() are called once, lazily, the first time the
subsystem that needs them initializes - not eagerly at enable time. This is so that a plugin which
loads before EverNifeCore still gets the types/parsers registered when it first uses them:
-
ConfigFactory's static initializer callsEverNifeCore.getPlatform().registerConfigTypes(), teaching the config engine about the platform's own types (BukkitItemStack/Location, Hytale vectors, ...). Implementations must be idempotent. -
FinalCMDManager's static initializer callsEverNifeCore.getPlatform().registerArgParsers()after registering the global parsers, so the platform adds its own (see Argument Parsing).
Some things common needs are not a single method call but a class it references directly - a
scheduler, a player adapter, an argument wrapper. For these, the seam is a class whose bytecode is
swapped at runtime.
How it works:
- The
api-contractsmodule holds a stub class - often empty, or with only the memberscommoncompiles against. -
commondeclarescompileOnly project(':api-contracts'), so it compiles against the stub but never packages it. - Each platform module ships the real class under the exact same fully-qualified name.
- At runtime, the platform's real class is the only one on the classpath.
common's call sites resolve to it.
The stubs (all under api-contracts):
| Stub (FQN tail) | Real implementation lives in |
|---|---|
api.platoverride.player.FPlayerAdapter |
each platform's player adapter |
api.platoverride.argumento.ArgumentoAdapter |
each platform's argument adapter |
minecraft.argumento.MinecraftArgumento |
minecraft |
minecraft.scheduler.McFCScheduler |
minecraft |
hytale.scheduler.HyFCScheduler |
hytale |
api.platoverride.eclistener.IECBaseListener |
each platform's listener base |
api.platoverride.math.game.adapter.GameVecPlatformAdapterConverter |
each platform's vec converter |
Concretely. The McFCScheduler stub in api-contracts is just:
public class McFCScheduler {
public static McFCScheduler INSTANCE;
}The real McFCScheduler in minecraft - same package, same name - has the full Bukkit-backed
implementation (runSync, scheduleSyncInTicks, the SynchronizedAction async->main bridge, ...)
and assigns INSTANCE = new McFCScheduler(). FCScheduler.getMinecraftScheduler() in common
compiles against the stub and finds the real one at runtime.
Because the stub is
compileOnly, production JARs never contain it. The test runtime is the exception:common's tests addproject(':api-contracts')back so the stubs exist while no real platform is on the classpath.
When common needs something a platform must provide, pick the mechanism by shape:
A single behavior (a method call) -> add it to a provider interface.
- Add the method to
IPlatform(or introduce a new provider interface + anECProvidersaccessor). - Implement it in both
McPlatformandHyPlatform. - Call it from
commonthroughEverNifeCore.getPlatform().
A whole class common references directly -> add a platoverride stub.
- Add the stub class to
api-contractswith just the memberscommoncompiles against. - Ship a real class with the identical fully-qualified name in each platform module.
- Reference the stub from
common; the real class replaces it at runtime. - Never let a deployable JAR bundle the stub (
api-contractsiscompileOnly); add it to the test runtime if the core tests need it.
- Architecture Overview - the two mechanisms in context + startup order.
-
Hytale Platform - the second
IPlatformimplementation and its current state. -
Java Versions and Toolchains - why
api-contracts/commonare Java-8-bytecode modules. - Argument Parsing · Configuration - the subsystems behind the two lazy hooks.
EverNifeCore · Home · made by Petrus Pradella
Getting Started
Commands & Text
Player Data & Storage
- PlayerData & PDSections
- Accounts
- Storage Backends
- Inline Backends for Plugins
- Legacy Data Migration
- Cooldowns
Config & Minecraft Systems
- Configuration
- Scheduler & Threading
- Items & NBT
- GUI Framework
- Integrations
- Economy
- Version Compatibility
Architecture & Reference