Developer API
AstralTown exposes its data through the TownAPI static façade and a set of services hung off the main plugin instance. Custom Bukkit events provide hookpoints.
Maven coordinates
<dependency>
<groupId>com.astralrealms</groupId>
<artifactId>town</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
Repository: https://maven.astralrealms.fr/repository/maven-public/.
TownAPI
Static lookups suitable for any plugin that needs to read town state without taking a hard dependency on the service classes.
import com.astralrealms.town.TownAPI;
// Player <-> town
TownAPI.findPlayerSelectedTown(player); // Optional<Town>
TownAPI.setPlayerSelectedTown(player, townId);
TownAPI.findTownsByMember(playerUuid); // Collection<Town>
// Lookups
TownAPI.findTownByUniqueId(townId); // Optional<Town>
TownAPI.findTownByName("MyTown"); // Optional<Town>
TownAPI.findTownByLocation(location); // Optional<Town>
TownAPI.findTownByChunk(chunk); // Optional<Town>
// Permissions & settings (resolves subzone vs town vs wilderness)
TownAPI.hasPermission(player, location, PermissionType.BLOCK_PLACE);
TownAPI.hasSettingEnabled(location, TownSettings.PVP);
// Claim helpers
TownAPI.isWithinOwnTownClaims(player); // boolean
TownAPI.findClaimsByTown(townId); // Collection<Claim>
// Subzones
TownAPI.findSubZonesByTown(townId); // Collection<SubZone>
hasPermission is the recommended entry point for any external plugin that wants to know "may the player do X here" — it handles subzone scoping, the owner bypass, and the town.admin override.
Plugin handle
import com.astralrealms.town.AstralTown;
AstralTown plugin = AstralTown.getPlugin(AstralTown.class);
Services
Each service has a getter and a public surface. See Overview for the full list. Key examples:
// Towns
plugin.towns().findById(townId); // Optional<Town>
plugin.towns().findByPlayer(playerUuid); // Collection<Town>
plugin.towns().updateGlobally(townId); // CompletableFuture<Void>
// Claims
plugin.claims().findChunkOwner(chunk); // Optional<Town>
plugin.claims().countByTown(townId); // int
plugin.claims().calculatePrice(currentClaims); // double
// Bans
plugin.bans().findByTown(townId); // CompletableFuture<Collection<TownBan>>
plugin.bans().isBannedFromCached(playerId, townId); // boolean
// Logs
plugin.logs().findByTownId(townId); // CompletableFuture<List<TownLog>>
// Chat
plugin.chat().broadcast(town, component);
// Warps
plugin.warps().sortedWarps(); // sorted by weeklyVisits desc
Custom Bukkit events
See Events for the full catalogue. Typical use:
@EventHandler
public void onJoin(TownMemberJoinEvent event) {
Town town = event.getTown();
TownMember member = event.getMember();
Player player = event.getPlayer();
// Grant a "newcomer" badge, log analytics, …
}
@EventHandler
public void onClaim(TownClaimEvent event) {
Town town = event.getTown();
Claim claim = event.getClaim();
// Update your own world data here
}
For lifecycle clean-up — when a town disappears entirely — use TownDisbandedEvent (post-fact, non-cancellable, async-safe).
Custom actions and placeholders
AstralTown's 22 actions go through the standard AstralPaperAPI action registry, so any plugin can trigger them programmatically or chain them in their own menus. The placeholder namespaces (towns_*, town_*, role_*, member_*, warp_*, subzone_*, ban_*, invitation_*, log_*) resolve through AstralPaperAPI.createPlaceholderContainer(player) without extra wiring.
Reading per-player state
import com.astralrealms.town.storage.TownPlayerData;
import com.astralrealms.sync.SyncAPI;
SyncAPI.findData(player.getUniqueId(), TownPlayerData.class)
.ifPresent(data -> {
Optional<Town> selected = data.selectedTown();
boolean chatOn = data.chatEnabled();
Set<UUID> pinned = data.favoriteWarps();
});
Mutating these fields directly bypasses the user-facing confirmation messages — use the actions for normal flows; only touch the snapshot directly for admin tooling.
Last modified: 25 July 2026