mirror of
https://github.com/EngineHub/WorldEdit.git
synced 2024-11-27 04:00:48 +08:00
Fix left click on air being ignored and right click on block being handled twice (#2153)
* Fix left click on air being ignored and right click on block being handled twice * Factorize debouncing and cache event result * Fix ForgePlatform#server being always null
This commit is contained in:
parent
18cc29a26f
commit
5b4322e53a
@ -37,6 +37,7 @@
|
||||
import com.sk89q.worldedit.util.lifecycle.Lifecycled;
|
||||
import com.sk89q.worldedit.world.DataFixer;
|
||||
import com.sk89q.worldedit.world.registry.Registries;
|
||||
import io.papermc.lib.PaperLib;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.World;
|
||||
@ -234,6 +235,14 @@ public Set<SideEffect> getSupportedSideEffects() {
|
||||
return SUPPORTED_SIDE_EFFECTS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTickCount() {
|
||||
if (PaperLib.isPaper()) {
|
||||
return Bukkit.getCurrentTick();
|
||||
}
|
||||
return super.getTickCount();
|
||||
}
|
||||
|
||||
public void unregisterCommands() {
|
||||
dynamicCommands.unregisterCommands();
|
||||
}
|
||||
|
@ -25,6 +25,7 @@
|
||||
import com.sk89q.worldedit.entity.Player;
|
||||
import com.sk89q.worldedit.event.platform.SessionIdleEvent;
|
||||
import com.sk89q.worldedit.extension.platform.Actor;
|
||||
import com.sk89q.worldedit.internal.event.InteractionDebouncer;
|
||||
import com.sk89q.worldedit.util.Direction;
|
||||
import com.sk89q.worldedit.util.Location;
|
||||
import com.sk89q.worldedit.world.World;
|
||||
@ -52,6 +53,7 @@
|
||||
public class WorldEditListener implements Listener {
|
||||
|
||||
private final WorldEditPlugin plugin;
|
||||
private final InteractionDebouncer debouncer;
|
||||
|
||||
/**
|
||||
* Construct the object.
|
||||
@ -60,6 +62,7 @@ public class WorldEditListener implements Listener {
|
||||
*/
|
||||
public WorldEditListener(WorldEditPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
debouncer = new InteractionDebouncer(plugin.getInternalPlatform());
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
@ -93,62 +96,59 @@ public void onPlayerCommandSend(PlayerCommandSendEvent event) {
|
||||
*/
|
||||
@EventHandler
|
||||
public void onPlayerInteract(PlayerInteractEvent event) {
|
||||
if (!plugin.getInternalPlatform().isHookingEvents()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.useItemInHand() == Result.DENY) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.getHand() == EquipmentSlot.OFF_HAND) {
|
||||
if (!plugin.getInternalPlatform().isHookingEvents()
|
||||
|| event.useItemInHand() == Result.DENY
|
||||
|| event.getHand() == EquipmentSlot.OFF_HAND
|
||||
|| event.getAction() == Action.PHYSICAL) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Player player = plugin.wrapPlayer(event.getPlayer());
|
||||
|
||||
if (event.getAction() != Action.LEFT_CLICK_BLOCK) {
|
||||
Optional<Boolean> previousResult = debouncer.getDuplicateInteractionResult(player);
|
||||
if (previousResult.isPresent()) {
|
||||
if (previousResult.get()) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final World world = player.getWorld();
|
||||
final WorldEdit we = plugin.getWorldEdit();
|
||||
final Direction direction = BukkitAdapter.adapt(event.getBlockFace());
|
||||
final Block clickedBlock = event.getClickedBlock();
|
||||
final Location pos = clickedBlock == null ? null : new Location(world, clickedBlock.getX(), clickedBlock.getY(), clickedBlock.getZ());
|
||||
|
||||
Action action = event.getAction();
|
||||
if (action == Action.LEFT_CLICK_BLOCK) {
|
||||
final Block clickedBlock = event.getClickedBlock();
|
||||
final Location pos = new Location(world, clickedBlock.getX(), clickedBlock.getY(), clickedBlock.getZ());
|
||||
boolean result = false;
|
||||
switch (event.getAction()) {
|
||||
case LEFT_CLICK_BLOCK:
|
||||
result = we.handleBlockLeftClick(player, pos, direction) || we.handleArmSwing(player);
|
||||
break;
|
||||
case LEFT_CLICK_AIR:
|
||||
result = we.handleArmSwing(player);
|
||||
break;
|
||||
case RIGHT_CLICK_BLOCK:
|
||||
result = we.handleBlockRightClick(player, pos, direction) || we.handleRightClick(player);
|
||||
break;
|
||||
case RIGHT_CLICK_AIR:
|
||||
result = we.handleRightClick(player);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (we.handleBlockLeftClick(player, pos, direction)) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
|
||||
if (we.handleArmSwing(player)) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
|
||||
} else if (action == Action.LEFT_CLICK_AIR) {
|
||||
|
||||
if (we.handleArmSwing(player)) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
|
||||
} else if (action == Action.RIGHT_CLICK_BLOCK) {
|
||||
final Block clickedBlock = event.getClickedBlock();
|
||||
final Location pos = new Location(world, clickedBlock.getX(), clickedBlock.getY(), clickedBlock.getZ());
|
||||
|
||||
if (we.handleBlockRightClick(player, pos, direction)) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
|
||||
if (we.handleRightClick(player)) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
} else if (action == Action.RIGHT_CLICK_AIR) {
|
||||
if (we.handleRightClick(player)) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
debouncer.setLastInteraction(player, result);
|
||||
if (result) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerQuit(PlayerQuitEvent event) {
|
||||
debouncer.clearInteraction(plugin.wrapPlayer(event.getPlayer()));
|
||||
|
||||
plugin.getWorldEdit().getEventBus().post(new SessionIdleEvent(new BukkitPlayer.SessionKeyImpl(event.getPlayer())));
|
||||
}
|
||||
}
|
||||
|
@ -84,4 +84,8 @@ public void reload() {
|
||||
getTranslationManager().reload();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTickCount() {
|
||||
return System.nanoTime() / 50_000_000;
|
||||
}
|
||||
}
|
||||
|
@ -207,4 +207,12 @@ default void registerGameHooks() {
|
||||
* @return A set of supported side effects
|
||||
*/
|
||||
Set<SideEffect> getSupportedSideEffects();
|
||||
|
||||
/**
|
||||
* Get the number of ticks since the server started.
|
||||
* On some platforms this value may be an approximation based on the JVM run time.
|
||||
*
|
||||
* @return The number of ticks since the server started.
|
||||
*/
|
||||
long getTickCount();
|
||||
}
|
||||
|
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* 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 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 com.sk89q.worldedit.internal.event;
|
||||
|
||||
import com.sk89q.worldedit.extension.platform.Platform;
|
||||
import com.sk89q.worldedit.util.Identifiable;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public class InteractionDebouncer {
|
||||
private final Platform platform;
|
||||
private final Map<UUID, Interaction> lastInteractions = new HashMap<>();
|
||||
|
||||
public InteractionDebouncer(Platform platform) {
|
||||
this.platform = platform;
|
||||
}
|
||||
|
||||
public void clearInteraction(Identifiable player) {
|
||||
lastInteractions.remove(player.getUniqueId());
|
||||
}
|
||||
|
||||
public void setLastInteraction(Identifiable player, boolean result) {
|
||||
lastInteractions.put(player.getUniqueId(), new Interaction(platform.getTickCount(), result));
|
||||
}
|
||||
|
||||
public Optional<Boolean> getDuplicateInteractionResult(Identifiable player) {
|
||||
Interaction last = lastInteractions.get(player.getUniqueId());
|
||||
if (last == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
long now = platform.getTickCount();
|
||||
if (now - last.tick <= 1) {
|
||||
return Optional.of(last.result);
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private static class Interaction {
|
||||
public final long tick;
|
||||
public final boolean result;
|
||||
|
||||
public Interaction(long tick, boolean result) {
|
||||
this.tick = tick;
|
||||
this.result = result;
|
||||
}
|
||||
}
|
||||
}
|
@ -213,6 +213,11 @@ public Set<SideEffect> getSupportedSideEffects() {
|
||||
: SUPPORTED_SIDE_EFFECTS_NO_MIXIN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTickCount() {
|
||||
return FabricWorldEdit.LIFECYCLED_SERVER.valueOrThrow().getTickCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Actor> getConnectedUsers() {
|
||||
List<Actor> users = new ArrayList<>();
|
||||
|
@ -32,6 +32,7 @@
|
||||
import com.sk89q.worldedit.extension.platform.PlatformManager;
|
||||
import com.sk89q.worldedit.fabric.net.handler.WECUIPacketHandler;
|
||||
import com.sk89q.worldedit.internal.anvil.ChunkDeleter;
|
||||
import com.sk89q.worldedit.internal.event.InteractionDebouncer;
|
||||
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
||||
import com.sk89q.worldedit.util.Location;
|
||||
import com.sk89q.worldedit.util.lifecycle.Lifecycled;
|
||||
@ -81,6 +82,7 @@
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
@ -113,6 +115,7 @@ public static <T> Registry<T> getRegistry(ResourceKey<Registry<T>> key) {
|
||||
|
||||
public static FabricWorldEdit inst;
|
||||
|
||||
private InteractionDebouncer debouncer;
|
||||
private FabricPlatform platform;
|
||||
private FabricConfiguration config;
|
||||
private Path workingDir;
|
||||
@ -139,6 +142,7 @@ public void onInitialize() {
|
||||
}
|
||||
}
|
||||
this.platform = new FabricPlatform(this);
|
||||
debouncer = new InteractionDebouncer(platform);
|
||||
|
||||
WorldEdit.getInstance().getPlatformManager().register(platform);
|
||||
|
||||
@ -250,16 +254,16 @@ private void onStopServer(MinecraftServer minecraftServer) {
|
||||
WorldEdit.getInstance().getEventBus().post(new PlatformUnreadyEvent(platform));
|
||||
}
|
||||
|
||||
private boolean shouldSkip() {
|
||||
if (platform == null) {
|
||||
return true;
|
||||
}
|
||||
private boolean skipEvents() {
|
||||
return platform == null || !platform.isHookingEvents();
|
||||
}
|
||||
|
||||
return !platform.isHookingEvents(); // We have to be told to catch these events
|
||||
private boolean skipInteractionEvent(Player player, InteractionHand hand) {
|
||||
return skipEvents() || hand != InteractionHand.MAIN_HAND || player.level().isClientSide || !(player instanceof ServerPlayer);
|
||||
}
|
||||
|
||||
private InteractionResult onLeftClickBlock(Player playerEntity, Level world, InteractionHand hand, BlockPos blockPos, Direction direction) {
|
||||
if (shouldSkip() || hand == InteractionHand.OFF_HAND || world.isClientSide) {
|
||||
if (skipInteractionEvent(playerEntity, hand)) {
|
||||
return InteractionResult.PASS;
|
||||
}
|
||||
|
||||
@ -273,19 +277,14 @@ private InteractionResult onLeftClickBlock(Player playerEntity, Level world, Int
|
||||
);
|
||||
com.sk89q.worldedit.util.Direction weDirection = FabricAdapter.adaptEnumFacing(direction);
|
||||
|
||||
if (we.handleBlockLeftClick(player, pos, weDirection)) {
|
||||
return InteractionResult.SUCCESS;
|
||||
}
|
||||
boolean result = we.handleBlockLeftClick(player, pos, weDirection) || we.handleArmSwing(player);
|
||||
debouncer.setLastInteraction(player, result);
|
||||
|
||||
if (we.handleArmSwing(player)) {
|
||||
return InteractionResult.SUCCESS;
|
||||
}
|
||||
|
||||
return InteractionResult.PASS;
|
||||
return result ? InteractionResult.SUCCESS : InteractionResult.PASS;
|
||||
}
|
||||
|
||||
private InteractionResult onRightClickBlock(Player playerEntity, Level world, InteractionHand hand, BlockHitResult blockHitResult) {
|
||||
if (shouldSkip() || hand == InteractionHand.OFF_HAND || world.isClientSide) {
|
||||
if (skipInteractionEvent(playerEntity, hand)) {
|
||||
return InteractionResult.PASS;
|
||||
}
|
||||
|
||||
@ -299,36 +298,52 @@ private InteractionResult onRightClickBlock(Player playerEntity, Level world, In
|
||||
);
|
||||
com.sk89q.worldedit.util.Direction direction = FabricAdapter.adaptEnumFacing(blockHitResult.getDirection());
|
||||
|
||||
if (we.handleBlockRightClick(player, pos, direction)) {
|
||||
return InteractionResult.SUCCESS;
|
||||
boolean result = we.handleBlockRightClick(player, pos, direction) || we.handleRightClick(player);
|
||||
debouncer.setLastInteraction(player, result);
|
||||
|
||||
return result ? InteractionResult.SUCCESS : InteractionResult.PASS;
|
||||
}
|
||||
|
||||
public void onLeftClickAir(ServerPlayer playerEntity, InteractionHand hand) {
|
||||
if (skipInteractionEvent(playerEntity, hand)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (we.handleRightClick(player)) {
|
||||
return InteractionResult.SUCCESS;
|
||||
WorldEdit we = WorldEdit.getInstance();
|
||||
FabricPlayer player = adaptPlayer(playerEntity);
|
||||
|
||||
Optional<Boolean> previousResult = debouncer.getDuplicateInteractionResult(player);
|
||||
if (previousResult.isPresent()) {
|
||||
return;
|
||||
}
|
||||
|
||||
return InteractionResult.PASS;
|
||||
boolean result = we.handleArmSwing(player);
|
||||
debouncer.setLastInteraction(player, result);
|
||||
}
|
||||
|
||||
private InteractionResultHolder<ItemStack> onRightClickAir(Player playerEntity, Level world, InteractionHand hand) {
|
||||
ItemStack stackInHand = playerEntity.getItemInHand(hand);
|
||||
if (shouldSkip() || hand == InteractionHand.OFF_HAND || world.isClientSide) {
|
||||
if (skipInteractionEvent(playerEntity, hand)) {
|
||||
return InteractionResultHolder.pass(stackInHand);
|
||||
}
|
||||
|
||||
WorldEdit we = WorldEdit.getInstance();
|
||||
FabricPlayer player = adaptPlayer((ServerPlayer) playerEntity);
|
||||
|
||||
if (we.handleRightClick(player)) {
|
||||
return InteractionResultHolder.success(stackInHand);
|
||||
Optional<Boolean> previousResult = debouncer.getDuplicateInteractionResult(player);
|
||||
if (previousResult.isPresent()) {
|
||||
return previousResult.get() ? InteractionResultHolder.success(stackInHand) : InteractionResultHolder.pass(stackInHand);
|
||||
}
|
||||
|
||||
return InteractionResultHolder.pass(stackInHand);
|
||||
boolean result = we.handleRightClick(player);
|
||||
debouncer.setLastInteraction(player, result);
|
||||
|
||||
return result ? InteractionResultHolder.success(stackInHand) : InteractionResultHolder.pass(stackInHand);
|
||||
}
|
||||
|
||||
// TODO Pass empty left click to server
|
||||
|
||||
private void onPlayerDisconnect(ServerGamePacketListenerImpl handler, MinecraftServer server) {
|
||||
debouncer.clearInteraction(adaptPlayer(handler.player));
|
||||
|
||||
WorldEdit.getInstance().getEventBus()
|
||||
.post(new SessionIdleEvent(new FabricPlayer.SessionKeyImpl(handler.player)));
|
||||
}
|
||||
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* 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 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 com.sk89q.worldedit.fabric.mixin;
|
||||
|
||||
import net.minecraft.server.level.ServerPlayerGameMode;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
@Mixin(ServerPlayerGameMode.class)
|
||||
public interface AccessorServerPlayerGameMode {
|
||||
|
||||
@Accessor("isDestroyingBlock")
|
||||
boolean isDestroyingBlock();
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* 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 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 com.sk89q.worldedit.fabric.mixin;
|
||||
|
||||
import com.sk89q.worldedit.fabric.FabricWorldEdit;
|
||||
import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket;
|
||||
import net.minecraft.network.protocol.game.ServerboundSwingPacket;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.server.network.ServerGamePacketListenerImpl;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(ServerGamePacketListenerImpl.class)
|
||||
public class MixinServerGamePacketListenerImpl {
|
||||
@Shadow
|
||||
public ServerPlayer player;
|
||||
|
||||
private int ignoreSwingPackets;
|
||||
|
||||
@Inject(method = "handleAnimate", at = @At("HEAD"))
|
||||
private void onAnimate(ServerboundSwingPacket packet, CallbackInfo ci) {
|
||||
if (!((AccessorServerPlayerGameMode) this.player.gameMode).isDestroyingBlock()) {
|
||||
if (this.ignoreSwingPackets > 0) {
|
||||
this.ignoreSwingPackets--;
|
||||
} else if (FabricWorldEdit.inst != null) {
|
||||
FabricWorldEdit.inst.onLeftClickAir(this.player, packet.getHand());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "handlePlayerAction", at = @At("HEAD"))
|
||||
private void onAction(ServerboundPlayerActionPacket packet, CallbackInfo ci) {
|
||||
switch (packet.getAction()) {
|
||||
case DROP_ITEM, DROP_ALL_ITEMS, START_DESTROY_BLOCK -> this.ignoreSwingPackets++;
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -7,8 +7,10 @@
|
||||
"AccessorPrimaryLevelData",
|
||||
"AccessorDerivedLevelData",
|
||||
"AccessorServerChunkCache",
|
||||
"AccessorServerPlayerGameMode",
|
||||
"MixinLevelChunkSetBlockHook",
|
||||
"MixinMinecraftServer",
|
||||
"MixinServerGamePacketListenerImpl",
|
||||
"MixinServerPlayer"
|
||||
],
|
||||
"plugin": "com.sk89q.worldedit.fabric.internal.MixinConfigPlugin",
|
||||
|
@ -63,18 +63,14 @@
|
||||
class ForgePlatform extends AbstractPlatform implements MultiUserPlatform {
|
||||
|
||||
private final ForgeWorldEdit mod;
|
||||
private final MinecraftServer server;
|
||||
private final ForgeDataFixer dataFixer;
|
||||
private final @Nullable ForgeWatchdog watchdog;
|
||||
private @Nullable ForgeWatchdog watchdog;
|
||||
private boolean hookingEvents = false;
|
||||
private final ResourceLoader resourceLoader = new ForgeResourceLoader(WorldEdit.getInstance());
|
||||
|
||||
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() {
|
||||
@ -120,12 +116,18 @@ public int schedule(long delay, long period, Runnable task) {
|
||||
@Override
|
||||
@Nullable
|
||||
public ForgeWatchdog getWatchdog() {
|
||||
if (watchdog == null) {
|
||||
MinecraftServer server = ServerLifecycleHooks.getCurrentServer();
|
||||
if (server instanceof DedicatedServer) {
|
||||
watchdog = new ForgeWatchdog((DedicatedServer) server);
|
||||
}
|
||||
}
|
||||
return watchdog;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<? extends World> getWorlds() {
|
||||
Iterable<ServerLevel> worlds = server.getAllLevels();
|
||||
Iterable<ServerLevel> worlds = ServerLifecycleHooks.getCurrentServer().getAllLevels();
|
||||
List<World> ret = new ArrayList<>();
|
||||
for (ServerLevel world : worlds) {
|
||||
ret.add(new ForgeWorld(world));
|
||||
@ -139,7 +141,7 @@ public Player matchPlayer(Player player) {
|
||||
if (player instanceof ForgePlayer) {
|
||||
return player;
|
||||
} else {
|
||||
ServerPlayer entity = server.getPlayerList().getPlayerByName(player.getName());
|
||||
ServerPlayer entity = ServerLifecycleHooks.getCurrentServer().getPlayerList().getPlayerByName(player.getName());
|
||||
return entity != null ? new ForgePlayer(entity) : null;
|
||||
}
|
||||
}
|
||||
@ -150,7 +152,7 @@ public World matchWorld(World world) {
|
||||
if (world instanceof ForgeWorld) {
|
||||
return world;
|
||||
} else {
|
||||
for (ServerLevel ws : server.getAllLevels()) {
|
||||
for (ServerLevel ws : ServerLifecycleHooks.getCurrentServer().getAllLevels()) {
|
||||
if (((ServerLevelData) ws.getLevelData()).getLevelName().equals(world.getName())) {
|
||||
return new ForgeWorld(ws);
|
||||
}
|
||||
@ -162,6 +164,7 @@ public World matchWorld(World world) {
|
||||
|
||||
@Override
|
||||
public void registerCommands(CommandManager manager) {
|
||||
MinecraftServer server = ServerLifecycleHooks.getCurrentServer();
|
||||
if (server == null) {
|
||||
return;
|
||||
}
|
||||
@ -234,10 +237,15 @@ public Set<SideEffect> getSupportedSideEffects() {
|
||||
: SUPPORTED_SIDE_EFFECTS_NO_MIXIN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTickCount() {
|
||||
return ServerLifecycleHooks.getCurrentServer().getTickCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Actor> getConnectedUsers() {
|
||||
List<Actor> users = new ArrayList<>();
|
||||
PlayerList scm = server.getPlayerList();
|
||||
PlayerList scm = ServerLifecycleHooks.getCurrentServer().getPlayerList();
|
||||
for (ServerPlayer entity : scm.getPlayers()) {
|
||||
if (entity != null) {
|
||||
users.add(new ForgePlayer(entity));
|
||||
|
@ -31,10 +31,9 @@
|
||||
import com.sk89q.worldedit.extension.platform.Capability;
|
||||
import com.sk89q.worldedit.extension.platform.Platform;
|
||||
import com.sk89q.worldedit.extension.platform.PlatformManager;
|
||||
import com.sk89q.worldedit.forge.net.handler.InternalPacketHandler;
|
||||
import com.sk89q.worldedit.forge.net.handler.WECUIPacketHandler;
|
||||
import com.sk89q.worldedit.forge.net.packet.LeftClickAirEventMessage;
|
||||
import com.sk89q.worldedit.internal.anvil.ChunkDeleter;
|
||||
import com.sk89q.worldedit.internal.event.InteractionDebouncer;
|
||||
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
||||
import com.sk89q.worldedit.util.Direction;
|
||||
import com.sk89q.worldedit.util.Location;
|
||||
@ -45,6 +44,7 @@
|
||||
import com.sk89q.worldedit.world.item.ItemCategory;
|
||||
import com.sk89q.worldedit.world.item.ItemType;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
@ -52,12 +52,12 @@
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.event.CommandEvent;
|
||||
import net.minecraftforge.event.RegisterCommandsEvent;
|
||||
import net.minecraftforge.event.entity.player.PlayerEvent;
|
||||
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
|
||||
import net.minecraftforge.event.entity.player.PlayerInteractEvent.LeftClickEmpty;
|
||||
import net.minecraftforge.event.server.ServerAboutToStartEvent;
|
||||
import net.minecraftforge.event.server.ServerStartedEvent;
|
||||
import net.minecraftforge.event.server.ServerStoppingEvent;
|
||||
@ -83,6 +83,7 @@
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@ -105,6 +106,7 @@ public class ForgeWorldEdit {
|
||||
|
||||
public static ForgeWorldEdit inst;
|
||||
|
||||
private InteractionDebouncer debouncer;
|
||||
private ForgePlatform platform;
|
||||
private ForgeConfiguration config;
|
||||
private Path workingDir;
|
||||
@ -153,13 +155,13 @@ private void init(FMLCommonSetupEvent event) {
|
||||
setupPlatform();
|
||||
|
||||
WECUIPacketHandler.init();
|
||||
InternalPacketHandler.init();
|
||||
|
||||
LOGGER.info("WorldEdit for Forge (version " + getInternalVersion() + ") is loaded");
|
||||
}
|
||||
|
||||
private void setupPlatform() {
|
||||
this.platform = new ForgePlatform(this);
|
||||
debouncer = new InteractionDebouncer(platform);
|
||||
|
||||
WorldEdit.getInstance().getPlatformManager().register(platform);
|
||||
|
||||
@ -260,61 +262,100 @@ public void serverStarted(ServerStartedEvent event) {
|
||||
WorldEdit.getInstance().getEventBus().post(new PlatformReadyEvent(platform));
|
||||
}
|
||||
|
||||
private boolean skipEvents() {
|
||||
return platform == null || !platform.isHookingEvents();
|
||||
}
|
||||
|
||||
private boolean skipInteractionEvent(Player player, InteractionHand hand) {
|
||||
return skipEvents() || hand != InteractionHand.MAIN_HAND || player.level().isClientSide || !(player instanceof ServerPlayer);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onPlayerInteract(PlayerInteractEvent event) {
|
||||
if (platform == null) {
|
||||
public void onLeftClickBlock(PlayerInteractEvent.LeftClickBlock event) {
|
||||
if (skipInteractionEvent(event.getEntity(), event.getHand()) || event.getUseItem() == Event.Result.DENY) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!platform.isHookingEvents()) {
|
||||
return; // We have to be told to catch these events
|
||||
}
|
||||
ServerPlayer playerEntity = (ServerPlayer) event.getEntity();
|
||||
WorldEdit we = WorldEdit.getInstance();
|
||||
ForgePlayer player = adaptPlayer(playerEntity);
|
||||
ForgeWorld world = getWorld((ServerLevel) playerEntity.level());
|
||||
Direction direction = ForgeAdapter.adaptEnumFacing(event.getFace());
|
||||
|
||||
if (event.getLevel().isClientSide && event instanceof LeftClickEmpty) {
|
||||
// catch LCE, pass it to server
|
||||
InternalPacketHandler.getHandler().sendToServer(LeftClickAirEventMessage.INSTANCE);
|
||||
BlockPos blockPos = event.getPos();
|
||||
Location pos = new Location(world, blockPos.getX(), blockPos.getY(), blockPos.getZ());
|
||||
|
||||
boolean result = we.handleBlockLeftClick(player, pos, direction) || we.handleArmSwing(player);
|
||||
debouncer.setLastInteraction(player, result);
|
||||
|
||||
if (result) {
|
||||
event.setCanceled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onRightClickBlock(PlayerInteractEvent.RightClickBlock event) {
|
||||
if (skipInteractionEvent(event.getEntity(), event.getHand()) || event.getUseItem() == Event.Result.DENY) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean isLeftDeny = event instanceof PlayerInteractEvent.LeftClickBlock lcb
|
||||
&& lcb.getUseItem() == Event.Result.DENY;
|
||||
boolean isRightDeny = event instanceof PlayerInteractEvent.RightClickBlock rcb
|
||||
&& rcb.getUseItem() == Event.Result.DENY;
|
||||
if (isLeftDeny || isRightDeny || event.getEntity().level().isClientSide || event.getHand() == InteractionHand.OFF_HAND) {
|
||||
ServerPlayer playerEntity = (ServerPlayer) event.getEntity();
|
||||
WorldEdit we = WorldEdit.getInstance();
|
||||
ForgePlayer player = adaptPlayer(playerEntity);
|
||||
ForgeWorld world = getWorld((ServerLevel) playerEntity.level());
|
||||
Direction direction = ForgeAdapter.adaptEnumFacing(event.getFace());
|
||||
|
||||
BlockPos blockPos = event.getPos();
|
||||
Location pos = new Location(world, blockPos.getX(), blockPos.getY(), blockPos.getZ());
|
||||
|
||||
boolean result = we.handleBlockRightClick(player, pos, direction) || we.handleRightClick(player);
|
||||
debouncer.setLastInteraction(player, result);
|
||||
|
||||
if (result) {
|
||||
event.setCanceled(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void onLeftClickAir(ServerPlayer playerEntity, InteractionHand hand) {
|
||||
if (skipInteractionEvent(playerEntity, hand)) {
|
||||
return;
|
||||
}
|
||||
|
||||
WorldEdit we = WorldEdit.getInstance();
|
||||
ForgePlayer player = adaptPlayer((ServerPlayer) event.getEntity());
|
||||
ForgeWorld world = getWorld((ServerLevel) event.getEntity().level());
|
||||
Direction direction = ForgeAdapter.adaptEnumFacing(event.getFace());
|
||||
ForgePlayer player = adaptPlayer(playerEntity);
|
||||
|
||||
if (event instanceof PlayerInteractEvent.LeftClickEmpty) {
|
||||
we.handleArmSwing(player); // this event cannot be canceled
|
||||
} else if (event instanceof PlayerInteractEvent.LeftClickBlock) {
|
||||
Location pos = new Location(world, event.getPos().getX(), event.getPos().getY(), event.getPos().getZ());
|
||||
Optional<Boolean> previousResult = debouncer.getDuplicateInteractionResult(player);
|
||||
if (previousResult.isPresent()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (we.handleBlockLeftClick(player, pos, direction)) {
|
||||
boolean result = we.handleArmSwing(player);
|
||||
debouncer.setLastInteraction(player, result);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onRightClickItem(PlayerInteractEvent.RightClickItem event) {
|
||||
if (skipInteractionEvent(event.getEntity(), event.getHand())) {
|
||||
return;
|
||||
}
|
||||
|
||||
ServerPlayer playerEntity = (ServerPlayer) event.getEntity();
|
||||
WorldEdit we = WorldEdit.getInstance();
|
||||
ForgePlayer player = adaptPlayer(playerEntity);
|
||||
|
||||
Optional<Boolean> previousResult = debouncer.getDuplicateInteractionResult(player);
|
||||
if (previousResult.isPresent()) {
|
||||
if (previousResult.get()) {
|
||||
event.setCanceled(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (we.handleArmSwing(player)) {
|
||||
event.setCanceled(true);
|
||||
}
|
||||
} else if (event instanceof PlayerInteractEvent.RightClickBlock) {
|
||||
Location pos = new Location(world, event.getPos().getX(), event.getPos().getY(), event.getPos().getZ());
|
||||
boolean result = we.handleRightClick(player);
|
||||
debouncer.setLastInteraction(player, result);
|
||||
|
||||
if (we.handleBlockRightClick(player, pos, direction)) {
|
||||
event.setCanceled(true);
|
||||
}
|
||||
|
||||
if (we.handleRightClick(player)) {
|
||||
event.setCanceled(true);
|
||||
}
|
||||
} else if (event instanceof PlayerInteractEvent.RightClickItem) {
|
||||
if (we.handleRightClick(player)) {
|
||||
event.setCanceled(true);
|
||||
}
|
||||
if (result) {
|
||||
event.setCanceled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -337,6 +378,8 @@ public void onCommandEvent(CommandEvent event) throws CommandSyntaxException {
|
||||
@SubscribeEvent
|
||||
public void onPlayerLogOut(PlayerEvent.PlayerLoggedOutEvent event) {
|
||||
if (event.getEntity() instanceof ServerPlayer player) {
|
||||
debouncer.clearInteraction(adaptPlayer(player));
|
||||
|
||||
WorldEdit.getInstance().getEventBus()
|
||||
.post(new SessionIdleEvent(new ForgePlayer.SessionKeyImpl(player)));
|
||||
}
|
||||
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* 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 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 com.sk89q.worldedit.forge.mixin;
|
||||
|
||||
import net.minecraft.server.level.ServerPlayerGameMode;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
@Mixin(ServerPlayerGameMode.class)
|
||||
public interface AccessorServerPlayerGameMode {
|
||||
|
||||
@Accessor("isDestroyingBlock")
|
||||
boolean isDestroyingBlock();
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* 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 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 com.sk89q.worldedit.forge.mixin;
|
||||
|
||||
import com.sk89q.worldedit.forge.ForgeWorldEdit;
|
||||
import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket;
|
||||
import net.minecraft.network.protocol.game.ServerboundSwingPacket;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.server.network.ServerGamePacketListenerImpl;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(ServerGamePacketListenerImpl.class)
|
||||
public class MixinServerGamePacketListenerImpl {
|
||||
@Shadow
|
||||
public ServerPlayer player;
|
||||
|
||||
private int ignoreSwingPackets;
|
||||
|
||||
@Inject(method = "handleAnimate", at = @At("HEAD"))
|
||||
private void onAnimate(ServerboundSwingPacket packet, CallbackInfo ci) {
|
||||
if (!((AccessorServerPlayerGameMode) this.player.gameMode).isDestroyingBlock()) {
|
||||
if (this.ignoreSwingPackets > 0) {
|
||||
this.ignoreSwingPackets--;
|
||||
} else if (ForgeWorldEdit.inst != null) {
|
||||
ForgeWorldEdit.inst.onLeftClickAir(this.player, packet.getHand());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "handlePlayerAction", at = @At("HEAD"))
|
||||
private void onAction(ServerboundPlayerActionPacket packet, CallbackInfo ci) {
|
||||
switch (packet.getAction()) {
|
||||
case DROP_ITEM, DROP_ALL_ITEMS, START_DESTROY_BLOCK -> this.ignoreSwingPackets++;
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* 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 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 com.sk89q.worldedit.forge.net.handler;
|
||||
|
||||
import com.sk89q.worldedit.forge.net.packet.LeftClickAirEventMessage;
|
||||
import com.sk89q.worldedit.forge.net.packet.LeftClickAirEventMessage.Handler;
|
||||
import net.minecraftforge.network.simple.SimpleChannel;
|
||||
|
||||
public final class InternalPacketHandler {
|
||||
private static final int PROTOCOL_VERSION = 1;
|
||||
private static final SimpleChannel HANDLER = PacketHandlerUtil
|
||||
.buildLenientHandler("internal", PROTOCOL_VERSION)
|
||||
.simpleChannel();
|
||||
|
||||
private InternalPacketHandler() {
|
||||
}
|
||||
|
||||
public static void init() {
|
||||
HANDLER.registerMessage(0, LeftClickAirEventMessage.class,
|
||||
LeftClickAirEventMessage::encode, LeftClickAirEventMessage::decode, Handler::handle);
|
||||
}
|
||||
|
||||
public static SimpleChannel getHandler() {
|
||||
return HANDLER;
|
||||
}
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* 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 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 com.sk89q.worldedit.forge.net.packet;
|
||||
|
||||
import com.sk89q.worldedit.forge.ForgeWorldEdit;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraftforge.event.entity.player.PlayerInteractEvent.LeftClickEmpty;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class LeftClickAirEventMessage {
|
||||
|
||||
public static final LeftClickAirEventMessage INSTANCE = new LeftClickAirEventMessage();
|
||||
|
||||
public static final class Handler {
|
||||
public static void handle(final LeftClickAirEventMessage message, Supplier<NetworkEvent.Context> ctx) {
|
||||
NetworkEvent.Context context = ctx.get();
|
||||
context.enqueueWork(() -> ForgeWorldEdit.inst.onPlayerInteract(new LeftClickEmpty(Objects.requireNonNull(context.getSender()))));
|
||||
}
|
||||
}
|
||||
|
||||
public static LeftClickAirEventMessage decode(FriendlyByteBuf buf) {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public static void encode(LeftClickAirEventMessage msg, FriendlyByteBuf buf) {
|
||||
}
|
||||
|
||||
private LeftClickAirEventMessage() {
|
||||
}
|
||||
|
||||
}
|
@ -3,7 +3,9 @@
|
||||
"package": "com.sk89q.worldedit.forge.mixin",
|
||||
"compatibilityLevel": "JAVA_17",
|
||||
"mixins": [
|
||||
"MixinLevelChunkSetBlockHook"
|
||||
"AccessorServerPlayerGameMode",
|
||||
"MixinLevelChunkSetBlockHook",
|
||||
"MixinServerGamePacketListenerImpl"
|
||||
],
|
||||
"server": [
|
||||
],
|
||||
|
@ -183,6 +183,11 @@ public Set<SideEffect> getSupportedSideEffects() {
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTickCount() {
|
||||
return Sponge.server().runningTimeTicks().ticks();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Actor> getConnectedUsers() {
|
||||
return Sponge.server().onlinePlayers().stream().map(SpongePlayer::new).collect(toList());
|
||||
|
@ -35,6 +35,7 @@
|
||||
import com.sk89q.worldedit.extension.platform.PlatformManager;
|
||||
import com.sk89q.worldedit.internal.anvil.ChunkDeleter;
|
||||
import com.sk89q.worldedit.internal.command.CommandUtil;
|
||||
import com.sk89q.worldedit.internal.event.InteractionDebouncer;
|
||||
import com.sk89q.worldedit.sponge.config.SpongeConfiguration;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import com.sk89q.worldedit.world.block.BlockCategory;
|
||||
@ -47,7 +48,6 @@
|
||||
import org.spongepowered.api.Sponge;
|
||||
import org.spongepowered.api.block.BlockSnapshot;
|
||||
import org.spongepowered.api.block.BlockType;
|
||||
import org.spongepowered.api.block.BlockTypes;
|
||||
import org.spongepowered.api.block.entity.BlockEntity;
|
||||
import org.spongepowered.api.block.entity.CommandBlock;
|
||||
import org.spongepowered.api.command.Command;
|
||||
@ -56,8 +56,11 @@
|
||||
import org.spongepowered.api.command.CommandResult;
|
||||
import org.spongepowered.api.command.parameter.ArgumentReader;
|
||||
import org.spongepowered.api.config.ConfigDir;
|
||||
import org.spongepowered.api.data.type.HandTypes;
|
||||
import org.spongepowered.api.entity.living.player.server.ServerPlayer;
|
||||
import org.spongepowered.api.event.EventContextKeys;
|
||||
import org.spongepowered.api.event.Listener;
|
||||
import org.spongepowered.api.event.action.InteractEvent;
|
||||
import org.spongepowered.api.event.block.InteractBlockEvent;
|
||||
import org.spongepowered.api.event.filter.cause.Root;
|
||||
import org.spongepowered.api.event.item.inventory.InteractItemEvent;
|
||||
@ -107,6 +110,7 @@ public static SpongeWorldEdit inst() {
|
||||
private final SpongeConfiguration config;
|
||||
private final Path workingDir;
|
||||
|
||||
private InteractionDebouncer debouncer;
|
||||
private SpongePermissionsProvider provider;
|
||||
private SpongePlatform platform;
|
||||
|
||||
@ -128,6 +132,8 @@ public SpongeWorldEdit(Logger logger,
|
||||
@Listener
|
||||
public void onPluginConstruction(ConstructPluginEvent event) {
|
||||
this.platform = new SpongePlatform(this);
|
||||
debouncer = new InteractionDebouncer(platform);
|
||||
|
||||
WorldEdit.getInstance().getPlatformManager().register(platform);
|
||||
|
||||
this.provider = new SpongePermissionsProvider();
|
||||
@ -279,84 +285,117 @@ public List<CommandCompletion> complete(CommandCause cause, ArgumentReader.Mutab
|
||||
);
|
||||
}
|
||||
|
||||
private boolean isHookingEvents() {
|
||||
return platform != null && platform.isHookingEvents();
|
||||
private boolean skipEvents() {
|
||||
return platform == null || !platform.isHookingEvents();
|
||||
}
|
||||
|
||||
private boolean skipInteractionEvent(InteractEvent event) {
|
||||
return skipEvents() || event.context().get(EventContextKeys.USED_HAND).orElse(null) != HandTypes.MAIN_HAND.get();
|
||||
}
|
||||
|
||||
@Listener
|
||||
public void onPlayerItemInteract(InteractItemEvent.Secondary event, @Root ServerPlayer spongePlayer) {
|
||||
if (!isHookingEvents()) {
|
||||
public void onPlayerInteractItemPrimary(InteractItemEvent.Primary event, @Root ServerPlayer spongePlayer) {
|
||||
if (skipInteractionEvent(event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
WorldEdit we = WorldEdit.getInstance();
|
||||
|
||||
SpongePlayer player = SpongeAdapter.adapt(spongePlayer);
|
||||
if (we.handleRightClick(player)) {
|
||||
|
||||
Optional<Boolean> previousResult = debouncer.getDuplicateInteractionResult(player);
|
||||
if (previousResult.isPresent()) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean result = we.handleArmSwing(player);
|
||||
debouncer.setLastInteraction(player, result);
|
||||
}
|
||||
|
||||
@Listener
|
||||
public void onPlayerInteractItemSecondary(InteractItemEvent.Secondary event, @Root ServerPlayer spongePlayer) {
|
||||
if (skipInteractionEvent(event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
WorldEdit we = WorldEdit.getInstance();
|
||||
SpongePlayer player = SpongeAdapter.adapt(spongePlayer);
|
||||
|
||||
Optional<Boolean> previousResult = debouncer.getDuplicateInteractionResult(player);
|
||||
if (previousResult.isPresent()) {
|
||||
if (previousResult.get()) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
boolean result = we.handleRightClick(player);
|
||||
debouncer.setLastInteraction(player, result);
|
||||
|
||||
if (result) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Listener
|
||||
public void onPlayerInteract(InteractBlockEvent event, @Root ServerPlayer spongePlayer) {
|
||||
if (platform == null) {
|
||||
public void onPlayerInteractBlockPrimary(InteractBlockEvent.Primary.Start event, @Root ServerPlayer spongePlayer) {
|
||||
if (skipInteractionEvent(event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!platform.isHookingEvents()) {
|
||||
return; // We have to be told to catch these events
|
||||
}
|
||||
|
||||
WorldEdit we = WorldEdit.getInstance();
|
||||
|
||||
SpongePlayer player = SpongeAdapter.adapt(spongePlayer);
|
||||
|
||||
BlockSnapshot targetBlock = event.block();
|
||||
Optional<ServerLocation> optLoc = targetBlock.location();
|
||||
|
||||
BlockType interactedType = targetBlock.state().type();
|
||||
if (event instanceof InteractBlockEvent.Primary.Start) {
|
||||
InteractBlockEvent.Primary.Start eventCast = ((InteractBlockEvent.Primary.Start) event);
|
||||
if (interactedType != BlockTypes.AIR.get()) {
|
||||
if (!optLoc.isPresent()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ServerLocation loc = optLoc.get();
|
||||
com.sk89q.worldedit.util.Location pos = SpongeAdapter.adapt(
|
||||
loc, Vector3d.ZERO
|
||||
);
|
||||
|
||||
if (we.handleBlockLeftClick(player, pos, SpongeAdapter.adapt(eventCast.targetSide()))) {
|
||||
eventCast.setCancelled(true);
|
||||
}
|
||||
}
|
||||
if (we.handleArmSwing(player)) {
|
||||
eventCast.setCancelled(true);
|
||||
}
|
||||
} else if (event instanceof InteractBlockEvent.Secondary) {
|
||||
if (!optLoc.isPresent()) {
|
||||
return;
|
||||
}
|
||||
InteractBlockEvent.Secondary eventCast = ((InteractBlockEvent.Secondary) event);
|
||||
|
||||
boolean result = false;
|
||||
if (optLoc.isPresent()) {
|
||||
ServerLocation loc = optLoc.get();
|
||||
com.sk89q.worldedit.util.Location pos = SpongeAdapter.adapt(
|
||||
loc, Vector3d.ZERO
|
||||
);
|
||||
com.sk89q.worldedit.util.Location pos = SpongeAdapter.adapt(loc, Vector3d.ZERO);
|
||||
|
||||
if (we.handleBlockRightClick(player, pos, SpongeAdapter.adapt(eventCast.targetSide()))) {
|
||||
eventCast.setCancelled(true);
|
||||
}
|
||||
result = we.handleBlockLeftClick(player, pos, SpongeAdapter.adapt(event.targetSide()));
|
||||
}
|
||||
|
||||
if (we.handleRightClick(player)) {
|
||||
eventCast.setCancelled(true);
|
||||
}
|
||||
result = we.handleArmSwing(player) || result;
|
||||
debouncer.setLastInteraction(player, result);
|
||||
|
||||
if (result) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Listener
|
||||
public void onPlayerInteractBlockSecondary(InteractBlockEvent.Secondary event, @Root ServerPlayer spongePlayer) {
|
||||
if (skipInteractionEvent(event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
WorldEdit we = WorldEdit.getInstance();
|
||||
SpongePlayer player = SpongeAdapter.adapt(spongePlayer);
|
||||
|
||||
BlockSnapshot targetBlock = event.block();
|
||||
Optional<ServerLocation> optLoc = targetBlock.location();
|
||||
|
||||
boolean result = false;
|
||||
if (optLoc.isPresent()) {
|
||||
ServerLocation loc = optLoc.get();
|
||||
com.sk89q.worldedit.util.Location pos = SpongeAdapter.adapt(loc, Vector3d.ZERO);
|
||||
|
||||
result = we.handleBlockRightClick(player, pos, SpongeAdapter.adapt(event.targetSide()));
|
||||
}
|
||||
|
||||
result = we.handleRightClick(player) || result;
|
||||
debouncer.setLastInteraction(player, result);
|
||||
|
||||
if (result) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Listener
|
||||
public void onPlayerQuit(ServerSideConnectionEvent.Disconnect event) {
|
||||
debouncer.clearInteraction(SpongeAdapter.adapt(event.player()));
|
||||
|
||||
WorldEdit.getInstance().getEventBus()
|
||||
.post(new SessionIdleEvent(new SpongePlayer.SessionKeyImpl(event.player())));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user