Skip to content

Architecture Overview

Petrus Pradella edited this page Jul 23, 2026 · 1 revision

Architecture Overview

What this page covers: how EverNifeCore keeps one platform-agnostic core and lets each platform (Bukkit, Hytale) plug its own behavior underneath - the two abstraction mechanisms that make that possible, and the exact order things wake up in on a Bukkit server.


The core never imports Bukkit

EverNifeCore is a multi-platform framework built as a Gradle multi-project. The dividing line runs right through the middle of it:

  • common holds every platform-agnostic system - commands, config, locale, cooldown, placeholders, player data, scheduling, utilities. It has zero Bukkit imports. It compiles and runs against nothing but the JDK, the FinalCraft sibling libraries (EveryDatabase, EveryConfig, EveryLibs), and its own compileOnly view of the platform stubs.
  • minecraft is the Bukkit/Spigot/Paper implementation. It produces the deployable JAR.
  • hytale is the Hytale port (in development - see Hytale Platform).

Because common cannot see Bukkit, everything platform-specific has to reach it through a seam. There are exactly two seams, and every platform integration goes through one of them.

For the full module list and how they depend on each other, see Project Layout.


The two abstraction mechanisms

1. Runtime providers (ECProviders / IPlatform)

common asks the running platform to do things it can't do itself - look up a player, register a command, send an action bar - through a small registry of providers.

  • EverNifeCore.getProviders() returns the ECProviders registry.
  • EverNifeCore.getPlatform() returns the registered IPlatform - the main platform contract.

IPlatform covers player lookup (getPlayer, getOnlinePlayers), command register/unregister, ECListener register/unregister, console/player command execution, action bars, PAPI presence and parsing, placeholder-integration creation, log-adapter creation, vec/chat adapters, main-thread and first-tick scheduling bridges, and the two lazy bootstrap hooks registerConfigTypes() and registerArgParsers(). McPlatform (minecraft) and HyPlatform (hytale) are the two implementations.

Each platform's entry point registers its providers as early as possible. On Bukkit, inside the plugin's instance initializer:

EverNifeCore.getProviders().getBaseProvider().register(IECPluginExtractor.class, new McECPluginExtractor());
EverNifeCore.getProviders().getBaseProvider().register(IPlatform.class,           new McPlatform());
EverNifeCore.getProviders().getBaseProvider().register(ECEventDispatcher.class,    new McECEventDispatcher());

2. Compile-time stubs (the "platoverride" pattern)

Some seams are not a method call but a whole class that common references directly. For those, common declares compileOnly project(':api-contracts'), a module of stub classes. Each platform ships the real implementation under the same fully-qualified class name, replacing the stub at runtime. The stubs are never packaged into any deployable JAR.

FPlayerAdapter, ArgumentoAdapter, MinecraftArgumento, McFCScheduler, HyFCScheduler, IECBaseListener, and GameVecPlatformAdapterConverter are the stubs. For a step-by-step account of both mechanisms - including how to add a new platform seam - see Platform Abstraction.


Startup sequence (Bukkit)

The Bukkit entry point is br.com.finalcraft.evernifecore.minecraft.loader.EverNifeCoreBukkitPlugin. Three phases run in order:

1. Static block (class load). The very first thing, before anything else can touch the class:

ECoreDependencies.initialize();        // libby runtime deps - must run first
MinecraftVersion.disableBStats();      // NBT-API tweaks
MinecraftVersion.disableUpdateCheck();

ECoreDependencies.initialize() runs here (not in onEnable) on purpose: other plugins may depend on EverNifeCore and load its classes before Bukkit enables it, so the runtime libraries have to be present the instant the class initializes.

2. Instance initializer (construction). When Bukkit instantiates the plugin:

  1. instance = this - captured immediately.
  2. Registers the three providers (the snippet above).
  3. Picks the region grid for the running MC version (RegionGridOptions).
  4. McDefaultOntimeManager.initialize().
  5. EverNifeCore.instance.onLoaderInstantiate(...) - hands the core its ECPluginData.

3. onEnable().

  1. EverNifeCore.instance.onLoadPre() - the common-side bootstrap: ConfigManager, Cooldown, CommandRegisterer.
  2. Version banner + the NBT-API self-test (NBTSelfTest.run(); a failure is logged, never fatal - only item/GUI NBT would be affected).
  3. The Bukkit side: McConfigManager.initialize, Cooldown.initialize, McCommandRegisterer.registerCommands, VaultIntegration.initialize.
  4. Listeners: PlayerLoginListener, PlayerInteractListener, PluginListener, and the AuthMe-vs-Vanilla login variant chosen by whether AuthMe is enabled.
  5. WorldEditIntegration.initialize() if WorldEdit is present.
  6. FCTickUtil.getTickCount() starts the tick counter.

Shutdown (onDisable). HandlerList.unregisterAll(this) then EverNifeCore.instance.onUnload(), which calls PlayerController.shutdown() - flushing dirty player data and closing the storage backends.

The Hytale entry point (EverNifeCoreHytalePlugin) follows the same shape - register providers in the constructor, run onLoadPre()/onLoadPost() in setup(), flush on shutdown(). See Hytale Platform.


Where the data lives

The core no longer ships its own database layer. Persistence is delegated to the external EveryDatabase project (everydatabase-core + -manager, declared as api dependencies of common). Player data is a set of Jackson POJO sections persisted on whichever backend the server admin configures in storage.yml. See PlayerData and PDSections and Storage Backends, and the EveryDatabase developer wiki for the backend engine itself.


See also

Clone this wiki locally