Skip to main content

Developer API

Generated Java reference includes searchable classes, methods and signatures, with the source version and commit recorded for each module.

Requirements​

BetterStructures requires:

  • Java 21 or higher
  • Spigot/Paper server
  • WorldEdit plugin (hard dependency)

Optional dependencies:

  • WorldGuard - For region protection features (requires EliteMobs to also be installed for full integration)
  • EliteMobs - For elite mob spawning integration
  • MythicMobs - For mythic mob integration
  • Terralith, Iris, Terra, TerraformGenerator - For custom biome compatibility

Public Repository

Maven​

<repositories>
<repository>
<id>magmaguy-repo-releases</id>
<name>MagmaGuy's Repository</name>
<url>https://repo.magmaguy.com/releases</url>
</repository>
</repositories>

<dependency>
<groupId>com.magmaguy</groupId>
<artifactId>BetterStructures</artifactId>
<version>2.7.7</version>
<scope>provided</scope>
</dependency>

See the Maven repository for available versions. The example uses published version 2.7.7; current source is 2.7.7. Match your installed plugin.

Gradle

repositories {
maven {
name = "magmaguyRepoReleases"
url = uri("https://repo.magmaguy.com/releases")
}
}

dependencies {
compileOnly("com.magmaguy:BetterStructures:2.7.7")
}

See the Maven repository for available versions. The example uses published version 2.7.7; current source is 2.7.7. Match your installed plugin.

Events

Note: Events are in com.magmaguy.betterstructures.api

Package Imports​

import com.magmaguy.betterstructures.api.BuildCustomMarkerEvent;
import com.magmaguy.betterstructures.api.BuildPasteCompleteEvent;
import com.magmaguy.betterstructures.api.BuildPlaceEvent;
import com.magmaguy.betterstructures.api.ChestFillEvent;
import com.magmaguy.betterstructures.api.WorldGenerationFinishEvent;
import com.magmaguy.betterstructures.buildingfitter.FitAnything;
import com.magmaguy.betterstructures.modules.ModularWorld;
import com.magmaguy.betterstructures.thirdparty.WorldGuard;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Container;
import org.bukkit.entity.EntityType;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import java.util.List;

BuildPlaceEvent​

Called when a build is about to be placed. Exposes data about which build is going to be placed and where, among other things, through the FitAnything object.

Don't attempt to modify the build getting placed! You can modify minor things, but changing the whole build will probably result in a build with a poor fit.

This is cancellable.

ChestFillEvent​

Called when a chest is filled. Uses the container snapshot inventory to safely store the data to be applied.

You can modify the loot using the Inventory methods addItem() or removeItem() on the container's snapshot inventory obtained via getContainer().getSnapshotInventory().

You can also call getTreasureConfigFilename() to identify which loot table configuration was used to fill this chest.

This is cancellable.

BuildCustomMarkerEvent​

Called once for each [custom] sign after the structure's blocks, loot, mobs and entities are pasted. BetterStructures removes the sign first, so the block at getLocation() is air and you can place anything there.

  • getId() - Lines 2-4 of the sign joined without separators, for example Zombie_Plus
  • getLines() - Lines 2-4 individually, trimmed, with blank lines as empty strings
  • getLocation() - The sign's world location
  • getFitAnything() - The structure being pasted, as in BuildPlaceEvent

This event is NOT cancellable.

BuildPasteCompleteEvent​

Called once a regular structure is completely pasted, after every BuildCustomMarkerEvent for it. Pasting is spread over several ticks, so this is the reliable point to work with the placed structure. BuildPlaceEvent fires before any of it happens. If a finishing step fails, for example a sign boss that cannot spawn, the paste stops and this event does not fire.

  • getFitAnything() - The structure that was pasted
  • getLowestCorner() and getHighestCorner() - The pasted schematic's block corners in world coordinates, both inclusive
  • getBoundingBox() - The space covered by those blocks
  • getContainerLocations() - Every chest, trapped chest, shulker box and barrel from the schematic, whether or not BetterStructures filled it with loot

This event is NOT cancellable.

WorldGenerationFinishEvent​

Called when a modular dungeon generated in its own world (isWorldGeneration: true) finishes pasting. Dungeons pasted into existing worlds do not fire it. Provides access to the ModularWorld object containing all data about the generated world including spawn points, chest locations, and exit points.

This event is NOT cancellable.

You can access world data through the getModularWorld() method. See the ModularWorld section below for all available methods.

Example Event Listener

