Add optional WorldEdit soft-dependency; use its wand selection for pos1/pos2
Build / build (push) Successful in 1m19s

This commit is contained in:
Michael Burgess
2026-08-07 10:43:06 -04:00
parent d1d61c5d68
commit 412c9411b8
8 changed files with 107 additions and 4 deletions
+9 -1
View File
@@ -10,7 +10,7 @@ slightly faster round. Last player standing wins.
- PaperMC 1.21.x (API version `1.21`) - PaperMC 1.21.x (API version `1.21`)
- Java 21 - Java 21
- No other plugins required. Optional soft-dependencies: PlaceholderAPI, Vault. - No other plugins required. Optional soft-dependencies: PlaceholderAPI, Vault, WorldEdit.
## Installation ## Installation
@@ -97,6 +97,14 @@ Commands that require a physical location (`setlobby`, `setspawn`, `setspectator
### Floor selection details ### Floor selection details
- `pos1` / `pos2` define the two opposite corners of the rectangular floor region (any Y). - `pos1` / `pos2` define the two opposite corners of the rectangular floor region (any Y).
If [WorldEdit](https://enginehub.org/worldedit) is installed and you have an active WorldEdit
selection (made with the wand — left-click for position 1, right-click for position 2, or
`//pos1` / `//pos2`), running `/bp pos1 <arena>` / `/bp pos2 <arena>` adopts that selection's
corners directly instead of your standing location — no need to physically stand at each
corner. This is entirely optional: without a WorldEdit selection (or without WorldEdit
installed at all), the commands fall back to your current location exactly as before. Disable
it with `integrations.worldedit: false` in `config.yml` if you'd rather always use your
location even when WorldEdit is present.
- `setfloor` only changes which materials are *allowed* to be used/regenerated — it does not - `setfloor` only changes which materials are *allowed* to be used/regenerated — it does not
touch blocks in the world. touch blocks in the world.
- `generate` is the step that actually writes a random layout (biased evenly across the - `generate` is the step that actually writes a random layout (biased evenly across the
+10
View File
@@ -17,6 +17,7 @@ repositories {
maven { url = 'https://repo.papermc.io/repository/maven-public/' } maven { url = 'https://repo.papermc.io/repository/maven-public/' }
maven { url = 'https://repo.extendedclip.com/content/repositories/placeholderapi/' } maven { url = 'https://repo.extendedclip.com/content/repositories/placeholderapi/' }
maven { url = 'https://jitpack.io' } maven { url = 'https://jitpack.io' }
maven { url = 'https://maven.enginehub.org/repo/' }
} }
dependencies { dependencies {
@@ -25,6 +26,15 @@ dependencies {
compileOnly('com.github.MilkBowl:VaultAPI:1.7') { compileOnly('com.github.MilkBowl:VaultAPI:1.7') {
exclude group: 'org.bukkit', module: 'bukkit' exclude group: 'org.bukkit', module: 'bukkit'
} }
// transitive = false: we only need WorldEdit's API types at compile time (it's never
// shaded/bundled - the real WorldEdit plugin provides everything at runtime), and its
// transitive deps pin strict versions of guava/gson/fastutil that conflict with paper-api's.
compileOnly('com.sk89q.worldedit:worldedit-bukkit:7.3.8') {
transitive = false
}
compileOnly('com.sk89q.worldedit:worldedit-core:7.3.8') {
transitive = false
}
implementation 'org.xerial:sqlite-jdbc:3.47.1.0' implementation 'org.xerial:sqlite-jdbc:3.47.1.0'
implementation 'com.mysql:mysql-connector-j:9.1.0' implementation 'com.mysql:mysql-connector-j:9.1.0'
@@ -13,6 +13,7 @@ import us.tss3.blockparty.config.ArenaConfig;
import us.tss3.blockparty.logic.ArenaPhase; import us.tss3.blockparty.logic.ArenaPhase;
import us.tss3.blockparty.persistence.PlayerStats; import us.tss3.blockparty.persistence.PlayerStats;
import us.tss3.blockparty.session.PlayerSession; import us.tss3.blockparty.session.PlayerSession;
import us.tss3.blockparty.worldedit.WorldEditHook;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -219,7 +220,16 @@ public class BlockPartyCommand implements CommandExecutor, TabCompleter {
private void pos1(CommandSender sender, String[] args) { private void pos1(CommandSender sender, String[] args) {
if (!requireAdmin(sender, "blockparty.admin.setup") || !requirePlayer(sender)) return; if (!requireAdmin(sender, "blockparty.admin.setup") || !requirePlayer(sender)) return;
withArena(sender, args, arena -> { withArena(sender, args, arena -> {
Location loc = ((Player) sender).getLocation(); Player player = (Player) sender;
WorldEditHook.Corners selection = worldEditSelection(player);
if (selection != null) {
arena.getConfig().setPos1(selection.min());
arena.getConfig().setWorldName(selection.worldName());
plugin.getArenaManager().save(arena);
sender.sendMessage(plugin.getMessages().get("admin.pos1-set-we", Map.of("arena", arena.getConfig().getName())));
return;
}
Location loc = player.getLocation();
arena.getConfig().setPos1(new int[]{loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()}); arena.getConfig().setPos1(new int[]{loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()});
arena.getConfig().setWorldName(loc.getWorld().getName()); arena.getConfig().setWorldName(loc.getWorld().getName());
plugin.getArenaManager().save(arena); plugin.getArenaManager().save(arena);
@@ -230,13 +240,36 @@ public class BlockPartyCommand implements CommandExecutor, TabCompleter {
private void pos2(CommandSender sender, String[] args) { private void pos2(CommandSender sender, String[] args) {
if (!requireAdmin(sender, "blockparty.admin.setup") || !requirePlayer(sender)) return; if (!requireAdmin(sender, "blockparty.admin.setup") || !requirePlayer(sender)) return;
withArena(sender, args, arena -> { withArena(sender, args, arena -> {
Location loc = ((Player) sender).getLocation(); Player player = (Player) sender;
WorldEditHook.Corners selection = worldEditSelection(player);
if (selection != null) {
arena.getConfig().setPos2(selection.max());
plugin.getArenaManager().save(arena);
sender.sendMessage(plugin.getMessages().get("admin.pos2-set-we", Map.of("arena", arena.getConfig().getName())));
return;
}
Location loc = player.getLocation();
arena.getConfig().setPos2(new int[]{loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()}); arena.getConfig().setPos2(new int[]{loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()});
plugin.getArenaManager().save(arena); plugin.getArenaManager().save(arena);
sender.sendMessage(plugin.getMessages().get("admin.pos2-set", Map.of("arena", arena.getConfig().getName()))); sender.sendMessage(plugin.getMessages().get("admin.pos2-set", Map.of("arena", arena.getConfig().getName())));
}); });
} }
/** Returns the player's active WorldEdit selection if the WorldEdit plugin is installed,
* the integration is enabled, and they have one - otherwise null so callers fall back to
* the player's standing location. WorldEdit classes are only referenced once its plugin
* is confirmed present, so a server without WorldEdit installed never loads them. */
private WorldEditHook.Corners worldEditSelection(Player player) {
if (!plugin.getConfigManager().isWorldEditEnabled() || Bukkit.getPluginManager().getPlugin("WorldEdit") == null) {
return null;
}
try {
return WorldEditHook.getSelection(player);
} catch (Throwable t) {
return null;
}
}
private void setFloor(CommandSender sender, String[] args) { private void setFloor(CommandSender sender, String[] args) {
if (!requireAdmin(sender, "blockparty.admin.setup")) return; if (!requireAdmin(sender, "blockparty.admin.setup")) return;
// /bp setfloor <arena> <mat1,mat2,...> // /bp setfloor <arena> <mat1,mat2,...>
@@ -65,6 +65,10 @@ public class ConfigManager {
return config.getBoolean("integrations.vault", true); return config.getBoolean("integrations.vault", true);
} }
public boolean isWorldEditEnabled() {
return config.getBoolean("integrations.worldedit", true);
}
public List<String> getDefaultFloorMaterials() { public List<String> getDefaultFloorMaterials() {
return config.getStringList("default-floor-materials"); return config.getStringList("default-floor-materials");
} }
@@ -0,0 +1,43 @@
package us.tss3.blockparty.worldedit;
import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.Region;
import org.bukkit.entity.Player;
/**
* Reads a player's active WorldEdit selection (made with the WE wand / {@code //pos1} and
* {@code //pos2}) so BlockParty's floor-region commands can adopt it directly instead of
* requiring the player to stand at each corner separately. This class is only ever touched
* once the caller has confirmed the WorldEdit plugin is actually present (see
* BlockPartyCommand#worldEditSelection), so a server without WorldEdit installed never
* attempts to load these classes.
*/
public final class WorldEditHook {
private WorldEditHook() {
}
/** Two opposite corners of a cuboid selection, plus the world they're in. */
public record Corners(int[] min, int[] max, String worldName) {
}
/** Returns the player's current WorldEdit cuboid selection corners, or null if they have
* no active/complete selection (or anything about the lookup fails). */
public static Corners getSelection(Player player) {
try {
LocalSession session = WorldEdit.getInstance().getSessionManager().get(BukkitAdapter.adapt(player));
Region region = session.getSelection(BukkitAdapter.adapt(player.getWorld()));
BlockVector3 min = region.getMinimumPoint();
BlockVector3 max = region.getMaximumPoint();
return new Corners(
new int[]{min.x(), min.y(), min.z()},
new int[]{max.x(), max.y(), max.z()},
player.getWorld().getName());
} catch (Exception ex) {
return null;
}
}
}
+3
View File
@@ -80,3 +80,6 @@ integrations:
placeholderapi: true placeholderapi: true
# If false, money rewards are skipped even when Vault is installed (console commands are unaffected). # If false, money rewards are skipped even when Vault is installed (console commands are unaffected).
vault: true vault: true
# If true (default) and WorldEdit is installed, /blockparty pos1 and pos2 use your active
# WorldEdit wand selection instead of your standing location, when you have one selected.
worldedit: true
+2
View File
@@ -24,6 +24,8 @@ admin:
billboard-removed: "<green>Billboard removed for '%arena%'.</green>" billboard-removed: "<green>Billboard removed for '%arena%'.</green>"
pos1-set: "<green>Position 1 set for '%arena%'.</green>" pos1-set: "<green>Position 1 set for '%arena%'.</green>"
pos2-set: "<green>Position 2 set for '%arena%'.</green>" pos2-set: "<green>Position 2 set for '%arena%'.</green>"
pos1-set-we: "<green>Position 1 set for '%arena%' from your WorldEdit selection.</green>"
pos2-set-we: "<green>Position 2 set for '%arena%' from your WorldEdit selection.</green>"
floor-set: "<green>Floor palette for '%arena%' set to %count% material(s).</green>" floor-set: "<green>Floor palette for '%arena%' set to %count% material(s).</green>"
floor-generated: "<green>Floor generated for '%arena%'.</green>" floor-generated: "<green>Floor generated for '%arena%'.</green>"
reloaded: "<green>BlockParty configuration reloaded.</green>" reloaded: "<green>BlockParty configuration reloaded.</green>"
+1 -1
View File
@@ -4,7 +4,7 @@ main: us.tss3.blockparty.BlockPartyPlugin
api-version: '1.21' api-version: '1.21'
author: tss3 author: tss3
description: Colored-floor elimination minigame. description: Colored-floor elimination minigame.
softdepend: [PlaceholderAPI, Vault] softdepend: [PlaceholderAPI, Vault, WorldEdit]
commands: commands:
blockparty: blockparty: