HeliosCore — Developer Documentation
HeliosCore is a pure library plugin. Server owners drop it in /plugins and forget about it. This documentation is for
plugin developers who want to build on top of it.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. Declaring a Dependency
In your
plugin.yml:
Code (YAML):
depend
:
- HeliosCore
Or as a soft dependency if HeliosCore is optional:
Code (YAML):
softdepend
:
- HeliosCore
2. Resolving Services
All services are registered on the Bukkit Services Manager. Use
CoreServices.require() to resolve them safely — it throws a clear
IllegalStateException if HeliosCore is not loaded, which is far better than a silent null crash.
Code (Java):
import
dev.ninez.core.api.CoreServices
;
import
dev.ninez.core.api.database.DatabaseService
;
import
dev.ninez.core.api.profile.PlayerProfileService
;
public
class MyPlugin
extends JavaPlugin
{
private DatabaseService db
;
private PlayerProfileService profiles
;
@Override
public
void onEnable
(
)
{
this.
db
= CoreServices.
require
(
this, DatabaseService.
class
)
;
this.
profiles
= CoreServices.
require
(
this, PlayerProfileService.
class
)
;
}
}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
3. Service API Reference
DatabaseService
HikariCP-backed connection pool. Supports SQLite (default) and MySQL.
Code (Java):
// Check if the pool is alive (no query — instant)
boolean alive
= db.
isConnected
(
)
;
// Run a live SELECT 1 health check
boolean ok
= db.
testConnection
(
)
;
// Get the last error message
String err
= db.
lastError
(
)
;
// Borrow a connection for a query
try
(
Connection con
= db.
openConnection
(
)
;
PreparedStatement ps
= con.
prepareStatement
(
"SELECT * FROM my_table WHERE uuid = ?"
)
)
{
ps.
setString
(
1, player.
getUniqueId
(
).
toString
(
)
)
;
ResultSet rs
= ps.
executeQuery
(
)
;
// ...
}
// Which backend is active?
DatabaseType type
= db.
type
(
)
;
// DatabaseType.SQLITE or DatabaseType.MYSQL
String label
= db.
describe
(
)
;
// e.g. "SQLite (helioscore.db)"
Note: Always use try-with-resources. HeliosCore manages the pool lifecycle; never close the pool yourself.
---
PlayerProfileService
Persistent cache of player statistics updated each login/logout cycle.
Code (Java):
// Get a full snapshot (works for online AND offline players)
PlayerProfileSnapshot snap
= profiles.
profile
(offlinePlayer
)
;
PlayerProfileSnapshot snap
= profiles.
profile
(uuid,
"FallbackName"
)
;
// Key fields on PlayerProfileSnapshot:
snap.
username
(
)
// last known name
snap.
online
(
)
// currently online?
snap.
health
(
)
/ snap.
maxHealth
(
)
// last recorded HP
snap.
gameMode
(
)
// "survival", "creative", etc.
snap.
worldName
(
)
// last known world
snap.
blockX
/Y
/Z
(
)
// last known coords
snap.
ping
(
)
// last known ping ms
snap.
firstSeenAtMillis
(
)
// first join timestamp
snap.
lastSeenAtMillis
(
)
// last quit timestamp
snap.
joinCount
(
)
// total logins
snap.
totalPlaytimeTicks
(
)
// accumulated playtime
snap.
effectivePlaytimeTicks
(
)
// total + current live session
snap.
playerKills
(
)
/ snap.
deaths
(
)
/ snap.
killDeathRatio
(
)
// Register / unregister a custom data provider (extend your own profile cards)
profiles.
registerProvider
(myProvider
)
;
profiles.
unregisterProvider
(myProvider
)
;
// List registered provider IDs
List
<String
> ids
= profiles.
providerIds
(
)
;
// Retrieve all profile cards for a player (one per provider)
List
<PlayerProfileCard
> cards
= profiles.
cards
(offlinePlayer
)
;
// How many profiles are in memory?
int count
= profiles.
cachedProfileCount
(
)
;
---
CooldownService
Per-subject, per-scope in-memory cooldowns. A "scope" is just a string key for your feature (e.g.
"myplugin.daily_reward").
Code (Java):
CooldownService cooldowns
= CoreServices.
require
(
this, CooldownService.
class
)
;
// Try to start a cooldown — returns false if already active
boolean started
= cooldowns.
tryStart
(
"myplugin.warp", player.
getUniqueId
(
), Duration.
ofSeconds
(
30
)
)
;
// Check if a cooldown is active
boolean active
= cooldowns.
isActive
(
"myplugin.warp", player.
getUniqueId
(
)
)
;
// Get remaining duration
Duration remaining
= cooldowns.
remaining
(
"myplugin.warp", player.
getUniqueId
(
)
)
;
// Human-readable remaining time ("14s", "2m 5s")
String text
= cooldowns.
remainingText
(
"myplugin.warp", player.
getUniqueId
(
)
)
;
// Clear a specific entry
cooldowns.
clear
(
"myplugin.warp", player.
getUniqueId
(
)
)
;
// Unconditionally set (or overwrite) a cooldown
cooldowns.
set
(
"myplugin.warp", player.
getUniqueId
(
), Duration.
ofMinutes
(
5
)
)
;
// Clear everything under a scope (e.g. on plugin reload)
cooldowns.
clearScope
(
"myplugin.warp"
)
;
// Inspect active scopes and entry counts
Collection
<String
> scopes
= cooldowns.
activeScopes
(
)
;
int count
= cooldowns.
activeEntryCount
(
"myplugin.warp"
)
;
// Format any Duration to a human-readable string
String fmt
= cooldowns.
format
(Duration.
ofSeconds
(
90
)
)
;
// "1m 30s"
---
ConfigService
Managed YAML and JSON config files. All files are tracked and reloaded together on
/hcore reload.
Code (Java):
ConfigService configs
= CoreServices.
require
(
this, ConfigService.
class
)
;
// Load (or create from default) a YAML file relative to your plugin's data folder
YamlConfigFile yaml
= configs.
loadYaml
(
"settings.yml",
true
)
;
FileConfiguration fc
= yaml.
configuration
(
)
;
// Load a JSON file
JsonConfigFile json
= configs.
loadJson
(
"data/players.json",
false
)
;
// Reload all tracked files
configs.
reloadAll
(
)
;
// Look up an already-loaded file without re-loading it
Optional
<YamlConfigFile
> existing
= configs.
findYaml
(
"settings.yml"
)
;
Optional
<JsonConfigFile
> existingJson
= configs.
findJson
(
"data/players.json"
)
;
// List all tracked file paths
Collection
<String
> loaded
= configs.
loadedFiles
(
)
;
// Resolve a path relative to your plugin's data folder
Path absolute
= configs.
resolve
(
"data/export.csv"
)
;
---
GuiService
Shared inventory click/close event routing. Extend
Menu and open via the service so clicks are routed correctly without each plugin registering its own listener.
Code (Java):
GuiService guis
= CoreServices.
require
(
this, GuiService.
class
)
;
// Open a menu for a player
guis.
open
(
new MyShopMenu
(player
), player
)
;
// Check what a player is viewing
Optional
<Menu
> current
= guis.
currentMenu
(player
)
;
boolean viewing
= guis.
isViewing
(player, MyShopMenu.
class
)
;
---
PermissionService
Registers Bukkit
Permission objects at startup. Compatible with LuckPerms and any Bukkit-aware permissions manager.
Code (Java):
PermissionService perms
= CoreServices.
require
(
this, PermissionService.
class
)
;
// Register a single node
perms.
registerPermission
(
"myplugin.command.fly",
"Allows use of /fly",
PermissionDefault.
OP,
Map.
of
(
)
)
;
// Register from a PermissionNode record (preferred for bulk registration)
perms.
registerPermissions
(MyPermissionNodes.
defaults
(
)
)
;
// Check permission (same as sender.hasPermission but works with the registry)
boolean allowed
= perms.
has
(sender,
"myplugin.command.fly"
)
;
---
PlayerService
Fast player lookup with fuzzy name matching.
Code (Java):
PlayerService players
= CoreServices.
require
(
this, PlayerService.
class
)
;
// Find an online player by name fragment
PlayerLookupResult
<Player
> result
= players.
findOnlinePlayer
(
"Notc"
)
;
if
(result.
found
(
)
&&
!result.
multiple
(
)
)
{
Player target
= result.
player
(
)
;
}
if
(result.
multiple
(
)
)
{
sender.
sendMessage
(
"Multiple matches: "
+ result.
describeMatches
(
5
)
)
;
}
// Find any known player (online or offline, from Bukkit's cache)
PlayerLookupResult
<OfflinePlayer
> offline
= players.
findKnownPlayer
(
"Notch"
)
;
// Get all online player names for tab-complete
List
<String
> names
= players.
onlinePlayerNames
(
)
;
// Fuzzy search — returns all matches, not just one
List
<Player
> matches
= players.
searchOnlinePlayers
(
"notc"
)
;
// Display-name aware label (falls back to supplied string if no display name)
String label
= players.
displayName
(offlinePlayer,
"Unknown"
)
;
---
TelemetryService
Optional usage telemetry. Register your plugin as a contributor to include your metrics in any telemetry payloads.
Code (Java):
TelemetryService telemetry
= CoreServices.
require
(
this, TelemetryService.
class
)
;
// Is telemetry enabled on this server?
if
(telemetry.
enabled
(
)
)
{
telemetry.
registerContributor
(myContributor
)
;
}
// Unique install ID for this server
String id
= telemetry.
installId
(
)
;
// Unregister when your plugin disables
telemetry.
unregisterContributor
(myContributor
)
;
// List registered contributor IDs
List
<String
> contributors
= telemetry.
contributorIds
(
)
;
// Take an immediate snapshot (metrics as of right now)
TelemetrySnapshot snap
= telemetry.
snapshotNow
(
)
;
// Extra metadata
boolean remote
= telemetry.
remotePublishingEnabled
(
)
;
String env
= telemetry.
environment
(
)
;
// e.g. "production"
String endpoint
= telemetry.
remoteEndpoint
(
)
;
String summary
= telemetry.
lastPublishSummary
(
)
;
---
DashboardService
Register your plugin as a contributor to the HeliosCore live dashboard. Contributors get a tile, a status card, and optional action/SSE endpoints.
Code (Java):
DashboardService dash
= CoreServices.
require
(
this, DashboardService.
class
)
;
// Register your plugin (call from onEnable)
dash.
register
(
new MyDashboardContributor
(
this
)
)
;
// Unregister (call from onDisable)
dash.
unregister
(
"myplugin"
)
;
// Inspect runtime state
boolean running
= dash.
isRunning
(
)
;
int port
= dash.
port
(
)
;
List
<DashboardContributor
> all
= dash.
contributors
(
)
;
---
LocaleService
Server-side i18n with per-player locale overrides. Messages are stored in YAML locale files (
lang/en_US.yml). Plugins register their own translation bundles so all translations go through one system.
Code (Java):
LocaleService locale
= CoreServices.
require
(
this, LocaleService.
class
)
;
// Simple key lookup — uses server default locale
String msg
= locale.
message
(
"chat.no-permission"
)
;
// With MessageFormat-style argument substitution ({0}, {1}, …)
String msg
= locale.
message
(
"punishment.ban", playerName, reason
)
;
// Per-player locale (falls back to server default)
String msg
= locale.
messageFor
(player.
getUniqueId
(
),
"chat.no-permission"
)
;
String msg
= locale.
messageFor
(player.
getUniqueId
(
),
"punishment.ban", playerName, reason
)
;
// Override a specific player's locale
locale.
setPlayerLocale
(player.
getUniqueId
(
),
"de_DE"
)
;
String playerLocale
= locale.
playerLocale
(player.
getUniqueId
(
)
)
;
// "de_DE"
// Register your own translation bundle
// Keys are auto-namespaced as <namespace>.<key>
locale.
registerBundle
(
"myplugin",
"en_US",
Map.
of
(
"warp.teleported",
"Teleported to {0}.",
"warp.not-found",
"Warp ''{0}'' does not exist."
)
)
;
// Server default locale tag
String def
= locale.
defaultLocale
(
)
;
// e.g. "en_US"
---
PlaceholderService
Central placeholder registry. If PlaceholderAPI is present, a PAPI expansion is registered automatically so
%helios_<pluginId>_<identifier>% works in any PAPI-compatible plugin.
Code (Java):
PlaceholderService placeholders
= CoreServices.
require
(
this, PlaceholderService.
class
)
;
// Register a provider (replaces existing one with same pluginId)
placeholders.
register
(
new MyPlaceholderProvider
(
)
)
;
// Unregister on disable
placeholders.
unregister
(
"myplugin"
)
;
// Resolve manually (e.g. for internal usage without PAPI)
String val
= placeholders.
resolve
(offlinePlayer,
"myplugin_rank"
)
;
// List all registered providers
List
<String
> providers
= placeholders.
registeredProviders
(
)
;
// Is PAPI integration active?
boolean papi
= placeholders.
papiActive
(
)
;
---
PermissionMetaService
LuckPerms meta bridge. Resolves prefix/suffix/meta values from LuckPerms (or any other provider registered at runtime).
Code (Java):
PermissionMetaService meta
= CoreServices.
require
(
this, PermissionMetaService.
class
)
;
// Resolve all meta for a player
PermissionMetaSnapshot snap
= meta.
resolve
(offlinePlayer
)
;
// snap provides prefix, suffix, and arbitrary meta keys
// Check if a real external provider (e.g. LuckPerms) is hooked
boolean hasProvider
= meta.
hasExternalProvider
(
)
;
String provider
= meta.
providerName
(
)
;
// e.g. "LuckPerms"
---
LogService
Universal structured logging for block, command, and audit events. Stored with a configurable retention policy and queryable by type, actor, or time range.
Code (Java):
LogService logs
= CoreServices.
require
(
this, LogService.
class
)
;
// Log a structured entry (async — non-blocking)
logs.
log
(LogEntry.
builder
(
)
.
type
(LogEntry.
LogType.
COMMAND
)
.
actor
(sender.
getName
(
)
)
.
action
(
"WARP"
)
.
target
(warpName
)
.
details
(
"Teleported to "
+ warpName
)
.
build
(
)
)
;
// Log and await confirmation
CompletableFuture
<Boolean
> result
= logs.
logAsync
(entry
)
;
// Query with a LogQuery filter (type, actor, time range, limit, …)
List
<LogEntry
> entries
= logs.
query
(LogQuery.
builder
(
).
actor
(
"Steve"
).
limit
(
25
).
build
(
)
)
;
// Convenience queries
List
<LogEntry
> byType
= logs.
getEntriesByType
(LogEntry.
LogType.
COMMAND,
50
)
;
List
<LogEntry
> byActor
= logs.
getEntriesByActor
(
"Steve",
25
)
;
List
<LogEntry
> inRange
= logs.
getEntriesByTime
(Instant.
now
(
).
minusSeconds
(
3600
), Instant.
now
(
),
100
)
;
// Stats and maintenance
long total
= logs.
getTotalEntries
(
)
;
logs.
pruneOldEntries
(
30
)
;
// delete entries older than 30 days
String json
= logs.
exportAsJson
(entries
)
;
LogStorageInfo info
= logs.
getStorageInfo
(
)
;
logs.
reload
(
)
;
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
4. Command Framework
HeliosCore ships
RoutedCommand +
BaseSubcommand. Extend them to get free tab-completion, permission gating, and the interactive
/yourcommand help output.
Code (Java):
public
final
class MySubcommand
extends BaseSubcommand
{
public MySubcommand
(JavaPlugin plugin
)
{
super
(
plugin,
"warp",
// subcommand name
List.
of
(
"go",
"tp"
),
// aliases
"myplugin.command.warp",
// permission node
"Teleport to a warp point.",
// description shown in help
"warp <name>"
// usage shown in help
)
;
}
@Override
public
boolean execute
(CommandSender sender,
String label,
String
[
] args
)
{
// your logic
return
true
;
}
@Override
public List
<String
> suggest
(CommandSender sender,
String
[
] args
)
{
return
List.
of
(
"spawn",
"home",
"market"
)
;
}
}
Wire it up:
Code (Java):
RoutedCommand router
=
new RoutedCommand
(
this,
"myplugin",
List.
of
(
new MySubcommand
(
this
),
new AnotherSubcommand
(
this
)
)
)
;
PluginCommand cmd
= getCommand
(
"myplugin"
)
;
cmd.
setExecutor
(router
)
;
cmd.
setTabCompleter
(router
)
;
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
5. Rich Chat Utilities — Fmt
dev.ninez.core.api.text.Fmt is a public component factory for building premium interactive chat output using Paper's Adventure API.
Code (Java):
import
dev.ninez.core.api.text.Fmt
;
// Key/value row: " Label: value"
sender.
sendMessage
(Fmt.
kv
(
"Version",
"1.0.0"
)
)
;
sender.
sendMessage
(Fmt.
kv
(
"Status",
"online", NamedTextColor.
GREEN
)
)
;
// Clickable command link with hover tooltip
sender.
sendMessage
(Fmt.
clickCommand
(
"Run Test",
"/hcore database test",
"Click to test the DB"
)
)
;
sender.
sendMessage
(Fmt.
suggestCommand
(
"Configure",
"/hcore reload",
"Click to fill command"
)
)
;
// Status indicators
sender.
sendMessage
(Fmt.
ok
(
"Connection passed"
)
)
;
// ✔ green
sender.
sendMessage
(Fmt.
fail
(
"Connection failed"
)
)
;
// ✘ red
sender.
sendMessage
(Fmt.
warn
(
"Pool is idle"
)
)
;
// ⚠ yellow
// Toggle pills
sender.
sendMessage
(Fmt.
pill
(enabled
)
)
;
// [ON] / [OFF]
sender.
sendMessage
(Fmt.
pillEnabled
(enabled
)
)
;
// [ENABLED] / [DISABLED]
// Pagination footer with clickable prev/next
sender.
sendMessage
(Fmt.
paginationFooter
(
"/myplugin list", page, totalPages
)
)
;
// Section header and divider
sender.
sendMessage
(Fmt.
sectionHeader
(
"My Feature"
)
)
;
sender.
sendMessage
(Fmt.
divider
(
)
)
;
// Format playtime ticks → "2h 15m", "3d 5h", "< 1m"
String time
= Fmt.
formatPlaytime
(player.
getStatistic
(Statistic.
PLAY_ONE_MINUTE
)
)
;
Colour constants:
Fmt.BRAND (gold),
Fmt.ACCENT (aqua),
Fmt.LABEL (gray),
Fmt.VALUE (white),
Fmt.MUTED (dark gray),
Fmt.POSITIVE (green),
Fmt.NEGATIVE (red),
Fmt.WARNING (yellow)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
6. Events
NinezAuditRecordEvent
Fired on the main thread whenever any Helios plugin records a staff audit action (e.g. HeliosAdmin logging a kick, freeze, or vanish). Listen to this to forward audit entries to your own logging system, Discord bot, or database.
Code (Java):
@EventHandler
public
void onAudit
(NinezAuditRecordEvent event
)
{
String plugin
= event.
sourcePlugin
(
)
;
// e.g. "HeliosAdmin"
String actor
= event.
actorName
(
)
;
// staff member's name
String action
= event.
action
(
)
;
// "KICK", "FREEZE", "VANISH", etc.
String target
= event.
targetName
(
)
;
String outcome
= event.
outcome
(
)
;
// "SUCCESS" or "FAILED"
String details
= event.
details
(
)
;
String time
= event.
loggedAt
(
)
;
// ISO-8601 timestamp
}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
7. Gradle / Maven Dependency
Publish HeliosCore to your local Maven repository (
./gradlew publishToMavenLocal) then reference it:
Gradle (build.gradle):
Code (Groovy):
repositories
{
mavenLocal
(
)
}
dependencies
{
compileOnly
"dev.ninez:HeliosCore:1.0.0"
}
Maven (pom.xml):
Code (XML):
<dependency>
<groupId>dev.ninez
</groupId>
<artifactId>HeliosCore
</artifactId>
<version>1.0.0
</version>
<scope>provided
</scope>
</dependency>
Use
compileOnly /
provided — HeliosCore is on the server classpath at runtime; you must not shade it.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Made by Helios Labs ·
Part of the Helios Ecosystem
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━