public class MyBetterStructuresListener implements Listener {

@EventHandler
public void onBuildPlace(BuildPlaceEvent event) {
FitAnything build = event.getFitAnything();
Location location = build.getLocation();
// Custom logic here
// event.setCancelled(true); // To prevent placement
}

@EventHandler
public void onChestFill(ChestFillEvent event) {
Container container = event.getContainer();
Inventory inv = container.getSnapshotInventory();
// Modify inventory here
inv.addItem(new ItemStack(Material.DIAMOND, 1));
}

@EventHandler
public void onCustomMarker(BuildCustomMarkerEvent event) {
if (!event.getId().equals("Zombie_Plus")) return;
// Spawn your own mob at the marker
event.getLocation().getWorld().spawnEntity(event.getLocation().add(0.5, 0, 0.5), EntityType.ZOMBIE);
}

@EventHandler
public void onPasteComplete(BuildPasteCompleteEvent event) {
for (Location containerLocation : event.getContainerLocations()) {
if (!(containerLocation.getBlock().getState() instanceof Container container)) continue;
// Replace the contents with your own loot
container.getInventory().clear();
container.getInventory().addItem(new ItemStack(Material.EMERALD, 3));
}
}

@EventHandler
public void onWorldGenFinish(WorldGenerationFinishEvent event) {
ModularWorld world = event.getModularWorld();
// Access generated world data
List<Location> spawns = world.getSpawnLocations();
}
}

Key classes

FitAnything​

The FitAnything class is the class that gets instantiated when a build gets pasted in and handles every aspect of the paste, including filling chests and spawning mobs.

Available getter methods:

  • getSchematicContainer() - Returns the schematic container
  • getSchematicClipboard() - Returns the WorldEdit Clipboard object
  • getSchematicOffset() - Returns the Vector offset for placement
  • getLocation() - Returns the placement Location

These methods are useful when creating custom region protections or analyzing build placements.

ModularWorld​

The ModularWorld class represents a generated modular world and is provided through the WorldGenerationFinishEvent. It contains all location data and methods for spawning entities and containers in the generated world.

Key methods:

  • getWorld() - Returns the Bukkit World instance
  • getWorldFolder() - Returns the world's File folder
  • getCenter() - Returns the dungeon's starting point, which the difficulty bands are measured from
  • getHorizontalSize() - Returns the dungeon's width in blocks, (2 × radius − 1) × moduleSizeXZ
  • getDifficultyId(Location) - Returns the EliteMobs instance difficulty ID for a location: 2 within a sixth of the width from the center (measured as the larger of the X and Z offsets), 1 within a third, otherwise 0
  • getSpawnLocations() - Returns all marked spawn locations (List<Location>)
  • getChestLocations() - Returns all chest spawn locations (List<Location>)
  • getBarrelLocations() - Returns all barrel spawn locations (List<Location>)
  • getExitLocations() - Returns all exit point locations. Note that ExitLocation is a private nested record, so from outside the plugin you can only hold the result as a wildcard list; there is currently no public type for its elements.
  • spawnChests() - Spawns chests at marked locations, returns List<Block>
  • spawnBarrels() - Spawns barrels at marked locations, returns List<Block>
  • spawnInaccessibleExitLocations() - Spawns upper elevator schematics, returns List<Location>
  • spawnAccessibleExitLocations() - Spawns lower elevator schematics, returns List<Location>
  • spawnOtherEntities() - On the next tick, processes any spawn pool signs the paste has not already handled, then fires WorldGenerationFinishEvent again. The paste normally handles every pool sign itself. Calling it from a WorldGenerationFinishEvent listener makes it repeat every tick.
  • spawnInstancedEntities() - Spawns the queued instanced spawn-pool bosses at their pool's maxLevel, each with the difficulty ID from getDifficultyId, and returns List<InstancedBossEntity>. Each boss carries the spawn pool filename as custom data under the key betterstructures:spawnpool.

WorldGuard​

The WorldGuard class handles the WorldGuard region protections. The utility method public static ProtectedRegion generateProtectedRegion(FitAnything fitAnything, String regionName) is made available for developers to easily hook in a custom region protection scheme on top of BetterStructures.

Other useful methods:

  • checkArea(Location, Player) - Check if a location is in a BetterStructures-protected region (returns true if protected, sends a warning message to the player)
  • Protect(...) - Create protected regions programmatically
  • Unprotect(CustomBossEntity) - Remove region protection when a boss is killed