diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java index c940059bd..f195ed7de 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -191,6 +191,7 @@ public class EditSession implements Extent, AutoCloseable { private final MultiStageReorder reorderExtent; private final MaskingExtent maskingExtent; private final BlockChangeLimiter changeLimiter; + private final List watchdogExtents = new ArrayList<>(2); private final Extent bypassReorderHistory; private final Extent bypassHistory; @@ -225,7 +226,9 @@ public class EditSession implements Extent, AutoCloseable { extent = fastModeExtent = new FastModeExtent(world, false); if (watchdog != null) { // Reset watchdog before world placement - extent = new WatchdogTickingExtent(extent, watchdog); + WatchdogTickingExtent watchdogExtent = new WatchdogTickingExtent(extent, watchdog); + extent = watchdogExtent; + watchdogExtents.add(watchdogExtent); } extent = survivalExtent = new SurvivalModeExtent(extent, world); extent = new BlockQuirkExtent(extent, world); @@ -242,7 +245,9 @@ public class EditSession implements Extent, AutoCloseable { if (watchdog != null) { // reset before buffering extents, since they may buffer all changes // before the world-placement reset can happen, and still cause halts - extent = new WatchdogTickingExtent(extent, watchdog); + WatchdogTickingExtent watchdogExtent = new WatchdogTickingExtent(extent, watchdog); + extent = watchdogExtent; + watchdogExtents.add(watchdogExtent); } this.bypassHistory = new DataValidatorExtent(extent, world); @@ -550,6 +555,24 @@ public class EditSession implements Extent, AutoCloseable { } } + /** + * Check if this session will tick the watchdog. + * + * @return {@code true} if any watchdog extent is enabled + */ + public boolean isTickingWatchdog() { + return watchdogExtents.stream().anyMatch(WatchdogTickingExtent::isEnabled); + } + + /** + * Set all watchdog extents to the given mode. + */ + public void setTickingWatchdog(boolean active) { + for (WatchdogTickingExtent extent : watchdogExtents) { + extent.setEnabled(active); + } + } + /** * Get the number of blocks changed, including repeated block changes. * diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java index 63e915ebe..9ac50111e 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java @@ -102,6 +102,7 @@ public class LocalSession { private transient EditSession.ReorderMode reorderMode = EditSession.ReorderMode.MULTI_STAGE; private transient List> lastDistribution; private transient World worldOverride; + private transient boolean tickingWatchdog = false; // Saved properties private String lastScript; @@ -294,6 +295,14 @@ public class LocalSession { this.worldOverride = worldOverride; } + public boolean isTickingWatchdog() { + return tickingWatchdog; + } + + public void setTickingWatchdog(boolean tickingWatchdog) { + this.tickingWatchdog = tickingWatchdog; + } + /** * Get the default region selector. * @@ -951,6 +960,7 @@ public class LocalSession { if (editSession.getSurvivalExtent() != null) { editSession.getSurvivalExtent().setStripNbt(!actor.hasPermission("worldedit.setnbt")); } + editSession.setTickingWatchdog(tickingWatchdog); return editSession; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/GeneralCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/GeneralCommands.java index 5fdf2087b..495b43663 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/GeneralCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/GeneralCommands.java @@ -26,10 +26,12 @@ import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.command.util.CommandPermissions; import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator; +import com.sk89q.worldedit.command.util.HookMode; import com.sk89q.worldedit.command.util.WorldEditAsyncCommandBuilder; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.input.DisallowedUsageException; import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.util.formatting.component.PaginationBox; import com.sk89q.worldedit.util.formatting.text.Component; @@ -198,6 +200,29 @@ public class GeneralCommands { } } + @Command( + name = "/watchdog", + desc = "Changes watchdog hook state.", + descFooter = "This is dependent on platform implementation. " + + "Not all platforms support watchdog hooks, or contain a watchdog." + ) + @CommandPermissions("worldedit.watchdog") + public void watchdog(Actor actor, LocalSession session, + @Arg(desc = "The mode to set the watchdog hook to", def = "") + HookMode hookMode) { + if (WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.GAME_HOOKS).getWatchdog() == null) { + actor.printError("This platform has no watchdog hook."); + return; + } + boolean previousMode = session.isTickingWatchdog(); + if (hookMode != null && (hookMode == HookMode.ACTIVE) == previousMode) { + actor.printError("Watchdog hook already " + (previousMode ? "active" : "inactive") + "."); + return; + } + session.setTickingWatchdog(!previousMode); + actor.print("Watchdog hook now " + (previousMode ? "inactive" : "active") + "."); + } + @Command( name = "gmask", aliases = {"/gmask"}, diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/EnumConverter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/EnumConverter.java index 41f8bae07..d1268e466 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/EnumConverter.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/EnumConverter.java @@ -21,6 +21,7 @@ package com.sk89q.worldedit.command.argument; import com.google.common.collect.ImmutableSet; import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.command.util.HookMode; import com.sk89q.worldedit.util.TreeGenerator; import org.enginehub.piston.CommandManager; import org.enginehub.piston.converter.ArgumentConverter; @@ -46,6 +47,8 @@ public final class EnumConverter { full(EditSession.ReorderMode.class, r -> ImmutableSet.of(r.getDisplayName()), null)); + commandManager.registerConverter(Key.of(HookMode.class), + basic(HookMode.class)); } private static > ArgumentConverter basic(Class enumClass) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/HookMode.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/HookMode.java new file mode 100644 index 000000000..113a45613 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/HookMode.java @@ -0,0 +1,24 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.command.util; + +public enum HookMode { + ACTIVE, INACTIVE +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/world/WatchdogTickingExtent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/world/WatchdogTickingExtent.java index bb06fb29f..78ddc9db8 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/world/WatchdogTickingExtent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/world/WatchdogTickingExtent.java @@ -38,7 +38,12 @@ import javax.annotation.Nullable; */ public class WatchdogTickingExtent extends AbstractDelegateExtent { + // Number of operations we run per tick to the watchdog + private static final int OPS_PER_TICK = 100; + private final Watchdog watchdog; + private boolean enabled; + private int ops; /** * Create a new instance. @@ -51,22 +56,40 @@ public class WatchdogTickingExtent extends AbstractDelegateExtent { this.watchdog = watchdog; } + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + private void onOperation() { + if (enabled) { + ops++; + if (ops == OPS_PER_TICK) { + watchdog.tick(); + ops = 0; + } + } + } + @Override public > boolean setBlock(BlockVector3 location, T block) throws WorldEditException { - watchdog.tick(); + onOperation(); return super.setBlock(location, block); } @Nullable @Override public Entity createEntity(Location location, BaseEntity entity) { - watchdog.tick(); + onOperation(); return super.createEntity(location, entity); } @Override public boolean setBiome(BlockVector2 position, BiomeType biome) { - watchdog.tick(); + onOperation(); return super.setBiome(position, biome); } } diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricPlatform.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricPlatform.java index 00c63f4e4..f67fdcc59 100644 --- a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricPlatform.java +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricPlatform.java @@ -34,6 +34,7 @@ import net.minecraft.SharedConstants; import net.minecraft.server.MinecraftServer; import net.minecraft.server.PlayerManager; import net.minecraft.server.dedicated.DedicatedServer; +import net.minecraft.server.dedicated.MinecraftDedicatedServer; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.world.ServerWorld; import net.minecraft.util.Identifier; @@ -64,8 +65,8 @@ class FabricPlatform extends AbstractPlatform implements MultiUserPlatform { this.mod = mod; this.server = server; this.dataFixer = new FabricDataFixer(getDataVersion()); - this.watchdog = server instanceof DedicatedServer - ? new FabricWatchdog((MixinMinecraftServer) (Object) server) : null; + this.watchdog = server instanceof MinecraftDedicatedServer + ? new FabricWatchdog((MinecraftDedicatedServer) server) : null; } boolean isHookingEvents() { diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWatchdog.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWatchdog.java index 554e14b9d..b0f25c685 100644 --- a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWatchdog.java +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWatchdog.java @@ -1,19 +1,39 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package com.sk89q.worldedit.fabric; import com.sk89q.worldedit.extension.platform.Watchdog; import com.sk89q.worldedit.fabric.mixin.MixinMinecraftServer; +import net.minecraft.server.dedicated.MinecraftDedicatedServer; import net.minecraft.util.SystemUtil; class FabricWatchdog implements Watchdog { - private final MixinMinecraftServer server; + private final MinecraftDedicatedServer server; - FabricWatchdog(MixinMinecraftServer server) { + FabricWatchdog(MinecraftDedicatedServer server) { this.server = server; } @Override public void tick() { - server.timeReference = SystemUtil.getMeasuringTimeMs(); + ((MixinMinecraftServer) (Object) server).timeReference = SystemUtil.getMeasuringTimeMs(); } } diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/mixin/MixinMinecraftServer.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/mixin/MixinMinecraftServer.java index 597871cea..de54985b8 100644 --- a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/mixin/MixinMinecraftServer.java +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/mixin/MixinMinecraftServer.java @@ -1,3 +1,22 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package com.sk89q.worldedit.fabric.mixin; import net.minecraft.server.MinecraftServer; @@ -11,8 +30,8 @@ import org.spongepowered.asm.mixin.Shadow; @Mixin(MinecraftServer.class) public abstract class MixinMinecraftServer extends NonBlockingThreadExecutor implements SnooperListener, CommandOutput, AutoCloseable, Runnable { - public MixinMinecraftServer(String string_1) { - super(string_1); + public MixinMinecraftServer(String name) { + super(name); } @Shadow