This commit is contained in:
Brian Neumann-Fopiano
2026-07-16 12:26:01 -04:00
parent 55b7460b9a
commit 699247b1de
327 changed files with 30138 additions and 3934 deletions
@@ -1,35 +0,0 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2026 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package art.arcane.iris.core.commands;
final class BukkitNativeStructureLocatePolicy {
private static final String MONUMENT_KEY = "minecraft:monument";
private static final String UNAVAILABLE_MESSAGE = "Native monument locating is unavailable on Paper, Purpur, and Folia because a cold search can stall the server thread. The monument lookup was skipped; monument generation is unaffected.";
private BukkitNativeStructureLocatePolicy() {
}
static boolean isUnavailable(String structureKey) {
return structureKey != null && MONUMENT_KEY.equalsIgnoreCase(structureKey.trim());
}
static String unavailableMessage() {
return UNAVAILABLE_MESSAGE;
}
}
@@ -21,12 +21,16 @@ package art.arcane.iris.core.commands;
import art.arcane.iris.Iris;
import art.arcane.iris.platform.bukkit.BukkitPlatform;
import art.arcane.iris.core.service.ObjectStudioSaveService;
import art.arcane.iris.core.structure.NativeStructureLocateCapability;
import art.arcane.iris.engine.framework.Engine;
import art.arcane.iris.engine.framework.IrisStructureLocator;
import art.arcane.iris.engine.framework.NativeStructureGenerationPolicy;
import art.arcane.iris.engine.platform.EngineBukkitOps;
import art.arcane.iris.engine.framework.StructureReachability;
import art.arcane.iris.engine.object.IrisBiome;
import art.arcane.iris.engine.object.IrisNativeStructureDecision;
import art.arcane.iris.engine.object.IrisRegion;
import art.arcane.iris.engine.object.NativeStructureGenerationStatus;
import art.arcane.iris.util.common.director.DirectorExecutor;
import art.arcane.iris.util.common.director.specialhandlers.ObjectHandler;
import art.arcane.iris.util.common.director.specialhandlers.StructureHandler;
@@ -117,13 +121,34 @@ public class CommandFind implements DirectorExecutor {
return;
}
if (IrisStructureLocator.isPlaced(e, structure)) {
locateIrisStructure(e, structure, commandSender);
String structureKey = structure == null ? "" : structure.trim();
Structure nativeStructure = resolveNativeStructure(structureKey);
if (nativeStructure != null) {
IrisNativeStructureDecision decision = NativeStructureGenerationPolicy.resolve(
e, structureKey, false);
if (!decision.generate()
&& decision.status() != NativeStructureGenerationStatus.REPLACED_BY_IRIS) {
commandSender.sendMessage(C.RED + NativeStructureGenerationPolicy.generationStatusMessage(
structureKey, decision.status()));
return;
}
if (decision.status() == NativeStructureGenerationStatus.REPLACED_BY_IRIS) {
locateIrisStructure(e, structureKey, commandSender);
return;
}
if (NativeStructureLocateCapability.isPaperUnavailable(structureKey)) {
commandSender.sendMessage(C.RED + NativeStructureLocateCapability.unavailableMessage());
return;
}
}
if (nativeStructure == null && IrisStructureLocator.isPlaced(e, structureKey)) {
locateIrisStructure(e, structureKey, commandSender);
return;
}
if (BukkitNativeStructureLocatePolicy.isUnavailable(structure)) {
commandSender.sendMessage(C.RED + BukkitNativeStructureLocatePolicy.unavailableMessage());
if (nativeStructure == null) {
commandSender.sendMessage(C.RED + "Unknown structure: " + structureKey);
return;
}
@@ -135,42 +160,44 @@ public class CommandFind implements DirectorExecutor {
World targetWorld = target.getWorld();
Location origin = target.getLocation();
commandSender.sendMessage(C.GRAY + "Locating " + structure + "...");
commandSender.sendMessage(C.GRAY + "Locating " + structureKey + "...");
J.s(() -> {
try {
Registry<Structure> structureRegistry = RegistryAccess.registryAccess().getRegistry(RegistryKey.STRUCTURE);
Structure match = null;
for (Structure candidate : structureRegistry) {
NamespacedKey key = structureRegistry.getKey(candidate);
if (key != null && key.toString().equalsIgnoreCase(structure)) {
match = candidate;
break;
}
}
if (match == null) {
sendStructureMessage(target, commandSender, C.RED + "Unknown structure: " + structure);
return;
}
if (!StructureReachability.isReachable(e, structure)) {
KList<String> miss = StructureReachability.missingBiomeKeys(e, structure);
if (!StructureReachability.isReachable(e, structureKey)) {
KList<String> miss = StructureReachability.missingBiomeKeys(e, structureKey);
sendStructureMessage(target, commandSender,
C.YELLOW + structure + " cannot generate in this world (its required biomes are not produced by this pack"
C.YELLOW + structureKey + " cannot generate in this world (its required biomes are not produced by this pack"
+ (miss.isEmpty() ? "" : ": needs " + String.join("/", miss)) + ").");
return;
}
StructureSearchResult result = targetWorld.locateNearestStructure(origin, match, 100, false);
StructureSearchResult result = targetWorld.locateNearestStructure(
origin, nativeStructure, 100, false);
if (result == null || result.getLocation() == null) {
sendStructureMessage(target, commandSender, C.YELLOW + "No " + structure + " found within range of you.");
sendStructureMessage(target, commandSender,
C.YELLOW + "No " + structureKey + " found within range of you.");
return;
}
prepareStructureTeleport(target, targetWorld, commandSender, structure, result.getLocation(), false);
prepareStructureTeleport(
target, targetWorld, commandSender, structureKey, result.getLocation(), false);
} catch (Throwable t) {
sendStructureMessage(target, commandSender, C.RED + "Could not locate " + structure + ": " + t.getClass().getSimpleName());
Iris.reportError("Could not locate structure '" + structure + "'.", t);
sendStructureMessage(target, commandSender,
C.RED + "Could not locate " + structureKey + ": " + t.getClass().getSimpleName());
Iris.reportError("Could not locate structure '" + structureKey + "'.", t);
}
});
}
private static Structure resolveNativeStructure(String structureKey) {
Registry<Structure> structureRegistry = RegistryAccess.registryAccess().getRegistry(RegistryKey.STRUCTURE);
for (Structure candidate : structureRegistry) {
NamespacedKey key = structureRegistry.getKey(candidate);
if (key != null && key.toString().equalsIgnoreCase(structureKey)) {
return candidate;
}
}
return null;
}
private void locateIrisStructure(Engine engine, String structure, VolmitSender commandSender) {
Player target = player();
if (target == null) {
@@ -22,15 +22,13 @@ import art.arcane.iris.Iris;
import art.arcane.iris.platform.bukkit.BukkitPlatform;
import art.arcane.iris.core.link.WorldEditLink;
import art.arcane.iris.core.loader.IrisData;
import art.arcane.iris.core.loader.ResourceLoader;
import art.arcane.iris.core.runtime.ObjectStudioActivation;
import art.arcane.iris.core.runtime.WorldRuntimeControlService;
import art.arcane.iris.core.service.ObjectSVC;
import art.arcane.iris.core.service.StudioSVC;
import art.arcane.iris.core.service.WandSVC;
import art.arcane.iris.core.tools.IrisConverter;
import art.arcane.iris.core.tools.PlausibilizeMode;
import art.arcane.iris.core.tools.TreePlausibilizer;
import art.arcane.iris.core.tools.TreePlausibilizeBatch;
import art.arcane.iris.engine.framework.Engine;
import art.arcane.iris.engine.object.IObjectPlacer;
import art.arcane.iris.engine.object.IrisDimension;
@@ -353,199 +351,63 @@ public class CommandObject implements DirectorExecutor {
}
}
@Director(description = "Make tree leaves vanilla-decay-plausible (every leaf within 6 blocks of a log)",
@Director(description = "Grow organic branches through the canopy so every leaf survives vanilla decay",
origin = DirectorOrigin.BOTH)
public void plausibilize(
@Param(description = "Object key, prefix (trees/), or filesystem path",
customHandler = ObjectTargetHandler.class)
String target,
@Param(description = "DEFAULT: tentacle logs, delete orphans. NORMALIZE: + flip persistent=false. FOLIAGE_OVERATURE: add leaves to bridge orphans, no deletions. SMOKE: wipe & repaint canopy shell.",
defaultValue = "DEFAULT")
PlausibilizeMode mode,
@Param(description = "Analyze only, do not write", defaultValue = "false")
@Param(name = "dryrun", description = "dryrun=true analyzes only, writes nothing", defaultValue = "false")
boolean dryRun,
@Param(description = "Canopy shell radius (SMOKE only), clamped [0,5]", defaultValue = "2")
int radius
@Param(name = "reach", description = "reach=N max branch length in blocks from existing wood; farther leaf clusters are pinned persistent instead. reach=0 grows unlimited", defaultValue = "12")
int reach
) {
List<Target> targets = resolveTargets(target);
IrisData nearest = data();
List<TreePlausibilizeBatch.Target> targets = TreePlausibilizeBatch.resolve(target, nearest);
if (targets.isEmpty() && nearest == null) {
targets = resolveFromPacks(target);
}
if (targets.isEmpty()) {
sender().sendMessage(C.RED + "No objects matched: " + target);
return;
}
sender().sendMessage(C.IRIS + "Plausibilize [" + mode.name()
+ (dryRun ? " DRY" : "")
+ (mode == PlausibilizeMode.SMOKE ? " r=" + radius : "")
sender().sendMessage(C.IRIS + "Plausibilize [reach=" + reach + (dryRun ? ", DRY" : "")
+ "] queued " + targets.size() + " object(s)");
org.bukkit.command.CommandSender s = sender();
J.a(() -> runPlausibilize(targets, dryRun, mode, radius, s));
List<TreePlausibilizeBatch.Target> queued = targets;
J.a(() -> TreePlausibilizeBatch.run(queued, dryRun, reach, nearest, (String line) ->
s.sendMessage(line.startsWith("Done:") || line.startsWith("Totals:") || line.startsWith("[")
? C.IRIS + line
: C.GRAY + " " + line)));
}
private List<Target> resolveTargets(String target) {
List<Target> out = new ArrayList<>();
if (target == null || target.isEmpty()) {
return out;
}
File direct = new File(target);
if (direct.isFile() && target.toLowerCase().endsWith(".iob")) {
out.add(new Target(direct.getName().replaceAll("\\.iob$", ""), direct));
return out;
}
if (direct.isDirectory()) {
walkIob(direct, direct, out);
return out;
}
IrisData irisData = data();
if (irisData != null) {
ResourceLoader<IrisObject> loader = irisData.getObjectLoader();
if (!target.endsWith("/") && loader.findFile(target) != null) {
out.add(new Target(target, null));
return out;
}
String prefix = target.endsWith("/") ? target : target + "/";
for (String k : loader.getPossibleKeys()) {
if (k.startsWith(prefix)) {
out.add(new Target(k, null));
}
}
return out;
}
private static List<TreePlausibilizeBatch.Target> resolveFromPacks(String target) {
List<TreePlausibilizeBatch.Target> out = new ArrayList<>();
File packsFolder = Iris.instance.getDataFolder("packs");
File[] packs = packsFolder.listFiles(File::isDirectory);
if (packs != null) {
for (File pack : packs) {
File objectsRoot = new File(pack, "objects");
if (!objectsRoot.isDirectory()) continue;
File candidate = new File(objectsRoot, target + ".iob");
if (candidate.isFile()) {
out.add(new Target(pack.getName() + "/" + target, candidate));
continue;
}
File candidateDir = new File(objectsRoot, target);
if (candidateDir.isDirectory()) {
walkIob(candidateDir, objectsRoot, out);
}
if (packs == null) {
return out;
}
for (File pack : packs) {
File objectsRoot = new File(pack, "objects");
if (!objectsRoot.isDirectory()) {
continue;
}
File candidate = new File(objectsRoot, target + ".iob");
if (candidate.isFile()) {
out.add(new TreePlausibilizeBatch.Target(pack.getName() + "/" + target, candidate));
continue;
}
File candidateDir = new File(objectsRoot, target);
if (candidateDir.isDirectory()) {
TreePlausibilizeBatch.walkIob(candidateDir, objectsRoot, out);
}
}
return out;
}
private static void walkIob(File root, File keyRoot, List<Target> out) {
File[] kids = root.listFiles();
if (kids == null) return;
for (File f : kids) {
if (f.isDirectory()) {
walkIob(f, keyRoot, out);
} else if (f.getName().toLowerCase().endsWith(".iob")) {
String rel = keyRoot.toPath().relativize(f.toPath()).toString()
.replace(File.separatorChar, '/')
.replaceAll("\\.iob$", "");
out.add(new Target(rel, f));
}
}
}
private record Target(String key, File file) {
}
private static void runPlausibilize(
List<Target> targets,
boolean dryRun,
PlausibilizeMode mode,
int radius,
org.bukkit.command.CommandSender s
) {
int processed = 0;
int skipped = 0;
int failed = 0;
int changed = 0;
long totalLogsAdded = 0L;
long totalLeavesAdded = 0L;
long totalLeavesRemoved = 0L;
long totalNormalized = 0L;
long totalUnreachableAfter = 0L;
int progressStep = Math.max(1, targets.size() / 20);
int index = 0;
for (Target t : targets) {
index++;
try {
IrisObject o = loadTarget(t);
if (o == null) {
s.sendMessage(C.YELLOW + " skip " + t.key() + ": failed to load");
skipped++;
continue;
}
TreePlausibilizer.Result r = dryRun
? TreePlausibilizer.analyze(o, mode, radius)
: TreePlausibilizer.apply(o, mode, radius);
if (r.skipReason() != null) {
s.sendMessage(C.YELLOW + " skip " + t.key() + ": " + r.skipReason());
skipped++;
continue;
}
boolean touched = r.logsAdded() > 0 || r.leavesAdded() > 0
|| r.leavesRemoved() > 0 || r.leavesNormalized() > 0;
if (!dryRun && touched) {
File dest = o.getLoadFile() != null ? o.getLoadFile() : t.file();
if (dest != null) {
o.write(dest);
changed++;
}
}
processed++;
totalLogsAdded += r.logsAdded();
totalLeavesAdded += r.leavesAdded();
totalLeavesRemoved += r.leavesRemoved();
totalNormalized += r.leavesNormalized();
totalUnreachableAfter += r.unreachableAfter();
if (touched || targets.size() == 1) {
s.sendMessage(C.GRAY + " " + t.key()
+ C.WHITE + " +" + r.logsAdded() + " logs"
+ C.WHITE + " +" + r.leavesAdded() + " leaves"
+ C.WHITE + " -" + r.leavesRemoved() + " removed"
+ (r.leavesNormalized() > 0 ? C.WHITE + " ~" + r.leavesNormalized() + " normalized" : "")
+ (r.unreachableAfter() > 0 ? C.YELLOW + " " + r.unreachableAfter() + " unreachable" : ""));
}
if (targets.size() > 1 && index % progressStep == 0) {
s.sendMessage(C.IRIS + " [" + index + "/" + targets.size() + "]");
}
} catch (Throwable e) {
s.sendMessage(C.RED + " fail " + t.key() + ": " + e.getClass().getSimpleName()
+ ": " + e.getMessage());
e.printStackTrace();
failed++;
}
}
s.sendMessage(C.IRIS + "Done: " + processed + " processed, " + changed + " changed, "
+ skipped + " skipped, " + failed + " failed");
s.sendMessage(C.IRIS + "Totals: +" + totalLogsAdded + " logs, +" + totalLeavesAdded + " leaves, -"
+ totalLeavesRemoved + " removed, ~" + totalNormalized + " normalized, "
+ totalUnreachableAfter + " unreachable");
}
private static IrisObject loadTarget(Target t) throws IOException {
if (t.file() != null) {
IrisObject o = new IrisObject();
o.read(t.file());
o.setLoadFile(t.file());
return o;
}
return IrisData.loadAnyObject(t.key(), null);
}
@Director(description = "Convert .schem files in the 'convert' folder to .iob files.")
public void convert () {
try {
@@ -21,18 +21,23 @@ package art.arcane.iris.core.commands;
import art.arcane.iris.Iris;
import art.arcane.iris.core.loader.IrisData;
import art.arcane.iris.core.structure.BulkStructureImporter;
import art.arcane.iris.core.structure.NativeStructureLocateCapability;
import art.arcane.iris.core.structure.StructureCaptureImporter;
import art.arcane.iris.core.structure.StructureImporter;
import art.arcane.iris.core.structure.StructureIndexService;
import art.arcane.iris.engine.framework.Engine;
import art.arcane.iris.engine.framework.IrisStructureLocator;
import art.arcane.iris.engine.framework.NativeStructureGenerationPolicy;
import art.arcane.iris.engine.framework.PlacedStructurePiece;
import art.arcane.iris.engine.framework.StructureAssembler;
import art.arcane.iris.engine.framework.StructureReachability;
import art.arcane.iris.engine.object.IObjectPlacer;
import art.arcane.iris.engine.object.IrisDimension;
import art.arcane.iris.engine.object.IrisNativeStructureDecision;
import art.arcane.iris.engine.object.IrisObjectPlacement;
import art.arcane.iris.engine.object.IrisPosition;
import art.arcane.iris.engine.object.IrisStructure;
import art.arcane.iris.engine.object.NativeStructureGenerationStatus;
import art.arcane.iris.engine.object.ObjectPlaceMode;
import art.arcane.volmlib.util.collection.KList;
import art.arcane.iris.util.common.director.DirectorExecutor;
@@ -158,14 +163,23 @@ public class CommandStructure implements DirectorExecutor {
private void runVerification(Engine engine, KList<String> structureKeys, int centerX, int centerZ,
int searchRadius, VolmitSender commandSender, Player target) {
KList<String> messages = new KList<>();
Set<String> reachable;
try {
reachable = StructureReachability.reachableKeys(engine);
} catch (Throwable error) {
messages.add(C.RED + "Structure verification could not resolve native biome reachability.");
sendVerificationMessages(commandSender, target, messages);
Iris.reportError("Could not resolve native structure biome reachability for verification.", error);
return;
Map<String, IrisNativeStructureDecision> decisions = new HashMap<>(structureKeys.size());
boolean requiresNativeReachability = false;
for (String keyName : structureKeys) {
IrisNativeStructureDecision decision = NativeStructureGenerationPolicy.resolve(engine, keyName, false);
decisions.put(keyName, decision);
requiresNativeReachability |= decision.generate();
}
Set<String> reachable = Set.of();
if (requiresNativeReachability) {
try {
reachable = StructureReachability.reachableKeys(engine);
} catch (Throwable error) {
messages.add(C.RED + "Structure verification could not resolve native biome reachability.");
sendVerificationMessages(commandSender, target, messages);
Iris.reportError("Could not resolve native structure biome reachability for verification.", error);
return;
}
}
int located = 0;
int nativeEligible = 0;
@@ -175,8 +189,8 @@ public class CommandStructure implements DirectorExecutor {
int searchLimited = 0;
int errors = 0;
for (String keyName : structureKeys) {
boolean irisPlaced = IrisStructureLocator.isPlaced(engine, keyName);
if (irisPlaced) {
IrisNativeStructureDecision decision = decisions.get(keyName);
if (decision.status() == NativeStructureGenerationStatus.REPLACED_BY_IRIS) {
try {
IrisStructureLocator.LocateResult result =
IrisStructureLocator.locate(engine, keyName, centerX, centerZ, searchRadius);
@@ -201,7 +215,7 @@ public class CommandStructure implements DirectorExecutor {
}
continue;
}
if (!engine.getDimension().getImportedStructures().shouldGenerate(keyName)) {
if (!decision.generate()) {
disabled++;
messages.add(C.GRAY + "[disabled] " + C.WHITE + keyName);
continue;
@@ -213,20 +227,20 @@ public class CommandStructure implements DirectorExecutor {
+ (missing.isEmpty() ? "" : C.YELLOW + " needs " + String.join("/", missing)));
continue;
}
if (BukkitNativeStructureLocatePolicy.isUnavailable(keyName)) {
nativeEligible++;
if (NativeStructureLocateCapability.isPaperUnavailable(keyName)) {
unavailable++;
messages.add(C.RED + "[unavailable] " + C.WHITE + keyName + C.RED + ": "
+ BukkitNativeStructureLocatePolicy.unavailableMessage());
messages.add(C.YELLOW + "[native-eligible/locate-unavailable] " + C.WHITE + keyName + C.YELLOW + ": "
+ NativeStructureLocateCapability.unavailableMessage());
continue;
}
nativeEligible++;
messages.add(C.GREEN + "[native-eligible] " + C.WHITE + keyName);
}
messages.add(C.GREEN + "Structure verify: " + C.WHITE + located + C.GREEN + " Iris placements located, "
+ C.WHITE + nativeEligible + C.GREEN + " native structures eligible, "
+ C.WHITE + disabled + C.GREEN + " disabled by policy, "
+ C.WHITE + unreachable + C.GREEN + " biome-unreachable, "
+ C.WHITE + unavailable + C.GREEN + " unavailable on this platform, "
+ C.WHITE + unavailable + C.GREEN + " generation-eligible but unavailable to synchronous locate, "
+ C.WHITE + searchLimited + C.GREEN + " density searches safety-limited, "
+ C.WHITE + errors + C.GREEN + " errors. Native eligibility is checked without running blocking live locates.");
sendVerificationMessages(commandSender, target, messages);
@@ -285,12 +299,13 @@ public class CommandStructure implements DirectorExecutor {
sender().sendMessage(C.RED + "Could not resolve the pack for dimension " + dimension.getLoadKey());
return;
}
IrisStructure s = IrisData.loadAnyStructure(structure, data);
IrisStructure s = data.load(IrisStructure.class, structure, false);
if (s == null) {
sender().sendMessage(C.RED + "No iris structure '" + structure + "' in this pack");
return;
}
StructureAssembler assembler = new StructureAssembler(data, s, 0, 64, 0);
StructureAssembler assembler = StructureAssembler.forData(
data, s, new IrisPosition(0, 64, 0));
KList<PlacedStructurePiece> pieces = assembler.assemble(new RNG(1234));
if (pieces == null || pieces.isEmpty()) {
sender().sendMessage(C.RED + "Structure '" + structure + "' assembled 0 pieces (check startPool '" + s.getStartPool() + "')");
@@ -321,13 +336,14 @@ public class CommandStructure implements DirectorExecutor {
sender().sendMessage(C.RED + "Could not resolve the pack for dimension " + dimension.getLoadKey());
return;
}
IrisStructure s = IrisData.loadAnyStructure(structure, data);
IrisStructure s = data.load(IrisStructure.class, structure, false);
if (s == null) {
sender().sendMessage(C.RED + "No iris structure '" + structure + "' in this pack");
return;
}
Location loc = player().getLocation();
StructureAssembler assembler = new StructureAssembler(data, s, loc.getBlockX(), loc.getBlockY(), loc.getBlockZ());
StructureAssembler assembler = StructureAssembler.forData(
data, s, new IrisPosition(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()));
RNG rng = new RNG((long) loc.getBlockX() * 341873128712L + loc.getBlockZ());
KList<PlacedStructurePiece> pieces = assembler.assemble(rng);
if (pieces == null || pieces.isEmpty()) {
@@ -26,6 +26,7 @@ import art.arcane.iris.core.gui.NoiseExplorerGUI;
import art.arcane.iris.core.gui.VisionGUI;
import art.arcane.iris.core.loader.IrisData;
import art.arcane.iris.core.project.IrisProject;
import art.arcane.iris.core.service.BoardSVC;
import art.arcane.iris.core.service.StudioSVC;
import art.arcane.iris.core.structure.BulkStructureImporter;
import art.arcane.iris.core.structure.FeatureImporter;
@@ -193,6 +194,25 @@ public class CommandStudio implements DirectorExecutor {
}));
}
@Director(description = "Toggle your Studio debug scoreboard", aliases = {"board", "sidebar", "sb"}, origin = DirectorOrigin.PLAYER)
public void scoreboard() {
Player target = player();
VolmitSender commandSender = sender();
if (!J.runEntity(target, () -> {
PlatformChunkGenerator generator = IrisToolbelt.access(target.getWorld());
if (generator == null || !generator.isStudio()) {
commandSender.sendMessage(C.RED + "You must be in a Studio world to toggle the debug scoreboard.");
return;
}
boolean visible = Iris.service(BoardSVC.class).toggle(target);
commandSender.sendMessage((visible ? C.GREEN : C.YELLOW)
+ "Studio debug scoreboard " + (visible ? "enabled." : "disabled."));
})) {
commandSender.sendMessage(C.RED + "Could not update the Studio debug scoreboard right now.");
}
}
@Director(description = "Create a new studio project", aliases = "+", sync = true)
public void create(
@Param(description = "The name of this new Iris Project.", defaultValue = "studio")
@@ -259,7 +279,7 @@ public class CommandStudio implements DirectorExecutor {
Inventory inv = Bukkit.createInventory(null, 27 * 2);
try {
EngineBukkitOps.addItems(engine(), true, inv, RNG.r, tables, InventorySlotType.STORAGE, player().getWorld(), player().getLocation().getBlockX(), player().getLocation().getBlockY(), player().getLocation().getBlockZ(), 1);
EngineBukkitOps.addItems(engine(), true, inv, tables, InventorySlotType.STORAGE, player().getWorld(), player().getLocation().getBlockX(), player().getLocation().getBlockY(), player().getLocation().getBlockZ());
} catch (Throwable e) {
Iris.reportError(e);
sender().sendMessage(C.RED + "Cannot add items to virtual inventory because of: " + e.getMessage());
@@ -286,7 +306,7 @@ public class CommandStudio implements DirectorExecutor {
inv.clear();
}
EngineBukkitOps.addItems(engine, true, inv, new RNG(RNG.r.imax()), tables, InventorySlotType.STORAGE, player.getWorld(), player.getLocation().getBlockX(), player.getLocation().getBlockY(), player.getLocation().getBlockZ(), 1);
EngineBukkitOps.addItems(engine, true, inv, tables, InventorySlotType.STORAGE, player.getWorld(), player.getLocation().getBlockX(), player.getLocation().getBlockY(), player.getLocation().getBlockZ());
}, fast ? 5 : 35));
sender().sendMessage(C.GREEN + "Opening inventory now!");
@@ -829,7 +849,7 @@ public class CommandStudio implements DirectorExecutor {
sender().sendMessage(C.RED + "No studio world is open!");
return true;
}
if (!engine().isStudio()) {
if (!IrisToolbelt.isStudio(world())) {
sender().sendMessage(C.RED + "You must be in a studio world!");
return true;
}