Compare commits

...

25 Commits

Author SHA1 Message Date
Pierre Maurice Schwang
12c56b3973 fix: compile against 1.20.4 2025-09-05 23:16:01 +02:00
Pierre Maurice Schwang
190c73493d fix: missing continue and license header 2025-09-03 23:10:06 +02:00
Pierre Maurice Schwang
58492ef59e fix: signs with component lines 2025-09-03 23:00:55 +02:00
Pierre Maurice Schwang
2dad3c2fe1 feat: sign contents 2025-09-03 00:34:29 +02:00
Pierre Maurice Schwang
44e024df5e feat: use NMS to populate tile entities 2025-08-31 01:27:28 +02:00
Pierre Maurice Schwang
5ed4dee99e fix: block nbt population 2025-08-24 02:07:16 +02:00
Pierre Maurice Schwang
c592a2ecc8 chore: 1.17 is not supported anymore 2025-08-24 02:06:51 +02:00
renovate[bot]
cd6a32cf44 Update adventure to v4.24.0 (#4733)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-18 02:06:13 +00:00
renovate[bot]
273c0ad989 Update Ilshidur/action-discord action to v0.4.0 (#4734)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-18 02:05:32 +00:00
renovate[bot]
774da7183b Update dependency com.gradleup.shadow to v8.3.9 (#4730)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-11 00:56:21 +00:00
renovate[bot]
e083015ab2 Update dependency com.github.spotbugs:spotbugs-annotations to v4.9.4 (#4729)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-11 00:55:56 +00:00
Jordan
3f577d039b feat: add placeholder for multiple owners (#4711)
- closes #4695
2025-08-08 19:51:53 +02:00
Leviaria
4d2e4a3d1a fix: tab completion for /plot grant add/check <name> commands (#4720)
Fix tab completion for /plot grant add/check <name> commands

Fixed an issue where player name tab completion was not working correctly 
for /plot grant add <name> and /plot grant check <name> commands.

Previously, the tab completion logic would only attempt to complete player 
names after all arguments were processed, making it impossible to get 
player suggestions when typing the second argument.

Now the completion properly handles:
- /plot grant <TAB> → shows "add", "check" subcommands
- /plot grant add <TAB> → shows player list
- /plot grant check <TAB> → shows player list

Fixes #4382
2025-08-06 21:44:10 +02:00
renovate[bot]
e5d36579b1 Update fawe to v2.13.1 (#4724)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-04 01:52:49 +00:00
renovate[bot]
f0cde251bd Update dependency net.essentialsx:EssentialsX to v2.21.2 (#4723)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-04 01:52:09 +00:00
Jordan
58016bb1c8 fix: allow admin override when setting plot context in command (#4712)
* fix: allow admin override when setting plot context in command
 - when admins (plots.admin or console) attempted to use /plot x;z to set context on a plot they are denied on, it would not work
 - fixes #4696

* Print error to user if denied
2025-08-03 11:13:48 +02:00
Alexander Brandes
e5943ba627 Update adventure-platform-bukkit to 4.4.1
Signed-off-by: Alexander Brandes <mc.cache@web.de>
2025-07-30 09:01:56 +02:00
Alexander Brandes
07dfdeef2c Back to snapshot for development
Signed-off-by: Alexander Brandes <mc.cache@web.de>
2025-07-29 21:20:36 +02:00
Alexander Brandes
025b08e716 Release 7.5.6
Signed-off-by: Alexander Brandes <mc.cache@web.de>
2025-07-29 21:16:56 +02:00
Alexander Brandes
921435689e Update adventure-platform-bukkit to 4.4.1-SNAPSHOT (#4717)
Signed-off-by: Alexander Brandes <mc.cache@web.de>
2025-07-29 21:09:20 +02:00
Alexander Brandes
aae154b23a Back to snapshot for development
Signed-off-by: Alexander Brandes <mc.cache@web.de>
2025-07-29 18:36:10 +02:00
Alexander Brandes
0f33465d76 Release 7.5.5
Signed-off-by: Alexander Brandes <mc.cache@web.de>
2025-07-29 18:33:50 +02:00
renovate[bot]
438f1d9656 Update junit-framework monorepo (#4715)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-28 16:00:43 +00:00
renovate[bot]
cb38aeef93 Update dependency com.diffplug.spotless to v7.2.1 (#4714)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-28 16:00:13 +00:00
Jordan
7be0655b86 fix: do not create south road twice on unlink (#4713)
- this was changed from else if to if in 7623698a00 (diff-b05df598e90ce5418467b07e84ef85a80be8bac7c70929063ace9ede4d5cf52eL1000) (for unknown reason)
 - fixes #4704
2025-07-28 13:44:55 +02:00
15 changed files with 460 additions and 308 deletions

View File

@@ -11,7 +11,7 @@ jobs:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
DISCORD_USERNAME: PlotSquared Release
DISCORD_AVATAR: https://raw.githubusercontent.com/IntellectualSites/Assets/main/plugins/PlotSquared/PlotSquared.png
uses: Ilshidur/action-discord@0.3.2
uses: Ilshidur/action-discord@0.4.0
with:
args: |
"<@&525015541815967744> <@&679322738552471574> <@&699293353862496266>"

View File

@@ -41,6 +41,7 @@ dependencies {
compileOnly(libs.luckperms)
compileOnly(libs.essentialsx) {
exclude(group = "org.spigotmc")
exclude(group = "io.papermc.paper")
}
compileOnly(libs.mvdwapi) { isTransitive = false }
@@ -74,7 +75,7 @@ tasks.named<ShadowJar>("shadowJar") {
relocate("net.kyori.examination", "com.plotsquared.core.configuration.examination")
relocate("io.papermc.lib", "com.plotsquared.bukkit.paperlib")
relocate("org.bstats", "com.plotsquared.metrics")
relocate("org.enginehub", "com.plotsquared.squirrelid")
relocate("org.enginehub.squirrelid", "com.plotsquared.squirrelid")
relocate("org.khelekore.prtree", "com.plotsquared.prtree")
relocate("com.google.inject", "com.plotsquared.google")
relocate("org.aopalliance", "com.plotsquared.core.aopalliance")

View File

@@ -90,12 +90,7 @@ public class BukkitPlotGenerator extends ChunkGenerator implements GeneratorWrap
this.plotGenerator = generator;
this.platformGenerator = this;
this.populators = new ArrayList<>();
int minecraftMinorVersion = PlotSquared.platform().serverVersion()[1];
if (minecraftMinorVersion >= 17) {
this.populators.add(new BlockStatePopulator(this.plotGenerator));
} else {
this.populators.add(new LegacyBlockStatePopulator(this.plotGenerator));
}
this.populators.add(new BlockStatePopulator(this.plotGenerator));
this.full = true;
this.useNewGenerationMethods = PlotSquared.platform().serverVersion()[1] >= 19;
this.biomeProvider = new BukkitPlotBiomeProvider();

View File

@@ -36,6 +36,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Random;
@Deprecated(since = "TODO")
final class LegacyBlockStatePopulator extends BlockPopulator {
private final IndependentPlotGenerator plotGenerator;

View File

@@ -0,0 +1,80 @@
/*
* PlotSquared, a land and world management plugin for Minecraft.
* Copyright (C) IntellectualSites <https://intellectualsites.com>
* Copyright (C) IntellectualSites 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.plotsquared.bukkit.schematic;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.sk89q.jnbt.ByteArrayTag;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.IntArrayTag;
import com.sk89q.jnbt.ListTag;
import com.sk89q.jnbt.LongArrayTag;
import com.sk89q.jnbt.Tag;
import java.lang.reflect.Type;
final class NbtGsonSerializer implements JsonSerializer<Tag> {
@Override
public JsonElement serialize(final Tag src, final Type typeOfSrc, final JsonSerializationContext context) {
if (src instanceof CompoundTag compoundTag) {
JsonObject object = new JsonObject();
compoundTag.getValue().forEach((s, tag) -> object.add(s, context.serialize(tag)));
return object;
}
if (src instanceof ListTag listTag) {
JsonArray array = new JsonArray();
listTag.getValue().forEach(tag -> array.add(context.serialize(tag)));
return array;
}
if (src instanceof ByteArrayTag byteArrayTag) {
JsonArray array = new JsonArray();
for (final byte b : byteArrayTag.getValue()) {
array.add(b);
}
return array;
}
if (src instanceof IntArrayTag intArrayTag) {
JsonArray array = new JsonArray();
for (final int i : intArrayTag.getValue()) {
array.add(i);
}
return array;
}
if (src instanceof LongArrayTag longArrayTag) {
JsonArray array = new JsonArray();
for (final long l : longArrayTag.getValue()) {
array.add(l);
}
return array;
}
if (src.getValue() instanceof Number number) {
return new JsonPrimitive(number);
}
if (src.getValue() instanceof String string) {
return new JsonPrimitive(string);
}
throw new IllegalArgumentException("Don't know how to serialize " + src);
}
}

View File

@@ -18,158 +18,93 @@
*/
package com.plotsquared.bukkit.schematic;
import com.destroystokyo.paper.profile.PlayerProfile;
import com.destroystokyo.paper.profile.ProfileProperty;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.plotsquared.bukkit.util.BukkitUtil;
import com.sk89q.jnbt.ByteTag;
import com.plotsquared.core.PlotSquared;
import com.plotsquared.core.util.ReflectionUtils;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.ListTag;
import com.sk89q.jnbt.ShortTag;
import com.sk89q.jnbt.StringTag;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.blocks.BaseItemStack;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.world.item.ItemType;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
import com.sk89q.worldedit.extension.platform.NoCapablePlatformException;
import io.papermc.lib.PaperLib;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.DyeColor;
import org.bukkit.World;
import org.bukkit.block.Banner;
import org.bukkit.block.Block;
import org.bukkit.block.Container;
import org.bukkit.block.BlockState;
import org.bukkit.block.Sign;
import org.bukkit.block.Skull;
import org.bukkit.block.banner.Pattern;
import org.bukkit.block.banner.PatternType;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.block.sign.Side;
import org.bukkit.block.sign.SignSide;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.jetbrains.annotations.ApiStatus;
import java.util.ArrayList;
import java.util.HashMap;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.UUID;
import java.util.Locale;
import java.util.function.Consumer;
/**
* This class (attempts to) restore block tile entity data, after the underlying block state has been placed.
* This is used on chunk population (world generation) and in the platforms queue handler (as a fallback for WorldEdit placement).
* <br />
* This class relies heavily on reflective access, native minecraft methods and non-standardized WorldEdit / FAWE methods. It's
* extremely prone to breakage between versions (Minecraft and/or (FA)WE), but supports most if not all possible tile entities.
* Given the previous logic of this class was also non-reliable between version updates, and did only support a small subset of
* tile entities, it's a fair trade-off.
*/
@ApiStatus.Internal
public class StateWrapper {
private static final Logger LOGGER = LogManager.getLogger("PlotSquared/" + StateWrapper.class.getSimpleName());
private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
private static final Gson GSON = new GsonBuilder().registerTypeHierarchyAdapter(Tag.class, new NbtGsonSerializer()).create();
private static final String CRAFTBUKKIT_PACKAGE = Bukkit.getServer().getClass().getPackageName();
private static final boolean FORCE_UPDATE_STATE = true;
private static final boolean UPDATE_TRIGGER_PHYSICS = false;
private static final boolean SUPPORTED = PlotSquared.platform().serverVersion()[1] > 20 ||
(PlotSquared.platform().serverVersion()[1] == 20 && PlotSquared.platform().serverVersion()[2] >= 4);
private static final String INITIALIZATION_ERROR_TEMPLATE = """
Failed to initialize StateWrapper: {}
Block-/Tile-Entities, pasted by schematics for example, won't be updated with their respective block data. This affects things like sign text, banner patterns, skulls, etc.
Try updating your Server Software, PlotSquared and WorldEdit / FastAsyncWorldEdit first. If the issue persists, report it on the issue tracker.
""";
private static boolean NOT_SUPPORTED_NOTIFIED = false;
private static boolean FAILED_INITIALIZATION = false;
private static BukkitImplAdapter ADAPTER = null;
private static Class<?> LIN_TAG_CLASS = null;
private static Class<?> JNBT_TAG_CLASS = null;
private static Class<?> CRAFT_BLOCK_ENTITY_STATE_CLASS = null;
private static MethodHandle PAPERWEIGHT_ADAPTER_FROM_NATIVE = null;
private static MethodHandle CRAFT_BLOCK_ENTITY_STATE_LOAD_DATA = null;
private static MethodHandle CRAFT_BLOCK_ENTITY_STATE_UPDATE = null;
private static MethodHandle TO_LIN_TAG = null;
// SIGN HACK
private static boolean PAPER_SIGN_NOTIFIED = false;
private static boolean FAILED_SIGN_INITIALIZATION = false;
private static Object KYORI_GSON_SERIALIZER = null;
private static MethodHandle GSON_SERIALIZER_DESERIALIZE_TREE = null;
private static MethodHandle BUKKIT_SIGN_SIDE_LINE_SET = null;
public CompoundTag tag;
private boolean paperErrorTextureSent = false;
private static final Logger LOGGER = LogManager.getLogger("PlotSquared/" + StateWrapper.class.getSimpleName());
public StateWrapper(CompoundTag tag) {
this.tag = tag;
}
public static String jsonToColourCode(String str) {
str = str.replace("{\"extra\":", "").replace("],\"text\":\"\"}", "]")
.replace("[{\"color\":\"black\",\"text\":\"", "&0")
.replace("[{\"color\":\"dark_blue\",\"text\":\"", "&1")
.replace("[{\"color\":\"dark_green\",\"text\":\"", "&2")
.replace("[{\"color\":\"dark_aqua\",\"text\":\"", "&3")
.replace("[{\"color\":\"dark_red\",\"text\":\"", "&4")
.replace("[{\"color\":\"dark_purple\",\"text\":\"", "&5")
.replace("[{\"color\":\"gold\",\"text\":\"", "&6")
.replace("[{\"color\":\"gray\",\"text\":\"", "&7")
.replace("[{\"color\":\"dark_gray\",\"text\":\"", "&8")
.replace("[{\"color\":\"blue\",\"text\":\"", "&9")
.replace("[{\"color\":\"green\",\"text\":\"", "&a")
.replace("[{\"color\":\"aqua\",\"text\":\"", "&b")
.replace("[{\"color\":\"red\",\"text\":\"", "&c")
.replace("[{\"color\":\"light_purple\",\"text\":\"", "&d")
.replace("[{\"color\":\"yellow\",\"text\":\"", "&e")
.replace("[{\"color\":\"white\",\"text\":\"", "&f")
.replace("[{\"obfuscated\":true,\"text\":\"", "&k")
.replace("[{\"bold\":true,\"text\":\"", "&l")
.replace("[{\"strikethrough\":true,\"text\":\"", "&m")
.replace("[{\"underlined\":true,\"text\":\"", "&n")
.replace("[{\"italic\":true,\"text\":\"", "&o").replace("[{\"color\":\"black\",", "&0")
.replace("[{\"color\":\"dark_blue\",", "&1")
.replace("[{\"color\":\"dark_green\",", "&2")
.replace("[{\"color\":\"dark_aqua\",", "&3").replace("[{\"color\":\"dark_red\",", "&4")
.replace("[{\"color\":\"dark_purple\",", "&5").replace("[{\"color\":\"gold\",", "&6")
.replace("[{\"color\":\"gray\",", "&7").replace("[{\"color\":\"dark_gray\",", "&8")
.replace("[{\"color\":\"blue\",", "&9").replace("[{\"color\":\"green\",", "&a")
.replace("[{\"color\":\"aqua\",", "&b").replace("[{\"color\":\"red\",", "&c")
.replace("[{\"color\":\"light_purple\",", "&d").replace("[{\"color\":\"yellow\",", "&e")
.replace("[{\"color\":\"white\",", "&f").replace("[{\"obfuscated\":true,", "&k")
.replace("[{\"bold\":true,", "&l").replace("[{\"strikethrough\":true,", "&m")
.replace("[{\"underlined\":true,", "&n").replace("[{\"italic\":true,", "&o")
.replace("{\"color\":\"black\",\"text\":\"", "&0")
.replace("{\"color\":\"dark_blue\",\"text\":\"", "&1")
.replace("{\"color\":\"dark_green\",\"text\":\"", "&2")
.replace("{\"color\":\"dark_aqua\",\"text\":\"", "&3")
.replace("{\"color\":\"dark_red\",\"text\":\"", "&4")
.replace("{\"color\":\"dark_purple\",\"text\":\"", "&5")
.replace("{\"color\":\"gold\",\"text\":\"", "&6")
.replace("{\"color\":\"gray\",\"text\":\"", "&7")
.replace("{\"color\":\"dark_gray\",\"text\":\"", "&8")
.replace("{\"color\":\"blue\",\"text\":\"", "&9")
.replace("{\"color\":\"green\",\"text\":\"", "&a")
.replace("{\"color\":\"aqua\",\"text\":\"", "&b")
.replace("{\"color\":\"red\",\"text\":\"", "&c")
.replace("{\"color\":\"light_purple\",\"text\":\"", "&d")
.replace("{\"color\":\"yellow\",\"text\":\"", "&e")
.replace("{\"color\":\"white\",\"text\":\"", "&f")
.replace("{\"obfuscated\":true,\"text\":\"", "&k")
.replace("{\"bold\":true,\"text\":\"", "&l")
.replace("{\"strikethrough\":true,\"text\":\"", "&m")
.replace("{\"underlined\":true,\"text\":\"", "&n")
.replace("{\"italic\":true,\"text\":\"", "&o").replace("{\"color\":\"black\",", "&0")
.replace("{\"color\":\"dark_blue\",", "&1").replace("{\"color\":\"dark_green\",", "&2")
.replace("{\"color\":\"dark_aqua\",", "&3").replace("{\"color\":\"dark_red\",", "&4")
.replace("{\"color\":\"dark_purple\",", "&5").replace("{\"color\":\"gold\",", "&6")
.replace("{\"color\":\"gray\",", "&7").replace("{\"color\":\"dark_gray\",", "&8")
.replace("{\"color\":\"blue\",", "&9").replace("{\"color\":\"green\",", "&a")
.replace("{\"color\":\"aqua\",", "&b").replace("{\"color\":\"red\",", "&c")
.replace("{\"color\":\"light_purple\",", "&d").replace("{\"color\":\"yellow\",", "&e")
.replace("{\"color\":\"white\",", "&f").replace("{\"obfuscated\":true,", "&k")
.replace("{\"bold\":true,", "&l").replace("{\"strikethrough\":true,", "&m")
.replace("{\"underlined\":true,", "&n").replace("{\"italic\":true,", "&o")
.replace("\"color\":\"black\",\"text\":\"", "&0")
.replace("\"color\":\"dark_blue\",\"text\":\"", "&1")
.replace("\"color\":\"dark_green\",\"text\":\"", "&2")
.replace("\"color\":\"dark_aqua\",\"text\":\"", "&3")
.replace("\"color\":\"dark_red\",\"text\":\"", "&4")
.replace("\"color\":\"dark_purple\",\"text\":\"", "&5")
.replace("\"color\":\"gold\",\"text\":\"", "&6")
.replace("\"color\":\"gray\",\"text\":\"", "&7")
.replace("\"color\":\"dark_gray\",\"text\":\"", "&8")
.replace("\"color\":\"blue\",\"text\":\"", "&9")
.replace("\"color\":\"green\",\"text\":\"", "&a")
.replace("\"color\":\"aqua\",\"text\":\"", "&b")
.replace("\"color\":\"red\",\"text\":\"", "&c")
.replace("\"color\":\"light_purple\",\"text\":\"", "&d")
.replace("\"color\":\"yellow\",\"text\":\"", "&e")
.replace("\"color\":\"white\",\"text\":\"", "&f")
.replace("\"obfuscated\":true,\"text\":\"", "&k")
.replace("\"bold\":true,\"text\":\"", "&l")
.replace("\"strikethrough\":true,\"text\":\"", "&m")
.replace("\"underlined\":true,\"text\":\"", "&n")
.replace("\"italic\":true,\"text\":\"", "&o").replace("\"color\":\"black\",", "&0")
.replace("\"color\":\"dark_blue\",", "&1").replace("\"color\":\"dark_green\",", "&2")
.replace("\"color\":\"dark_aqua\",", "&3").replace("\"color\":\"dark_red\",", "&4")
.replace("\"color\":\"dark_purple\",", "&5").replace("\"color\":\"gold\",", "&6")
.replace("\"color\":\"gray\",", "&7").replace("\"color\":\"dark_gray\",", "&8")
.replace("\"color\":\"blue\",", "&9").replace("\"color\":\"green\",", "&a")
.replace("\"color\":\"aqua\",", "&b").replace("\"color\":\"red\",", "&c")
.replace("\"color\":\"light_purple\",", "&d").replace("\"color\":\"yellow\",", "&e")
.replace("\"color\":\"white\",", "&f").replace("\"obfuscated\":true,", "&k")
.replace("\"bold\":true,", "&l").replace("\"strikethrough\":true,", "&m")
.replace("\"underlined\":true,", "&n").replace("\"italic\":true,", "&o")
.replace("[{\"text\":\"", "&0").replace("{\"text\":\"", "&0").replace("\"},", "")
.replace("\"}]", "").replace("\"}", "");
str = ChatColor.translateAlternateColorCodes('&', str);
return str;
}
/**
* Restore the TileEntity data to the given world at the given coordinates.
*
@@ -193,157 +128,264 @@ public class StateWrapper {
* @param block Block to restore to
* @return true if successful
*/
@SuppressWarnings("deprecation") // #setLine is needed for Spigot compatibility
public boolean restoreTag(@NonNull Block block) {
if (this.tag == null) {
if (this.tag == null || FAILED_INITIALIZATION) {
return false;
}
org.bukkit.block.BlockState state = block.getState();
switch (getId()) {
case "chest", "beacon", "brewingstand", "dispenser", "dropper", "furnace", "hopper", "shulkerbox" -> {
if (!(state instanceof Container container)) {
return false;
}
List<Tag> itemsTag = this.tag.getListTag("Items").getValue();
Inventory inv = container.getSnapshotInventory();
for (Tag itemTag : itemsTag) {
CompoundTag itemComp = (CompoundTag) itemTag;
ItemType type = ItemType.REGISTRY.get(itemComp.getString("id").toLowerCase());
if (type == null) {
continue;
}
int count = itemComp.getByte("Count");
int slot = itemComp.getByte("Slot");
CompoundTag tag = (CompoundTag) itemComp.getValue().get("tag");
BaseItemStack baseItemStack = new BaseItemStack(type, tag, count);
ItemStack itemStack = BukkitAdapter.adapt(baseItemStack);
inv.setItem(slot, itemStack);
}
container.update(true, false);
return true;
if (!SUPPORTED) {
if (!NOT_SUPPORTED_NOTIFIED) {
NOT_SUPPORTED_NOTIFIED = true;
LOGGER.error(INITIALIZATION_ERROR_TEMPLATE, "Your server version is not supported. 1.20.4 or later is required");
}
case "sign" -> {
if (state instanceof Sign sign) {
sign.setLine(0, jsonToColourCode(tag.getString("Text1")));
sign.setLine(1, jsonToColourCode(tag.getString("Text2")));
sign.setLine(2, jsonToColourCode(tag.getString("Text3")));
sign.setLine(3, jsonToColourCode(tag.getString("Text4")));
state.update(true);
return true;
}
return false;
}
if (ADAPTER == null) {
try {
findNbtCompoundClassType(clazz -> LIN_TAG_CLASS = clazz, clazz -> JNBT_TAG_CLASS = clazz);
ReflectionUtils.RefClass worldEditPluginRefClass = ReflectionUtils.getRefClass(WorldEditPlugin.class);
WorldEditPlugin worldEditPlugin = (WorldEditPlugin) worldEditPluginRefClass
.getMethod("getInstance")
.of(null)
.call();
ADAPTER = (BukkitImplAdapter) worldEditPluginRefClass
.getMethod("getBukkitImplAdapter")
.of(worldEditPlugin)
.call();
PAPERWEIGHT_ADAPTER_FROM_NATIVE = findPaperweightAdapterFromNativeMethodHandle(
ADAPTER.getClass(), LIN_TAG_CLASS, JNBT_TAG_CLASS
);
TO_LIN_TAG = findToLinTagMethodHandle(LIN_TAG_CLASS);
} catch (NoSuchMethodException | ClassNotFoundException | IllegalAccessException | NoCapablePlatformException e) {
LOGGER.error(INITIALIZATION_ERROR_TEMPLATE, "Failed to access required WorldEdit methods", e);
FAILED_INITIALIZATION = true;
return false;
}
case "skull" -> {
if (state instanceof Skull skull) {
CompoundTag skullOwner = ((CompoundTag) this.tag.getValue().get("SkullOwner"));
if (skullOwner == null) {
return true;
}
String player = skullOwner.getString("Name");
if (player != null && !player.isEmpty()) {
try {
skull.setOwningPlayer(Bukkit.getOfflinePlayer(player));
skull.update(true);
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
final CompoundTag properties = (CompoundTag) skullOwner.getValue().get("Properties");
if (properties == null) {
return false;
}
final ListTag textures = properties.getListTag("textures");
if (textures.getValue().isEmpty()) {
return false;
}
final CompoundTag textureCompound = (CompoundTag) textures.getValue().get(0);
if (textureCompound == null) {
return false;
}
String textureValue = textureCompound.getString("Value");
if (textureValue == null) {
return false;
}
if (!PaperLib.isPaper()) {
if (!paperErrorTextureSent) {
paperErrorTextureSent = true;
LOGGER.error("Failed to populate skull data in your road schematic - This is a Spigot limitation.");
}
return false;
}
final PlayerProfile profile = Bukkit.createProfile(UUID.randomUUID());
profile.setProperty(new ProfileProperty("textures", textureValue));
skull.setPlayerProfile(profile);
skull.update(true);
return true;
}
try {
CRAFT_BLOCK_ENTITY_STATE_CLASS = Class.forName(CRAFTBUKKIT_PACKAGE + ".block.CraftBlockEntityState");
CRAFT_BLOCK_ENTITY_STATE_LOAD_DATA = findCraftBlockEntityStateLoadDataMethodHandle(CRAFT_BLOCK_ENTITY_STATE_CLASS);
CRAFT_BLOCK_ENTITY_STATE_UPDATE = findCraftBlockEntityStateUpdateMethodHandle(CRAFT_BLOCK_ENTITY_STATE_CLASS);
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException e) {
LOGGER.error(INITIALIZATION_ERROR_TEMPLATE, "Failed to initialize required native method accessors", e);
FAILED_INITIALIZATION = true;
return false;
}
case "banner" -> {
if (state instanceof Banner banner) {
List<Tag> patterns = this.tag.getListTag("Patterns").getValue();
if (patterns == null || patterns.isEmpty()) {
return false;
}
banner.setPatterns(patterns.stream().map(t -> (CompoundTag) t).map(compoundTag -> {
DyeColor color = DyeColor.getByWoolData((byte) compoundTag.getInt("Color"));
PatternType patternType = PatternType.getByIdentifier(compoundTag.getString("Pattern"));
if (color == null || patternType == null) {
return null;
}
return new Pattern(color, patternType);
}).filter(Objects::nonNull).toList());
banner.update(true);
return true;
}
}
try {
final BlockState blockState = block.getState();
if (!CRAFT_BLOCK_ENTITY_STATE_CLASS.isAssignableFrom(blockState.getClass())) {
return false;
}
// get native tag
Object nativeTag = PAPERWEIGHT_ADAPTER_FROM_NATIVE.invoke(
ADAPTER,
LIN_TAG_CLASS == null ? this.tag : TO_LIN_TAG.invoke(this.tag)
);
CRAFT_BLOCK_ENTITY_STATE_LOAD_DATA.invoke(blockState, nativeTag);
if (blockState instanceof Sign sign) {
Object text;
if ((text = tag.getValue().get("front_text")) != null && text instanceof CompoundTag textTag) {
setSignTextHack(sign, textTag, true);
}
if ((text = tag.getValue().get("back_text")) != null && text instanceof CompoundTag textTag) {
setSignTextHack(sign, textTag, false);
}
}
CRAFT_BLOCK_ENTITY_STATE_UPDATE.invoke(blockState, FORCE_UPDATE_STATE, UPDATE_TRIGGER_PHYSICS);
} catch (Throwable e) {
LOGGER.error("Failed to update tile entity", e);
}
return false;
}
public String getId() {
String tileid = this.tag.getString("id").toLowerCase();
if (tileid.startsWith("minecraft:")) {
tileid = tileid.replace("minecraft:", "");
/**
* Set sign content on the bukkit tile entity. The server does not load sign content applied via the main logic
* (CraftBlockEntity#load), as the SignEntity needs to have a valid ServerLevel assigned to it.
* That's not possible on worldgen; therefore, this hack has to be used additionally.
* <br />
* Modern sign content (non-plain-text sign lines) require Paper.
*
* @param sign The sign to apply data onto.
* @param text The compound tag containing the data for the sign side ({@code front_text} / {@code back_text})
* @param front If the compound tag contains the data for the front side.
* @throws Throwable if something went wrong when reflectively updating the sign.
*/
private static void setSignTextHack(Sign sign, CompoundTag text, boolean front) throws Throwable {
final SignSide side = sign.getSide(front ? Side.FRONT : Side.BACK);
if (text.containsKey("color")) {
//noinspection UnstableApiUsage
side.setColor(DyeColor.legacyValueOf(text.getString("color").toUpperCase(Locale.ROOT)));
}
return tileid;
}
public List<CompoundTag> serializeInventory(ItemStack[] items) {
List<CompoundTag> tags = new ArrayList<>();
for (int i = 0; i < items.length; ++i) {
if (items[i] != null) {
Map<String, Tag> tagData = serializeItem(items[i]);
tagData.put("Slot", new ByteTag((byte) i));
tags.add(new CompoundTag(tagData));
if (text.containsKey("has_glowing_text")) {
side.setGlowingText(text.getByte("has_glowing_text") == 1);
}
List<Tag> lines = text.getList("messages");
if (lines != null) {
for (int i = 0; i < Math.min(lines.size(), 3); i++) {
Tag line = lines.get(i);
if (line instanceof StringTag stringTag) {
//noinspection deprecation - Paper deprecatiom
side.setLine(i, stringTag.getValue());
continue;
}
if (line instanceof ListTag || line instanceof CompoundTag) {
if (!initializeSignHack()) {
continue;
}
// Minecraft uses mixed lists / arrays in their sign texts. One line can be a complex component, whereas
// the following line could simply be a string. Those simpler lines are represented as `{"": ""}` (only in
// SNBT those will be shown as a standard string). Adventure can't parse those, so we handle these lines as
// plaintext lines (can't contain any other extra data either way).
if (line instanceof CompoundTag compoundTag && compoundTag.getValue().containsKey("")) {
//noinspection deprecation - Paper deprecatiom
side.setLine(i, compoundTag.getString(""));
continue;
}
// serializes the line content from JNBT to Gson JSON objects, passes that to adventure and deserializes
// into an adventure component.
BUKKIT_SIGN_SIDE_LINE_SET.invoke(
side, i, GSON_SERIALIZER_DESERIALIZE_TREE.invoke(
KYORI_GSON_SERIALIZER,
GSON.toJsonTree(line.getValue())
)
);
}
}
}
return tags;
}
public Map<String, Tag> serializeItem(ItemStack item) {
Map<String, Tag> data = new HashMap<>();
data.put("id", new StringTag(item.getType().name()));
data.put("Damage", new ShortTag(item.getDurability()));
data.put("Count", new ByteTag((byte) item.getAmount()));
if (!item.getEnchantments().isEmpty()) {
List<CompoundTag> enchantmentList = new ArrayList<>();
for (Entry<Enchantment, Integer> entry : item.getEnchantments().entrySet()) {
Map<String, Tag> enchantment = new HashMap<>();
enchantment.put("id", new StringTag(entry.getKey().toString()));
enchantment.put("lvl", new ShortTag(entry.getValue().shortValue()));
enchantmentList.add(new CompoundTag(enchantment));
}
Map<String, Tag> auxData = new HashMap<>();
auxData.put("ench", new ListTag(CompoundTag.class, enchantmentList));
data.put("tag", new CompoundTag(auxData));
private static boolean initializeSignHack() {
if (FAILED_SIGN_INITIALIZATION) {
return false;
}
return data;
if (KYORI_GSON_SERIALIZER != null) {
return true; // already initialized
}
if (!PaperLib.isPaper()) {
if (!PAPER_SIGN_NOTIFIED) {
PAPER_SIGN_NOTIFIED = true;
LOGGER.error("Can't populate non-plain sign line. To load modern sign content, use Paper.");
}
return false;
}
try {
char[] dontObfuscate = new char[]{
'n', 'e', 't', '.', 'k', 'y', 'o', 'r', 'i', '.', 'a', 'd', 'v', 'e', 'n', 't', 'u', 'r', 'e', '.',
't', 'e', 'x', 't', '.', 's', 'e', 'r', 'i', 'a', 'l', 'i', 'z', 'e', 'r', '.', 'g', 's', 'o', 'n', '.',
'G', 's', 'o', 'n', 'C', 'o', 'm', 'p', 'o', 'n', 'e', 'n', 't', 'S', 'e', 'r', 'i', 'a', 'l', 'i', 'z', 'e', 'r'
};
Class<?> gsonComponentSerializerClass = Class.forName(new String(dontObfuscate));
KYORI_GSON_SERIALIZER = Arrays.stream(gsonComponentSerializerClass.getMethods())
.filter(method -> method.getName().equals("gson"))
.findFirst()
.orElseThrow().invoke(null);
GSON_SERIALIZER_DESERIALIZE_TREE = LOOKUP.unreflect(Arrays
.stream(gsonComponentSerializerClass.getMethods())
.filter(method -> method.getName().equals("deserializeFromTree") && method.getParameterCount() == 1)
.findFirst()
.orElseThrow());
BUKKIT_SIGN_SIDE_LINE_SET = LOOKUP.unreflect(Arrays.stream(SignSide.class.getMethods())
.filter(method -> method.getName().equals("line") && method.getParameterCount() == 2)
.findFirst()
.orElseThrow());
return true;
} catch (Throwable e) {
FAILED_SIGN_INITIALIZATION = true;
LOGGER.error("Failed to initialize sign-hack. Signs populated by schematics might not have their line contents.", e);
return false;
}
}
/**
* Initialize the used NBT tag class. For modern FAWE and WE that'll be Lin - for older ones JNBT.
*
* @throws ClassNotFoundException if neither can be found.
*/
private static void findNbtCompoundClassType(Consumer<Class<?>> linClass, Consumer<Class<?>> jnbtClass) throws
ClassNotFoundException {
try {
linClass.accept(Class.forName("org.enginehub.linbus.tree.LinTag"));
} catch (ClassNotFoundException e) {
jnbtClass.accept(Class.forName("com.sk89q.jnbt.Tag"));
}
}
/**
* Finds the {@code toLinTag} method on the {@code ToLinTag} interface, if lin-bus is available in the classpath.
* <br />
* Required to access the underlying lin tag of the used JNBT tag by PlotSquared, so it can be converted into the platforms
* native tag later.
*
* @param linTagClass {@code Tag} class of lin-bus, or {@code null} if not available.
* @return the MethodHandle for {@code toLinTag}, or {@code null} if lin-bus is not available in the classpath.
* @throws ClassNotFoundException if the {@code ToLinTag} class could not be found.
* @throws NoSuchMethodException if no {@code toLinTag} method exists.
* @throws IllegalAccessException shouldn't happen.
*/
private static MethodHandle findToLinTagMethodHandle(Class<?> linTagClass) throws ClassNotFoundException,
NoSuchMethodException, IllegalAccessException {
if (linTagClass == null) {
return null;
}
return LOOKUP.findVirtual(
Class.forName("org.enginehub.linbus.tree.ToLinTag"),
"toLinTag",
MethodType.methodType(linTagClass)
);
}
/**
* Find the method (handle) to convert from native (= WE/FAWE) NBT tags to minecraft NBT tags.
* <br />
* Depending on the used version of WE/FAWE, this differs:
* <ul>
* <li>On WE/FAWE version pre LinBus introduction: {@code fromNative(org.sk89q.jnbt.Tag)}</li>
* <li>On WE versions post LinBus introduction: {@code fromNative(org.enginehub.linbus.tree.LinTag)}</li>
* <li>On FAWE versions post LinBus introduction: {@code fromNativeLin(org.enginehub.linbus.tree.LinTag)}</li>
* </ul>
*
* @param adapterClass The bukkit adapter implementation class
* @param linTagClass The lin-bus {@code Tag} class, if existing - otherwise {@code null}
* @param jnbtTagClass The jnbt {@code Tag} class, if lin-bus was not found in classpath - otherwise {@code null}
* @return the method.
* @throws IllegalAccessException shouldn't happen as private lookup is used.
* @throws NoSuchMethodException if the method couldn't be found.
*/
private static MethodHandle findPaperweightAdapterFromNativeMethodHandle(
Class<?> adapterClass, Class<?> linTagClass, Class<?> jnbtTagClass
) throws IllegalAccessException, NoSuchMethodException {
final MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(adapterClass, LOOKUP);
if (jnbtTagClass != null) {
// usage of JNBT = identical method signatures for WE and FAWE
return lookup.findVirtual(adapterClass, "fromNative", MethodType.methodType(Object.class, jnbtTagClass));
}
try {
// FAWE
return lookup.findVirtual(adapterClass, "fromNativeLin", MethodType.methodType(Object.class, linTagClass));
} catch (NoSuchMethodException e) {
// WE
return lookup.findVirtual(adapterClass, "fromNative", MethodType.methodType(Object.class, linTagClass));
}
}
private static MethodHandle findCraftBlockEntityStateLoadDataMethodHandle(Class<?> craftBlockEntityStateClass) throws
NoSuchMethodException, IllegalAccessException, ClassNotFoundException {
for (final Method method : craftBlockEntityStateClass.getMethods()) {
if (method.getName().equals("loadData") && method.getParameterCount() == 1) {
return LOOKUP.unreflect(method);
}
}
throw new NoSuchMethodException("Couldn't find #loadData(CompoundTag) in " + craftBlockEntityStateClass.getName());
}
private static MethodHandle findCraftBlockEntityStateUpdateMethodHandle(Class<?> craftBlockEntityStateClass) throws
NoSuchMethodException, IllegalAccessException, ClassNotFoundException {
for (final Method method : craftBlockEntityStateClass.getMethods()) {
if (method.getReturnType().equals(Boolean.TYPE) && method.getParameterCount() == 2 &&
method.getParameterTypes()[0] == Boolean.TYPE && method.getParameterTypes()[1] == Boolean.TYPE) {
return LOOKUP.unreflect(method);
}
}
throw new NoSuchMethodException("Couldn't find method for #update(boolean, boolean) in " + craftBlockEntityStateClass.getName());
}
}

View File

@@ -177,8 +177,14 @@ public class Grant extends Command {
commands.addAll(TabCompletions.completePlayers(player, args[0], Collections.emptyList()));
}
return commands;
} else if (args.length == 2) {
final String subcommand = args[0].toLowerCase();
if ((subcommand.equals("add") && player.hasPermission(Permission.PERMISSION_GRANT_ADD)) ||
(subcommand.equals("check") && player.hasPermission(Permission.PERMISSION_GRANT_CHECK))) {
return TabCompletions.completePlayers(player, args[1], Collections.emptyList());
}
}
return TabCompletions.completePlayers(player, String.join(",", args).trim(), Collections.emptyList());
return Collections.emptyList();
}
}

View File

@@ -283,32 +283,41 @@ public class MainCommand extends Command {
}
}
private CompletableFuture<Optional<CommandExecutionData>> preparePlotArgument(@Nullable Plot newPlot,
@Nonnull CommandExecutionData data,
@Nullable PlotArea area) {
if (newPlot != null && (data.player() instanceof ConsolePlayer
|| (area != null && area.equals(newPlot.getArea()))
|| data.player().hasPermission(Permission.PERMISSION_ADMIN)
|| data.player().hasPermission(Permission.PERMISSION_ADMIN_AREA_SUDO))
&& !newPlot.isDenied(data.player().getUUID())) {
return fetchPlotCenterLocation(newPlot)
.thenApply(newLoc -> {
if (!data.player().canTeleport(newLoc)) {
data.player().sendMessage(TranslatableCaption.of("border.denied"));
return Optional.empty();
}
// Save meta
var originalCommandMeta = setCommandScope(data.player(), new TemporaryCommandMeta(newLoc, newPlot));
return Optional.of(new CommandExecutionData(
data.player(),
Arrays.copyOfRange(data.args(), 1, data.args().length), // Trimmed command
data.confirm(),
data.whenDone(),
originalCommandMeta
));
});
private CompletableFuture<Optional<CommandExecutionData>> preparePlotArgument(
@Nullable Plot newPlot,
@Nonnull CommandExecutionData data,
@Nullable PlotArea area
) {
if (newPlot == null) {
return CompletableFuture.completedFuture(Optional.of(data));
}
return CompletableFuture.completedFuture(Optional.of(data));
final PlotPlayer<?> player = data.player();
final boolean isAdmin = player instanceof ConsolePlayer || player.hasPermission(Permission.PERMISSION_ADMIN);
final boolean isDenied = newPlot.isDenied(player.getUUID());
if (!isAdmin) {
if (isDenied) {
throw new CommandException(TranslatableCaption.of("deny.cannot_interact"));
}
if (area != null && area.equals(newPlot.getArea()) && !player.hasPermission(Permission.PERMISSION_ADMIN_AREA_SUDO)) {
return CompletableFuture.completedFuture(Optional.of(data));
}
}
return fetchPlotCenterLocation(newPlot)
.thenApply(newLoc -> {
if (!player.canTeleport(newLoc)) {
player.sendMessage(TranslatableCaption.of("border.denied"));
return Optional.empty();
}
// Save meta
var originalCommandMeta = setCommandScope(player, new TemporaryCommandMeta(newLoc, newPlot));
return Optional.of(new CommandExecutionData(
player,
Arrays.copyOfRange(data.args(), 1, data.args().length), // Trimmed command
data.confirm(),
data.whenDone(),
originalCommandMeta
));
});
}
private Optional<CommandExecutionData> prepareFlagArgument(@Nonnull CommandExecutionData data, @Nonnull PlotArea area) {

View File

@@ -352,7 +352,7 @@ public class Plot {
* @param arg The search term
* @param message If a message should be sent to the player if a plot cannot be found
* @return The plot if only 1 result is found, or null
* @since TODO
* @since 7.5.5
*/
public static @Nullable Plot getPlotFromStringUnchecked(
final @Nullable PlotPlayer<?> player,
@@ -434,7 +434,7 @@ public class Plot {
* @param string plot id/area + id
* @param player {@link PlotPlayer} player to notify if plot is invalid (outside bounds)
* @return New or existing plot object
* @since TODO
* @since 7.5.5
*/
public static @Nullable Plot fromString(
final @Nullable PlotArea defaultArea,
@@ -457,7 +457,7 @@ public class Plot {
* @param defaultArea if no area is specified
* @param string plot id/area + id
* @return New or existing plot object
* @since TODO
* @since 7.5.5
*/
public static @Nullable Plot fromStringUnchecked(final @Nullable PlotArea defaultArea, final @NonNull String string) {
final String[] split = string.split("[;,]");

View File

@@ -371,8 +371,7 @@ public final class PlotModificationManager {
manager.createRoadSouthEast(current, queue);
}
}
}
if (current.isMerged(Direction.SOUTH)) {
} else if (current.isMerged(Direction.SOUTH)) {
manager.createRoadSouth(current, queue);
}
}

View File

@@ -67,7 +67,7 @@ public class PlotTitle {
* Provides a string representation of this plot title value (used in placeholders).
*
* @return the plot title representation in the format {@code "<title>" "<subtitle>"}
* @since TODO
* @since 7.5.5
*/
@Override
public String toString() {

View File

@@ -49,10 +49,12 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
/**
* Registry that contains {@link Placeholder placeholders}
@@ -127,6 +129,22 @@ public final class PlaceholderRegistry {
}
return legacyComponent(TranslatableCaption.of("info.unknown"), player);
});
this.createPlaceholder("currentplot_owners", (player, plot) -> {
if (plot.getFlag(ServerPlotFlag.class)) {
return legacyComponent(TranslatableCaption.of("info.server"), player);
}
final Set<UUID> plotOwners = plot.getOwners();
if (plotOwners.isEmpty()) {
return legacyComponent(TranslatableCaption.of("generic.generic_unowned"), player);
}
return plotOwners.stream().map(PlotSquared.platform().playerManager()::getUsernameCaption).map(f -> {
try {
return f.get(Settings.UUID.BLOCKING_TIMEOUT, TimeUnit.MILLISECONDS).getComponent(player);
} catch (final Exception ignored) {
return legacyComponent(TranslatableCaption.of("info.unknown"), player);
}
}).collect(Collectors.joining(", "));
});
this.createPlaceholder("currentplot_members", (player, plot) -> {
if (plot.getMembers().isEmpty() && plot.getTrusted().isEmpty()) {
return legacyComponent(TranslatableCaption.of("info.none"), player);

View File

@@ -415,6 +415,7 @@
"deny.denied_added": "<prefix><dark_aqua>You successfully denied the player from this plot.</dark_aqua>",
"deny.no_enter": "<prefix><red>You are denied from the plot <red><gold><plot></gold><red> and therefore not allowed to enter.</red>",
"deny.you_got_denied": "<prefix><red>You are denied from the plot you were previously on, and got teleported to spawn.</red>",
"deny.cannot_interact": "<prefix><red>You are denied from the plot <red><gold><plot></gold><red> and therefore cannot interact with it.</red>",
"deny.cant_remove_owner": "<prefix><red>You can't remove the plot owner.</red>",
"kick.player_not_in_plot": "<prefix><red>The player <gray><player></gray> is not on this plot.</red>",
"kick.cannot_kick_player": "<prefix><red>You cannot kick the player <gray><player></gray>.</red>",

View File

@@ -20,7 +20,7 @@ plugins {
}
group = "com.intellectualsites.plotsquared"
version = "7.5.5-SNAPSHOT"
version = "7.5.7-SNAPSHOT"
if (!File("$rootDir/.git").exists()) {
logger.lifecycle("""
@@ -67,8 +67,8 @@ subprojects {
dependencies {
// Tests
testImplementation("org.junit.jupiter:junit-jupiter:5.13.3")
testRuntimeOnly("org.junit.platform:junit-platform-launcher:1.13.3")
testImplementation("org.junit.jupiter:junit-jupiter:5.13.4")
testRuntimeOnly("org.junit.platform:junit-platform-launcher:1.13.4")
}
plugins.withId("java") {

View File

@@ -2,21 +2,21 @@
# Platform expectations
paper = "1.20.4-R0.1-SNAPSHOT"
guice = "7.0.0"
spotbugs = "4.9.3"
spotbugs = "4.9.4"
checkerqual = "3.49.5"
gson = "2.10"
guava = "31.1-jre"
snakeyaml = "2.0"
adventure = "4.23.0"
adventure-bukkit = "4.4.0"
adventure = "4.24.0"
adventure-bukkit = "4.4.1"
log4j = "2.19.0"
# Plugins
worldedit = "7.2.20"
fawe = "2.13.0"
fawe = "2.13.1"
placeholderapi = "2.11.6"
luckperms = "5.5"
essentialsx = "2.21.1"
essentialsx = "2.21.2"
mvdwapi = "3.1.1"
# Third party
@@ -33,9 +33,9 @@ vault = "1.7.1"
serverlib = "2.3.7"
# Gradle plugins
shadow = "8.3.8"
shadow = "8.3.9"
grgit = "4.1.1"
spotless = "7.2.0"
spotless = "7.2.1"
publish = "0.34.0"
runPaper = "2.3.1"