mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-08-27 04:37:47 +00:00
dwa
This commit is contained in:
@@ -1,3 +1,3 @@
|
||||
[01:34:50] [Test worker/INFO]: [STDERR]: [Iris/WARN] Native structure terrain envelope at 0,0 was clipped to Minecraft's 8-chunk structure reference range
|
||||
[01:34:50] [Test worker/INFO]: [STDERR]: [Iris/WARN] Native structure burial at 0,0 clamped to world floor: wanted -19, used -4
|
||||
[01:34:50] [Test worker/INFO]: [STDERR]: [Iris/WARN] Native structure burial at 0,0 clamped to world floor: wanted -5, used -2
|
||||
[10:47:09] [Test worker/INFO]: [STDERR]: [Iris/WARN] Native structure terrain envelope at 0,0 was clipped to Minecraft's 8-chunk structure reference range
|
||||
[10:47:09] [Test worker/INFO]: [STDERR]: [Iris/WARN] Native structure burial at 0,0 clamped to world floor: wanted -19, used -4
|
||||
[10:47:09] [Test worker/INFO]: [STDERR]: [Iris/WARN] Native structure burial at 0,0 clamped to world floor: wanted -5, used -2
|
||||
|
||||
+6
-2
@@ -53,15 +53,19 @@ final class VanillaStructureBiomes {
|
||||
if (structure == null) {
|
||||
throw new IllegalArgumentException("Registered structure does not exist: " + structureKey);
|
||||
}
|
||||
boolean hasFilterEntries = false;
|
||||
for (Holder<Biome> holder : structure.biomes()) {
|
||||
hasFilterEntries = true;
|
||||
Optional<ResourceKey<Biome>> key = holder.unwrapKey();
|
||||
if (key.isPresent()) {
|
||||
keys.add(key.get().identifier().toString());
|
||||
}
|
||||
}
|
||||
if (keys.isEmpty()) {
|
||||
// An empty biome filter is legal datapack content (opt-in structures whose tags only
|
||||
// reference absent modded biomes); the structure is unreachable, not an error state.
|
||||
if (keys.isEmpty() && hasFilterEntries) {
|
||||
throw new IllegalStateException("Registered structure '" + structureKey
|
||||
+ "' exposes no registered biome keys");
|
||||
+ "' has biome filter entries but none resolve to registered biome keys");
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package art.arcane.iris.core.nms.v26_2_R1;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* A registered structure with an EMPTY biome filter is legal datapack content: opt-in structures
|
||||
* (e.g. Towns and Towers' towns_and_towers:exclusives/*) ship biome tags that only reference
|
||||
* optional modded biomes, so the filter resolves to zero entries on a vanilla server. Resolving
|
||||
* biome keys for such a structure must return an empty set (the structure is simply unreachable),
|
||||
* never throw — a throw here aborts the whole /iris structure verify run and degrades /iris find.
|
||||
* The defensive throw is only kept for a NON-empty filter whose holders all fail to resolve keys.
|
||||
*/
|
||||
public class VanillaStructureBiomesEmptyFilterContractTest {
|
||||
@Test
|
||||
public void emptyBiomeFilterReturnsEmptyKeysInsteadOfThrowing() throws IOException {
|
||||
String source = Files.readString(Path.of(System.getProperty("iris.vanillaStructureBiomesSource")));
|
||||
|
||||
assertTrue(source.contains("if (keys.isEmpty() && hasFilterEntries)"));
|
||||
assertTrue(source.contains("has biome filter entries but none resolve to registered biome keys"));
|
||||
assertFalse(source.contains("exposes no registered biome keys"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void moddedHookMatchesEmptyFilterContract() throws IOException {
|
||||
String source = Files.readString(Path.of(System.getProperty("iris.moddedStructureHooksSource")));
|
||||
|
||||
assertTrue(source.contains("if (keys.isEmpty() && hasFilterEntries)"));
|
||||
assertTrue(source.contains("has biome filter entries but none resolve to registered biome keys"));
|
||||
assertFalse(source.contains("exposes no registered biome keys"));
|
||||
}
|
||||
}
|
||||
@@ -581,6 +581,10 @@ public class Iris extends VolmitPlugin implements Listener, ReloadAware {
|
||||
watch::normalizeSettingsContent
|
||||
);
|
||||
configHotloadEngine.configure(3_000L, List.of(watch.settingsFile()), List.of());
|
||||
// Stale-temp cleanup must complete before services enable: StudioSVC.onEnable downloads
|
||||
// packs through cache/temp on an async thread, and a concurrent delete of that folder
|
||||
// truncated pack imports mid-copy (partial packs/<key> without dimensions/).
|
||||
IO.delete(getTemp());
|
||||
services.values().forEach(IrisService::onEnable);
|
||||
services.values().forEach(this::registerListener);
|
||||
addShutdownHook();
|
||||
@@ -593,7 +597,6 @@ public class Iris extends VolmitPlugin implements Listener, ReloadAware {
|
||||
}
|
||||
|
||||
J.s(() -> {
|
||||
J.a(() -> IO.delete(getTemp()));
|
||||
J.a(this::bstats);
|
||||
J.ar(() -> settingsHotloadWatch.checkConfigHotload(configHotloadEngine), 60);
|
||||
J.sr(this::tickQueue, 0);
|
||||
|
||||
+1
-1
@@ -57,7 +57,7 @@ public final class BukkitWorldReconciler {
|
||||
assert dim != null && gen != null;
|
||||
|
||||
Iris.info(C.LIGHT_PURPLE + "Preparing Spawn for " + s + "' using Iris:" + generator + "...");
|
||||
WorldCreator c = WorldCreator.ofKey(worldKey)
|
||||
WorldCreator c = WorldCreatorCompat.ofKey(worldKey)
|
||||
.generator(gen)
|
||||
.environment(BukkitEnvironment.from(dim.getEnvironment()));
|
||||
Long stagedSeed = IrisWorlds.readBukkitWorldSeed(s);
|
||||
|
||||
+11
@@ -22,9 +22,11 @@ import art.arcane.iris.Iris;
|
||||
import art.arcane.iris.core.lifecycle.WorldLifecycleStaging;
|
||||
import art.arcane.iris.core.loader.IrisData;
|
||||
import art.arcane.iris.core.pack.BrokenPackException;
|
||||
import art.arcane.iris.core.pack.PackDownloader;
|
||||
import art.arcane.iris.core.pack.PackValidationRegistry;
|
||||
import art.arcane.iris.core.pack.PackValidationResult;
|
||||
import art.arcane.iris.core.pack.PackValidator;
|
||||
import art.arcane.iris.spi.IrisPlatforms;
|
||||
import art.arcane.iris.core.service.StudioSVC;
|
||||
import art.arcane.iris.engine.object.IrisDimension;
|
||||
import art.arcane.iris.engine.object.IrisWorld;
|
||||
@@ -61,6 +63,9 @@ public final class IrisWorldGeneratorResolver {
|
||||
}
|
||||
PackValidationRegistry.clear();
|
||||
for (File packDir : packDirs) {
|
||||
if (packDir.getName().contains(".importing-")) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
PackValidationResult result = PackValidator.validate(packDir);
|
||||
PackValidationRegistry.publish(result);
|
||||
@@ -90,6 +95,12 @@ public final class IrisWorldGeneratorResolver {
|
||||
IrisDimension dimension = pack.isDirectory() ? IrisData.get(pack).getDimensionLoader().load(id) : null;
|
||||
if (dimension == null) dimension = IrisData.loadAnyDimension(id, null);
|
||||
if (dimension == null) {
|
||||
File packsRoot = IrisPlatforms.get().dataFolderNoCreate(StudioSVC.WORKSPACE_NAME);
|
||||
if (PackDownloader.isPackPresent(packsRoot, id)) {
|
||||
Iris.error("Pack '" + id + "' exists at " + new File(packsRoot, id).getPath()
|
||||
+ " but its dimension failed to load; not redownloading. Fix or delete the pack folder.");
|
||||
return null;
|
||||
}
|
||||
Iris.warn("Unable to find dimension type " + id + " Looking for online packs...");
|
||||
Iris.service(StudioSVC.class).downloadSearch(new VolmitSender(Bukkit.getConsoleSender()), id, false);
|
||||
dimension = IrisData.loadAnyDimension(id, null);
|
||||
|
||||
@@ -40,8 +40,7 @@ import art.arcane.volmlib.util.collection.KList;
|
||||
import art.arcane.volmlib.util.director.DirectorOrigin;
|
||||
import art.arcane.volmlib.util.director.annotations.Director;
|
||||
import art.arcane.volmlib.util.director.annotations.Param;
|
||||
import io.papermc.paper.registry.RegistryAccess;
|
||||
import io.papermc.paper.registry.RegistryKey;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.Registry;
|
||||
@@ -187,7 +186,10 @@ public class CommandFind implements DirectorExecutor {
|
||||
}
|
||||
|
||||
private static Structure resolveNativeStructure(String structureKey) {
|
||||
Registry<Structure> structureRegistry = RegistryAccess.registryAccess().getRegistry(RegistryKey.STRUCTURE);
|
||||
Registry<Structure> structureRegistry = Bukkit.getRegistry(Structure.class);
|
||||
if (structureRegistry == null) {
|
||||
return null;
|
||||
}
|
||||
for (Structure candidate : structureRegistry) {
|
||||
NamespacedKey key = structureRegistry.getKey(candidate);
|
||||
if (key != null && key.toString().equalsIgnoreCase(structureKey)) {
|
||||
|
||||
+7
-7
@@ -49,8 +49,6 @@ import art.arcane.iris.engine.platform.PlatformChunkGenerator;
|
||||
import art.arcane.iris.util.common.plugin.VolmitSender;
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
import art.arcane.iris.util.common.scheduling.J;
|
||||
import io.papermc.paper.registry.RegistryAccess;
|
||||
import io.papermc.paper.registry.RegistryKey;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.NamespacedKey;
|
||||
@@ -146,11 +144,13 @@ public class CommandStructure implements DirectorExecutor {
|
||||
return;
|
||||
}
|
||||
KList<String> structureKeys = new KList<>();
|
||||
Registry<Structure> structureRegistry = RegistryAccess.registryAccess().getRegistry(RegistryKey.STRUCTURE);
|
||||
for (Structure structure : structureRegistry) {
|
||||
NamespacedKey key = structureRegistry.getKey(structure);
|
||||
if (key != null) {
|
||||
structureKeys.add(key.toString());
|
||||
Registry<Structure> structureRegistry = Bukkit.getRegistry(Structure.class);
|
||||
if (structureRegistry != null) {
|
||||
for (Structure structure : structureRegistry) {
|
||||
NamespacedKey key = structureRegistry.getKey(structure);
|
||||
if (key != null) {
|
||||
structureKeys.add(key.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
VolmitSender commandSender = sender();
|
||||
|
||||
+10
-3
@@ -1,6 +1,7 @@
|
||||
package art.arcane.iris.core.service;
|
||||
package art.arcane.iris.core.commands;
|
||||
|
||||
import art.arcane.iris.Iris;
|
||||
import art.arcane.iris.core.service.CommandSVC;
|
||||
import io.papermc.paper.command.brigadier.BasicCommand;
|
||||
import io.papermc.paper.command.brigadier.CommandSourceStack;
|
||||
import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents;
|
||||
@@ -9,13 +10,19 @@ import org.bukkit.command.CommandSender;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
final class PaperCommandRegistrar {
|
||||
/**
|
||||
* Paper-only command registration. Lives outside {@code art.arcane.iris.core.service} on purpose:
|
||||
* that package is eagerly Class.forName-scanned by JarScanner during enable, and resolving this
|
||||
* class on plain Spigot throws NoClassDefFoundError for the Paper lifecycle types. CommandSVC
|
||||
* loads it reflectively only when the Bukkit plugin.yml command is unavailable (Paper path).
|
||||
*/
|
||||
public final class PaperCommandRegistrar {
|
||||
private static final String ROOT_COMMAND = "iris";
|
||||
|
||||
private PaperCommandRegistrar() {
|
||||
}
|
||||
|
||||
static void register(Iris plugin, CommandSVC commandService) {
|
||||
public static void register(Iris plugin, CommandSVC commandService) {
|
||||
plugin.getLifecycleManager().registerEventHandler(LifecycleEvents.COMMANDS, event ->
|
||||
event.registrar().register(
|
||||
ROOT_COMMAND,
|
||||
@@ -159,7 +159,7 @@ public class CommandSVC implements IrisService, CommandExecutor, TabCompleter, D
|
||||
return true;
|
||||
}
|
||||
|
||||
void executeRoot(CommandSender sender, String label, String[] args) {
|
||||
public void executeRoot(CommandSender sender, String label, String[] args) {
|
||||
if (!sender.hasPermission(ROOT_PERMISSION)) {
|
||||
sender.sendMessage(IrisLanguage.text(
|
||||
IrisMessages.COMMAND_PERMISSION_DENIED,
|
||||
@@ -171,7 +171,7 @@ public class CommandSVC implements IrisService, CommandExecutor, TabCompleter, D
|
||||
J.aBukkit(() -> executeCommand(sender, label, args));
|
||||
}
|
||||
|
||||
List<String> tabCompleteRoot(CommandSender sender, String alias, String[] args) {
|
||||
public List<String> tabCompleteRoot(CommandSender sender, String alias, String[] args) {
|
||||
List<String> suggestions = runDirectorTab(sender, alias, args);
|
||||
if (sender instanceof Player player && IrisSettings.get().getGeneral().isCommandSounds()) {
|
||||
player.playSound(player.getLocation(), Sound.BLOCK_AMETHYST_BLOCK_CHIME, 0.25f, RNG.r.f(0.125f, 1.95f));
|
||||
@@ -190,7 +190,7 @@ public class CommandSVC implements IrisService, CommandExecutor, TabCompleter, D
|
||||
private void registerPaperCommand() {
|
||||
try {
|
||||
Class<?> registrarType = Class.forName(
|
||||
"art.arcane.iris.core.service.PaperCommandRegistrar",
|
||||
"art.arcane.iris.core.commands.PaperCommandRegistrar",
|
||||
true,
|
||||
getClass().getClassLoader()
|
||||
);
|
||||
|
||||
@@ -25,6 +25,7 @@ final class FellingRun {
|
||||
volatile int effectStride = 1;
|
||||
volatile ItemStack expectedTool;
|
||||
volatile List<TreeMember> work = List.of();
|
||||
volatile String abortReason;
|
||||
|
||||
FellingRun(
|
||||
TreeClaim claim,
|
||||
|
||||
+50
-4
@@ -67,6 +67,9 @@ final class TreeFellingRunner {
|
||||
)
|
||||
);
|
||||
List<TreeMarkerTraversal.Position> positions = positionsForFelling(discovery, run.candidate.trigger());
|
||||
if (!discovery.complete()) {
|
||||
run.abortReason = "discovery-incomplete";
|
||||
}
|
||||
preflight(run, positions, discovery.complete());
|
||||
} catch (Throwable error) {
|
||||
IrisLogging.reportError("Failed to discover an Iris tree for felling.", error);
|
||||
@@ -96,6 +99,7 @@ final class TreeFellingRunner {
|
||||
Runnable task = () -> {
|
||||
try {
|
||||
if (!run.candidate.world().isChunkLoaded(chunk.x(), chunk.z())) {
|
||||
run.abortReason = "chunk-unloaded";
|
||||
failed.set(true);
|
||||
return;
|
||||
}
|
||||
@@ -151,6 +155,7 @@ final class TreeFellingRunner {
|
||||
|
||||
List<TreeMember> ordered = orderMembers(run.candidate.trigger(), members);
|
||||
if (ordered.isEmpty() || !ordered.getFirst().position().equals(run.candidate.trigger())) {
|
||||
run.abortReason = "trigger-drift";
|
||||
finish(run);
|
||||
return;
|
||||
}
|
||||
@@ -212,8 +217,9 @@ final class TreeFellingRunner {
|
||||
"Failed to prepare an Iris tree-feller block.",
|
||||
() -> prepareBreak(run, member)
|
||||
);
|
||||
if (!J.runRegion(run.candidate.world(), chunk.x(), chunk.z(), task)) {
|
||||
if (!runMemberRegion(run, chunk.x(), chunk.z(), task)) {
|
||||
if (member.log()) {
|
||||
run.abortReason = "schedule-refused";
|
||||
finish(run);
|
||||
} else {
|
||||
continueRun(run);
|
||||
@@ -221,10 +227,34 @@ final class TreeFellingRunner {
|
||||
}
|
||||
}
|
||||
|
||||
// On Paper, J.runRegion/J.runEntity always defer a full tick even when already on the
|
||||
// main thread, which collapsed the pulse pacing to one block per tick and forced players
|
||||
// to hold sneak for 10+ seconds on large trees. Folia keeps the scheduler hop.
|
||||
static boolean shouldRunInline(boolean folia, boolean primaryThread) {
|
||||
return !folia && primaryThread;
|
||||
}
|
||||
|
||||
private boolean runMemberRegion(FellingRun run, int chunkX, int chunkZ, Runnable task) {
|
||||
if (shouldRunInline(J.isFolia(), Bukkit.isPrimaryThread())) {
|
||||
task.run();
|
||||
return true;
|
||||
}
|
||||
return J.runRegion(run.candidate.world(), chunkX, chunkZ, task);
|
||||
}
|
||||
|
||||
private boolean runMemberEntity(FellingRun run, Runnable task) {
|
||||
if (shouldRunInline(J.isFolia(), Bukkit.isPrimaryThread())) {
|
||||
task.run();
|
||||
return true;
|
||||
}
|
||||
return J.runEntity(run.candidate.player(), task);
|
||||
}
|
||||
|
||||
private void prepareBreak(FellingRun run, TreeMember member) {
|
||||
Block block = liveMemberBlock(run, member);
|
||||
if (block == null) {
|
||||
if (member.log()) {
|
||||
run.abortReason = "member-drift";
|
||||
finish(run);
|
||||
} else {
|
||||
continueRun(run);
|
||||
@@ -247,7 +277,8 @@ final class TreeFellingRunner {
|
||||
"Failed to reserve Iris tree-feller tool durability.",
|
||||
() -> reserveDamage(run, member)
|
||||
);
|
||||
if (!J.runEntity(run.candidate.player(), task)) {
|
||||
if (!runMemberEntity(run, task)) {
|
||||
run.abortReason = "schedule-refused";
|
||||
finish(run);
|
||||
}
|
||||
}
|
||||
@@ -255,6 +286,7 @@ final class TreeFellingRunner {
|
||||
private void reserveDamage(FellingRun run, TreeMember member) {
|
||||
Player player = run.candidate.player();
|
||||
if (!isRunControlActive(run, player)) {
|
||||
run.abortReason = "control-released";
|
||||
finish(run);
|
||||
return;
|
||||
}
|
||||
@@ -264,11 +296,13 @@ final class TreeFellingRunner {
|
||||
|| inventory.getHeldItemSlot() != run.heldSlot
|
||||
|| !current.isSimilar(run.expectedTool)
|
||||
|| !TreeFellerSVC.isAxe(current)) {
|
||||
run.abortReason = "tool-changed";
|
||||
finish(run);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!reserveLogCost(run)) {
|
||||
run.abortReason = "log-cost-refused";
|
||||
finish(run);
|
||||
return;
|
||||
}
|
||||
@@ -304,8 +338,8 @@ final class TreeFellingRunner {
|
||||
Runnable task = () -> runMutationTask(run, member, reservation, mutationSucceeded);
|
||||
boolean scheduled;
|
||||
try {
|
||||
scheduled = J.runRegion(
|
||||
run.candidate.world(),
|
||||
scheduled = runMemberRegion(
|
||||
run,
|
||||
position.x() >> 4,
|
||||
position.z() >> 4,
|
||||
task
|
||||
@@ -368,8 +402,10 @@ final class TreeFellingRunner {
|
||||
try {
|
||||
if (probe.isCancelled()) {
|
||||
if (reservation.charged() || reservation.logCostReserved()) {
|
||||
run.abortReason = "probe-cancelled";
|
||||
refundAndFinish(run, reservation);
|
||||
} else if (member.log()) {
|
||||
run.abortReason = "probe-cancelled";
|
||||
finish(run);
|
||||
} else {
|
||||
continueRun(run);
|
||||
@@ -379,6 +415,9 @@ final class TreeFellingRunner {
|
||||
|
||||
block = liveMemberBlock(run, member);
|
||||
if (run.finished.get() || block == null) {
|
||||
if (block == null) {
|
||||
run.abortReason = "member-drift";
|
||||
}
|
||||
probe.setCancelled(true);
|
||||
refundAndFinish(run, reservation);
|
||||
return;
|
||||
@@ -525,6 +564,7 @@ final class TreeFellingRunner {
|
||||
|
||||
private void completeSuccessfulMutation(FellingRun run, DamageReservation reservation) {
|
||||
if (reservation.broke()) {
|
||||
run.abortReason = "tool-broke";
|
||||
finish(run);
|
||||
return;
|
||||
}
|
||||
@@ -564,6 +604,9 @@ final class TreeFellingRunner {
|
||||
}
|
||||
}
|
||||
run.presentation.finish();
|
||||
IrisLogging.debug("Tree feller run ended: members=" + run.work.size()
|
||||
+ " processed=" + run.processed.get()
|
||||
+ " reason=" + (run.abortReason == null ? "complete" : run.abortReason));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -573,6 +616,9 @@ final class TreeFellingRunner {
|
||||
return;
|
||||
}
|
||||
for (FellingRun run : List.copyOf(runs)) {
|
||||
if (run.abortReason == null) {
|
||||
run.abortReason = "halted";
|
||||
}
|
||||
finish(run);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ folia-supported: true
|
||||
api-version: '${apiVersion}'
|
||||
load: STARTUP
|
||||
authors: [ cyberpwn, NextdoorPsycho, Vatuu ]
|
||||
website: volmit.com
|
||||
website: VolmitSoftware.com
|
||||
description: More than a Dimension!
|
||||
permissions:
|
||||
iris.treefeller:
|
||||
|
||||
@@ -20,7 +20,7 @@ softdepend:
|
||||
- WorldEdit
|
||||
loadbefore: [ Multiverse-Core ]
|
||||
authors: [ cyberpwn, NextdoorPsycho, Vatuu ]
|
||||
website: volmit.com
|
||||
website: VolmitSoftware.com
|
||||
description: More than a Dimension!
|
||||
commands:
|
||||
iris:
|
||||
|
||||
@@ -21,10 +21,15 @@ import static org.junit.Assert.assertTrue;
|
||||
public class IrisDiagnosticsTest {
|
||||
@Test
|
||||
public void serviceInitializationSkipsPackageHelperClasses() throws Exception {
|
||||
Class<?> registrar = Class.forName("art.arcane.iris.core.service.PaperCommandRegistrar");
|
||||
Class<?> registrar = Class.forName("art.arcane.iris.core.commands.PaperCommandRegistrar");
|
||||
|
||||
assertFalse(Iris.isConcreteImplementation(registrar, IrisService.class));
|
||||
assertTrue(Iris.isConcreteImplementation(CommandSVC.class, IrisService.class));
|
||||
// The service package is eagerly Class.forName-scanned by JarScanner at enable; a class
|
||||
// referencing Paper-only types there prints an NCDFE stack trace on plain Spigot.
|
||||
assertFalse(
|
||||
"Paper-only command registrar must stay out of the JarScanner-scanned services package",
|
||||
"art.arcane.iris.core.service".equals(registrar.getPackageName()));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+4
-3
@@ -1,5 +1,6 @@
|
||||
package art.arcane.iris.core.service;
|
||||
package art.arcane.iris.core.commands;
|
||||
|
||||
import art.arcane.iris.core.service.CommandSVC;
|
||||
import io.papermc.paper.command.brigadier.BasicCommand;
|
||||
import io.papermc.paper.command.brigadier.CommandSourceStack;
|
||||
import org.bukkit.Location;
|
||||
@@ -74,14 +75,14 @@ public class PaperCommandRegistrarTest {
|
||||
private String[] arguments;
|
||||
|
||||
@Override
|
||||
void executeRoot(CommandSender sender, String label, String[] args) {
|
||||
public void executeRoot(CommandSender sender, String label, String[] args) {
|
||||
this.sender = sender;
|
||||
this.label = label;
|
||||
this.arguments = args;
|
||||
}
|
||||
|
||||
@Override
|
||||
List<String> tabCompleteRoot(CommandSender sender, String alias, String[] args) {
|
||||
public List<String> tabCompleteRoot(CommandSender sender, String alias, String[] args) {
|
||||
return List.of("first", "second");
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package art.arcane.iris.core.service;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class TreeFellingRunnerPacingTest {
|
||||
@Test
|
||||
public void memberTasksRunInlineOnlyOnNonFoliaMainThread() {
|
||||
assertTrue(TreeFellingRunner.shouldRunInline(false, true));
|
||||
assertFalse(TreeFellingRunner.shouldRunInline(false, false));
|
||||
assertFalse(TreeFellingRunner.shouldRunInline(true, true));
|
||||
assertFalse(TreeFellingRunner.shouldRunInline(true, false));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user