API
The LoriTime API lets other plugins read stored player online time, resolve known player identities, and write audited manual time adjustments.
Compatibility
Section titled “Compatibility”| Platform | LoriTime version | Supported |
|---|---|---|
| Paper | 2.0.0 - current | Yes |
| Folia | 2.0.0 - current | Yes |
| Velocity | 2.0.0 - current | Yes |
Dependency Setup
Section titled “Dependency Setup”There is currently no public Maven or Gradle repository for LoriTime. Add the LoriTime jar to your build manually and declare a plugin dependency or soft dependency.
Paper / Folia
Section titled “Paper / Folia”name: MyPluginversion: 1.0main: myplugin.MyPluginauthor: MaxMustermanndescription: A plugin that hooks into LoriTime.softdepend: - LoriTimeUse depend instead of softdepend if your plugin cannot run without LoriTime.
Velocity
Section titled “Velocity”Add LoriTime as a dependency in your Velocity plugin annotation or plugin metadata according to your build setup.
Recommended Facade
Section titled “Recommended Facade”Use LoriTimeAPI.service() for new integrations. It returns an Optional<LoriTimeService> because LoriTime may not be installed or initialized yet. LoriTimeService is the stable interface; do not depend on LoriTime’s internal service implementation classes.
import com.jannik_kuehn.common.api.LoriTimeAPI;import com.jannik_kuehn.common.api.LoriTimeService;
public final class LoriTimeHook {
private LoriTimeService loriTime;
public void enable() { LoriTimeAPI.service().ifPresent(service -> this.loriTime = service); }}If LoriTime is a hard dependency and your plugin enables after LoriTime, the optional should normally be present during your enable phase.
Reading Player Data
Section titled “Reading Player Data”Public Player Model
Section titled “Public Player Model”LoriTimePlayer is the stable public player identity contract. It exposes only the player’s UUID and latest known name. Time operations always use the UUID as the exact player identity; the name is display or audit metadata.
Use LoriTimePlayerRef when your plugin needs to keep or create a player reference:
LoriTimePlayer player = new LoriTimePlayerRef(uniqueId, playerName);LoriTime internals may use richer sender objects for permissions, messages, console state, or online state. Those sender details are not part of the public LoriTimePlayer contract.
Resolve UUID by Name
Section titled “Resolve UUID by Name”loriTime.findUuid("Lorias_").thenAccept(optionalUniqueId -> { optionalUniqueId.ifPresent(uniqueId -> { // Use the UUID in your plugin. });});Resolve Latest Name by UUID
Section titled “Resolve Latest Name by UUID”loriTime.findName(uniqueId).thenAccept(optionalName -> { optionalName.ifPresent(name -> { // Use the latest known name in your plugin. });});Read Online Time
Section titled “Read Online Time”loriTime.getOnlineTime(uniqueId).thenAccept(optionalOnlineTime -> { optionalOnlineTime.ifPresent(duration -> { long seconds = duration.toSeconds(); });});If you already have a LoriTimePlayer, use the player overload:
loriTime.getOnlineTime(player).thenAccept(optionalOnlineTime -> { optionalOnlineTime.ifPresent(duration -> { // Use the player's time. });});An empty result means LoriTime does not currently have stored data for that player.
Read Scoped Online Time
Section titled “Read Scoped Online Time”Use TimeScope to query global, server, or world totals:
import com.jannik_kuehn.common.api.storage.TimeScope;
loriTime.getOnlineTime(uniqueId, TimeScope.server("survival")).thenAccept(optionalOnlineTime -> { optionalOnlineTime.ifPresent(duration -> { long seconds = duration.toSeconds(); });});
loriTime.getOnlineTime(uniqueId, TimeScope.world("survival", "world"));Read Time In A Range
Section titled “Read Time In A Range”Use TimeRange to query a bounded inclusive-start/exclusive-end history window. Ranged totals clip session rows to the requested window and include manual adjustments whose audit timestamp is inside the range.
import com.jannik_kuehn.common.api.storage.TimeRange;import com.jannik_kuehn.common.api.storage.TimeScope;
TimeRange lastSevenDays = TimeRange.between( Instant.now().minus(Duration.ofDays(7)), Instant.now());
loriTime.getOnlineTime(uniqueId, TimeScope.server("survival"), lastSevenDays) .thenAccept(optionalOnlineTime -> { optionalOnlineTime.ifPresent(duration -> { long seconds = duration.toSeconds(); }); });The ranged overload is part of the LoriTime 2 API surface.
Writing Manual Adjustments
Section titled “Writing Manual Adjustments”Use signed durations. Positive values add time, negative values remove time. Durations must be precise to whole seconds.
System/API Adjustment
Section titled “System/API Adjustment”loriTime.addTime(uniqueId, Duration.ofMinutes(10)).thenRun(() -> { // The write was attempted successfully.});loriTime.addTime(uniqueId, Duration.ofMinutes(-5));The same operation can target a public player reference:
loriTime.addTime(player, Duration.ofMinutes(10));These adjustments are stored with LoriTime’s stable API actor name.
To write a scoped adjustment, pass a TimeScope:
loriTime.addTime(uniqueId, Duration.ofMinutes(10), TimeScope.server("survival"));loriTime.addTime(uniqueId, Duration.ofMinutes(-5), TimeScope.world("survival", "world"));Actor-Aware Adjustment
Section titled “Actor-Aware Adjustment”loriTime.addTime( uniqueId, Duration.ofMinutes(10), actorUniqueId, actorName).thenRun(() -> { // The actor-aware write completed.});Or pass public player identities for both target and actor:
loriTime.addTime( targetPlayer, Duration.ofMinutes(10), actorPlayer);Actor-aware adjustments preserve the actor UUID and actor name in LoriTime’s adjustment history.
Actor-aware adjustments can also be scoped:
loriTime.addTime(uniqueId, Duration.ofMinutes(10), actorUniqueId, actorName, TimeScope.server("survival"));Errors
Section titled “Errors”Facade methods validate null inputs and unsupported duration values before scheduling storage work. Storage failures complete the returned future exceptionally with LoriTimeApiException, so integrations do not need to handle raw storage or SQL exceptions from the public facade.
loriTime.addTime(uniqueId, Duration.ofSeconds(30)).exceptionally(ex -> { Throwable cause = ex.getCause(); if (cause instanceof LoriTimeApiException apiException) { getLogger().warning("Could not update LoriTime: " + apiException.getMessage()); } return null;});Threading and Storage Modes
Section titled “Threading and Storage Modes”The facade returns CompletableFuture values. LoriTime schedules the blocking storage work asynchronously, and future continuations run in that asynchronous completion context unless you reschedule them. If your continuation touches Paper, Folia, Velocity or other platform-thread-bound APIs, reschedule that work through your platform scheduler first.
In slave mode, facade reads follow the same deterministic fallback behavior as LoriTime’s local consumers. If the local instance only has cached data, an unknown player or cache miss may return an empty result until LoriTime receives data from the master.
API Surface
Section titled “API Surface”LoriTimeAPI.service(), the LoriTimeService interface, LoriTimePlayer, and immutable public player references are the stable public integration surface for normal third-party plugins.
Internal classes such as LoriTimePlugin, platform adapters, scheduler adapters, storage lifecycle managers, storage contracts, accumulators, configuration, localization, and updater state are not part of the public integration API unless a future change introduces a dedicated advanced extension API.