Astral Realms Documentation Help

Homes

A home is a per-player named teleport point. Homes are not global like warps — each player has their own independent set, held on their HomePlayerData. That data is serialized and replicated across the network by AstralSync (HomeSnapshotAdapter, key astralhomes:homes, registered via SyncAPI.registerAdapter) — homes are never written to the plugin's MariaDB database, which only backs the global warps table (see Overview → Persistence split).

Model

Field

Type

Notes

name

String

Looked up case-insensitively. Must be ≤ 16 characters and match ^[a-zA-Z_:-]+$.

location

NetworkLocation

x/y/z/yaw/pitch + worldName + serverId — a full network-wide coordinate, not a plain Bukkit Location, so a home can point at a different server than the one the player is currently on.

HomePlayerData.homes is a Set<Home> — one set per player. Home and HomePlayerData both expose placeholders (namespaces home and homes respectively; homename/location, homeshomes/lastLocation/teleportationRequestEnabled) — see Placeholders.

Setting a home

/sethome <name> walks through validation, in this order, before a home is actually written:

Step

Check

Failure message

1

Name length ≤ 16 characters

HOME_NAME_TOO_LONG

2

Name matches ^[a-zA-Z_:-]+$

HOME_NAME_INVALID

3

Current world is not in blacklist (config.yml)

HOME_BLACKLISTED_WORLD

4

SetHomeEvent is not cancelled

HOME_CREATION_CANCELLED

5

If the name is new (not an existing home), homes.size() < limit(player)

HOME_LIMIT_REACHED (%limit%)

Steps 1–2 run in SetHomeCommand before HomeService.set is even called; steps 3–5 run inside HomeService.set. SetHomeEvent is cancellable and carries the player, the requested name, and the Bukkit Location, so other plugins can veto a home being set at a particular spot.

If the name already exists on the player (case-insensitive match via HomePlayerData.findHomeByName), the limit check (step 5) is skipped entirely and the existing Home's location is updated in place instead of creating a new entry — re-setting a home never counts against the limit. Either way, a successful set calls HomePlayerData.addHome(home) and sends HOME_CREATED (placeholders %home_name%, %home_location_x%/%home_location_y%/%home_location_z%).

Home limits

The number of homes a player may hold comes from the limits permission → integer map in config.yml:

limits: astralhomes.default: 3 astralhomes.vip: 5 astralhomes.mvp: 10

MainConfiguration.limit(player) starts from a floor of 1 and raises it with Math.max for every permission in the map the player holds — so the highest matching value wins (a player holding both astralhomes.default and astralhomes.mvp gets 10, not 3). A player who holds none of the configured permissions still gets the hard-coded floor of 1.

Teleporting home

/home <name> (aliases /homes, /h) resolves <name> against the player's own homes only (HomeContextResolverHomeService.findByName); an unknown name fails command parsing with Home not found: <name> rather than a messages.yml entry. On a match, HomeService.teleport(player, home):

  1. Runs the shared warmup.

  2. On completion, resolves AstralCore's TeleportationService and calls teleport(player.getUniqueId(), home.location()). Because home.location() is a NetworkLocation carrying a serverId, this can send the player to a different server in the network, not just to another spot on the current one.

  3. If TeleportationService isn't registered at all, UNEXPECTED_ERROR is sent immediately — this path is not logged, unlike an in-flight teleport failure.

  4. Once the (async) teleport completes: an exception sends UNEXPECTED_ERROR and logs the failure; otherwise HOME_TELEPORT_SUCCESS or HOME_TELEPORT_FAILURE is sent depending on the returned TeleportationResponsePacket.Response#success() flag.

Homes GUI

/home with no argument does not teleport — it opens the homes menu instead: MenuContainer.computeAndOpen(player, "homes"). If the menu can't be computed, the error is logged and the player gets a plain red "An error occurred while opening your homes menu. Please try again later." message (not a HomesMessages/messages.yml entry). Buttons inside that menu can invoke the delete-home action to remove a home straight from the GUI — see Developer API.

Warmup

Home teleports (and warps, spawn, and accepted teleport requests) go through the shared WarmupService before the actual teleport runs. The duration comes from the warmup permission → duration map in config.yml:

warmup: astralhomes.default: "5s" astralhomes.vip: "3s" astralhomes.mvp: "0s"

MainConfiguration.warmup(player) starts from a hard-coded floor of Duration.ofSeconds(3), then walks the map's entries — in the order Configurate deserialized them, which follows the YAML file's own order — and, for each permission the player holds, overwrites the running value with that entry's duration if either the entry's duration is strictly smaller than the running value, or the running value has already been reduced to zero. In the shipped config the three entries are listed in strictly descending duration order (default: 5svip: 3smvp: 0s), so this always resolves to the smallest duration the player has permission for — in effect "lowest matching permission wins":

  • A permission mapped to 0s (e.g. astralhomes.mvp) wins outright whenever the player holds it, because it is the last entry in the file and nothing after it can un-zero the value.

  • A permission mapped to a duration ≥ 3 seconds — including the shipped astralhomes.default: 5s — never lowers the floor. A player holding only that permission ends up with the hard-coded 3s warmup, not the configured 5s.

  • A duration of zero or negative resolves to an instant teleport — no warmup, no action-bar countdown.

This "lowest wins" outcome is a property of the shipped file's descending order, not of the algorithm itself — the value.isZero() clause means a zero-duration match doesn't act as a true floor. If an admin reorders config.yml (or adds a permission) so that a larger duration is listed after a zero-duration one the player also holds, that later, larger entry overwrites the zero.

While a warmup is active, WARMUP_STARTED is sent once (placeholder %time%, human-readable via DurationParser) and the player's action bar shows a 50-segment green/gray progress bar with a percentage, refreshed every tick until the warmup resolves.

WarmupListener cancels the pending warmup — completing its future with false, which surfaces as WARMUP_CANCELLED — on:

Event

Condition

PlayerMoveEvent

event.hasChangedBlock() — only a block-to-block move cancels it, not just turning the camera

PlayerQuitEvent

Always

Deleting a home

/delhome <name> (aliases /removehome, /deletehome, /rmhome) resolves <name> the same way as /home <name> and calls HomeService.delete(player, home):

  1. HomePlayerData.removeHome(home) removes it from the player's home set.

  2. HOME_DELETED is sent (placeholder %home_name%; unlike HOME_CREATED it carries no location placeholders).

  3. The informational, non-cancellable DeleteHomeEvent fires afterward — the home is already gone by the time other plugins observe it — carrying the player, the home's former name, and its Bukkit Location.

If no HomePlayerData can be found for the player at all, nothing is deleted; the error is logged and UNEXPECTED_ERROR is sent instead.

Admin reset

/home reset <player> — a subcommand of /home, gated by astralhomes.admin.reset — looks up the target's HomePlayerData via SyncAPI.findData and calls HomePlayerData.clearHomes(), wiping every home the target has with no confirmation step and no event fired. The subcommand is also marked @Private, which ACF excludes from tab completion and help pages entirely (for every player, not just those lacking the permission) — access is controlled solely by @CommandPermission, not by that visibility flag. The feedback messages ("All homes for <player> have been reset." / "No home data found for <player> .") are sent as plain hard-coded Components rather than through HomesMessages/messages.yml, so — unlike the rest of this page's messages — they are not localizable or themeable through messages.yml.

See Commands for the full /sethome//home//delhome syntax and permission summary, and Overview → Persistence split for how home storage compares to warp storage.

Last modified: 25 July 2026