Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions config/checkstyle/import-control.xml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
<allow pkg="com.destroystokyo.paper"/>
<allow pkg="io.papermc.paper"/>
<allow pkg="org.spigotmc" />
<allow pkg="io.canvasmc.canvas"/>
</subpackage>

</subpackage>
Expand Down
7 changes: 7 additions & 0 deletions worldguard-bukkit/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ dependencies {
exclude("org.slf4j", "slf4j-api")
exclude("junit", "junit")
}
// Vendored locally: just the 3 io.canvasmc.canvas.event.*TeleportAsyncEvent* class
// files (extracted from canvas-api), not the whole canvas-api jar - that jar bundles
// its own full copy of org.bukkit.* compiled for a newer JDK, which conflicts with
// paperApi on the compile classpath. CanvasMC doesn't publish canvas-api for this old
// a version line anyway. Only used at compile time - see WorldGuardCanvasListener
// and WorldGuardPlugin#isCanvas().
"compileOnly"(files("libs/canvas-teleport-events.jar"))

"implementation"(libs.paperLib)
"implementation"(libs.bstats.bukkit)
Expand Down
Binary file added worldguard-bukkit/libs/canvas-teleport-events.jar
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
import com.sk89q.worldguard.bukkit.listener.WorldGuardBlockListener;
import com.sk89q.worldguard.bukkit.listener.WorldGuardCommandBookListener;
import com.sk89q.worldguard.bukkit.listener.WorldGuardEntityListener;
import com.sk89q.worldguard.bukkit.listener.WorldGuardCanvasListener;
import com.sk89q.worldguard.bukkit.listener.WorldGuardHangingListener;
import com.sk89q.worldguard.bukkit.listener.WorldGuardPlayerListener;
import com.sk89q.worldguard.bukkit.listener.WorldGuardServerListener;
Expand Down Expand Up @@ -190,6 +191,9 @@ public void accept(Object ignored) {
(new WorldGuardVehicleListener(this)).registerEvents();
(new WorldGuardServerListener(this)).registerEvents();
(new WorldGuardHangingListener(this)).registerEvents();
if (this.isCanvas()) {
(new WorldGuardCanvasListener(this)).registerEvents();
}

// Modules
(playerMoveListener = new PlayerMoveListener(this)).registerEvents();
Expand Down Expand Up @@ -575,4 +579,26 @@ public boolean isFolia() {
return folia.getValue();
}

private final LazyReference<Boolean> canvas = LazyReference.from(() -> {
try {
Class.forName("io.canvasmc.canvas.event.EntityTeleportAsyncEvent");
return true;
} catch (ClassNotFoundException e) {
return false;
}
});

/**
* Whether the server exposes CanvasMC's own teleport event API
* ({@code io.canvasmc.canvas.event.EntityTeleportAsyncEvent}). Canvas (a Folia
* fork) documents that the vanilla {@link org.bukkit.event.player.PlayerTeleportEvent}
* does not reliably fire for entity-driven teleports under region threading, and
* added this replacement instead of fixing the old API.
*
* @see <a href="https://docs.canvasmc.io/canvas/developers/api/events/">Canvas events docs</a>
*/
public boolean isCanvas() {
return canvas.getValue();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,15 @@ private void setDelegateEventMaterialOptions(DelegateEvent event, Material fromT

@EventHandler(ignoreCancelled = true)
public void onEntityChangeBlock(EntityChangeBlockEvent event) {
Block block = event.getBlock();
Block block;
try {
block = event.getBlock();
} catch (NullPointerException e) {
// Some platforms can fire this event referencing a block whose location no
// longer resolves to a loaded world. Nothing useful can be done, so bail out.
// See EngineHub/WorldGuard#2238
return;
}
Entity entity = event.getEntity();
Material toType = event.getTo();
Material fromType = block.getType();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/*
* WorldGuard, a suite of tools for Minecraft
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldGuard 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 <http://www.gnu.org/licenses/>.
*/

package com.sk89q.worldguard.bukkit.listener;

import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldguard.LocalPlayer;
import com.sk89q.worldguard.WorldGuard;
import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
import com.sk89q.worldguard.config.ConfigurationManager;
import com.sk89q.worldguard.config.WorldConfiguration;
import com.sk89q.worldguard.protection.ApplicableRegionSet;
import com.sk89q.worldguard.protection.flags.Flags;
import com.sk89q.worldguard.protection.flags.StateFlag;
import com.sk89q.worldguard.protection.regions.RegionQuery;
import io.canvasmc.canvas.event.EntityTeleportAsyncEvent;
import org.bukkit.GameMode;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
import org.bukkit.inventory.ItemStack;

/**
* Re-implements the ENDERPEARL/CHORUS_TELEPORT flag checks using Canvas's own
* {@link EntityTeleportAsyncEvent} instead of the vanilla
* {@link org.bukkit.event.player.PlayerTeleportEvent}.
*
* <p>Canvas (a Folia fork) documents that the vanilla event "doesn't function" for
* entity-driven teleports under region threading, and added this event as a
* replacement rather than fixing the old API. This listener is only registered when
* {@link WorldGuardPlugin#isCanvas()} is true, so it has no effect on other platforms.</p>
*
* @see <a href="https://docs.canvasmc.io/canvas/developers/api/events/">Canvas events docs</a>
*/
public class WorldGuardCanvasListener extends AbstractListener {

public WorldGuardCanvasListener(WorldGuardPlugin plugin) {
super(plugin);
}

@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void onEntityTeleportAsync(EntityTeleportAsyncEvent event) {
if (!(event.getEntity() instanceof Player player)) {
return;
}
if (com.sk89q.worldguard.bukkit.util.Entities.isNPC(player)) {
return;
}

TeleportCause cause = event.getCause();
StateFlag flag;
if (cause == TeleportCause.ENDER_PEARL) {
flag = Flags.ENDERPEARL;
} else if (cause == TeleportCause.CHORUS_FRUIT) {
flag = Flags.CHORUS_TELEPORT;
} else {
return;
}

LocalPlayer localPlayer = getPlugin().wrapPlayer(player);
ConfigurationManager cfg = getConfig();
WorldConfiguration wcfg = getWorldConfig(player.getWorld());

if (!wcfg.useRegions || !cfg.usePlayerTeleports) {
return;
}
if (WorldGuard.getInstance().getPlatform().getSessionManager().hasBypass(localPlayer, localPlayer.getWorld())) {
return;
}

RegionQuery query = WorldGuard.getInstance().getPlatform().getRegionContainer().createQuery();
ApplicableRegionSet setFrom = query.getApplicableRegions(BukkitAdapter.adapt(event.getFrom()));
ApplicableRegionSet setTo = query.getApplicableRegions(BukkitAdapter.adapt(event.getTo()));

boolean cancel = false;
String message = null;
if (!setFrom.testState(localPlayer, flag)) {
cancel = true;
message = setFrom.queryValue(localPlayer, Flags.EXIT_DENY_MESSAGE);
} else if (!setTo.testState(localPlayer, flag)) {
cancel = true;
message = setTo.queryValue(localPlayer, Flags.ENTRY_DENY_MESSAGE);
}

if (cancel) {
if (message != null && !message.isEmpty()) {
player.sendMessage(message);
}
event.setCancelled(true);
// The pearl/fruit is consumed before this event fires, so give it back.
if (player.getGameMode() != GameMode.CREATIVE) {
Material refund = cause == TeleportCause.ENDER_PEARL ? Material.ENDER_PEARL : Material.CHORUS_FRUIT;
player.getInventory().addItem(new ItemStack(refund, 1));
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -812,9 +812,19 @@ public void onFoodChange(FoodLevelChangeEvent event) {
public void onEntityChangeBlock(EntityChangeBlockEvent event) {
Entity ent = event.getEntity();

Block changedBlock;
try {
changedBlock = event.getBlock();
} catch (NullPointerException e) {
// Some platforms can fire this event referencing a block whose location no
// longer resolves to a loaded world. Nothing useful can be done, so bail out.
// See EngineHub/WorldGuard#2238
return;
}

WorldConfiguration wcfg = getWorldConfig(ent.getWorld());
if (ent instanceof FallingBlock) {
Material id = event.getBlock().getType();
Material id = changedBlock.getType();

if (id == Material.GRAVEL && wcfg.noPhysicsGravel) {
event.setCancelled(true);
Expand All @@ -836,7 +846,7 @@ public void onEntityChangeBlock(EntityChangeBlockEvent event) {
return;
}
if (wcfg.useRegions) {
Location location = event.getBlock().getLocation();
Location location = changedBlock.getLocation();
if (!StateFlag.test(WorldGuard.getInstance().getPlatform().getRegionContainer().createQuery().queryState(BukkitAdapter.adapt(location), (RegionAssociable) null, Flags.WITHER_DAMAGE))) {
event.setCancelled(true);
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,20 @@ public void onHangingBreak(HangingBreakEvent event) {
event.setCancelled(true);
}
}
} else {
// Explosions from mobs are not covered by HangingBreakByEntity
if (hanging instanceof Painting && wcfg.blockEntityPaintingDestroy
&& event.getCause() == RemoveCause.EXPLOSION) {
} else if (event.getCause() == RemoveCause.EXPLOSION || event.getCause() == RemoveCause.PHYSICS) {
// Explosions from mobs, and physics-caused breaks (e.g. a boat colliding
// with the hanging entity), are not covered by HangingBreakByEntityEvent,
// so there's no attacker entity available to check here.
// See EngineHub/WorldGuard#1434 for the PHYSICS case (boats breaking item frames).
if (hanging instanceof Painting
&& (wcfg.blockEntityPaintingDestroy
|| (wcfg.useRegions
&& !StateFlag.test(WorldGuard.getInstance().getPlatform().getRegionContainer().createQuery().queryState(BukkitAdapter.adapt(hanging.getLocation()), (RegionAssociable) null, Flags.ENTITY_PAINTING_DESTROY))))) {
event.setCancelled(true);
} else if (hanging instanceof ItemFrame && wcfg.blockEntityItemFrameDestroy
&& event.getCause() == RemoveCause.EXPLOSION) {
} else if (hanging instanceof ItemFrame
&& (wcfg.blockEntityItemFrameDestroy
|| (wcfg.useRegions
&& !StateFlag.test(WorldGuard.getInstance().getPlatform().getRegionContainer().createQuery().queryState(BukkitAdapter.adapt(hanging.getLocation()), (RegionAssociable) null, Flags.ENTITY_ITEM_FRAME_DESTROY))))) {
event.setCancelled(true);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,10 @@ public void onPlayerTeleport(PlayerTeleportEvent event) {
player.sendMessage(message);
}
event.setCancelled(true);
// The pearl is consumed on throw, before this event fires, so give it back.
if (player.getGameMode() != org.bukkit.GameMode.CREATIVE) {
player.getInventory().addItem(new ItemStack(Material.ENDER_PEARL, 1));
}
return;
}
}
Expand All @@ -401,6 +405,10 @@ public void onPlayerTeleport(PlayerTeleportEvent event) {
player.sendMessage(message);
}
event.setCancelled(true);
// The fruit is consumed on eating, before this event fires, so give it back.
if (player.getGameMode() != org.bukkit.GameMode.CREATIVE) {
player.getInventory().addItem(new ItemStack(Material.CHORUS_FRUIT, 1));
}
return;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,8 @@ public FileHandler(String pathPattern, int cacheSize, String worldName, Logger l
} else if (group.matches("%Y")) {
rep = String.valueOf(calendar.get(Calendar.YEAR));
} else if (group.matches("%m")) {
rep = String.format("%02d", calendar.get(Calendar.MONTH));
// Calendar.MONTH is 0-indexed (January = 0), but the %m log path token should be 1-indexed.
rep = String.format("%02d", calendar.get(Calendar.MONTH) + 1);
} else if (group.matches("%d")) {
rep = String.format("%02d", calendar.get(Calendar.DAY_OF_MONTH));
} else if (group.matches("%W")) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -597,7 +597,9 @@ public void flag(CommandContext args, Actor sender) throws CommandException {
if (value != null) {
// Set the flag if [value] was given even if [-g group] was given as well
try {
value = setFlag(existing, foundFlag, sender, value).toString();
Object parsedValue = setFlag(existing, foundFlag, sender, value);
// Some flags (e.g. StateFlag) parse "none" to a null value to clear the flag.
value = parsedValue == null ? "none" : parsedValue.toString();
} catch (InvalidFlagFormat e) {
throw new CommandException(e.getMessage());
}
Expand Down