From 8af68fc884a8a32ba19b74d7e95efc5e74457894 Mon Sep 17 00:00:00 2001 From: Kenzie Togami Date: Wed, 4 Sep 2019 20:55:47 -0700 Subject: [PATCH] Initial watchdog setup. Bukkit needs adapters, everything needs testing. --- .../bukkit/BukkitServerInterface.java | 15 ++++ .../worldedit/bukkit/BukkitWatchdog.java | 38 ++++++++++ .../bukkit/adapter/BukkitImplAdapter.java | 13 ++++ .../java/com/sk89q/worldedit/EditSession.java | 14 ++++ .../extension/platform/Platform.java | 9 +++ .../extension/platform/Watchdog.java | 29 ++++++++ .../extent/world/WatchdogTickingExtent.java | 72 +++++++++++++++++++ .../worldedit/fabric/FabricPlatform.java | 11 +++ .../worldedit/fabric/FabricWatchdog.java | 19 +++++ .../fabric/mixin/MixinMinecraftServer.java | 20 ++++++ .../src/main/resources/worldedit.mixins.json | 3 +- worldedit-forge/build.gradle.kts | 2 + .../sk89q/worldedit/forge/ForgePlatform.java | 10 +++ .../sk89q/worldedit/forge/ForgeWatchdog.java | 38 ++++++++++ .../resources/META-INF/accesstransformer.cfg | 1 + 15 files changed, 293 insertions(+), 1 deletion(-) create mode 100644 worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWatchdog.java create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/Watchdog.java create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/extent/world/WatchdogTickingExtent.java create mode 100644 worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWatchdog.java create mode 100644 worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/mixin/MixinMinecraftServer.java create mode 100644 worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeWatchdog.java create mode 100644 worldedit-forge/src/main/resources/META-INF/accesstransformer.cfg diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java index ccc025d8c..5bdffb5e9 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java @@ -28,6 +28,8 @@ import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.extension.platform.MultiUserPlatform; import com.sk89q.worldedit.extension.platform.Preference; +import com.sk89q.worldedit.extension.platform.Watchdog; +import com.sk89q.worldedit.util.concurrency.LazyReference; import com.sk89q.worldedit.world.DataFixer; import com.sk89q.worldedit.world.registry.Registries; import org.bukkit.Bukkit; @@ -52,6 +54,14 @@ public class BukkitServerInterface implements MultiUserPlatform { public WorldEditPlugin plugin; private CommandRegistration dynamicCommands; private boolean hookingEvents; + private final LazyReference watchdog = LazyReference.from(() -> { + if (plugin.getBukkitImplAdapter() != null) { + return plugin.getBukkitImplAdapter().supportsWatchdog() + ? new BukkitWatchdog(plugin.getBukkitImplAdapter()) + : null; + } + return null; + }); public BukkitServerInterface(WorldEditPlugin plugin, Server server) { this.plugin = plugin; @@ -103,6 +113,11 @@ public class BukkitServerInterface implements MultiUserPlatform { return Bukkit.getScheduler().scheduleSyncRepeatingTask(plugin, task, delay, period); } + @Override + public Watchdog getWatchdog() { + return watchdog.getValue(); + } + @Override public List getWorlds() { List worlds = server.getWorlds(); diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWatchdog.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWatchdog.java new file mode 100644 index 000000000..1e37852a6 --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWatchdog.java @@ -0,0 +1,38 @@ +/* + * 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.bukkit; + +import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; +import com.sk89q.worldedit.extension.platform.Watchdog; + +class BukkitWatchdog implements Watchdog { + + private final BukkitImplAdapter adapter; + + BukkitWatchdog(BukkitImplAdapter adapter) { + this.adapter = adapter; + } + + @Override + public void tick() { + adapter.tickWatchdog(); + } + +} diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java index f21d0917a..4bb915c56 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java @@ -62,6 +62,19 @@ public interface BukkitImplAdapter { @Nullable DataFixer getDataFixer(); + /** + * @return {@code true} if {@link #tickWatchdog()} is implemented + */ + default boolean supportsWatchdog() { + return false; + } + + /** + * Tick the server watchdog, if possible. + */ + default void tickWatchdog() { + } + /** * Get the block at the given location. * 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 e3bcac4b1..c940059bd 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -22,6 +22,8 @@ package com.sk89q.worldedit; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.event.extent.EditSessionEvent; +import com.sk89q.worldedit.extension.platform.Capability; +import com.sk89q.worldedit.extension.platform.Watchdog; import com.sk89q.worldedit.extent.ChangeSetExtent; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.extent.MaskingExtent; @@ -38,6 +40,7 @@ import com.sk89q.worldedit.extent.world.BlockQuirkExtent; import com.sk89q.worldedit.extent.world.ChunkLoadingExtent; import com.sk89q.worldedit.extent.world.FastModeExtent; import com.sk89q.worldedit.extent.world.SurvivalModeExtent; +import com.sk89q.worldedit.extent.world.WatchdogTickingExtent; import com.sk89q.worldedit.function.GroundFunction; import com.sk89q.worldedit.function.RegionMaskingFilter; import com.sk89q.worldedit.function.biome.BiomeReplace; @@ -214,10 +217,16 @@ public class EditSession implements Extent, AutoCloseable { this.world = world; if (world != null) { + Watchdog watchdog = WorldEdit.getInstance().getPlatformManager() + .queryCapability(Capability.GAME_HOOKS).getWatchdog(); Extent extent; // These extents are ALWAYS used extent = fastModeExtent = new FastModeExtent(world, false); + if (watchdog != null) { + // Reset watchdog before world placement + extent = new WatchdogTickingExtent(extent, watchdog); + } extent = survivalExtent = new SurvivalModeExtent(extent, world); extent = new BlockQuirkExtent(extent, world); extent = new ChunkLoadingExtent(extent, world); @@ -230,6 +239,11 @@ public class EditSession implements Extent, AutoCloseable { extent = reorderExtent = new MultiStageReorder(extent, false); extent = chunkBatchingExtent = new ChunkBatchingExtent(extent); extent = wrapExtent(extent, eventBus, event, Stage.BEFORE_REORDER); + 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); + } this.bypassHistory = new DataValidatorExtent(extent, world); // These extents can be skipped by calling smartSetBlock() diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/Platform.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/Platform.java index fcbd6ff29..d82ed52c1 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/Platform.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/Platform.java @@ -83,6 +83,15 @@ public interface Platform { */ int schedule(long delay, long period, Runnable task); + /** + * Get the watchdog service. + * + * @return the watchdog service, or {@code null} if none + */ + default @Nullable Watchdog getWatchdog() { + return null; + } + /** * Get a list of available or loaded worlds. * diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/Watchdog.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/Watchdog.java new file mode 100644 index 000000000..1f822f90f --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/Watchdog.java @@ -0,0 +1,29 @@ +/* + * 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.extension.platform; + +/** + * Interface to a {@link Platform}'s watchdog service. + */ +public interface Watchdog { + + void tick(); + +} 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 new file mode 100644 index 000000000..bb06fb29f --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/world/WatchdogTickingExtent.java @@ -0,0 +1,72 @@ +/* + * 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.extent.world; + +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.entity.BaseEntity; +import com.sk89q.worldedit.entity.Entity; +import com.sk89q.worldedit.extension.platform.Watchdog; +import com.sk89q.worldedit.extent.AbstractDelegateExtent; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.math.BlockVector2; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.world.biome.BiomeType; +import com.sk89q.worldedit.world.block.BlockStateHolder; + +import javax.annotation.Nullable; + +/** + * Extent that ticks the watchdog before every world-affecting action. + */ +public class WatchdogTickingExtent extends AbstractDelegateExtent { + + private final Watchdog watchdog; + + /** + * Create a new instance. + * + * @param extent the extent + * @param watchdog the watchdog to reset + */ + public WatchdogTickingExtent(Extent extent, Watchdog watchdog) { + super(extent); + this.watchdog = watchdog; + } + + @Override + public > boolean setBlock(BlockVector3 location, T block) throws WorldEditException { + watchdog.tick(); + return super.setBlock(location, block); + } + + @Nullable + @Override + public Entity createEntity(Location location, BaseEntity entity) { + watchdog.tick(); + return super.createEntity(location, entity); + } + + @Override + public boolean setBiome(BlockVector2 position, BiomeType biome) { + watchdog.tick(); + 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 69ea094b2..00c63f4e4 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 @@ -26,12 +26,14 @@ import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.extension.platform.MultiUserPlatform; import com.sk89q.worldedit.extension.platform.Preference; +import com.sk89q.worldedit.fabric.mixin.MixinMinecraftServer; import com.sk89q.worldedit.world.DataFixer; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.registry.Registries; import net.minecraft.SharedConstants; import net.minecraft.server.MinecraftServer; import net.minecraft.server.PlayerManager; +import net.minecraft.server.dedicated.DedicatedServer; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.world.ServerWorld; import net.minecraft.util.Identifier; @@ -55,12 +57,15 @@ class FabricPlatform extends AbstractPlatform implements MultiUserPlatform { private final FabricWorldEdit mod; private final MinecraftServer server; private final FabricDataFixer dataFixer; + private final @Nullable FabricWatchdog watchdog; private boolean hookingEvents = false; FabricPlatform(FabricWorldEdit mod, MinecraftServer server) { this.mod = mod; this.server = server; this.dataFixer = new FabricDataFixer(getDataVersion()); + this.watchdog = server instanceof DedicatedServer + ? new FabricWatchdog((MixinMinecraftServer) (Object) server) : null; } boolean isHookingEvents() { @@ -97,6 +102,12 @@ class FabricPlatform extends AbstractPlatform implements MultiUserPlatform { return -1; } + @Override + @Nullable + public FabricWatchdog getWatchdog() { + return watchdog; + } + @Override public List getWorlds() { Iterable worlds = server.getWorlds(); 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 new file mode 100644 index 000000000..554e14b9d --- /dev/null +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWatchdog.java @@ -0,0 +1,19 @@ +package com.sk89q.worldedit.fabric; + +import com.sk89q.worldedit.extension.platform.Watchdog; +import com.sk89q.worldedit.fabric.mixin.MixinMinecraftServer; +import net.minecraft.util.SystemUtil; + +class FabricWatchdog implements Watchdog { + + private final MixinMinecraftServer server; + + FabricWatchdog(MixinMinecraftServer server) { + this.server = server; + } + + @Override + public void tick() { + 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 new file mode 100644 index 000000000..597871cea --- /dev/null +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/mixin/MixinMinecraftServer.java @@ -0,0 +1,20 @@ +package com.sk89q.worldedit.fabric.mixin; + +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.ServerTask; +import net.minecraft.server.command.CommandOutput; +import net.minecraft.util.NonBlockingThreadExecutor; +import net.minecraft.util.snooper.SnooperListener; +import org.spongepowered.asm.mixin.Mixin; +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); + } + + @Shadow + public long timeReference; +} diff --git a/worldedit-fabric/src/main/resources/worldedit.mixins.json b/worldedit-fabric/src/main/resources/worldedit.mixins.json index 165bf44ce..ecd0ffd49 100644 --- a/worldedit-fabric/src/main/resources/worldedit.mixins.json +++ b/worldedit-fabric/src/main/resources/worldedit.mixins.json @@ -3,7 +3,8 @@ "package": "com.sk89q.worldedit.fabric.mixin", "compatibilityLevel": "JAVA_8", "mixins": [ - "MixinServerPlayerEntity" + "MixinServerPlayerEntity", + "MixinMinecraftServer" ], "server": [ ], diff --git a/worldedit-forge/build.gradle.kts b/worldedit-forge/build.gradle.kts index e71c04295..b339aa62b 100644 --- a/worldedit-forge/build.gradle.kts +++ b/worldedit-forge/build.gradle.kts @@ -34,6 +34,8 @@ configure { "version" to "20190913-$mappingsMinecraftVersion" )) + accessTransformer(file("src/main/resources/META-INF/accesstransformer.cfg")) + runs { val runConfig = Action { properties(mapOf( diff --git a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgePlatform.java b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgePlatform.java index 93fea255f..f7a82a344 100644 --- a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgePlatform.java +++ b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgePlatform.java @@ -32,6 +32,7 @@ import com.sk89q.worldedit.world.registry.Registries; import net.minecraft.command.Commands; import net.minecraft.entity.player.ServerPlayerEntity; import net.minecraft.server.MinecraftServer; +import net.minecraft.server.dedicated.DedicatedServer; import net.minecraft.server.management.PlayerList; import net.minecraft.util.ResourceLocation; import net.minecraft.util.SharedConstants; @@ -56,12 +57,15 @@ class ForgePlatform extends AbstractPlatform implements MultiUserPlatform { private final ForgeWorldEdit mod; private final MinecraftServer server; private final ForgeDataFixer dataFixer; + private final @Nullable ForgeWatchdog watchdog; private boolean hookingEvents = false; ForgePlatform(ForgeWorldEdit mod) { this.mod = mod; this.server = ServerLifecycleHooks.getCurrentServer(); this.dataFixer = new ForgeDataFixer(getDataVersion()); + this.watchdog = server instanceof DedicatedServer + ? new ForgeWatchdog((DedicatedServer) server) : null; } boolean isHookingEvents() { @@ -98,6 +102,12 @@ class ForgePlatform extends AbstractPlatform implements MultiUserPlatform { return -1; } + @Override + @Nullable + public ForgeWatchdog getWatchdog() { + return watchdog; + } + @Override public List getWorlds() { Iterable worlds = server.getWorlds(); diff --git a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeWatchdog.java b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeWatchdog.java new file mode 100644 index 000000000..25f72c2c5 --- /dev/null +++ b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeWatchdog.java @@ -0,0 +1,38 @@ +/* + * 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.forge; + +import com.sk89q.worldedit.extension.platform.Watchdog; +import net.minecraft.server.dedicated.DedicatedServer; +import net.minecraft.util.Util; + +class ForgeWatchdog implements Watchdog { + + private final DedicatedServer server; + + ForgeWatchdog(DedicatedServer server) { + this.server = server; + } + + @Override + public void tick() { + server.serverTime = Util.milliTime(); + } +} diff --git a/worldedit-forge/src/main/resources/META-INF/accesstransformer.cfg b/worldedit-forge/src/main/resources/META-INF/accesstransformer.cfg new file mode 100644 index 000000000..9c1530b89 --- /dev/null +++ b/worldedit-forge/src/main/resources/META-INF/accesstransformer.cfg @@ -0,0 +1 @@ +public net.minecraft.server.MinecraftServer field_211151_aa # serverTime \ No newline at end of file