Compare commits

..

38 Commits

Author SHA1 Message Date
Pierre Maurice Schwang
8002d170f5 chore: split logic per platform 2025-10-21 00:05:45 +02:00
Pierre Maurice Schwang
52e8ba6ddd chore: simplify sign logic 2025-10-21 00:05:45 +02:00
Pierre Maurice Schwang
2f1fe633af fix: missing continue and license header 2025-10-21 00:05:25 +02:00
Pierre Maurice Schwang
dc30408ed0 fix: signs with component lines 2025-10-21 00:05:25 +02:00
Pierre Maurice Schwang
313ea8b266 feat: sign contents 2025-10-21 00:05:25 +02:00
Pierre Maurice Schwang
1ad0e700a5 feat: use NMS to populate tile entities 2025-10-21 00:05:24 +02:00
Pierre Maurice Schwang
590f442a0f fix: block nbt population 2025-10-21 00:05:24 +02:00
Pierre Maurice Schwang
308527e822 chore: 1.17 is not supported anymore 2025-10-21 00:05:24 +02:00
renovate[bot]
1f32909ffd Update fawe to v2.14.0 (#4783)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-20 00:58:28 +00:00
renovate[bot]
1785693fe4 Update dependency com.github.spotbugs:spotbugs-annotations to v4.9.8 (#4782)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-20 00:57:53 +00:00
Hannes Greule
df738b279d fix: missing fallback if current plot is not explicitly overridden (#4779) 2025-10-19 15:00:24 +02:00
renovate[bot]
b7b25252c6 Update dependency com.gradleup.shadow to v9 (#4760)
* Update dependency com.gradleup.shadow to v9

* Fix missing shadow runtimes on Gradle 9

Signed-off-by: Alexander Brandes <mc.cache@web.de>

---------

Signed-off-by: Alexander Brandes <mc.cache@web.de>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Alexander Brandes <mc.cache@web.de>
2025-10-13 20:58:00 +00:00
renovate[bot]
7123f51bb5 Update gradle/actions action to v5 (#4776)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-13 20:48:31 +00:00
renovate[bot]
897afa894e Update junit-framework monorepo to v6 (major) (#4777)
Update junit-framework monorepo to v6

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-13 20:48:17 +00:00
renovate[bot]
2238609551 Update github/codeql-action action to v4 (#4775)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-13 20:47:56 +00:00
renovate[bot]
255959232b Update Gradle to v9 (#4768)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Alexander Brandes <mc.cache@web.de>
2025-10-13 22:43:06 +02:00
FlorianMichael
622c9f1d13 fix: don't break cow shearing if mob-spawning flag is set to false (#4752) 2025-10-13 22:25:12 +02:00
renovate[bot]
e7aff3982e Update adventure to v4.25.0 (#4774)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-13 00:38:22 +00:00
renovate[bot]
db7ea780f9 Update dependency xyz.jpenilla.run-paper to v3.0.2 (#4773)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-13 00:37:50 +00:00
renovate[bot]
db188150d7 Update dependency xyz.jpenilla.run-paper to v3.0.1 (#4766)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-06 15:34:41 +00:00
renovate[bot]
cfd8401515 Update dependency org.checkerframework:checker-qual to v3.51.1 (#4765)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-06 15:34:23 +00:00
renovate[bot]
c887cbe28c Update junit-framework monorepo (#4767)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-06 17:33:19 +02:00
renovate[bot]
59183c1412 Update dependency com.diffplug.spotless to v8 (#4763)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-29 01:13:36 +00:00
renovate[bot]
439fb3a8ea Update dependency xyz.jpenilla.run-paper to v3 (#4764)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-29 01:11:55 +00:00
renovate[bot]
78d6ca1deb Update dependency com.github.spotbugs:spotbugs-annotations to v4.9.6 (#4759)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-22 00:41:24 +00:00
Alexander Brandes
03aa1be5a3 Back to snapshot for development
Signed-off-by: Alexander Brandes <mc.cache@web.de>
2025-09-19 21:56:38 +02:00
Alexander Brandes
2e3832f1bd Release 7.5.8
Signed-off-by: Alexander Brandes <mc.cache@web.de>
2025-09-19 21:55:57 +02:00
Pierre Maurice Schwang
05af41f832 fix: binary incompatibility on 1.21.1 (#4758) 2025-09-19 08:55:39 +02:00
renovate[bot]
b613318a29 Update fawe to v2.13.2 (#4754)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-15 01:06:16 +00:00
renovate[bot]
c2f8356042 Update dependency org.checkerframework:checker-qual to v3.51.0 (#4755)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-15 01:05:57 +00:00
Alexander Brandes
109e6059f8 Back to snapshot for development
Signed-off-by: Alexander Brandes <mc.cache@web.de>
2025-09-09 21:56:03 +02:00
Alexander Brandes
f01c287f89 Release 7.5.7
Signed-off-by: Alexander Brandes <mc.cache@web.de>
2025-09-09 21:55:30 +02:00
renovate[bot]
fd8cf3c475 Update dependency org.checkerframework:checker-qual to v3.50.0 (#4749)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-08 01:36:33 +00:00
renovate[bot]
8c9957edeb Update actions/setup-java action to v5 (#4750)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-08 01:36:15 +00:00
Jordan
aaba2b5b1a fix: improve error handling in backups (#4740)
* fix: improve error handling in backups
 - fixes #4460

* cleanup

* Update Core/src/main/java/com/plotsquared/core/backup/Backup.java

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Alexander Brandes <mc.cache@web.de>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-09-01 20:29:52 +02:00
Jordan
02a65c8855 fix: use PlotPlayer#getCurrentPlot across commands to allow /<command> <id> usage (#4738)
* fix: use PlotPlayer#getCurrentPlot across commands to allow /<command> <id> usage
 - fixes #4526
 - supersedes #4675

* cleanup
2025-08-30 20:33:13 +02:00
Jordan
8a5fa26796 fix: allow extended world height when teleporting player on join (#4739) 2025-08-30 20:32:40 +02:00
renovate[bot]
13cbb7e083 Update actions/checkout action to v5 (#4746)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-25 01:08:14 +00:00
56 changed files with 912 additions and 626 deletions

View File

@@ -9,11 +9,11 @@ jobs:
os: [ ubuntu-latest, windows-latest, macos-latest ] os: [ ubuntu-latest, windows-latest, macos-latest ]
steps: steps:
- name: Checkout Repository - name: Checkout Repository
uses: actions/checkout@v4 uses: actions/checkout@v5
- name: Validate Gradle Wrapper - name: Validate Gradle Wrapper
uses: gradle/actions/wrapper-validation@v4 uses: gradle/actions/wrapper-validation@v5
- name: Setup Java - name: Setup Java
uses: actions/setup-java@v4 uses: actions/setup-java@v5
with: with:
distribution: temurin distribution: temurin
java-version: 21 java-version: 21

View File

@@ -9,11 +9,11 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout Repository - name: Checkout Repository
uses: actions/checkout@v4 uses: actions/checkout@v5
- name: Validate Gradle Wrapper - name: Validate Gradle Wrapper
uses: gradle/actions/wrapper-validation@v4 uses: gradle/actions/wrapper-validation@v5
- name: Setup Java - name: Setup Java
uses: actions/setup-java@v4 uses: actions/setup-java@v5
with: with:
distribution: temurin distribution: temurin
java-version: 21 java-version: 21

View File

@@ -20,17 +20,17 @@ jobs:
language: [ 'java' ] language: [ 'java' ]
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v5
- name: Setup Java - name: Setup Java
uses: actions/setup-java@v4 uses: actions/setup-java@v5
with: with:
distribution: temurin distribution: temurin
java-version: 21 java-version: 21
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@v3 uses: github/codeql-action/init@v4
with: with:
languages: ${{ matrix.language }} languages: ${{ matrix.language }}
- name: Autobuild - name: Autobuild
uses: github/codeql-action/autobuild@v3 uses: github/codeql-action/autobuild@v4
- name: Perform CodeQL Analysis - name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3 uses: github/codeql-action/analyze@v4

View File

@@ -17,6 +17,19 @@ repositories {
} }
} }
// Make sure we control the exact version of paper being included, while dropping spigot + bukkit
configurations.all {
exclude("org.bukkit")
exclude("org.spigotmc")
resolutionStrategy.eachDependency {
if (requested.group == "io.papermc.paper" && requested.name == "paper-api") {
useVersion(checkNotNull(libs.paper.orNull?.version))
because("specific paper version is required to prevent binary incompatibilities on older versions")
}
}
}
dependencies { dependencies {
api(projects.plotsquaredCore) api(projects.plotsquaredCore)
@@ -28,20 +41,13 @@ dependencies {
implementation(libs.paperlib) implementation(libs.paperlib)
// Plugins // Plugins
compileOnly(libs.worldeditBukkit) { compileOnly(libs.worldeditBukkit)
exclude(group = "org.bukkit")
exclude(group = "org.spigotmc")
}
compileOnly(libs.faweBukkit) { isTransitive = false } compileOnly(libs.faweBukkit) { isTransitive = false }
testImplementation(libs.faweBukkit) { isTransitive = false } testImplementation(libs.faweBukkit) { isTransitive = false }
compileOnly(libs.vault) { compileOnly(libs.vault)
exclude(group = "org.bukkit")
}
compileOnly(libs.placeholderapi) compileOnly(libs.placeholderapi)
compileOnly(libs.luckperms) compileOnly(libs.luckperms)
compileOnly(libs.essentialsx) { compileOnly(libs.essentialsx)
exclude(group = "org.spigotmc")
}
compileOnly(libs.mvdwapi) { isTransitive = false } compileOnly(libs.mvdwapi) { isTransitive = false }
// Other libraries // Other libraries
@@ -74,7 +80,7 @@ tasks.named<ShadowJar>("shadowJar") {
relocate("net.kyori.examination", "com.plotsquared.core.configuration.examination") relocate("net.kyori.examination", "com.plotsquared.core.configuration.examination")
relocate("io.papermc.lib", "com.plotsquared.bukkit.paperlib") relocate("io.papermc.lib", "com.plotsquared.bukkit.paperlib")
relocate("org.bstats", "com.plotsquared.metrics") 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("org.khelekore.prtree", "com.plotsquared.prtree")
relocate("com.google.inject", "com.plotsquared.google") relocate("com.google.inject", "com.plotsquared.google")
relocate("org.aopalliance", "com.plotsquared.core.aopalliance") relocate("org.aopalliance", "com.plotsquared.core.aopalliance")

View File

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

View File

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

View File

@@ -160,7 +160,7 @@ public class EntityEventListener implements Listener {
return; return;
} }
} }
case "REINFORCEMENTS", "NATURAL", "MOUNT", "PATROL", "RAID", "SHEARED", "SILVERFISH_BLOCK", "ENDER_PEARL", case "REINFORCEMENTS", "NATURAL", "MOUNT", "PATROL", "RAID", "SILVERFISH_BLOCK", "ENDER_PEARL",
"TRAP", "VILLAGE_DEFENSE", "VILLAGE_INVASION", "BEEHIVE", "CHUNK_GEN", "NETHER_PORTAL", "TRAP", "VILLAGE_DEFENSE", "VILLAGE_INVASION", "BEEHIVE", "CHUNK_GEN", "NETHER_PORTAL",
"FROZEN", "SPELL", "DEFAULT" -> { "FROZEN", "SPELL", "DEFAULT" -> {
if (!area.isMobSpawning()) { if (!area.isMobSpawning()) {

View File

@@ -202,7 +202,7 @@ public class PaperListener implements Listener {
return; return;
} }
} }
case "REINFORCEMENTS", "NATURAL", "MOUNT", "PATROL", "RAID", "SHEARED", "SILVERFISH_BLOCK", "ENDER_PEARL", "TRAP", "VILLAGE_DEFENSE", "VILLAGE_INVASION", "BEEHIVE", "CHUNK_GEN" -> { case "REINFORCEMENTS", "NATURAL", "MOUNT", "PATROL", "RAID", "SILVERFISH_BLOCK", "ENDER_PEARL", "TRAP", "VILLAGE_DEFENSE", "VILLAGE_INVASION", "BEEHIVE", "CHUNK_GEN" -> {
if (!area.isMobSpawning()) { if (!area.isMobSpawning()) {
event.setShouldAbortSpawn(true); event.setShouldAbortSpawn(true);
event.setCancelled(true); event.setCancelled(true);

View File

@@ -52,6 +52,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Objects;
import java.util.function.Consumer; import java.util.function.Consumer;
public class BukkitQueueCoordinator extends BasicQueueCoordinator { public class BukkitQueueCoordinator extends BasicQueueCoordinator {
@@ -210,8 +211,13 @@ public class BukkitQueueCoordinator extends BasicQueueCoordinator {
BaseBlock block = getWorld().getBlock(blockVector3).toBaseBlock(tag); BaseBlock block = getWorld().getBlock(blockVector3).toBaseBlock(tag);
getWorld().setBlock(blockVector3, block, getSideEffectSet(SideEffectState.NONE)); getWorld().setBlock(blockVector3, block, getSideEffectSet(SideEffectState.NONE));
} catch (WorldEditException ignored) { } catch (WorldEditException ignored) {
StateWrapper sw = new StateWrapper(tag); StateWrapper.INSTANCE.restore(
sw.restoreTag(getWorld().getName(), blockVector3.getX(), blockVector3.getY(), blockVector3.getZ()); getWorld().getName(),
blockVector3.getX(),
blockVector3.getY(),
blockVector3.getZ(),
tag
);
} }
}); });
} }
@@ -295,9 +301,7 @@ public class BukkitQueueCoordinator extends BasicQueueCoordinator {
existing.setBlockData(blockData, false); existing.setBlockData(blockData, false);
if (block.hasNbtData()) { if (block.hasNbtData()) {
CompoundTag tag = block.getNbtData(); CompoundTag tag = block.getNbtData();
StateWrapper sw = new StateWrapper(tag); StateWrapper.INSTANCE.restore(existing, Objects.requireNonNull(tag));
sw.restoreTag(existing);
} }
} }
} }

View File

@@ -34,6 +34,8 @@ import org.bukkit.entity.EntityType;
import org.bukkit.generator.LimitedRegion; import org.bukkit.generator.LimitedRegion;
import org.checkerframework.checker.nullness.qual.NonNull; 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. * Wraps a {@link LimitedRegion} inside a {@link com.plotsquared.core.queue.QueueCoordinator} so it can be written to.
* *
@@ -44,7 +46,6 @@ public class LimitedRegionWrapperQueue extends DelegateQueueCoordinator {
private static final Logger LOGGER = LogManager.getLogger("PlotSquared/" + LimitedRegionWrapperQueue.class.getSimpleName()); private static final Logger LOGGER = LogManager.getLogger("PlotSquared/" + LimitedRegionWrapperQueue.class.getSimpleName());
private final LimitedRegion limitedRegion; private final LimitedRegion limitedRegion;
private boolean useOtherRestoreTagMethod = false;
/** /**
* @since 6.9.0 * @since 6.9.0
@@ -64,20 +65,11 @@ public class LimitedRegionWrapperQueue extends DelegateQueueCoordinator {
boolean result = setBlock(x, y, z, id.toImmutableState()); boolean result = setBlock(x, y, z, id.toImmutableState());
if (result && id.hasNbtData()) { if (result && id.hasNbtData()) {
CompoundTag tag = id.getNbtData(); CompoundTag tag = id.getNbtData();
StateWrapper sw = new StateWrapper(tag);
try { try {
if (useOtherRestoreTagMethod && getWorld() != null) { StateWrapper.INSTANCE.restore(limitedRegion.getBlockState(x, y, z).getBlock(), Objects.requireNonNull(tag));
sw.restoreTag(getWorld().getName(), x, y, z);
} else {
sw.restoreTag(limitedRegion.getBlockState(x, y, z).getBlock());
}
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
LOGGER.error("Error attempting to populate tile entity into the world at location {},{},{}", x, y, z, e); LOGGER.error("Error attempting to populate tile entity into the world at location {},{},{}", x, y, z, e);
return false; 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; return result;
@@ -113,9 +105,8 @@ public class LimitedRegionWrapperQueue extends DelegateQueueCoordinator {
@Override @Override
public boolean setTile(final int x, final int y, final int z, @NonNull final CompoundTag tag) { public boolean setTile(final int x, final int y, final int z, @NonNull final CompoundTag tag) {
StateWrapper sw = new StateWrapper(tag);
try { try {
return sw.restoreTag(limitedRegion.getBlockState(x, y, z).getBlock()); return StateWrapper.INSTANCE.restore(limitedRegion.getBlockState(x, y, z).getBlock(), tag);
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
LOGGER.error("Error attempting to populate tile entity into the world at location {},{},{}", x, y, z, e); LOGGER.error("Error attempting to populate tile entity into the world at location {},{},{}", x, y, z, e);
return false; return false;

View File

@@ -27,6 +27,8 @@ import com.plotsquared.core.util.WorldUtil;
import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.CompoundTag;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Objects;
/** /**
* Schematic Handler. * Schematic Handler.
*/ */
@@ -39,8 +41,8 @@ public class BukkitSchematicHandler extends SchematicHandler {
} }
@Override @Override
public boolean restoreTile(QueueCoordinator queue, CompoundTag ct, int x, int y, int z) { public boolean restoreTile(QueueCoordinator queue, CompoundTag tag, int x, int y, int z) {
return new StateWrapper(ct).restoreTag(queue.getWorld().getName(), x, y, z); return StateWrapper.INSTANCE.restore(Objects.requireNonNull(queue.getWorld()).getName(), x, y, z, tag);
} }
} }

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,332 +18,71 @@
*/ */
package com.plotsquared.bukkit.schematic; 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.plotsquared.bukkit.util.BukkitUtil;
import com.sk89q.jnbt.ByteTag; import com.plotsquared.core.PlotSquared;
import com.sk89q.jnbt.CompoundTag; 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 io.papermc.lib.PaperLib;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; 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.World;
import org.bukkit.block.Banner;
import org.bukkit.block.Block; 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.checkerframework.checker.nullness.qual.NonNull;
import org.jetbrains.annotations.ApiStatus;
import java.util.ArrayList; @ApiStatus.Internal
import java.util.HashMap; public sealed interface StateWrapper permits StateWrapperSpigot, StateWrapper.Factory.NoopStateWrapper {
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.UUID;
public class StateWrapper { StateWrapper INSTANCE = Factory.createStateWrapper();
public CompoundTag tag; boolean restore(final @NonNull Block block, final @NonNull CompoundTag data);
private boolean paperErrorTextureSent = false; default boolean restore(final String worldName, final int x, final int y, final int z, final CompoundTag data) {
private static final Logger LOGGER = LogManager.getLogger("PlotSquared/" + StateWrapper.class.getSimpleName()); final World world = BukkitUtil.getWorld(worldName);
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) { if (world == null) {
return false; return false;
} }
return restoreTag(world.getBlockAt(x, y, z)); return this.restore(world.getBlockAt(x, y, z), data);
} }
/** @ApiStatus.Internal
* Restore the TileEntity data to the given block final class Factory {
*
* @param block Block to restore to private static final Logger LOGGER = LogManager.getLogger("PlotSquared/" + StateWrapper.class.getSimpleName());
* @return true if successful
*/ private static final String INITIALIZATION_ERROR_TEMPLATE = """
@SuppressWarnings("deprecation") // #setLine is needed for Spigot compatibility Failed to initialize StateWrapper: {}
public boolean restoreTag(@NonNull Block block) { 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.
if (this.tag == null) { Try updating your Server Software, PlotSquared and WorldEdit / FastAsyncWorldEdit first. If the issue persists, report it on the issue tracker.
return false; """;
}
org.bukkit.block.BlockState state = block.getState(); private static StateWrapper createStateWrapper() {
switch (getId()) { int[] serverVersion = PlotSquared.platform().serverVersion();
case "chest", "beacon", "brewingstand", "dispenser", "dropper", "furnace", "hopper", "shulkerbox" -> { if (PaperLib.isPaper() && (serverVersion[1] == 21 && serverVersion[2] >= 5) || serverVersion[1] > 21) {
if (!(state instanceof Container container)) { try {
return false; return new StateWrapperPaper1_21_5();
} catch (Exception e) {
LOGGER.error("Failed to initialize Paper-specific state wrapper, falling back to Spigot", e);
} }
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" -> { try {
if (state instanceof Sign sign) { return new StateWrapperSpigot();
sign.setLine(0, jsonToColourCode(tag.getString("Text1"))); } catch (Exception e) {
sign.setLine(1, jsonToColourCode(tag.getString("Text2"))); LOGGER.error(INITIALIZATION_ERROR_TEMPLATE, StateWrapperSpigot.class.getSimpleName(), e);
sign.setLine(2, jsonToColourCode(tag.getString("Text3"))); }
sign.setLine(3, jsonToColourCode(tag.getString("Text4"))); return new NoopStateWrapper();
state.update(true); }
return true;
}
@ApiStatus.Internal
static final class NoopStateWrapper implements StateWrapper {
@Override
public boolean restore(final @NonNull Block block, final @NonNull CompoundTag data) {
return false; 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;
} }
} }

View File

@@ -0,0 +1,155 @@
/*
* 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.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.Tag;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bukkit.DyeColor;
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.util.Arrays;
import java.util.List;
import java.util.Locale;
final class StateWrapperPaper1_21_5 extends StateWrapperSpigot {
private static final Logger LOGGER = LogManager.getLogger("PlotSquared/" + StateWrapperPaper1_21_5.class.getSimpleName());
private static final Gson GSON = new GsonBuilder().registerTypeHierarchyAdapter(Tag.class, new NbtGsonSerializer()).create();
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 StateWrapperPaper1_21_5() {
super();
try {
initializeSignHack();
LOGGER.info("Using {} for block data population", StateWrapperPaper1_21_5.class.getSimpleName());
} catch (Throwable e) {
throw new RuntimeException("Failed to initialize sign hack", e);
}
}
@Override
public void postEntityStateLoad(final @NonNull BlockState blockState, final @NonNull CompoundTag data) throws Throwable {
// signs need special handling during generation
if (blockState instanceof Sign sign) {
if (data.getValue().get("front_text") instanceof CompoundTag textTag) {
setSignTextHack(sign.getSide(Side.FRONT), textTag);
}
if (data.getValue().get("back_text") instanceof CompoundTag textTag) {
setSignTextHack(sign.getSide(Side.BACK), textTag);
}
}
}
@Override
public Logger logger() {
return StateWrapperPaper1_21_5.LOGGER;
}
/**
* 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 side The sign side to apply data onto.
* @param text The compound tag containing the data for the sign side ({@code front_text} / {@code back_text})
* @throws Throwable if something went wrong when reflectively updating the sign.
*/
private static void setSignTextHack(SignSide side, CompoundTag text) throws Throwable {
if (text.containsKey("color")) {
//noinspection UnstableApiUsage
side.setColor(DyeColor.legacyValueOf(text.getString("color").toUpperCase(Locale.ROOT)));
}
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);
Object content = line.getValue();
// 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).
if (line instanceof CompoundTag compoundTag && compoundTag.getValue().containsKey("")) {
content = compoundTag.getValue().get("");
}
// absolute garbage way to try to handle stringified components (pre 1.21.5)
else if (content instanceof String contentAsString && (contentAsString.startsWith("{") || contentAsString.startsWith("["))) {
try {
content = JsonParser.parseString(contentAsString);
} catch (JsonSyntaxException e) {
// well, it wasn't JSON after all
}
}
// serializes the line content from JNBT to Gson JSON objects, passes that to adventure and deserializes
// into an adventure component.
// pass all possible types of content into the deserializer (Strings, Compounds, Arrays), even though Strings
// could be set directly via Sign#setLine(int, String). The overhead is minimal, the serializer can handle
// strings - and we don't have to use the deprecated method.
BUKKIT_SIGN_SIDE_LINE_SET.invoke(
side, i, GSON_SERIALIZER_DESERIALIZE_TREE.invoke(
KYORI_GSON_SERIALIZER,
content instanceof JsonElement ? content : GSON.toJsonTree(content)
)
);
}
}
}
private static void initializeSignHack() throws Throwable {
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());
}
}

View File

@@ -0,0 +1,210 @@
/*
* 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.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.checkerframework.checker.nullness.qual.NonNull;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
import java.util.function.Consumer;
sealed class StateWrapperSpigot implements StateWrapper permits StateWrapperPaper1_21_5 {
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());
static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
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;
public StateWrapperSpigot() {
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) {
throw new RuntimeException("Failed to access required WorldEdit methods", e);
}
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) {
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,
LIN_TAG_CLASS == null ? data : TO_LIN_TAG.invoke(data)
);
// load block entity data
CRAFT_BLOCK_ENTITY_STATE_LOAD_DATA.invoke(blockState, nativeTag);
postEntityStateLoad(blockState, data);
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 void postEntityStateLoad(final @NonNull BlockState blockState, final @NonNull CompoundTag data) throws Throwable {
}
public Logger logger() {
return StateWrapperSpigot.LOGGER;
}
/**
* 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

@@ -18,6 +18,8 @@
*/ */
package com.plotsquared.core.backup; package com.plotsquared.core.backup;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import java.io.IOException; import java.io.IOException;
@@ -30,12 +32,14 @@ import java.nio.file.Path;
*/ */
public class Backup { public class Backup {
private static final Logger LOGGER = LogManager.getLogger("PlotSquared/" + Backup.class.getSimpleName());
private final BackupProfile owner; private final BackupProfile owner;
private final long creationTime; private final long creationTime;
@Nullable @Nullable
private final Path file; private final Path file;
Backup(final BackupProfile owner, final long creationTime, final Path file) { Backup(final BackupProfile owner, final long creationTime, @Nullable final Path file) {
this.owner = owner; this.owner = owner;
this.creationTime = creationTime; this.creationTime = creationTime;
this.file = file; this.file = file;
@@ -49,7 +53,7 @@ public class Backup {
try { try {
Files.deleteIfExists(file); Files.deleteIfExists(file);
} catch (final IOException e) { } catch (final IOException e) {
e.printStackTrace(); LOGGER.error("Error deleting backup at {}", file, e);
} }
} }
} }

View File

@@ -21,14 +21,15 @@ package com.plotsquared.core.backup;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted; import com.google.inject.assistedinject.Assisted;
import com.plotsquared.core.configuration.caption.TranslatableCaption; import com.plotsquared.core.configuration.caption.TranslatableCaption;
import com.plotsquared.core.player.ConsolePlayer; import com.plotsquared.core.exception.PlotSquaredException;
import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.player.PlotPlayer;
import com.plotsquared.core.plot.Plot; import com.plotsquared.core.plot.Plot;
import com.plotsquared.core.plot.schematic.Schematic; import com.plotsquared.core.plot.schematic.Schematic;
import com.plotsquared.core.util.SchematicHandler; import com.plotsquared.core.util.SchematicHandler;
import com.plotsquared.core.util.task.RunnableVal; import com.plotsquared.core.util.task.RunnableVal;
import com.plotsquared.core.util.task.TaskManager; import com.plotsquared.core.util.task.TaskManager;
import net.kyori.adventure.text.minimessage.MiniMessage; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
@@ -51,7 +52,7 @@ import java.util.concurrent.CompletableFuture;
*/ */
public class PlayerBackupProfile implements BackupProfile { public class PlayerBackupProfile implements BackupProfile {
static final MiniMessage MINI_MESSAGE = MiniMessage.builder().build(); private static final Logger LOGGER = LogManager.getLogger("PlotSquared/" + PlayerBackupProfile.class.getSimpleName());
private final UUID owner; private final UUID owner;
private final Plot plot; private final Plot plot;
@@ -87,7 +88,7 @@ public class PlayerBackupProfile implements BackupProfile {
Files.createDirectory(path); Files.createDirectory(path);
} }
} catch (final Exception e) { } catch (final Exception e) {
e.printStackTrace(); LOGGER.error("Error resolving {} from {}", child, parent, e);
} }
return path; return path;
} }
@@ -104,7 +105,7 @@ public class PlayerBackupProfile implements BackupProfile {
try { try {
Files.createDirectories(path); Files.createDirectories(path);
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); LOGGER.error("Error creating directory {}", path, e);
return Collections.emptyList(); return Collections.emptyList();
} }
} }
@@ -117,11 +118,11 @@ public class PlayerBackupProfile implements BackupProfile {
backups.add( backups.add(
new Backup(this, basicFileAttributes.creationTime().toMillis(), file)); new Backup(this, basicFileAttributes.creationTime().toMillis(), file));
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); LOGGER.error("Error getting attributes for file {} to create backup", file, e);
} }
}); });
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); LOGGER.error("Error walking files from {}", path, e);
} }
backups.sort(Comparator.comparingLong(Backup::getCreationTime).reversed()); backups.sort(Comparator.comparingLong(Backup::getCreationTime).reversed());
return (this.backupCache = backups); return (this.backupCache = backups);
@@ -133,7 +134,7 @@ public class PlayerBackupProfile implements BackupProfile {
public void destroy() { public void destroy() {
this.listBackups().whenCompleteAsync((backups, error) -> { this.listBackups().whenCompleteAsync((backups, error) -> {
if (error != null) { if (error != null) {
error.printStackTrace(); LOGGER.error("Error while listing backups", error);
} }
backups.forEach(Backup::delete); backups.forEach(Backup::delete);
this.backupCache = null; this.backupCache = null;
@@ -141,10 +142,12 @@ public class PlayerBackupProfile implements BackupProfile {
} }
public @NonNull Path getBackupDirectory() { public @NonNull Path getBackupDirectory() {
return resolve(resolve( return resolve(
resolve(backupManager.getBackupPath(), Objects.requireNonNull(plot.getArea().toString(), "plot area id")), resolve(
Objects.requireNonNull(plot.getId().toDashSeparatedString(), "plot id") resolve(backupManager.getBackupPath(), Objects.requireNonNull(plot.getArea().toString(), "plot area id")),
), Objects.requireNonNull(owner.toString(), "owner")); Objects.requireNonNull(plot.getId().toDashSeparatedString(), "plot id")
), Objects.requireNonNull(owner.toString(), "owner")
);
} }
@Override @Override
@@ -156,7 +159,8 @@ public class PlayerBackupProfile implements BackupProfile {
backups.get(backups.size() - 1).delete(); backups.get(backups.size() - 1).delete();
} }
final List<Plot> plots = Collections.singletonList(plot); final List<Plot> plots = Collections.singletonList(plot);
final boolean result = this.schematicHandler.exportAll(plots, getBackupDirectory().toFile(), final boolean result = this.schematicHandler.exportAll(
plots, getBackupDirectory().toFile(),
"%world%-%id%-" + System.currentTimeMillis(), () -> "%world%-%id%-" + System.currentTimeMillis(), () ->
future.complete(new Backup(this, System.currentTimeMillis(), null)) future.complete(new Backup(this, System.currentTimeMillis(), null))
); );
@@ -180,7 +184,7 @@ public class PlayerBackupProfile implements BackupProfile {
try { try {
schematic = this.schematicHandler.getSchematic(backup.getFile().toFile()); schematic = this.schematicHandler.getSchematic(backup.getFile().toFile());
} catch (SchematicHandler.UnsupportedFormatException e) { } catch (SchematicHandler.UnsupportedFormatException e) {
e.printStackTrace(); LOGGER.error("Unsupported format for backup {}", backup.getFile(), e);
} }
if (schematic == null) { if (schematic == null) {
future.completeExceptionally(new IllegalArgumentException( future.completeExceptionally(new IllegalArgumentException(
@@ -200,10 +204,9 @@ public class PlayerBackupProfile implements BackupProfile {
if (value) { if (value) {
future.complete(null); future.complete(null);
} else { } else {
future.completeExceptionally(new RuntimeException(MINI_MESSAGE.escapeTags( future.completeExceptionally(new PlotSquaredException(
TranslatableCaption TranslatableCaption
.of("schematics.schematic_paste_failed") .of("schematics.schematic_paste_failed")));
.getComponent(ConsolePlayer.getConsole()))));
} }
} }
} }

View File

@@ -32,6 +32,8 @@ import com.plotsquared.core.util.task.TaskManager;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.tag.Tag; import net.kyori.adventure.text.minimessage.tag.Tag;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
@@ -47,6 +49,7 @@ import java.util.concurrent.TimeUnit;
@Singleton @Singleton
public class SimpleBackupManager implements BackupManager { public class SimpleBackupManager implements BackupManager {
private static final Logger LOGGER = LogManager.getLogger("PlotSquared/" + SimpleBackupManager.class.getSimpleName());
private final Path backupPath; private final Path backupPath;
private final boolean automaticBackup; private final boolean automaticBackup;
private final int backupLimit; private final int backupLimit;
@@ -112,7 +115,12 @@ public class SimpleBackupManager implements BackupManager {
TagResolver.resolver("reason", Tag.inserting(Component.text(throwable.getMessage()))) TagResolver.resolver("reason", Tag.inserting(Component.text(throwable.getMessage())))
); );
} }
throwable.printStackTrace(); LOGGER.error(
"Error creating backup for plot {};{} and player {}",
plot.getArea(),
plot.getId(),
player == null ? "null" : player.getName(), throwable
);
} else { } else {
if (player != null) { if (player != null) {
player.sendMessage(TranslatableCaption.of("backups.backup_automatic_finished")); player.sendMessage(TranslatableCaption.of("backups.backup_automatic_finished"));
@@ -128,6 +136,7 @@ public class SimpleBackupManager implements BackupManager {
return this.automaticBackup; return this.automaticBackup;
} }
@NonNull
public Path getBackupPath() { public Path getBackupPath() {
return this.backupPath; return this.backupPath;
} }

View File

@@ -21,7 +21,6 @@ package com.plotsquared.core.command;
import com.plotsquared.core.PlotSquared; import com.plotsquared.core.PlotSquared;
import com.plotsquared.core.configuration.Settings; import com.plotsquared.core.configuration.Settings;
import com.plotsquared.core.configuration.caption.TranslatableCaption; import com.plotsquared.core.configuration.caption.TranslatableCaption;
import com.plotsquared.core.location.Location;
import com.plotsquared.core.permissions.Permission; import com.plotsquared.core.permissions.Permission;
import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.player.PlotPlayer;
import com.plotsquared.core.plot.Plot; import com.plotsquared.core.plot.Plot;
@@ -58,8 +57,7 @@ public class Alias extends SubCommand {
return false; return false;
} }
Location location = player.getLocation(); Plot plot = player.getCurrentPlot();
Plot plot = location.getPlotAbs();
if (plot == null) { if (plot == null) {
player.sendMessage(TranslatableCaption.of("errors.not_in_plot")); player.sendMessage(TranslatableCaption.of("errors.not_in_plot"));
return false; return false;

View File

@@ -24,6 +24,7 @@ import com.plotsquared.core.backup.BackupProfile;
import com.plotsquared.core.backup.NullBackupProfile; import com.plotsquared.core.backup.NullBackupProfile;
import com.plotsquared.core.backup.PlayerBackupProfile; import com.plotsquared.core.backup.PlayerBackupProfile;
import com.plotsquared.core.configuration.caption.TranslatableCaption; import com.plotsquared.core.configuration.caption.TranslatableCaption;
import com.plotsquared.core.exception.PlotSquaredException;
import com.plotsquared.core.permissions.Permission; import com.plotsquared.core.permissions.Permission;
import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.player.PlotPlayer;
import com.plotsquared.core.plot.Plot; import com.plotsquared.core.plot.Plot;
@@ -32,6 +33,8 @@ import com.plotsquared.core.util.task.RunnableVal3;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.tag.Tag; import net.kyori.adventure.text.minimessage.tag.Tag;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import java.nio.file.Files; import java.nio.file.Files;
@@ -57,6 +60,8 @@ import java.util.stream.Stream;
permission = "plots.backup") permission = "plots.backup")
public final class Backup extends Command { public final class Backup extends Command {
private static final Logger LOGGER = LogManager.getLogger("PlotSquared/" + Backup.class.getSimpleName());
private final BackupManager backupManager; private final BackupManager backupManager;
@Inject @Inject
@@ -326,20 +331,43 @@ public final class Backup extends Command {
if (backupProfile instanceof NullBackupProfile) { if (backupProfile instanceof NullBackupProfile) {
player.sendMessage( player.sendMessage(
TranslatableCaption.of("backups.backup_impossible"), TranslatableCaption.of("backups.backup_impossible"),
TagResolver.resolver("plot", Tag.inserting( TagResolver.resolver(
TranslatableCaption.of("generic.generic_other").toComponent(player) "plot", Tag.inserting(
)) TranslatableCaption.of("generic.generic_other").toComponent(player)
)
)
); );
} else { } else {
backupProfile.listBackups().whenComplete((backups, throwable) -> { backupProfile.listBackups().whenComplete((backups, throwable) -> {
if (throwable != null) { if (throwable != null) {
Component reason;
if (throwable instanceof PlotSquaredException pe) {
reason = pe.getCaption().toComponent(player);
} else {
reason = Component.text(throwable.getMessage());
}
player.sendMessage( player.sendMessage(
TranslatableCaption.of("backups.backup_load_failure"), TranslatableCaption.of("backups.backup_load_failure"),
TagResolver.resolver("reason", Tag.inserting(Component.text(throwable.getMessage()))) TagResolver.resolver("reason", Tag.inserting(reason))
);
LOGGER.error("Error loading player ({}) backup", player.getName(), throwable);
return;
}
if (number < 1 || number > backups.size()) {
player.sendMessage(
TranslatableCaption.of("backups.backup_impossible"),
TagResolver.resolver(
"plot",
Tag.inserting(TranslatableCaption
.of("generic.generic_invalid_choice")
.toComponent(player))
)
); );
throwable.printStackTrace();
} else { } else {
if (number < 1 || number > backups.size()) { final com.plotsquared.core.backup.Backup backup =
backups.get(number - 1);
if (backup == null || backup.getFile() == null || !Files
.exists(backup.getFile())) {
player.sendMessage( player.sendMessage(
TranslatableCaption.of("backups.backup_impossible"), TranslatableCaption.of("backups.backup_impossible"),
TagResolver.resolver( TagResolver.resolver(
@@ -350,37 +378,23 @@ public final class Backup extends Command {
) )
); );
} else { } else {
final com.plotsquared.core.backup.Backup backup = CmdConfirm.addPending(
backups.get(number - 1); player, "/plot backup load " + number,
if (backup == null || backup.getFile() == null || !Files () -> backupProfile.restoreBackup(backup, player)
.exists(backup.getFile())) { .whenComplete((n, error) -> {
player.sendMessage( if (error != null) {
TranslatableCaption.of("backups.backup_impossible"), player.sendMessage(
TagResolver.resolver( TranslatableCaption.of("backups.backup_load_failure"),
"plot", TagResolver.resolver(
Tag.inserting(TranslatableCaption "reason",
.of("generic.generic_invalid_choice") Tag.inserting(Component.text(error.getMessage()))
.toComponent(player)) )
) );
); } else {
} else { player.sendMessage(TranslatableCaption.of("backups.backup_load_success"));
CmdConfirm.addPending(player, "/plot backup load " + number, }
() -> backupProfile.restoreBackup(backup, player) })
.whenComplete((n, error) -> { );
if (error != null) {
player.sendMessage(
TranslatableCaption.of("backups.backup_load_failure"),
TagResolver.resolver(
"reason",
Tag.inserting(Component.text(error.getMessage()))
)
);
} else {
player.sendMessage(TranslatableCaption.of("backups.backup_load_success"));
}
})
);
}
} }
} }
}); });

View File

@@ -20,6 +20,7 @@ package com.plotsquared.core.command;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.plotsquared.core.PlotSquared; import com.plotsquared.core.PlotSquared;
import com.plotsquared.core.configuration.Settings;
import com.plotsquared.core.configuration.caption.TranslatableCaption; import com.plotsquared.core.configuration.caption.TranslatableCaption;
import com.plotsquared.core.events.PlayerBuyPlotEvent; import com.plotsquared.core.events.PlayerBuyPlotEvent;
import com.plotsquared.core.events.Result; import com.plotsquared.core.events.Result;
@@ -84,8 +85,9 @@ public class Buy extends Command {
checkTrue(plot.hasOwner(), TranslatableCaption.of("info.plot_unowned")); checkTrue(plot.hasOwner(), TranslatableCaption.of("info.plot_unowned"));
checkTrue(!plot.isOwner(player.getUUID()), TranslatableCaption.of("economy.cannot_buy_own")); checkTrue(!plot.isOwner(player.getUUID()), TranslatableCaption.of("economy.cannot_buy_own"));
Set<Plot> plots = plot.getConnectedPlots(); Set<Plot> plots = plot.getConnectedPlots();
int plotCount = Settings.Limit.GLOBAL ? player.getPlotCount() : player.getPlotCount(plot.getWorldName());
checkTrue( checkTrue(
player.getPlotCount() + plots.size() <= player.getAllowedPlots(), plotCount + plots.size() <= player.getAllowedPlots(),
TranslatableCaption.of("permission.cant_claim_more_plots"), TranslatableCaption.of("permission.cant_claim_more_plots"),
TagResolver.resolver("amount", Tag.inserting(Component.text(player.getAllowedPlots()))) TagResolver.resolver("amount", Tag.inserting(Component.text(player.getAllowedPlots())))
); );

View File

@@ -26,7 +26,6 @@ import com.plotsquared.core.events.PlayerClaimPlotEvent;
import com.plotsquared.core.events.PlotMergeEvent; import com.plotsquared.core.events.PlotMergeEvent;
import com.plotsquared.core.events.Result; import com.plotsquared.core.events.Result;
import com.plotsquared.core.location.Direction; import com.plotsquared.core.location.Direction;
import com.plotsquared.core.location.Location;
import com.plotsquared.core.permissions.Permission; import com.plotsquared.core.permissions.Permission;
import com.plotsquared.core.player.MetaDataAccess; import com.plotsquared.core.player.MetaDataAccess;
import com.plotsquared.core.player.PlayerMetaDataKeys; import com.plotsquared.core.player.PlayerMetaDataKeys;
@@ -72,8 +71,7 @@ public class Claim extends SubCommand {
if (args.length >= 1) { if (args.length >= 1) {
schematic = args[0]; schematic = args[0];
} }
Location location = player.getLocation(); Plot plot = player.getCurrentPlot();
Plot plot = location.getPlotAbs();
if (plot == null) { if (plot == null) {
player.sendMessage(TranslatableCaption.of("errors.not_in_plot")); player.sendMessage(TranslatableCaption.of("errors.not_in_plot"));
return false; return false;
@@ -90,7 +88,7 @@ public class Claim extends SubCommand {
boolean force = event.getEventResult() == Result.FORCE; boolean force = event.getEventResult() == Result.FORCE;
int currentPlots = Settings.Limit.GLOBAL ? int currentPlots = Settings.Limit.GLOBAL ?
player.getPlotCount() : player.getPlotCount() :
player.getPlotCount(location.getWorldName()); player.getPlotCount(plot.getWorldName());
final PlotArea area = plot.getArea(); final PlotArea area = plot.getArea();

View File

@@ -68,8 +68,8 @@ public class Continue extends SubCommand {
return false; return false;
} }
int size = plot.getConnectedPlots().size(); int size = plot.getConnectedPlots().size();
if (!Settings.Done.COUNTS_TOWARDS_LIMIT && (player.getAllowedPlots() int plotCount = Settings.Limit.GLOBAL ? player.getPlotCount() : player.getPlotCount(plot.getWorldName());
< player.getPlotCount() + size)) { if (!Settings.Done.COUNTS_TOWARDS_LIMIT && (player.getAllowedPlots() < plotCount + size)) {
player.sendMessage( player.sendMessage(
TranslatableCaption.of("permission.cant_claim_more_plots"), TranslatableCaption.of("permission.cant_claim_more_plots"),
TagResolver.resolver("amount", Tag.inserting(Component.text(player.getAllowedPlots()))) TagResolver.resolver("amount", Tag.inserting(Component.text(player.getAllowedPlots())))

View File

@@ -19,7 +19,6 @@
package com.plotsquared.core.command; package com.plotsquared.core.command;
import com.plotsquared.core.configuration.caption.TranslatableCaption; import com.plotsquared.core.configuration.caption.TranslatableCaption;
import com.plotsquared.core.location.Location;
import com.plotsquared.core.permissions.Permission; import com.plotsquared.core.permissions.Permission;
import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.player.PlotPlayer;
import com.plotsquared.core.plot.Plot; import com.plotsquared.core.plot.Plot;
@@ -37,8 +36,7 @@ public class Copy extends SubCommand {
@Override @Override
public boolean onCommand(final PlotPlayer<?> player, String[] args) { public boolean onCommand(final PlotPlayer<?> player, String[] args) {
Location location = player.getLocation(); Plot plot1 = player.getCurrentPlot();
Plot plot1 = location.getPlotAbs();
if (plot1 == null) { if (plot1 == null) {
player.sendMessage(TranslatableCaption.of("errors.not_in_plot")); player.sendMessage(TranslatableCaption.of("errors.not_in_plot"));
return false; return false;

View File

@@ -22,7 +22,6 @@ import com.google.inject.Inject;
import com.plotsquared.core.configuration.caption.TranslatableCaption; import com.plotsquared.core.configuration.caption.TranslatableCaption;
import com.plotsquared.core.generator.HybridPlotWorld; import com.plotsquared.core.generator.HybridPlotWorld;
import com.plotsquared.core.generator.HybridUtils; import com.plotsquared.core.generator.HybridUtils;
import com.plotsquared.core.location.Location;
import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.player.PlotPlayer;
import com.plotsquared.core.plot.Plot; import com.plotsquared.core.plot.Plot;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
@@ -47,8 +46,7 @@ public class CreateRoadSchematic extends SubCommand {
@Override @Override
public boolean onCommand(PlotPlayer<?> player, String[] args) { public boolean onCommand(PlotPlayer<?> player, String[] args) {
Location location = player.getLocation(); Plot plot = player.getCurrentPlot();
Plot plot = location.getPlotAbs();
if (plot == null) { if (plot == null) {
player.sendMessage(TranslatableCaption.of("errors.not_in_plot")); player.sendMessage(TranslatableCaption.of("errors.not_in_plot"));
return false; return false;
@@ -57,7 +55,7 @@ public class CreateRoadSchematic extends SubCommand {
player.sendMessage(TranslatableCaption.of("schematics.schematic_too_large")); player.sendMessage(TranslatableCaption.of("schematics.schematic_too_large"));
return false; return false;
} }
if (!(location.getPlotArea() instanceof HybridPlotWorld)) { if (!(plot.getArea() instanceof HybridPlotWorld)) {
player.sendMessage(TranslatableCaption.of("errors.not_in_plot_world")); player.sendMessage(TranslatableCaption.of("errors.not_in_plot_world"));
} }
this.hybridUtils.setupRoadSchematic(plot); this.hybridUtils.setupRoadSchematic(plot);

View File

@@ -22,7 +22,6 @@ import com.google.inject.Inject;
import com.plotsquared.core.configuration.caption.TranslatableCaption; import com.plotsquared.core.configuration.caption.TranslatableCaption;
import com.plotsquared.core.generator.HybridPlotManager; import com.plotsquared.core.generator.HybridPlotManager;
import com.plotsquared.core.generator.HybridUtils; import com.plotsquared.core.generator.HybridUtils;
import com.plotsquared.core.location.Location;
import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.player.PlotPlayer;
import com.plotsquared.core.plot.Plot; import com.plotsquared.core.plot.Plot;
import com.plotsquared.core.plot.PlotArea; import com.plotsquared.core.plot.PlotArea;
@@ -57,8 +56,7 @@ public class DebugRoadRegen extends SubCommand {
@Override @Override
public boolean onCommand(PlotPlayer<?> player, String[] args) { public boolean onCommand(PlotPlayer<?> player, String[] args) {
Location location = player.getLocation(); Plot plot = player.getCurrentPlot();
Plot plot = location.getPlotAbs();
if (args.length < 1) { if (args.length < 1) {
player.sendMessage( player.sendMessage(
TranslatableCaption.of("commandconfig.command_syntax"), TranslatableCaption.of("commandconfig.command_syntax"),
@@ -92,8 +90,7 @@ public class DebugRoadRegen extends SubCommand {
} }
public boolean regenPlot(PlotPlayer<?> player) { public boolean regenPlot(PlotPlayer<?> player) {
Location location = player.getLocation(); PlotArea area = player.getContextualPlotArea();
PlotArea area = location.getPlotArea();
if (area == null) { if (area == null) {
player.sendMessage(TranslatableCaption.of("errors.not_in_plot_world")); player.sendMessage(TranslatableCaption.of("errors.not_in_plot_world"));
return false; return false;
@@ -148,10 +145,10 @@ public class DebugRoadRegen extends SubCommand {
return false; return false;
} }
Location location = player.getLocation(); PlotArea area = player.getContextualPlotArea();
PlotArea area = location.getPlotArea();
if (area == null) { if (area == null) {
player.sendMessage(TranslatableCaption.of("errors.not_in_plot_world")); player.sendMessage(TranslatableCaption.of("errors.not_in_plot_world"));
return false;
} }
Plot plot = player.getCurrentPlot(); Plot plot = player.getCurrentPlot();
PlotManager manager = area.getPlotManager(); PlotManager manager = area.getPlotManager();

View File

@@ -23,7 +23,6 @@ import com.plotsquared.core.configuration.Settings;
import com.plotsquared.core.configuration.caption.TranslatableCaption; import com.plotsquared.core.configuration.caption.TranslatableCaption;
import com.plotsquared.core.events.Result; import com.plotsquared.core.events.Result;
import com.plotsquared.core.events.TeleportCause; import com.plotsquared.core.events.TeleportCause;
import com.plotsquared.core.location.Location;
import com.plotsquared.core.permissions.Permission; import com.plotsquared.core.permissions.Permission;
import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.player.PlotPlayer;
import com.plotsquared.core.plot.Plot; import com.plotsquared.core.plot.Plot;
@@ -61,8 +60,7 @@ public class Delete extends SubCommand {
@Override @Override
public boolean onCommand(final PlotPlayer<?> player, String[] args) { public boolean onCommand(final PlotPlayer<?> player, String[] args) {
Location location = player.getLocation(); final Plot plot = player.getCurrentPlot();
final Plot plot = location.getPlotAbs();
if (plot == null) { if (plot == null) {
player.sendMessage(TranslatableCaption.of("errors.not_in_plot")); player.sendMessage(TranslatableCaption.of("errors.not_in_plot"));
return false; return false;
@@ -92,7 +90,7 @@ public class Delete extends SubCommand {
final java.util.Set<Plot> plots = plot.getConnectedPlots(); final java.util.Set<Plot> plots = plot.getConnectedPlots();
final int currentPlots = Settings.Limit.GLOBAL ? final int currentPlots = Settings.Limit.GLOBAL ?
player.getPlotCount() : player.getPlotCount() :
player.getPlotCount(location.getWorldName()); player.getPlotCount(plot.getWorldName());
Runnable run = () -> { Runnable run = () -> {
if (plot.getRunning() > 0) { if (plot.getRunning() > 0) {
player.sendMessage(TranslatableCaption.of("errors.wait_for_timer")); player.sendMessage(TranslatableCaption.of("errors.wait_for_timer"));

View File

@@ -70,8 +70,7 @@ public class Deny extends SubCommand {
@Override @Override
public boolean onCommand(PlotPlayer<?> player, String[] args) { public boolean onCommand(PlotPlayer<?> player, String[] args) {
Location location = player.getLocation(); final Plot plot = player.getCurrentPlot();
final Plot plot = location.getPlotAbs();
if (plot == null) { if (plot == null) {
player.sendMessage(TranslatableCaption.of("errors.not_in_plot")); player.sendMessage(TranslatableCaption.of("errors.not_in_plot"));
return false; return false;

View File

@@ -26,7 +26,6 @@ import com.plotsquared.core.events.PlotDoneEvent;
import com.plotsquared.core.events.PlotFlagAddEvent; import com.plotsquared.core.events.PlotFlagAddEvent;
import com.plotsquared.core.events.Result; import com.plotsquared.core.events.Result;
import com.plotsquared.core.generator.HybridUtils; import com.plotsquared.core.generator.HybridUtils;
import com.plotsquared.core.location.Location;
import com.plotsquared.core.permissions.Permission; import com.plotsquared.core.permissions.Permission;
import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.player.PlotPlayer;
import com.plotsquared.core.plot.Plot; import com.plotsquared.core.plot.Plot;
@@ -61,8 +60,7 @@ public class Done extends SubCommand {
@Override @Override
public boolean onCommand(final PlotPlayer<?> player, String[] args) { public boolean onCommand(final PlotPlayer<?> player, String[] args) {
Location location = player.getLocation(); final Plot plot = player.getCurrentPlot();
final Plot plot = location.getPlotAbs();
if ((plot == null) || !plot.hasOwner()) { if ((plot == null) || !plot.hasOwner()) {
player.sendMessage(TranslatableCaption.of("errors.not_in_plot")); player.sendMessage(TranslatableCaption.of("errors.not_in_plot"));
return false; return false;

View File

@@ -73,7 +73,7 @@ public class Download extends SubCommand {
@Override @Override
public boolean onCommand(final PlotPlayer<?> player, String[] args) { public boolean onCommand(final PlotPlayer<?> player, String[] args) {
String world = player.getLocation().getWorldName(); String world = player.getCurrentPlot().getWorldName();
if (!this.plotAreaManager.hasPlotArea(world)) { if (!this.plotAreaManager.hasPlotArea(world)) {
player.sendMessage(TranslatableCaption.of("errors.not_in_plot_world")); player.sendMessage(TranslatableCaption.of("errors.not_in_plot_world"));
return false; return false;

View File

@@ -27,7 +27,6 @@ import com.plotsquared.core.configuration.caption.TranslatableCaption;
import com.plotsquared.core.events.PlotFlagAddEvent; import com.plotsquared.core.events.PlotFlagAddEvent;
import com.plotsquared.core.events.PlotFlagRemoveEvent; import com.plotsquared.core.events.PlotFlagRemoveEvent;
import com.plotsquared.core.events.Result; import com.plotsquared.core.events.Result;
import com.plotsquared.core.location.Location;
import com.plotsquared.core.permissions.Permission; import com.plotsquared.core.permissions.Permission;
import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.player.PlotPlayer;
import com.plotsquared.core.plot.Plot; import com.plotsquared.core.plot.Plot;
@@ -179,8 +178,7 @@ public final class FlagCommand extends Command {
* @return {@code true} if the player is allowed to modify the flags at their current location * @return {@code true} if the player is allowed to modify the flags at their current location
*/ */
private static boolean checkRequirements(final @NonNull PlotPlayer<?> player) { private static boolean checkRequirements(final @NonNull PlotPlayer<?> player) {
final Location location = player.getLocation(); final Plot plot = player.getCurrentPlot();
final Plot plot = location.getPlotAbs();
if (plot == null) { if (plot == null) {
player.sendMessage(TranslatableCaption.of("errors.not_in_plot")); player.sendMessage(TranslatableCaption.of("errors.not_in_plot"));
return false; return false;
@@ -344,7 +342,7 @@ public final class FlagCommand extends Command {
if (plotFlag == null) { if (plotFlag == null) {
return; return;
} }
Plot plot = player.getLocation().getPlotAbs(); Plot plot = player.getCurrentPlot();
PlotFlagAddEvent event = eventDispatcher.callFlagAdd(plotFlag, plot); PlotFlagAddEvent event = eventDispatcher.callFlagAdd(plotFlag, plot);
if (event.getEventResult() == Result.DENY) { if (event.getEventResult() == Result.DENY) {
player.sendMessage( player.sendMessage(
@@ -409,7 +407,7 @@ public final class FlagCommand extends Command {
if (plotFlag == null) { if (plotFlag == null) {
return; return;
} }
Plot plot = player.getLocation().getPlotAbs(); Plot plot = player.getCurrentPlot();
PlotFlagAddEvent event = eventDispatcher.callFlagAdd(plotFlag, plot); PlotFlagAddEvent event = eventDispatcher.callFlagAdd(plotFlag, plot);
if (event.getEventResult() == Result.DENY) { if (event.getEventResult() == Result.DENY) {
player.sendMessage( player.sendMessage(
@@ -419,7 +417,7 @@ public final class FlagCommand extends Command {
return; return;
} }
boolean force = event.getEventResult() == Result.FORCE; boolean force = event.getEventResult() == Result.FORCE;
final PlotFlag localFlag = player.getLocation().getPlotAbs().getFlagContainer() final PlotFlag localFlag = player.getCurrentPlot().getFlagContainer()
.getFlag(event.getFlag().getClass()); .getFlag(event.getFlag().getClass());
if (!force) { if (!force) {
for (String entry : args[1].split(",")) { for (String entry : args[1].split(",")) {
@@ -444,7 +442,7 @@ public final class FlagCommand extends Command {
return; return;
} }
boolean result = boolean result =
player.getLocation().getPlotAbs().setFlag(localFlag.merge(parsed.getValue())); player.getCurrentPlot().setFlag(localFlag.merge(parsed.getValue()));
if (!result) { if (!result) {
player.sendMessage(TranslatableCaption.of("flag.flag_not_added")); player.sendMessage(TranslatableCaption.of("flag.flag_not_added"));
return; return;
@@ -484,7 +482,7 @@ public final class FlagCommand extends Command {
if (flag == null) { if (flag == null) {
return; return;
} }
final Plot plot = player.getLocation().getPlotAbs(); final Plot plot = player.getCurrentPlot();
final PlotFlag<?, ?> flagWithOldValue = plot.getFlagContainer().getFlag(flag.getClass()); final PlotFlag<?, ?> flagWithOldValue = plot.getFlagContainer().getFlag(flag.getClass());
PlotFlagRemoveEvent event = eventDispatcher.callFlagRemove(flag, plot); PlotFlagRemoveEvent event = eventDispatcher.callFlagRemove(flag, plot);
if (event.getEventResult() == Result.DENY) { if (event.getEventResult() == Result.DENY) {
@@ -687,7 +685,7 @@ public final class FlagCommand extends Command {
.build() .build()
); );
// Default value // Default value
final String defaultValue = player.getLocation().getPlotArea().getFlagContainer() final String defaultValue = player.getCurrentPlot().getArea().getFlagContainer()
.getFlagErased(plotFlag.getClass()).toString(); .getFlagErased(plotFlag.getClass()).toString();
player.sendMessage( player.sendMessage(
TranslatableCaption.of("flag.flag_info_default_value"), TranslatableCaption.of("flag.flag_info_default_value"),

View File

@@ -65,8 +65,7 @@ public class Kick extends SubCommand {
@Override @Override
public boolean onCommand(PlotPlayer<?> player, String[] args) { public boolean onCommand(PlotPlayer<?> player, String[] args) {
Location location = player.getLocation(); Plot plot = player.getCurrentPlot();
Plot plot = location.getPlot();
if (plot == null) { if (plot == null) {
player.sendMessage(TranslatableCaption.of("errors.not_in_plot")); player.sendMessage(TranslatableCaption.of("errors.not_in_plot"));
return false; return false;
@@ -124,7 +123,7 @@ public class Kick extends SubCommand {
); );
return; return;
} }
Location spawn = this.worldUtil.getSpawn(location.getWorldName()); Location spawn = this.worldUtil.getSpawn(plot.getWorldName());
player2.sendMessage(TranslatableCaption.of("kick.you_got_kicked")); player2.sendMessage(TranslatableCaption.of("kick.you_got_kicked"));
if (plot.equals(spawn.getPlot())) { if (plot.equals(spawn.getPlot())) {
Location newSpawn = this.worldUtil.getSpawn(this.plotAreaManager.getAllWorlds()[0]); Location newSpawn = this.worldUtil.getSpawn(this.plotAreaManager.getAllWorlds()[0]);
@@ -148,8 +147,7 @@ public class Kick extends SubCommand {
@Override @Override
public Collection<Command> tab(final PlotPlayer<?> player, final String[] args, final boolean space) { public Collection<Command> tab(final PlotPlayer<?> player, final String[] args, final boolean space) {
Location location = player.getLocation(); Plot plot = player.getCurrentPlot();
Plot plot = location.getPlotAbs();
if (plot == null) { if (plot == null) {
return Collections.emptyList(); return Collections.emptyList();
} }

View File

@@ -150,8 +150,8 @@ public class ListCmd extends SubCommand {
page = 0; page = 0;
} }
String world = player.getLocation().getWorldName(); PlotArea area = player.getContextualPlotArea();
PlotArea area = player.getApplicablePlotArea(); String world = area != null ? area.getWorldName() : "";
String arg = args[0].toLowerCase(); String arg = args[0].toLowerCase();
final boolean[] sort = new boolean[]{true}; final boolean[] sort = new boolean[]{true};

View File

@@ -68,11 +68,6 @@ public class Load extends SubCommand {
@Override @Override
public boolean onCommand(final PlotPlayer<?> player, final String[] args) { public boolean onCommand(final PlotPlayer<?> player, final String[] args) {
final String world = player.getLocation().getWorldName();
if (!this.plotAreaManager.hasPlotArea(world)) {
player.sendMessage(TranslatableCaption.of("errors.not_in_plot_world"));
return false;
}
final Plot plot = player.getCurrentPlot(); final Plot plot = player.getCurrentPlot();
if (plot == null) { if (plot == null) {
player.sendMessage(TranslatableCaption.of("errors.not_in_plot")); player.sendMessage(TranslatableCaption.of("errors.not_in_plot"));

View File

@@ -20,7 +20,6 @@ package com.plotsquared.core.command;
import com.plotsquared.core.configuration.caption.TranslatableCaption; import com.plotsquared.core.configuration.caption.TranslatableCaption;
import com.plotsquared.core.events.TeleportCause; import com.plotsquared.core.events.TeleportCause;
import com.plotsquared.core.location.Location;
import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.player.PlotPlayer;
import com.plotsquared.core.plot.Plot; import com.plotsquared.core.plot.Plot;
@@ -36,8 +35,7 @@ public class Middle extends SubCommand {
@Override @Override
public boolean onCommand(PlotPlayer<?> player, String[] arguments) { public boolean onCommand(PlotPlayer<?> player, String[] arguments) {
Location location = player.getLocation(); Plot plot = player.getCurrentPlot();
Plot plot = location.getPlot();
if (plot == null) { if (plot == null) {
player.sendMessage(TranslatableCaption.of("errors.not_in_plot")); player.sendMessage(TranslatableCaption.of("errors.not_in_plot"));
return false; return false;

View File

@@ -20,7 +20,6 @@ package com.plotsquared.core.command;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.plotsquared.core.configuration.caption.TranslatableCaption; import com.plotsquared.core.configuration.caption.TranslatableCaption;
import com.plotsquared.core.location.Location;
import com.plotsquared.core.permissions.Permission; import com.plotsquared.core.permissions.Permission;
import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.player.PlotPlayer;
import com.plotsquared.core.plot.Plot; import com.plotsquared.core.plot.Plot;
@@ -55,8 +54,7 @@ public class Move extends SubCommand {
RunnableVal3<Command, Runnable, Runnable> confirm, RunnableVal3<Command, Runnable, Runnable> confirm,
RunnableVal2<Command, CommandResult> whenDone RunnableVal2<Command, CommandResult> whenDone
) { ) {
Location location = player.getLocation(); Plot plot1 = player.getCurrentPlot();
Plot plot1 = location.getPlotAbs();
if (plot1 == null) { if (plot1 == null) {
player.sendMessage(TranslatableCaption.of("errors.not_in_plot")); player.sendMessage(TranslatableCaption.of("errors.not_in_plot"));
return CompletableFuture.completedFuture(false); return CompletableFuture.completedFuture(false);

View File

@@ -23,7 +23,6 @@ import com.plotsquared.core.configuration.caption.TranslatableCaption;
import com.plotsquared.core.events.PlotFlagAddEvent; import com.plotsquared.core.events.PlotFlagAddEvent;
import com.plotsquared.core.events.PlotFlagRemoveEvent; import com.plotsquared.core.events.PlotFlagRemoveEvent;
import com.plotsquared.core.events.Result; import com.plotsquared.core.events.Result;
import com.plotsquared.core.location.Location;
import com.plotsquared.core.permissions.Permission; import com.plotsquared.core.permissions.Permission;
import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.player.PlotPlayer;
import com.plotsquared.core.plot.Plot; import com.plotsquared.core.plot.Plot;
@@ -74,8 +73,7 @@ public class Music extends SubCommand {
@Override @Override
public boolean onCommand(PlotPlayer<?> player, String[] args) { public boolean onCommand(PlotPlayer<?> player, String[] args) {
Location location = player.getLocation(); final Plot plot = player.getCurrentPlot();
final Plot plot = location.getPlotAbs();
if (plot == null) { if (plot == null) {
player.sendMessage(TranslatableCaption.of("errors.not_in_plot")); player.sendMessage(TranslatableCaption.of("errors.not_in_plot"));
return false; return false;

View File

@@ -21,7 +21,6 @@ package com.plotsquared.core.command;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.plotsquared.core.configuration.caption.TranslatableCaption; import com.plotsquared.core.configuration.caption.TranslatableCaption;
import com.plotsquared.core.database.DBFunc; import com.plotsquared.core.database.DBFunc;
import com.plotsquared.core.location.Location;
import com.plotsquared.core.permissions.Permission; import com.plotsquared.core.permissions.Permission;
import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.player.PlotPlayer;
import com.plotsquared.core.plot.Plot; import com.plotsquared.core.plot.Plot;
@@ -56,8 +55,7 @@ public class Remove extends SubCommand {
@Override @Override
public boolean onCommand(PlotPlayer<?> player, String[] args) { public boolean onCommand(PlotPlayer<?> player, String[] args) {
Location location = player.getLocation(); Plot plot = player.getCurrentPlot();
Plot plot = location.getPlotAbs();
if (plot == null) { if (plot == null) {
player.sendMessage(TranslatableCaption.of("errors.not_in_plot")); player.sendMessage(TranslatableCaption.of("errors.not_in_plot"));
return false; return false;
@@ -132,8 +130,7 @@ public class Remove extends SubCommand {
@Override @Override
public Collection<Command> tab(final PlotPlayer<?> player, final String[] args, final boolean space) { public Collection<Command> tab(final PlotPlayer<?> player, final String[] args, final boolean space) {
Location location = player.getLocation(); Plot plot = player.getCurrentPlot();
Plot plot = location.getPlotAbs();
if (plot == null) { if (plot == null) {
return Collections.emptyList(); return Collections.emptyList();
} }

View File

@@ -22,7 +22,6 @@ import com.google.common.collect.Lists;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.plotsquared.core.configuration.Settings; import com.plotsquared.core.configuration.Settings;
import com.plotsquared.core.configuration.caption.TranslatableCaption; import com.plotsquared.core.configuration.caption.TranslatableCaption;
import com.plotsquared.core.location.Location;
import com.plotsquared.core.permissions.Permission; import com.plotsquared.core.permissions.Permission;
import com.plotsquared.core.player.ConsolePlayer; import com.plotsquared.core.player.ConsolePlayer;
import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.player.PlotPlayer;
@@ -102,8 +101,7 @@ public class SchematicCmd extends SubCommand {
); );
break; break;
} }
Location loc = player.getLocation(); final Plot plot = player.getCurrentPlot();
final Plot plot = loc.getPlotAbs();
if (plot == null) { if (plot == null) {
player.sendMessage(TranslatableCaption.of("errors.not_in_plot")); player.sendMessage(TranslatableCaption.of("errors.not_in_plot"));
return false; return false;
@@ -247,8 +245,7 @@ public class SchematicCmd extends SubCommand {
player.sendMessage(TranslatableCaption.of("error.task_in_process")); player.sendMessage(TranslatableCaption.of("error.task_in_process"));
return false; return false;
} }
Location location = player.getLocation(); Plot plot = player.getCurrentPlot();
Plot plot = location.getPlotAbs();
if (plot == null) { if (plot == null) {
player.sendMessage(TranslatableCaption.of("errors.not_in_plot")); player.sendMessage(TranslatableCaption.of("errors.not_in_plot"));
return false; return false;

View File

@@ -78,7 +78,7 @@ public class Set extends SubCommand {
@Override @Override
public boolean set(PlotPlayer<?> player, final Plot plot, String value) { public boolean set(PlotPlayer<?> player, final Plot plot, String value) {
final PlotArea plotArea = player.getLocation().getPlotArea(); final PlotArea plotArea = player.getContextualPlotArea();
if (plotArea == null) { if (plotArea == null) {
return false; return false;
} }

View File

@@ -19,7 +19,6 @@
package com.plotsquared.core.command; package com.plotsquared.core.command;
import com.plotsquared.core.configuration.caption.TranslatableCaption; import com.plotsquared.core.configuration.caption.TranslatableCaption;
import com.plotsquared.core.location.Location;
import com.plotsquared.core.permissions.Permission; import com.plotsquared.core.permissions.Permission;
import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.player.PlotPlayer;
import com.plotsquared.core.plot.Plot; import com.plotsquared.core.plot.Plot;
@@ -32,8 +31,7 @@ public abstract class SetCommand extends SubCommand {
@Override @Override
public boolean onCommand(PlotPlayer<?> player, String[] args) { public boolean onCommand(PlotPlayer<?> player, String[] args) {
Location location = player.getLocation(); Plot plot = player.getCurrentPlot();
Plot plot = location.getPlotAbs();
if (plot == null) { if (plot == null) {
player.sendMessage(TranslatableCaption.of("errors.not_in_plot")); player.sendMessage(TranslatableCaption.of("errors.not_in_plot"));
return false; return false;

View File

@@ -19,7 +19,6 @@
package com.plotsquared.core.command; package com.plotsquared.core.command;
import com.plotsquared.core.configuration.caption.TranslatableCaption; import com.plotsquared.core.configuration.caption.TranslatableCaption;
import com.plotsquared.core.location.Location;
import com.plotsquared.core.permissions.Permission; import com.plotsquared.core.permissions.Permission;
import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.player.PlotPlayer;
import com.plotsquared.core.plot.Plot; import com.plotsquared.core.plot.Plot;
@@ -44,8 +43,7 @@ public class Swap extends SubCommand {
RunnableVal3<Command, Runnable, Runnable> confirm, RunnableVal3<Command, Runnable, Runnable> confirm,
RunnableVal2<Command, CommandResult> whenDone RunnableVal2<Command, CommandResult> whenDone
) { ) {
Location location = player.getLocation(); Plot plot1 = player.getCurrentPlot();
Plot plot1 = location.getPlotAbs();
if (plot1 == null) { if (plot1 == null) {
player.sendMessage(TranslatableCaption.of("errors.not_in_plot")); player.sendMessage(TranslatableCaption.of("errors.not_in_plot"));
return CompletableFuture.completedFuture(false); return CompletableFuture.completedFuture(false);
@@ -79,8 +77,10 @@ public class Swap extends SubCommand {
String p1 = plot1.toString(); String p1 = plot1.toString();
String p2 = plot2.toString(); String p2 = plot2.toString();
return plot1.getPlotModificationManager().move(plot2, player, () -> { return plot1.getPlotModificationManager().move(
}, true).thenApply(result -> { plot2, player, () -> {
}, true
).thenApply(result -> {
if (result) { if (result) {
player.sendMessage( player.sendMessage(
TranslatableCaption.of("swap.swap_success"), TranslatableCaption.of("swap.swap_success"),

View File

@@ -22,7 +22,6 @@ import com.google.inject.Inject;
import com.plotsquared.core.configuration.caption.TranslatableCaption; import com.plotsquared.core.configuration.caption.TranslatableCaption;
import com.plotsquared.core.events.PlotUnlinkEvent; import com.plotsquared.core.events.PlotUnlinkEvent;
import com.plotsquared.core.events.Result; import com.plotsquared.core.events.Result;
import com.plotsquared.core.location.Location;
import com.plotsquared.core.permissions.Permission; import com.plotsquared.core.permissions.Permission;
import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.player.PlotPlayer;
import com.plotsquared.core.plot.Plot; import com.plotsquared.core.plot.Plot;
@@ -51,8 +50,7 @@ public class Unlink extends SubCommand {
@Override @Override
public boolean onCommand(final PlotPlayer<?> player, String[] args) { public boolean onCommand(final PlotPlayer<?> player, String[] args) {
Location location = player.getLocation(); final Plot plot = player.getCurrentPlot();
final Plot plot = location.getPlotAbs();
if (plot == null) { if (plot == null) {
player.sendMessage(TranslatableCaption.of("errors.not_in_plot")); player.sendMessage(TranslatableCaption.of("errors.not_in_plot"));
return false; return false;

View File

@@ -435,11 +435,6 @@ public class Settings extends Config {
public static String SCHEMATICS = "schematics"; public static String SCHEMATICS = "schematics";
public static String TEMPLATES = "templates"; public static String TEMPLATES = "templates";
@Comment({"If schematics used for generation should be searched for in the path.schematics location",
" - This setting exists and is `false` by default for backwards compatibility.",
" - If false then generation schematics must be located in `schematics`",
" - Schematics must still always be under GEN_ROAD_SCHEMATIC/<world> etc."})
public static boolean USE_SCHEMATICS_PATH_FOR_GEN_SCHEMATICS = false;
} }

View File

@@ -0,0 +1,62 @@
/*
* 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.core.exception;
import com.plotsquared.core.configuration.caption.Caption;
import com.plotsquared.core.configuration.caption.LocaleHolder;
/**
* Internal use only. Used to allow adventure captions to be used in an exception
*
* @since 7.5.7
*/
public final class PlotSquaredException extends RuntimeException {
private final Caption caption;
/**
* Create a new instance with the given caption
*
* @param caption caption
*/
public PlotSquaredException(Caption caption) {
this.caption = caption;
}
/**
* Create a new instance with the given caption and cause
*
* @param caption caption
* @param cause cause
*/
public PlotSquaredException(Caption caption, Exception cause) {
super(cause);
this.caption = caption;
}
@Override
public String getMessage() {
return caption.getComponent(LocaleHolder.console());
}
public Caption getCaption() {
return caption;
}
}

View File

@@ -140,8 +140,7 @@ public class HybridPlotWorld extends ClassicPlotWorld {
@NonNull @NonNull
@Override @Override
protected PlotManager createManager() { protected PlotManager createManager() {
return new HybridPlotManager( return new HybridPlotManager(this, PlotSquared.platform().regionManager(),
this, PlotSquared.platform().regionManager(),
PlotSquared.platform().injector().getInstance(ProgressSubscriberFactory.class) PlotSquared.platform().injector().getInstance(ProgressSubscriberFactory.class)
); );
} }
@@ -216,16 +215,15 @@ public class HybridPlotWorld extends ClassicPlotWorld {
// Try to determine root. This means that plot areas can have separate schematic // Try to determine root. This means that plot areas can have separate schematic
// directories // directories
String schematicFolder = Settings.Paths.USE_SCHEMATICS_PATH_FOR_GEN_SCHEMATICS ? Settings.Paths.SCHEMATICS : "schematics";
if (!(root = if (!(root =
FileUtils.getFile( FileUtils.getFile(
PlotSquared.platform().getDirectory(), PlotSquared.platform().getDirectory(),
schematicFolder + File.separator + "GEN_ROAD_SCHEMATIC" + File.separator + this.getWorldName() + File.separator + this.getId() "schematics/GEN_ROAD_SCHEMATIC/" + this.getWorldName() + "/" + this.getId()
)) ))
.exists()) { .exists()) {
root = FileUtils.getFile( root = FileUtils.getFile(
PlotSquared.platform().getDirectory(), PlotSquared.platform().getDirectory(),
schematicFolder + File.separator + "GEN_ROAD_SCHEMATIC" + File.separator + this.getWorldName() "schematics/GEN_ROAD_SCHEMATIC/" + this.getWorldName()
); );
} }

View File

@@ -290,7 +290,7 @@ 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} * @return the plot the player is standing on or null if standing on a road or not in a {@link PlotArea}
*/ */
public Plot getCurrentPlot() { public @Nullable Plot getCurrentPlot() {
try (final MetaDataAccess<Plot> lastPlotAccess = try (final MetaDataAccess<Plot> lastPlotAccess =
this.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_LAST_PLOT)) { this.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_LAST_PLOT)) {
if (lastPlotAccess.get().orElse(null) == null && !Settings.Enabled_Components.EVENTS) { if (lastPlotAccess.get().orElse(null) == null && !Settings.Enabled_Components.EVENTS) {
@@ -319,7 +319,7 @@ public abstract class PlotPlayer<P> implements CommandCaller, OfflinePlotPlayer,
*/ */
public int getPlotCount() { public int getPlotCount() {
if (!Settings.Limit.GLOBAL) { if (!Settings.Limit.GLOBAL) {
return getPlotCount(getLocation().getWorldName()); return getPlotCount(getContextualWorldName());
} }
final AtomicInteger count = new AtomicInteger(0); final AtomicInteger count = new AtomicInteger(0);
final UUID uuid = getUUID(); final UUID uuid = getUUID();
@@ -339,7 +339,7 @@ public abstract class PlotPlayer<P> implements CommandCaller, OfflinePlotPlayer,
public int getClusterCount() { public int getClusterCount() {
if (!Settings.Limit.GLOBAL) { if (!Settings.Limit.GLOBAL) {
return getClusterCount(getLocation().getWorldName()); return getClusterCount(getContextualWorldName());
} }
final AtomicInteger count = new AtomicInteger(0); final AtomicInteger count = new AtomicInteger(0);
this.plotAreaManager.forEachPlotArea(value -> { this.plotAreaManager.forEachPlotArea(value -> {
@@ -352,6 +352,34 @@ public abstract class PlotPlayer<P> implements CommandCaller, OfflinePlotPlayer,
return count.get(); 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 &ltid&gt 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 &ltid&gt 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. * Get the number of plots this player owns in the world.
* *
@@ -408,7 +436,11 @@ public abstract class PlotPlayer<P> implements CommandCaller, OfflinePlotPlayer,
} }
public PlotArea getApplicablePlotArea() { public PlotArea getApplicablePlotArea() {
return this.plotAreaManager.getApplicablePlotArea(getLocation()); Plot plot = getCurrentPlot();
if (plot == null) {
return this.plotAreaManager.getApplicablePlotArea(getLocation());
}
return plot.getArea();
} }
@Override @Override
@@ -614,16 +646,16 @@ public abstract class PlotPlayer<P> implements CommandCaller, OfflinePlotPlayer,
PlotId id = plot.getId(); PlotId id = plot.getId();
int x = id.getX(); int x = id.getX();
int z = id.getY(); int z = id.getY();
ByteBuffer buffer = ByteBuffer.allocate(13); ByteBuffer buffer = ByteBuffer.allocate(14);
buffer.putShort((short) x); buffer.putShort((short) x);
buffer.putShort((short) z); buffer.putShort((short) z);
Location location = getLocation(); Location location = getLocation();
buffer.putInt(location.getX()); buffer.putInt(location.getX());
buffer.put((byte) location.getY()); buffer.putShort((short) location.getY());
buffer.putInt(location.getZ()); buffer.putInt(location.getZ());
setPersistentMeta("quitLoc", buffer.array()); setPersistentMeta("quitLocV2", buffer.array());
} else if (hasPersistentMeta("quitLoc")) { } else if (hasPersistentMeta("quitLocV2")) {
removePersistentMeta("quitLoc"); removePersistentMeta("quitLocV2");
} }
if (plot != null) { if (plot != null) {
this.eventDispatcher.callLeave(this, plot); this.eventDispatcher.callLeave(this, plot);
@@ -678,80 +710,89 @@ public abstract class PlotPlayer<P> implements CommandCaller, OfflinePlotPlayer,
public void populatePersistentMetaMap() { public void populatePersistentMetaMap() {
if (Settings.Enabled_Components.PERSISTENT_META) { if (Settings.Enabled_Components.PERSISTENT_META) {
DBFunc.getPersistentMeta(getUUID(), new RunnableVal<>() { DBFunc.getPersistentMeta(
@Override getUUID(), new RunnableVal<>() {
public void run(Map<String, byte[]> value) { @Override
try { public void run(Map<String, byte[]> value) {
PlotPlayer.this.metaMap = value; try {
if (value.isEmpty()) { PlotPlayer.this.metaMap = value;
return; if (value.isEmpty()) {
} return;
if (PlotPlayer.this.getAttribute("debug")) {
debugModeEnabled.add(PlotPlayer.this);
}
if (!Settings.Teleport.ON_LOGIN) {
return;
}
PlotAreaManager manager = PlotPlayer.this.plotAreaManager;
if (!(manager instanceof SinglePlotAreaManager)) {
return;
}
PlotArea area = ((SinglePlotAreaManager) manager).getArea();
byte[] arr = PlotPlayer.this.getPersistentMeta("quitLoc");
if (arr == null) {
return;
}
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 = quitWorld.get() & 0xFF;
int z = quitWorld.getInt();
Plot plot = area.getOwnedPlot(id);
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 (PlotPlayer.this.getAttribute("debug")) {
if (getMeta("teleportOnLogin", true)) { debugModeEnabled.add(PlotPlayer.this);
plot.teleportPlayer( }
PlotPlayer.this,
result -> TaskManager.runTask(() -> { if (!Settings.Teleport.ON_LOGIN) {
if (getMeta("teleportOnLogin", true)) { return;
if (plot.isLoaded()) { }
teleport(location, TeleportCause.LOGIN); PlotAreaManager manager = PlotPlayer.this.plotAreaManager;
sendMessage(TranslatableCaption
.of("teleport.teleported_to_plot")); 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 (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"));
}
}
})
);
}
}
} catch (Throwable e) {
LOGGER.error("Error populating persistent meta for player {}", PlotPlayer.this.getName(), e);
} }
} }
} catch (Throwable e) {
e.printStackTrace();
} }
} );
});
} }
} }
@@ -819,7 +860,8 @@ public abstract class PlotPlayer<P> implements CommandCaller, OfflinePlotPlayer,
} }
@SuppressWarnings("unchecked") @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()); final byte[] value = this.getPersistentMeta(key.toString());
if (value == null) { if (value == null) {
return null; return null;
@@ -989,9 +1031,11 @@ public abstract class PlotPlayer<P> implements CommandCaller, OfflinePlotPlayer,
if (throwable != null) { if (throwable != null) {
sendMessage( sendMessage(
TranslatableCaption.of("errors.error"), TranslatableCaption.of("errors.error"),
TagResolver.resolver("value", Tag.inserting( TagResolver.resolver(
Component.text("Failed to resolve asynchronous caption replacements") "value", Tag.inserting(
)) Component.text("Failed to resolve asynchronous caption replacements")
)
)
); );
LOGGER.error("Failed to resolve asynchronous tagresolver(s) for " + caption, throwable); LOGGER.error("Failed to resolve asynchronous tagresolver(s) for " + caption, throwable);
} else { } else {

View File

@@ -541,7 +541,7 @@ public class Plot {
* *
* @return World name * @return World name
*/ */
public @Nullable String getWorldName() { public @NonNull String getWorldName() {
return area.getWorldName(); return area.getWorldName();
} }

View File

@@ -58,7 +58,7 @@ public class SinglePlot extends Plot {
} }
@Override @Override
public String getWorldName() { public @NonNull String getWorldName() {
return getId().toUnderscoreSeparatedString(); return getId().toUnderscoreSeparatedString();
} }

View File

@@ -135,6 +135,7 @@ public abstract class SchematicHandler {
} }
final String filename; final String filename;
final String website; final String website;
final @Nullable UUID finalUuid = uuid;
if (uuid == null) { if (uuid == null) {
uuid = UUID.randomUUID(); uuid = UUID.randomUUID();
website = Settings.Web.URL + "upload.php?" + uuid; website = Settings.Web.URL + "upload.php?" + uuid;
@@ -144,10 +145,11 @@ public abstract class SchematicHandler {
filename = file + '.' + extension; filename = file + '.' + extension;
} }
final URL url; final URL url;
String uri = Settings.Web.URL + "?key=" + uuid + "&type=" + extension;
try { try {
url = URI.create(Settings.Web.URL + "?key=" + uuid + "&type=" + extension).toURL(); url = URI.create(uri).toURL();
} catch (MalformedURLException e) { } catch (MalformedURLException e) {
e.printStackTrace(); LOGGER.error("Malformed URI `{}`", uri, e);
whenDone.run(); whenDone.run();
return; return;
} }
@@ -193,7 +195,7 @@ public abstract class SchematicHandler {
} }
TaskManager.runTask(whenDone); TaskManager.runTask(whenDone);
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); LOGGER.error("Error while uploading schematic for UUID {}", finalUuid, e);
TaskManager.runTask(whenDone); TaskManager.runTask(whenDone);
} }
}); });
@@ -388,8 +390,14 @@ public abstract class SchematicHandler {
} }
queue.enqueue(); queue.enqueue();
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace();
TaskManager.runTask(whenDone); TaskManager.runTask(whenDone);
LOGGER.error(
"Error pasting schematic to plot {};{} for player {}",
plot.getArea(),
plot.getId(),
actor == null ? "null" : actor.getName(),
e
);
} }
} }
@@ -456,7 +464,7 @@ public abstract class SchematicHandler {
Clipboard clip = reader.read(); Clipboard clip = reader.read();
return new Schematic(clip); return new Schematic(clip);
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); LOGGER.error("Error reading schematic from file {}", file.getAbsolutePath(), e);
} }
} else { } else {
throw new UnsupportedFormatException("This schematic format is not recognised or supported."); throw new UnsupportedFormatException("This schematic format is not recognised or supported.");
@@ -470,7 +478,7 @@ public abstract class SchematicHandler {
InputStream inputStream = Channels.newInputStream(readableByteChannel); InputStream inputStream = Channels.newInputStream(readableByteChannel);
return getSchematic(inputStream); return getSchematic(inputStream);
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); LOGGER.error("Error reading schematic from {}", url, e);
} }
return null; return null;
} }
@@ -486,7 +494,7 @@ public abstract class SchematicHandler {
Clipboard clip = schematicReader.read(); Clipboard clip = schematicReader.read();
return new Schematic(clip); return new Schematic(clip);
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); LOGGER.error("Error reading schematic", e);
} }
} }
return null; return null;
@@ -515,7 +523,7 @@ public abstract class SchematicHandler {
} }
return schematics; return schematics;
} catch (JsonParseException | IOException e) { } catch (JsonParseException | IOException e) {
e.printStackTrace(); LOGGER.error("Error retrieving saves for UUID {}", uuid, e);
} }
return null; return null;
} }
@@ -532,7 +540,7 @@ public abstract class SchematicHandler {
try (NBTOutputStream nos = new NBTOutputStream(new GZIPOutputStream(output, true))) { try (NBTOutputStream nos = new NBTOutputStream(new GZIPOutputStream(output, true))) {
nos.writeNamedTag("Schematic", tag); nos.writeNamedTag("Schematic", tag);
} catch (IOException e1) { } catch (IOException e1) {
e1.printStackTrace(); LOGGER.error("Error uploading schematic for UUID {}", uuid, e1);
} }
} }
}, whenDone); }, whenDone);
@@ -556,9 +564,9 @@ public abstract class SchematicHandler {
nbtStream.writeNamedTag("Schematic", tag); nbtStream.writeNamedTag("Schematic", tag);
} }
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
e.printStackTrace(); LOGGER.error("Error saving schematic at {}", path, e);
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); LOGGER.error("Error saving schematic at {}", path, e);
return false; return false;
} }
return true; return true;
@@ -581,7 +589,7 @@ public abstract class SchematicHandler {
schematic.put("BlockData", new ByteArrayTag(buffer.toByteArray())); schematic.put("BlockData", new ByteArrayTag(buffer.toByteArray()));
schematic.put("BlockEntities", new ListTag(CompoundTag.class, tileEntities)); schematic.put("BlockEntities", new ListTag(CompoundTag.class, tileEntities));
if (biomeBuffer.size() == 0 || biomePalette.size() == 0) { if (biomeBuffer.size() == 0 || biomePalette.isEmpty()) {
return; return;
} }
@@ -733,10 +741,7 @@ public abstract class SchematicHandler {
} }
BaseBlock block = aabb.getWorld().getFullBlock(point); BaseBlock block = aabb.getWorld().getFullBlock(point);
if (block.getNbtData() != null) { if (block.getNbtData() != null) {
Map<String, Tag> values = new HashMap<>(); Map<String, Tag> values = new HashMap<>(block.getNbtData().getValue());
for (Map.Entry<String, Tag> entry : block.getNbtData().getValue().entrySet()) {
values.put(entry.getKey(), entry.getValue());
}
// Positions are kept in NBT, we don't want that. // Positions are kept in NBT, we don't want that.
values.remove("x"); values.remove("x");

View File

@@ -20,7 +20,7 @@ plugins {
} }
group = "com.intellectualsites.plotsquared" group = "com.intellectualsites.plotsquared"
version = "7.5.7-SNAPSHOT" version = "7.5.9-SNAPSHOT"
if (!File("$rootDir/.git").exists()) { if (!File("$rootDir/.git").exists()) {
logger.lifecycle(""" logger.lifecycle("""
@@ -65,10 +65,16 @@ subprojects {
plugin<IdeaPlugin>() plugin<IdeaPlugin>()
} }
configurations.matching { it.name == "signatures" }.configureEach {
attributes {
attribute(Attribute.of("signatures-unique", String::class.java), "true")
}
}
dependencies { dependencies {
// Tests // Tests
testImplementation("org.junit.jupiter:junit-jupiter:5.13.4") testImplementation("org.junit.jupiter:junit-jupiter:6.0.0")
testRuntimeOnly("org.junit.platform:junit-platform-launcher:1.13.4") testRuntimeOnly("org.junit.platform:junit-platform-launcher:6.0.0")
} }
plugins.withId("java") { plugins.withId("java") {
@@ -95,9 +101,15 @@ subprojects {
} }
} }
val javaComponent = components["java"] as AdhocComponentWithVariants afterEvaluate {
javaComponent.withVariantsFromConfiguration(configurations["shadowRuntimeElements"]) { val javaComponent = components["java"] as AdhocComponentWithVariants
skip() configurations.findByName("shadowRuntimeElements")?.let { shadowRuntimeElements ->
javaComponent.withVariantsFromConfiguration(shadowRuntimeElements) {
skip()
}
} ?: run {
logger.warn("Configuration 'shadowRuntimeElements' does not exist.")
}
} }
signing { signing {

View File

@@ -2,18 +2,18 @@
# Platform expectations # Platform expectations
paper = "1.20.4-R0.1-SNAPSHOT" paper = "1.20.4-R0.1-SNAPSHOT"
guice = "7.0.0" guice = "7.0.0"
spotbugs = "4.9.4" spotbugs = "4.9.8"
checkerqual = "3.49.5" checkerqual = "3.51.1"
gson = "2.10" gson = "2.10"
guava = "31.1-jre" guava = "31.1-jre"
snakeyaml = "2.0" snakeyaml = "2.0"
adventure = "4.24.0" adventure = "4.25.0"
adventure-bukkit = "4.4.1" adventure-bukkit = "4.4.1"
log4j = "2.19.0" log4j = "2.19.0"
# Plugins # Plugins
worldedit = "7.2.20" worldedit = "7.2.20"
fawe = "2.13.1" fawe = "2.14.0"
placeholderapi = "2.11.6" placeholderapi = "2.11.6"
luckperms = "5.5" luckperms = "5.5"
essentialsx = "2.21.2" essentialsx = "2.21.2"
@@ -33,11 +33,11 @@ vault = "1.7.1"
serverlib = "2.3.7" serverlib = "2.3.7"
# Gradle plugins # Gradle plugins
shadow = "8.3.9" shadow = "9.2.2"
grgit = "4.1.1" grgit = "4.1.1"
spotless = "7.2.1" spotless = "8.0.0"
publish = "0.34.0" publish = "0.34.0"
runPaper = "2.3.1" runPaper = "3.0.2"
[libraries] [libraries]
# Platform expectations # Platform expectations

Binary file not shown.

View File

@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip
networkTimeout=10000 networkTimeout=10000
validateDistributionUrl=true validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME

5
gradlew vendored
View File

@@ -1,7 +1,7 @@
#!/bin/sh #!/bin/sh
# #
# Copyright © 2015-2021 the original authors. # Copyright © 2015 the original authors.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@@ -114,7 +114,6 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;; NONSTOP* ) nonstop=true ;;
esac esac
CLASSPATH="\\\"\\\""
# Determine the Java command to use to start the JVM. # Determine the Java command to use to start the JVM.
@@ -172,7 +171,6 @@ fi
# For Cygwin or MSYS, switch paths to Windows format before running java # For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" ) JAVACMD=$( cygpath --unix "$JAVACMD" )
@@ -212,7 +210,6 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
set -- \ set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \ "-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@" "$@"

3
gradlew.bat vendored
View File

@@ -70,11 +70,10 @@ goto fail
:execute :execute
@rem Setup the command line @rem Setup the command line
set CLASSPATH=
@rem Execute Gradle @rem Execute Gradle
"%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" %* "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
:end :end
@rem End local scope for the variables with windows NT shell @rem End local scope for the variables with windows NT shell