Homes API
Read, create, delete, and teleport players to their homes. Package net.godlycow.org.essc.api.home.
Overview
The homes module revolves around HomeManager. Data access is async (CompletableFuture, since storage is async); teleport state - cooldowns and pending warmups - is synchronous. Seven events cover the full home lifecycle.
Home
Immutable snapshot of a single home. Homes are compared by (owner, name).
| Method | Returns | Description |
|---|---|---|
| UUID getOwner() | UUID | UUID of the home owner. |
| String getName() | String | Home name, e.g. base. |
| String getWorldName() | String | Name of the world this home is in. |
| double getX() | double | X coordinate. |
| double getY() | double | Y coordinate. |
| double getZ() | double | Z coordinate. |
| float getYaw() | float | Yaw rotation. |
| float getPitch() | float | Pitch rotation. |
| long getCreatedAt() | long | Unix timestamp (millis) when the home was created. |
| Location toLocation(Server server) | Location | Resolves the stored coordinates into a Bukkit Location. |
Since Home stores raw coordinates, you can build the Location yourself, or let toLocation() do it:
// Option 1: one-call conversion.
Location loc = home.toLocation(server);
// Option 2: manual, e.g. to tweak coordinates first.
Location loc2 = new Location(
server.getWorld(home.getWorldName()),
home.getX(), home.getY(), home.getZ(),
home.getYaw(), home.getPitch()
);
HomeManager
Get it via api.getHomeManager(). Data reads/writes are async (CompletableFuture); teleport state is synchronous.
Data access
| Method | Returns | Description |
|---|---|---|
| boolean isHomeSystemEnabled() | boolean | Whether the homes system is enabled in the config. |
| CompletableFuture<Home> fetchHome(UUID owner, String name) | Home | Fetches a single home; completes with null if it doesn't exist. |
| CompletableFuture<List<Home>> fetchHomes(UUID owner) | List<Home> | Fetches all homes for an owner, including offline players. |
| CompletableFuture<Boolean> homeExists(UUID owner, String name) | boolean | Whether a named home exists for the owner. |
| CompletableFuture<Integer> getHomeCount(UUID owner) | int | How many homes the owner has stored. |
| CompletableFuture<Boolean> setHome(Player player, String name, Location location) | boolean | Creates or overwrites a home for the player. |
| CompletableFuture<Boolean> setHome(UUID owner, String name, Location location) | boolean | Same, but works for offline players. |
| CompletableFuture<Boolean> deleteHome(UUID owner, String name) | boolean | Deletes a home; completes false if it didn't exist. |
| int getMaxHomes(Player player) | int | Maximum homes allowed for the player based on their permissions. |
| Collection<String> getCachedHomeNames(UUID owner) | Collection<String> | Names currently held in the in-memory cache (fast, may be stale). |
| void clearCache(UUID owner) | void | Drops the cached home names for an owner. |
| void reload() | void | Reloads config + storage. Re-query anything you cached. |
Teleports & cooldowns
| Method | Returns | Description |
|---|---|---|
| boolean isOnCooldown(Player player) | boolean | Whether the player is inside the home teleport cooldown window. |
| long getRemainingCooldownSeconds(Player player) | long | Seconds left on the cooldown, or 0. |
| boolean hasPendingTeleport(Player player) | boolean | Whether the player currently has an in-progress (warmup) teleport. |
| void cancelTeleport(Player player) | void | Cancels the pending teleport (also cancels the warmup). |
| void startTeleport(Player player, Home home) | void | Starts the teleport flow for a player to a home, firing the warmup + teleport events. |
A typical async fetch - never block the main thread on the future:
HomeManager homes = api.getHomeManager();
homes.fetchHome(player.getUniqueId(), "base").thenAccept(home -> {
if (home == null) {
player.sendMessage("You don't have a home called 'base'.");
return;
}
player.teleportAsync(home.toLocation(server));
});
Teleports & cooldowns
The flow: startTeleport() → HomeWarmupStartEvent (cancellable, adjustable warmup) → HomeTeleportEvent after the warmup (cancellable) → player moves → HomePostTeleportEvent. Moving or logging out cancels the warmup and fires HomeWarmupCancelEvent.
// Start the full teleport flow (warmup + teleport + events).
if (homes.isOnCooldown(player)) {
player.sendMessage("Wait " + homes.getRemainingCooldownSeconds(player) + "s");
return;
}
if (homes.hasPendingTeleport(player)) {
player.sendMessage("A home teleport is already in progress.");
return;
}
homes.fetchHome(player.getUniqueId(), "base").thenAccept(home -> {
if (home != null) {
homes.startTeleport(player, home); // fires warmup/teleport events
}
});
HomeWarmupStartEvent or HomeTeleportEvent, or react after it completes.
Events
All seven home events live in net.godlycow.org.essc.api.home.event and extend org.bukkit.event.Event. Register listeners the usual way via org.bukkit.plugin.PluginManager.
HomeSetEvent - cancellable
Fired when a home is created or overwritten. Cancelling prevents the save.
@EventHandler
public void onHomeSet(HomeSetEvent event) {
Player p = event.getPlayer();
// Veto homes inside a protected region, with a custom reason.
if (isProtected(event.getLocation())) {
event.setCancelled(true);
event.setCancelReason("Location is protected.");
p.sendMessage("You can't set a home there!");
}
String homeName = event.getHomeName();
Location loc = event.getLocation();
}
HomeDeleteEvent - cancellable
Fired when a home is deleted.
@EventHandler
public void onHomeDelete(HomeDeleteEvent event) {
// Log every home deletion to a file.
getLogger().info(event.getPlayer().getName()
+ " deleted home '" + event.getHomeName() + "'");
}
HomeTeleportEvent - cancellable
Fired just before the player is moved to the home.
@EventHandler
public void onHomeTeleport(HomeTeleportEvent event) {
Home home = event.getHome();
// Block teleports into unloaded/vanished worlds.
if (server.getWorld(home.getWorldName()) == null) {
event.setCancelled(true);
event.setCancelReason("Home world is missing.");
}
}
HomeWarmupStartEvent - cancellable
Fired when the warmup begins. You can lengthen/shorten it or cancel outright.
@EventHandler
public void onHomeWarmupStart(HomeWarmupStartEvent event) {
// Double the warmup for players in combat.
if (isInCombat(event.getPlayer())) {
event.setWarmupSeconds(event.getWarmupSeconds() * 2);
}
// Give donors instant teleports.
if (event.getPlayer().hasPermission("vip.instant")) {
event.setWarmupSeconds(0);
}
}
HomeWarmupCancelEvent
Fired when a warmup is aborted. getReason() is one of PLAYER_OFFLINE, PLAYER_MOVED, or EVENT_CANCELLED.
@EventHandler
public void onHomeWarmupCancel(HomeWarmupCancelEvent event) {
switch (event.getReason()) {
case PLAYER_MOVED -> event.getPlayer().sendMessage("Teleport cancelled - you moved!");
case PLAYER_OFFLINE -> getLogger().info("Home warmup dropped for offline player.");
case EVENT_CANCELLED -> { /* another plugin vetoed it */ }
}
}
HomePostTeleportEvent
Fired after the player has been moved to their home. Use it for welcome effects.
@EventHandler
public void onHomePostTeleport(HomePostTeleportEvent event) {
Player p = event.getPlayer();
Location dest = event.getDestination();
p.playSound(dest, Sound.BLOCK_PORTAL_TRAVEL, 0.5f, 1.2f);
p.sendActionBar("Welcome home, " + p.getName() + "!");
}
HomeCooldownExpireEvent
Fired when a player's home teleport cooldown expires.
@EventHandler
public void onHomeCooldownExpire(HomeCooldownExpireEvent event) {
// previousTeleportTime is the millis timestamp of the last teleport.
long last = event.getPreviousTeleportTime();
getLogger().info(event.getPlayer().getName() + " home cooldown expired");
}
Worked examples
Give every new player a starter home
@EventHandler
public void onJoin(PlayerJoinEvent event) {
Player p = event.getPlayer();
HomeManager homes = api.getHomeManager();
homes.homeExists(p.getUniqueId(), "spawn")
.thenAccept(exists -> {
if (!exists) {
Location spawn = p.getWorld().getSpawnLocation();
homes.setHome(p, "spawn", spawn);
}
});
}
List a player's homes as formatted text
@Override
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
if (!(sender instanceof Player p)) return true;
api.getHomeManager().fetchHomes(p.getUniqueId()).thenAccept(homes -> {
if (homes.isEmpty()) {
p.sendMessage("You have no homes. Use /sethome.");
return;
}
StringBuilder sb = new StringBuilder("Homes (").append(homes.size()).append("): ");
for (int i = 0; i < homes.size(); i++) {
if (i > 0) sb.append(", ");
sb.append(homes.get(i).getName());
}
p.sendMessage(sb.toString());
});
return true;
}
Clean up homes when a player is banned
@EventHandler
public void onBan(BanListEvent event) { /* not a real event - illustrative */ }
// Using a UUID lookup:
public void purgePlayer(UUID uuid) {
HomeManager homes = api.getHomeManager();
homes.fetchHomes(uuid).thenAccept(list -> {
for (Home home : list) {
homes.deleteHome(uuid, home.getName());
}
homes.clearCache(uuid);
});
}