mirror of
https://github.com/IntellectualSites/PlotSquared.git
synced 2025-10-24 15:13:44 +02:00
Compare commits
2 Commits
fix/genera
...
copilot/fi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4e8f775772 | ||
|
|
31d27861e3 |
2
.github/workflows/build-pr.yml
vendored
2
.github/workflows/build-pr.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v5
|
||||
- name: Validate Gradle Wrapper
|
||||
uses: gradle/actions/wrapper-validation@v5
|
||||
uses: gradle/actions/wrapper-validation@v4
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
|
||||
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v5
|
||||
- name: Validate Gradle Wrapper
|
||||
uses: gradle/actions/wrapper-validation@v5
|
||||
uses: gradle/actions/wrapper-validation@v4
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
|
||||
6
.github/workflows/codeql.yml
vendored
6
.github/workflows/codeql.yml
vendored
@@ -27,10 +27,10 @@ jobs:
|
||||
distribution: temurin
|
||||
java-version: 21
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v4
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v4
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v4
|
||||
uses: github/codeql-action/analyze@v3
|
||||
|
||||
@@ -80,7 +80,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.squirrelid", "com.plotsquared.squirrelid")
|
||||
relocate("org.enginehub", "com.plotsquared.squirrelid")
|
||||
relocate("org.khelekore.prtree", "com.plotsquared.prtree")
|
||||
relocate("com.google.inject", "com.plotsquared.google")
|
||||
relocate("org.aopalliance", "com.plotsquared.core.aopalliance")
|
||||
|
||||
@@ -46,7 +46,6 @@ import com.plotsquared.bukkit.listener.WorldEvents;
|
||||
import com.plotsquared.bukkit.placeholder.PAPIPlaceholders;
|
||||
import com.plotsquared.bukkit.placeholder.PlaceholderFormatter;
|
||||
import com.plotsquared.bukkit.player.BukkitPlayerManager;
|
||||
import com.plotsquared.bukkit.schematic.StateWrapper;
|
||||
import com.plotsquared.bukkit.util.BukkitUtil;
|
||||
import com.plotsquared.bukkit.util.BukkitWorld;
|
||||
import com.plotsquared.bukkit.util.SetGenCB;
|
||||
@@ -290,18 +289,6 @@ public final class BukkitPlatform extends JavaPlugin implements Listener, PlotPl
|
||||
}
|
||||
}
|
||||
|
||||
// Validate compatibility of StateWrapper with the current running server version
|
||||
// Do this always, even if it's not required, to prevent running servers which fail to restore plot backups or
|
||||
// inserting broken plot / road templates.
|
||||
try {
|
||||
var instance = StateWrapper.INSTANCE;
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("Failed to initialize required classes for restoring tile entities. " +
|
||||
"PlotSquared will disable itself to prevent possible damages.", e);
|
||||
getServer().getPluginManager().disablePlugin(this);
|
||||
return;
|
||||
}
|
||||
|
||||
// We create the injector after PlotSquared has been initialized, so that we have access
|
||||
// to generated instances and settings
|
||||
this.injector = Guice
|
||||
|
||||
@@ -90,7 +90,12 @@ public class BukkitPlotGenerator extends ChunkGenerator implements GeneratorWrap
|
||||
this.plotGenerator = generator;
|
||||
this.platformGenerator = this;
|
||||
this.populators = new ArrayList<>();
|
||||
this.populators.add(new BlockStatePopulator(this.plotGenerator));
|
||||
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.full = true;
|
||||
this.useNewGenerationMethods = PlotSquared.platform().serverVersion()[1] >= 19;
|
||||
this.biomeProvider = new BukkitPlotBiomeProvider();
|
||||
|
||||
@@ -36,7 +36,6 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
@Deprecated(since = "TODO")
|
||||
final class LegacyBlockStatePopulator extends BlockPopulator {
|
||||
|
||||
private final IndependentPlotGenerator plotGenerator;
|
||||
|
||||
@@ -160,7 +160,7 @@ public class EntityEventListener implements Listener {
|
||||
return;
|
||||
}
|
||||
}
|
||||
case "REINFORCEMENTS", "NATURAL", "MOUNT", "PATROL", "RAID", "SILVERFISH_BLOCK", "ENDER_PEARL",
|
||||
case "REINFORCEMENTS", "NATURAL", "MOUNT", "PATROL", "RAID", "SHEARED", "SILVERFISH_BLOCK", "ENDER_PEARL",
|
||||
"TRAP", "VILLAGE_DEFENSE", "VILLAGE_INVASION", "BEEHIVE", "CHUNK_GEN", "NETHER_PORTAL",
|
||||
"FROZEN", "SPELL", "DEFAULT" -> {
|
||||
if (!area.isMobSpawning()) {
|
||||
|
||||
@@ -202,7 +202,7 @@ public class PaperListener implements Listener {
|
||||
return;
|
||||
}
|
||||
}
|
||||
case "REINFORCEMENTS", "NATURAL", "MOUNT", "PATROL", "RAID", "SILVERFISH_BLOCK", "ENDER_PEARL", "TRAP", "VILLAGE_DEFENSE", "VILLAGE_INVASION", "BEEHIVE", "CHUNK_GEN" -> {
|
||||
case "REINFORCEMENTS", "NATURAL", "MOUNT", "PATROL", "RAID", "SHEARED", "SILVERFISH_BLOCK", "ENDER_PEARL", "TRAP", "VILLAGE_DEFENSE", "VILLAGE_INVASION", "BEEHIVE", "CHUNK_GEN" -> {
|
||||
if (!area.isMobSpawning()) {
|
||||
event.setShouldAbortSpawn(true);
|
||||
event.setCancelled(true);
|
||||
|
||||
@@ -52,7 +52,6 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class BukkitQueueCoordinator extends BasicQueueCoordinator {
|
||||
@@ -211,13 +210,8 @@ public class BukkitQueueCoordinator extends BasicQueueCoordinator {
|
||||
BaseBlock block = getWorld().getBlock(blockVector3).toBaseBlock(tag);
|
||||
getWorld().setBlock(blockVector3, block, getSideEffectSet(SideEffectState.NONE));
|
||||
} catch (WorldEditException ignored) {
|
||||
StateWrapper.INSTANCE.restore(
|
||||
getWorld().getName(),
|
||||
blockVector3.getX(),
|
||||
blockVector3.getY(),
|
||||
blockVector3.getZ(),
|
||||
tag
|
||||
);
|
||||
StateWrapper sw = new StateWrapper(tag);
|
||||
sw.restoreTag(getWorld().getName(), blockVector3.getX(), blockVector3.getY(), blockVector3.getZ());
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -301,7 +295,9 @@ public class BukkitQueueCoordinator extends BasicQueueCoordinator {
|
||||
existing.setBlockData(blockData, false);
|
||||
if (block.hasNbtData()) {
|
||||
CompoundTag tag = block.getNbtData();
|
||||
StateWrapper.INSTANCE.restore(existing, Objects.requireNonNull(tag));
|
||||
StateWrapper sw = new StateWrapper(tag);
|
||||
|
||||
sw.restoreTag(existing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,8 +34,6 @@ import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.generator.LimitedRegion;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Wraps a {@link LimitedRegion} inside a {@link com.plotsquared.core.queue.QueueCoordinator} so it can be written to.
|
||||
*
|
||||
@@ -46,6 +44,7 @@ public class LimitedRegionWrapperQueue extends DelegateQueueCoordinator {
|
||||
private static final Logger LOGGER = LogManager.getLogger("PlotSquared/" + LimitedRegionWrapperQueue.class.getSimpleName());
|
||||
|
||||
private final LimitedRegion limitedRegion;
|
||||
private boolean useOtherRestoreTagMethod = false;
|
||||
|
||||
/**
|
||||
* @since 6.9.0
|
||||
@@ -65,11 +64,20 @@ public class LimitedRegionWrapperQueue extends DelegateQueueCoordinator {
|
||||
boolean result = setBlock(x, y, z, id.toImmutableState());
|
||||
if (result && id.hasNbtData()) {
|
||||
CompoundTag tag = id.getNbtData();
|
||||
StateWrapper sw = new StateWrapper(tag);
|
||||
try {
|
||||
StateWrapper.INSTANCE.restore(limitedRegion.getBlockState(x, y, z).getBlock(), Objects.requireNonNull(tag));
|
||||
if (useOtherRestoreTagMethod && getWorld() != null) {
|
||||
sw.restoreTag(getWorld().getName(), x, y, z);
|
||||
} else {
|
||||
sw.restoreTag(limitedRegion.getBlockState(x, y, z).getBlock());
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
LOGGER.error("Error attempting to populate tile entity into the world at location {},{},{}", x, y, z, e);
|
||||
return false;
|
||||
} catch (IllegalStateException e) {
|
||||
useOtherRestoreTagMethod = true;
|
||||
LOGGER.warn("IllegalStateException attempting to populate tile entity into the world at location {},{},{}. " +
|
||||
"Possibly on <=1.17.1, switching to secondary method.", x, y, z, e);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@@ -105,8 +113,9 @@ public class LimitedRegionWrapperQueue extends DelegateQueueCoordinator {
|
||||
|
||||
@Override
|
||||
public boolean setTile(final int x, final int y, final int z, @NonNull final CompoundTag tag) {
|
||||
StateWrapper sw = new StateWrapper(tag);
|
||||
try {
|
||||
return StateWrapper.INSTANCE.restore(limitedRegion.getBlockState(x, y, z).getBlock(), tag);
|
||||
return sw.restoreTag(limitedRegion.getBlockState(x, y, z).getBlock());
|
||||
} catch (IllegalArgumentException e) {
|
||||
LOGGER.error("Error attempting to populate tile entity into the world at location {},{},{}", x, y, z, e);
|
||||
return false;
|
||||
|
||||
@@ -27,8 +27,6 @@ import com.plotsquared.core.util.WorldUtil;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Schematic Handler.
|
||||
*/
|
||||
@@ -41,8 +39,8 @@ public class BukkitSchematicHandler extends SchematicHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean restoreTile(QueueCoordinator queue, CompoundTag tag, int x, int y, int z) {
|
||||
return StateWrapper.INSTANCE.restore(Objects.requireNonNull(queue.getWorld()).getName(), x, y, z, tag);
|
||||
public boolean restoreTile(QueueCoordinator queue, CompoundTag ct, int x, int y, int z) {
|
||||
return new StateWrapper(ct).restoreTag(queue.getWorld().getName(), x, y, z);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,35 +18,332 @@
|
||||
*/
|
||||
package com.plotsquared.bukkit.schematic;
|
||||
|
||||
import com.destroystokyo.paper.profile.PlayerProfile;
|
||||
import com.destroystokyo.paper.profile.ProfileProperty;
|
||||
import com.plotsquared.bukkit.util.BukkitUtil;
|
||||
import com.sk89q.jnbt.ByteTag;
|
||||
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 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.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.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
@ApiStatus.Internal
|
||||
public sealed interface StateWrapper permits StateWrapperSpigot {
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
StateWrapper INSTANCE = Factory.createStateWrapper();
|
||||
public class StateWrapper {
|
||||
|
||||
boolean restore(final @NonNull Block block, final @NonNull CompoundTag data);
|
||||
public CompoundTag tag;
|
||||
|
||||
default boolean restore(final String worldName, final int x, final int y, final int z, final CompoundTag data) {
|
||||
final World world = BukkitUtil.getWorld(worldName);
|
||||
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.
|
||||
*
|
||||
* @param worldName World name
|
||||
* @param x x position
|
||||
* @param y y position
|
||||
* @param z z position
|
||||
* @return true if successful
|
||||
*/
|
||||
public boolean restoreTag(String worldName, int x, int y, int z) {
|
||||
World world = BukkitUtil.getWorld(worldName);
|
||||
if (world == null) {
|
||||
return false;
|
||||
}
|
||||
return this.restore(world.getBlockAt(x, y, z), data);
|
||||
return restoreTag(world.getBlockAt(x, y, z));
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
final class Factory {
|
||||
|
||||
private static StateWrapper createStateWrapper() {
|
||||
return new StateWrapperSpigot();
|
||||
/**
|
||||
* Restore the TileEntity data to the given block
|
||||
*
|
||||
* @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) {
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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;
|
||||
|
||||
}
|
||||
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;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
String tileid = this.tag.getString("id").toLowerCase();
|
||||
if (tileid.startsWith("minecraft:")) {
|
||||
tileid = tileid.replace("minecraft:", "");
|
||||
}
|
||||
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));
|
||||
}
|
||||
}
|
||||
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));
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,266 +0,0 @@
|
||||
/*
|
||||
* 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.plotsquared.core.util.ReflectionUtils;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
||||
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
||||
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
||||
import com.sk89q.worldedit.extension.platform.NoCapablePlatformException;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.block.Sign;
|
||||
import org.bukkit.block.sign.Side;
|
||||
import org.bukkit.block.sign.SignSide;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
|
||||
final class StateWrapperSpigot implements StateWrapper {
|
||||
|
||||
private static final boolean FORCE_UPDATE_STATE = true;
|
||||
private static final boolean UPDATE_TRIGGER_PHYSICS = false;
|
||||
private static final String CRAFTBUKKIT_PACKAGE = Bukkit.getServer().getClass().getPackageName();
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger("PlotSquared/" + StateWrapperSpigot.class.getSimpleName());
|
||||
private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
|
||||
|
||||
private static BukkitImplAdapter ADAPTER = null;
|
||||
private static Class<?> LIN_TAG_CLASS = null;
|
||||
private static Class<?> CRAFT_BLOCK_ENTITY_STATE_CLASS = null;
|
||||
private static Field CRAFT_SIGN_SIDE_SIGN_TEXT = null;
|
||||
private static Field CRAFT_SIGN_SIDE_LINES = 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 CRAFT_BLOCK_ENTITY_STATE_GET_SNAPSHOT = null;
|
||||
private static MethodHandle SIGN_BLOCK_ENTITY_SET_TEXT = null;
|
||||
private static MethodHandle DECODER_PARSE = null;
|
||||
private static MethodHandle DATA_RESULT_RESULT = null;
|
||||
private static MethodHandle TO_LIN_TAG = null;
|
||||
|
||||
private static Object SIGN_TEXT_DIRECT_CODEC = null;
|
||||
private static Object NBT_OPS_INSTANCE = null;
|
||||
|
||||
public StateWrapperSpigot() {
|
||||
try {
|
||||
ReflectionUtils.RefClass worldEditPluginRefClass = ReflectionUtils.getRefClass(WorldEditPlugin.class);
|
||||
WorldEditPlugin worldEditPlugin = (WorldEditPlugin) worldEditPluginRefClass
|
||||
.getMethod("getInstance")
|
||||
.of(null)
|
||||
.call();
|
||||
ADAPTER = (BukkitImplAdapter) worldEditPluginRefClass
|
||||
.getMethod("getBukkitImplAdapter")
|
||||
.of(worldEditPlugin)
|
||||
.call();
|
||||
LIN_TAG_CLASS = Class.forName("org.enginehub.linbus.tree.LinTag"); // provided WE / FAWE version is too old
|
||||
PAPERWEIGHT_ADAPTER_FROM_NATIVE = findPaperweightAdapterFromNativeMethodHandle(ADAPTER.getClass());
|
||||
TO_LIN_TAG = findToLinTagMethodHandle();
|
||||
} catch (NoSuchMethodException | ClassNotFoundException | IllegalAccessException | NoCapablePlatformException e) {
|
||||
throw new RuntimeException("Failed to access required WorldEdit classes or methods", e);
|
||||
}
|
||||
try {
|
||||
final Class<?> SIGN_TEXT_CLASS = Class.forName("net.minecraft.world.level.block.entity.SignText");
|
||||
final Class<?> CRAFT_SIGN_SIDE_CLASS = Class.forName(CRAFTBUKKIT_PACKAGE + ".block.sign.CraftSignSide");
|
||||
CRAFT_SIGN_SIDE_SIGN_TEXT = CRAFT_SIGN_SIDE_CLASS.getDeclaredField("signText");
|
||||
CRAFT_SIGN_SIDE_SIGN_TEXT.setAccessible(true);
|
||||
CRAFT_SIGN_SIDE_LINES = CRAFT_SIGN_SIDE_CLASS.getDeclaredField("lines");
|
||||
CRAFT_SIGN_SIDE_LINES.setAccessible(true);
|
||||
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);
|
||||
CRAFT_BLOCK_ENTITY_STATE_GET_SNAPSHOT = findCraftBlockEntityStateSnapshotMethodHandle(CRAFT_BLOCK_ENTITY_STATE_CLASS);
|
||||
SIGN_BLOCK_ENTITY_SET_TEXT = findSignBlockEntitySetTextMethodHandle(
|
||||
Class.forName(Refraction.pickName(
|
||||
"net.minecraft.world.level.block.entity.SignBlockEntity",
|
||||
"net.minecraft.world.level.block.entity.TileEntitySign"
|
||||
)),
|
||||
SIGN_TEXT_CLASS
|
||||
);
|
||||
final Class<?> CODEC_CLASS = Class.forName("com.mojang.serialization.Codec");
|
||||
final Class<?> DECODER_CLASS = Class.forName("com.mojang.serialization.Decoder");
|
||||
final Class<?> DATA_RESULT_CLASS = Class.forName("com.mojang.serialization.DataResult");
|
||||
final Class<?> DYNAMIC_OPS_CLASS = Class.forName("com.mojang.serialization.DynamicOps");
|
||||
final Class<?> NBT_OPS_CLASS = Class.forName(Refraction.pickName(
|
||||
"net.minecraft.nbt.NbtOps",
|
||||
"net.minecraft.nbt.DynamicOpsNBT"
|
||||
));
|
||||
SIGN_TEXT_DIRECT_CODEC = Arrays.stream(SIGN_TEXT_CLASS.getFields())
|
||||
.filter(field -> Modifier.isStatic(field.getModifiers()) && Modifier.isPublic(field.getModifiers()))
|
||||
.filter(field -> field.getType() == CODEC_CLASS)
|
||||
.findFirst().orElseThrow().get(null);
|
||||
DECODER_PARSE = LOOKUP.findVirtual(
|
||||
DECODER_CLASS, "parse", MethodType.methodType(
|
||||
DATA_RESULT_CLASS, DYNAMIC_OPS_CLASS, Object.class
|
||||
)
|
||||
);
|
||||
NBT_OPS_INSTANCE = Arrays.stream(NBT_OPS_CLASS.getFields())
|
||||
.filter(field -> Modifier.isStatic(field.getModifiers()) && Modifier.isPublic(field.getModifiers()))
|
||||
.filter(field -> field.getType() == NBT_OPS_CLASS)
|
||||
.findFirst().orElseThrow().get(null);
|
||||
DATA_RESULT_RESULT = LOOKUP.findVirtual(
|
||||
DATA_RESULT_CLASS, "result",
|
||||
MethodType.methodType(Optional.class)
|
||||
);
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException("Failed to initialize required native method accessors", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean restore(final @NonNull Block block, final @NonNull CompoundTag data) {
|
||||
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, TO_LIN_TAG.invoke(data));
|
||||
// load block entity data
|
||||
CRAFT_BLOCK_ENTITY_STATE_LOAD_DATA.invoke(blockState, nativeTag);
|
||||
|
||||
// signs need to be handled explicitly (at least during worldgen)
|
||||
if (blockState instanceof Sign sign) {
|
||||
if (data.getValue().get("front_text") instanceof CompoundTag textTag) {
|
||||
setSignContents(true, sign.getSide(Side.FRONT), blockState, textTag);
|
||||
}
|
||||
if (data.getValue().get("back_text") instanceof CompoundTag textTag) {
|
||||
setSignContents(false, sign.getSide(Side.BACK), blockState, textTag);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
private static void setSignContents(boolean front, SignSide side, BlockState blockState, CompoundTag data) throws Throwable {
|
||||
Object nativeTag = PAPERWEIGHT_ADAPTER_FROM_NATIVE.invoke(ADAPTER, TO_LIN_TAG.invoke(data));
|
||||
Object dataResult = DECODER_PARSE.invoke(SIGN_TEXT_DIRECT_CODEC, NBT_OPS_INSTANCE, nativeTag);
|
||||
//noinspection rawtypes
|
||||
Object signText = ((Optional) DATA_RESULT_RESULT.invoke(dataResult)).orElseThrow();
|
||||
|
||||
// set the SignText on the underlying tile entity snapshot (SignBlockEntity)
|
||||
SIGN_BLOCK_ENTITY_SET_TEXT.invoke(CRAFT_BLOCK_ENTITY_STATE_GET_SNAPSHOT.invoke(blockState), signText, front);
|
||||
// and update the SignText field on the CraftSignSide - changes are otherwise not reflected
|
||||
CRAFT_SIGN_SIDE_SIGN_TEXT.set(side, signText);
|
||||
|
||||
// reset cached lines to null, so it can be re-retrieved from SignText (for API access etc.)
|
||||
CRAFT_SIGN_SIDE_LINES.set(side, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @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() throws ClassNotFoundException,
|
||||
NoSuchMethodException, IllegalAccessException {
|
||||
return LOOKUP.findVirtual(
|
||||
Class.forName("org.enginehub.linbus.tree.ToLinTag"),
|
||||
"toLinTag",
|
||||
MethodType.methodType(LIN_TAG_CLASS)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 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
|
||||
* @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) throws
|
||||
IllegalAccessException, NoSuchMethodException {
|
||||
final MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(adapterClass, LOOKUP);
|
||||
try {
|
||||
// FAWE
|
||||
return lookup.findVirtual(adapterClass, "fromNativeLin", MethodType.methodType(Object.class, LIN_TAG_CLASS));
|
||||
} catch (NoSuchMethodException e) {
|
||||
// WE
|
||||
return lookup.findVirtual(adapterClass, "fromNative", MethodType.methodType(Object.class, LIN_TAG_CLASS));
|
||||
}
|
||||
}
|
||||
|
||||
private static MethodHandle findCraftBlockEntityStateLoadDataMethodHandle(Class<?> craftBlockEntityStateClass) throws
|
||||
NoSuchMethodException, IllegalAccessException {
|
||||
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 {
|
||||
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());
|
||||
}
|
||||
|
||||
private static MethodHandle findCraftBlockEntityStateSnapshotMethodHandle(Class<?> craftBlockEntityStateClass) throws
|
||||
IllegalAccessException, NoSuchMethodException {
|
||||
// doesn't seem to be obfuscated, but protected
|
||||
final MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(craftBlockEntityStateClass, LOOKUP);
|
||||
return lookup.unreflect(craftBlockEntityStateClass.getDeclaredMethod("getSnapshot"));
|
||||
}
|
||||
|
||||
private static MethodHandle findSignBlockEntitySetTextMethodHandle(Class<?> signBlockEntity, Class<?> signText) throws
|
||||
NoSuchMethodException, IllegalAccessException {
|
||||
for (final Method method : signBlockEntity.getMethods()) {
|
||||
if (method.getReturnType() == Boolean.TYPE && method.getParameterCount() == 2
|
||||
&& method.getParameterTypes()[0] == signText && method.getParameterTypes()[1] == Boolean.TYPE) {
|
||||
return LOOKUP.unreflect(method);
|
||||
}
|
||||
}
|
||||
throw new NoSuchMethodException("Couldn't lookup SignBlockEntity#setText(SignText, boolean) boolean");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -90,7 +90,7 @@ public class DebugRoadRegen extends SubCommand {
|
||||
}
|
||||
|
||||
public boolean regenPlot(PlotPlayer<?> player) {
|
||||
PlotArea area = player.getContextualPlotArea();
|
||||
PlotArea area = player.getCurrentPlot().getArea();
|
||||
if (area == null) {
|
||||
player.sendMessage(TranslatableCaption.of("errors.not_in_plot_world"));
|
||||
return false;
|
||||
@@ -145,10 +145,9 @@ public class DebugRoadRegen extends SubCommand {
|
||||
return false;
|
||||
}
|
||||
|
||||
PlotArea area = player.getContextualPlotArea();
|
||||
PlotArea area = player.getCurrentPlot().getArea();
|
||||
if (area == null) {
|
||||
player.sendMessage(TranslatableCaption.of("errors.not_in_plot_world"));
|
||||
return false;
|
||||
}
|
||||
Plot plot = player.getCurrentPlot();
|
||||
PlotManager manager = area.getPlotManager();
|
||||
|
||||
@@ -150,8 +150,9 @@ public class ListCmd extends SubCommand {
|
||||
page = 0;
|
||||
}
|
||||
|
||||
PlotArea area = player.getContextualPlotArea();
|
||||
String world = area != null ? area.getWorldName() : "";
|
||||
Plot currentPlot = player.getCurrentPlot();
|
||||
String world = currentPlot != null ? currentPlot.getWorldName() : null;
|
||||
PlotArea area = currentPlot != null ? currentPlot.getArea() : null;
|
||||
String arg = args[0].toLowerCase();
|
||||
final boolean[] sort = new boolean[]{true};
|
||||
|
||||
@@ -226,13 +227,17 @@ public class ListCmd extends SubCommand {
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (!player.hasPermission("plots.list.world." + world)) {
|
||||
if (world != null && !player.hasPermission("plots.list.world." + world)) {
|
||||
player.sendMessage(
|
||||
TranslatableCaption.of("permission.no_permission"),
|
||||
TagResolver.resolver("node", Tag.inserting(Component.text("plots.list.world." + world)))
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (world == null) {
|
||||
player.sendMessage(TranslatableCaption.of("errors.not_in_plot_world"));
|
||||
return false;
|
||||
}
|
||||
plotConsumer.accept(PlotQuery.newQuery().inWorld(world));
|
||||
}
|
||||
case "expired" -> {
|
||||
@@ -257,7 +262,7 @@ public class ListCmd extends SubCommand {
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (!player.hasPermission("plots.list.world." + world)) {
|
||||
if (world != null && !player.hasPermission("plots.list.world." + world)) {
|
||||
player.sendMessage(
|
||||
TranslatableCaption.of("permission.no_permission"),
|
||||
TagResolver.resolver("node", Tag.inserting(Component.text("plots.list.world." + world)))
|
||||
|
||||
@@ -68,6 +68,11 @@ public class Load extends SubCommand {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(final PlotPlayer<?> player, final String[] args) {
|
||||
final String world = player.getCurrentPlot().getWorldName();
|
||||
if (!this.plotAreaManager.hasPlotArea(world)) {
|
||||
player.sendMessage(TranslatableCaption.of("errors.not_in_plot_world"));
|
||||
return false;
|
||||
}
|
||||
final Plot plot = player.getCurrentPlot();
|
||||
if (plot == null) {
|
||||
player.sendMessage(TranslatableCaption.of("errors.not_in_plot"));
|
||||
|
||||
@@ -78,7 +78,7 @@ public class Set extends SubCommand {
|
||||
|
||||
@Override
|
||||
public boolean set(PlotPlayer<?> player, final Plot plot, String value) {
|
||||
final PlotArea plotArea = player.getContextualPlotArea();
|
||||
final PlotArea plotArea = player.getCurrentPlot().getArea();
|
||||
if (plotArea == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -290,7 +290,8 @@ public abstract class PlotPlayer<P> implements CommandCaller, OfflinePlotPlayer,
|
||||
*
|
||||
* @return the plot the player is standing on or null if standing on a road or not in a {@link PlotArea}
|
||||
*/
|
||||
public @Nullable Plot getCurrentPlot() {
|
||||
@Nullable
|
||||
public Plot getCurrentPlot() {
|
||||
try (final MetaDataAccess<Plot> lastPlotAccess =
|
||||
this.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_LAST_PLOT)) {
|
||||
if (lastPlotAccess.get().orElse(null) == null && !Settings.Enabled_Components.EVENTS) {
|
||||
@@ -319,7 +320,7 @@ public abstract class PlotPlayer<P> implements CommandCaller, OfflinePlotPlayer,
|
||||
*/
|
||||
public int getPlotCount() {
|
||||
if (!Settings.Limit.GLOBAL) {
|
||||
return getPlotCount(getContextualWorldName());
|
||||
return getPlotCount(getCurrentPlot().getWorldName());
|
||||
}
|
||||
final AtomicInteger count = new AtomicInteger(0);
|
||||
final UUID uuid = getUUID();
|
||||
@@ -339,7 +340,7 @@ public abstract class PlotPlayer<P> implements CommandCaller, OfflinePlotPlayer,
|
||||
|
||||
public int getClusterCount() {
|
||||
if (!Settings.Limit.GLOBAL) {
|
||||
return getClusterCount(getContextualWorldName());
|
||||
return getClusterCount(getCurrentPlot().getWorldName());
|
||||
}
|
||||
final AtomicInteger count = new AtomicInteger(0);
|
||||
this.plotAreaManager.forEachPlotArea(value -> {
|
||||
@@ -352,34 +353,6 @@ public abstract class PlotPlayer<P> implements CommandCaller, OfflinePlotPlayer,
|
||||
return count.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the world name at the player's contextual position}
|
||||
* The contextual position can be affected when using a command with
|
||||
* an explicit plot override, e.g., `/plot <id> info`.
|
||||
*/
|
||||
private @NonNull String getContextualWorldName() {
|
||||
Plot current = getCurrentPlot();
|
||||
if (current != null) {
|
||||
return current.getWorldName();
|
||||
}
|
||||
return getLocation().getWorldName();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the plot area at the player's contextual position}
|
||||
* The contextual position can be affected when using a command with
|
||||
* an explicit plot override, e.g., `/plot <id> info`.
|
||||
*
|
||||
* @since TODO
|
||||
*/
|
||||
public @Nullable PlotArea getContextualPlotArea() {
|
||||
Plot current = getCurrentPlot();
|
||||
if (current != null) {
|
||||
return current.getArea();
|
||||
}
|
||||
return getLocation().getPlotArea();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of plots this player owns in the world.
|
||||
*
|
||||
@@ -710,87 +683,86 @@ public abstract class PlotPlayer<P> implements CommandCaller, OfflinePlotPlayer,
|
||||
|
||||
public void populatePersistentMetaMap() {
|
||||
if (Settings.Enabled_Components.PERSISTENT_META) {
|
||||
DBFunc.getPersistentMeta(
|
||||
getUUID(), new RunnableVal<>() {
|
||||
@Override
|
||||
public void run(Map<String, byte[]> value) {
|
||||
try {
|
||||
PlotPlayer.this.metaMap = value;
|
||||
if (value.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
DBFunc.getPersistentMeta(getUUID(), new RunnableVal<>() {
|
||||
@Override
|
||||
public void run(Map<String, byte[]> value) {
|
||||
try {
|
||||
PlotPlayer.this.metaMap = value;
|
||||
if (value.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (PlotPlayer.this.getAttribute("debug")) {
|
||||
debugModeEnabled.add(PlotPlayer.this);
|
||||
}
|
||||
if (PlotPlayer.this.getAttribute("debug")) {
|
||||
debugModeEnabled.add(PlotPlayer.this);
|
||||
}
|
||||
|
||||
if (!Settings.Teleport.ON_LOGIN) {
|
||||
return;
|
||||
}
|
||||
PlotAreaManager manager = PlotPlayer.this.plotAreaManager;
|
||||
if (!Settings.Teleport.ON_LOGIN) {
|
||||
return;
|
||||
}
|
||||
PlotAreaManager manager = PlotPlayer.this.plotAreaManager;
|
||||
|
||||
if (!(manager instanceof SinglePlotAreaManager)) {
|
||||
return;
|
||||
}
|
||||
PlotArea area = ((SinglePlotAreaManager) manager).getArea();
|
||||
boolean V2 = false;
|
||||
byte[] arr = PlotPlayer.this.getPersistentMeta("quitLoc");
|
||||
if (arr == null) {
|
||||
arr = PlotPlayer.this.getPersistentMeta("quitLocV2");
|
||||
if (arr == null) {
|
||||
return;
|
||||
}
|
||||
V2 = true;
|
||||
removePersistentMeta("quitLocV2");
|
||||
} else {
|
||||
removePersistentMeta("quitLoc");
|
||||
}
|
||||
if (!(manager instanceof SinglePlotAreaManager)) {
|
||||
return;
|
||||
}
|
||||
PlotArea area = ((SinglePlotAreaManager) manager).getArea();
|
||||
boolean V2 = false;
|
||||
byte[] arr = PlotPlayer.this.getPersistentMeta("quitLoc");
|
||||
if (arr == null) {
|
||||
arr = PlotPlayer.this.getPersistentMeta("quitLocV2");
|
||||
if (arr == null) {
|
||||
return;
|
||||
}
|
||||
V2 = true;
|
||||
removePersistentMeta("quitLocV2");
|
||||
} else {
|
||||
removePersistentMeta("quitLoc");
|
||||
}
|
||||
|
||||
if (!getMeta("teleportOnLogin", true)) {
|
||||
return;
|
||||
}
|
||||
ByteBuffer quitWorld = ByteBuffer.wrap(arr);
|
||||
final int plotX = quitWorld.getShort();
|
||||
final int plotZ = quitWorld.getShort();
|
||||
PlotId id = PlotId.of(plotX, plotZ);
|
||||
int x = quitWorld.getInt();
|
||||
int y = V2 ? quitWorld.getShort() : (quitWorld.get() & 0xFF);
|
||||
int z = quitWorld.getInt();
|
||||
Plot plot = area.getOwnedPlot(id);
|
||||
if (!getMeta("teleportOnLogin", true)) {
|
||||
return;
|
||||
}
|
||||
ByteBuffer quitWorld = ByteBuffer.wrap(arr);
|
||||
final int plotX = quitWorld.getShort();
|
||||
final int plotZ = quitWorld.getShort();
|
||||
PlotId id = PlotId.of(plotX, plotZ);
|
||||
int x = quitWorld.getInt();
|
||||
int y = V2 ? quitWorld.getShort() : (quitWorld.get() & 0xFF);
|
||||
int z = quitWorld.getInt();
|
||||
Plot plot = area.getOwnedPlot(id);
|
||||
|
||||
if (plot == null) {
|
||||
return;
|
||||
}
|
||||
if (plot == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Location location = Location.at(plot.getWorldName(), x, y, z);
|
||||
if (plot.isLoaded()) {
|
||||
TaskManager.runTask(() -> {
|
||||
if (getMeta("teleportOnLogin", true)) {
|
||||
teleport(location, TeleportCause.LOGIN);
|
||||
sendMessage(
|
||||
TranslatableCaption.of("teleport.teleported_to_plot"));
|
||||
}
|
||||
});
|
||||
} else if (!PlotSquared.get().isMainThread(Thread.currentThread())) {
|
||||
if (getMeta("teleportOnLogin", true)) {
|
||||
plot.teleportPlayer(
|
||||
PlotPlayer.this,
|
||||
result -> TaskManager.runTask(() -> {
|
||||
if (getMeta("teleportOnLogin", true)) {
|
||||
if (plot.isLoaded()) {
|
||||
teleport(location, TeleportCause.LOGIN);
|
||||
sendMessage(TranslatableCaption
|
||||
.of("teleport.teleported_to_plot"));
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
final Location location = Location.at(plot.getWorldName(), x, y, z);
|
||||
if (plot.isLoaded()) {
|
||||
TaskManager.runTask(() -> {
|
||||
if (getMeta("teleportOnLogin", true)) {
|
||||
teleport(location, TeleportCause.LOGIN);
|
||||
sendMessage(
|
||||
TranslatableCaption.of("teleport.teleported_to_plot"));
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
LOGGER.error("Error populating persistent meta for player {}", PlotPlayer.this.getName(), e);
|
||||
});
|
||||
} else if (!PlotSquared.get().isMainThread(Thread.currentThread())) {
|
||||
if (getMeta("teleportOnLogin", true)) {
|
||||
plot.teleportPlayer(
|
||||
PlotPlayer.this,
|
||||
result -> TaskManager.runTask(() -> {
|
||||
if (getMeta("teleportOnLogin", true)) {
|
||||
if (plot.isLoaded()) {
|
||||
teleport(location, TeleportCause.LOGIN);
|
||||
sendMessage(TranslatableCaption
|
||||
.of("teleport.teleported_to_plot"));
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
LOGGER.error("Error populating persistent meta for player {}", PlotPlayer.this.getName(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -860,8 +832,7 @@ public abstract class PlotPlayer<P> implements CommandCaller, OfflinePlotPlayer,
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Nullable
|
||||
<T> T getPersistentMeta(final @NonNull MetaDataKey<T> key) {
|
||||
@Nullable <T> T getPersistentMeta(final @NonNull MetaDataKey<T> key) {
|
||||
final byte[] value = this.getPersistentMeta(key.toString());
|
||||
if (value == null) {
|
||||
return null;
|
||||
@@ -1031,11 +1002,9 @@ public abstract class PlotPlayer<P> implements CommandCaller, OfflinePlotPlayer,
|
||||
if (throwable != null) {
|
||||
sendMessage(
|
||||
TranslatableCaption.of("errors.error"),
|
||||
TagResolver.resolver(
|
||||
"value", Tag.inserting(
|
||||
Component.text("Failed to resolve asynchronous caption replacements")
|
||||
)
|
||||
)
|
||||
TagResolver.resolver("value", Tag.inserting(
|
||||
Component.text("Failed to resolve asynchronous caption replacements")
|
||||
))
|
||||
);
|
||||
LOGGER.error("Failed to resolve asynchronous tagresolver(s) for " + caption, throwable);
|
||||
} else {
|
||||
|
||||
@@ -541,7 +541,7 @@ public class Plot {
|
||||
*
|
||||
* @return World name
|
||||
*/
|
||||
public @NonNull String getWorldName() {
|
||||
public @Nullable String getWorldName() {
|
||||
return area.getWorldName();
|
||||
}
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ public class SinglePlot extends Plot {
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull String getWorldName() {
|
||||
public String getWorldName() {
|
||||
return getId().toUnderscoreSeparatedString();
|
||||
}
|
||||
|
||||
|
||||
@@ -65,16 +65,10 @@ subprojects {
|
||||
plugin<IdeaPlugin>()
|
||||
}
|
||||
|
||||
configurations.matching { it.name == "signatures" }.configureEach {
|
||||
attributes {
|
||||
attribute(Attribute.of("signatures-unique", String::class.java), "true")
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Tests
|
||||
testImplementation("org.junit.jupiter:junit-jupiter:6.0.0")
|
||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher:6.0.0")
|
||||
testImplementation("org.junit.jupiter:junit-jupiter:5.13.4")
|
||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher:1.13.4")
|
||||
}
|
||||
|
||||
plugins.withId("java") {
|
||||
@@ -101,15 +95,9 @@ subprojects {
|
||||
}
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
val javaComponent = components["java"] as AdhocComponentWithVariants
|
||||
configurations.findByName("shadowRuntimeElements")?.let { shadowRuntimeElements ->
|
||||
javaComponent.withVariantsFromConfiguration(shadowRuntimeElements) {
|
||||
skip()
|
||||
}
|
||||
} ?: run {
|
||||
logger.warn("Configuration 'shadowRuntimeElements' does not exist.")
|
||||
}
|
||||
val javaComponent = components["java"] as AdhocComponentWithVariants
|
||||
javaComponent.withVariantsFromConfiguration(configurations["shadowRuntimeElements"]) {
|
||||
skip()
|
||||
}
|
||||
|
||||
signing {
|
||||
|
||||
@@ -2,18 +2,18 @@
|
||||
# Platform expectations
|
||||
paper = "1.20.4-R0.1-SNAPSHOT"
|
||||
guice = "7.0.0"
|
||||
spotbugs = "4.9.8"
|
||||
checkerqual = "3.51.1"
|
||||
spotbugs = "4.9.6"
|
||||
checkerqual = "3.51.0"
|
||||
gson = "2.10"
|
||||
guava = "31.1-jre"
|
||||
snakeyaml = "2.0"
|
||||
adventure = "4.25.0"
|
||||
adventure = "4.24.0"
|
||||
adventure-bukkit = "4.4.1"
|
||||
log4j = "2.19.0"
|
||||
|
||||
# Plugins
|
||||
worldedit = "7.2.20"
|
||||
fawe = "2.14.0"
|
||||
fawe = "2.13.2"
|
||||
placeholderapi = "2.11.6"
|
||||
luckperms = "5.5"
|
||||
essentialsx = "2.21.2"
|
||||
@@ -33,11 +33,11 @@ vault = "1.7.1"
|
||||
serverlib = "2.3.7"
|
||||
|
||||
# Gradle plugins
|
||||
shadow = "9.2.2"
|
||||
shadow = "8.3.9"
|
||||
grgit = "4.1.1"
|
||||
spotless = "8.0.0"
|
||||
spotless = "7.2.1"
|
||||
publish = "0.34.0"
|
||||
runPaper = "3.0.2"
|
||||
runPaper = "2.3.1"
|
||||
|
||||
[libraries]
|
||||
# Platform expectations
|
||||
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
5
gradlew
vendored
5
gradlew
vendored
@@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015 the original authors.
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -114,6 +114,7 @@ case "$( uname )" in #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH="\\\"\\\""
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
@@ -171,6 +172,7 @@ fi
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
@@ -210,6 +212,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
|
||||
"$@"
|
||||
|
||||
|
||||
3
gradlew.bat
vendored
3
gradlew.bat
vendored
@@ -70,10 +70,11 @@ goto fail
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
|
||||
Reference in New Issue
Block a user