Compare commits

..

13 Commits

Author SHA1 Message Date
SirYwell
c8e5cf163f fix remaining change (3) 2024-01-29 15:35:33 +01:00
SirYwell
628c520c1b fix remaining change (2) 2024-01-29 15:29:18 +01:00
SirYwell
e0c7c58aaa fix remaining change 2024-01-29 15:26:13 +01:00
SirYwell
a659832613 update cloud 2024-01-29 15:21:08 +01:00
Alexander Söderberg
2a0ad92a97 use cloud snapshot 2024-01-15 07:54:26 +01:00
Alexander Söderberg
780c65e68c migrate to cloud-processors-requirements 2024-01-13 09:45:20 +01:00
Alexander Söderberg
92a00ef60a use cloud-requirements 2024-01-13 09:45:19 +01:00
Alexander Söderberg
3a1b373c8e don't use async suggestions with brigadier 2024-01-13 09:45:18 +01:00
Alexander Söderberg
1a277bbbeb add requirement inheritance 2024-01-13 09:45:17 +01:00
Alexander Söderberg
24d7fe626d improve the requirement system 2024-01-13 09:45:16 +01:00
Alexander Söderberg
b62a237dab port over remaining flag commands 2024-01-13 09:45:15 +01:00
Alexander Söderberg
2ac2f6648e port over flag remove 2024-01-13 09:45:14 +01:00
Alexander Söderberg
6088dd8409 feature: use cloud v2 for commands 2024-01-13 09:45:06 +01:00
116 changed files with 2768 additions and 2356 deletions

View File

@@ -24,16 +24,20 @@ body:
- type: dropdown
attributes:
label: Server Version
description: Which server version are you using? If your server version is not listed, it is not supported. Update to a supported version first.
description: Which server version version you using? If your server version is not listed, it is not supported. Update to a supported version first.
multiple: false
options:
- '1.21.4'
- '1.21.3'
- '1.21.1'
- '1.20.6'
- '1.20.4'
- '1.20'
- '1.19.4'
- '1.19.3'
- '1.19.2'
- '1.19.1'
- '1.19'
- '1.18.2'
- '1.18.1'
- '1.17.1'
- '1.16.5'
validations:
required: true

View File

@@ -1,15 +1,15 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended",
":semanticCommitsDisabled",
"schedule:earlyMondays"
"config:base",
":semanticCommitsDisabled"
],
"automerge": true,
"labels": [
"dependencies"
],
"rebaseWhen": "conflicted",
"schedule": ["on the first day of the month"],
"ignoreDeps": [
"com.google.code.gson:gson",
"com.google.guava:guava",

View File

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

View File

@@ -11,11 +11,11 @@ jobs:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Validate Gradle Wrapper
uses: gradle/actions/wrapper-validation@v4
uses: gradle/wrapper-validation-action@v1
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 21
java-version: 17
- name: Clean Build
run: ./gradlew clean build

View File

@@ -11,12 +11,12 @@ jobs:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Validate Gradle Wrapper
uses: gradle/actions/wrapper-validation@v4
uses: gradle/wrapper-validation-action@v1
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 21
java-version: 17
- name: Clean Build
run: ./gradlew clean build
- name: Determine release status
@@ -29,18 +29,18 @@ jobs:
fi
- name: Publish Release
if: ${{ runner.os == 'Linux' && env.STATUS == 'release' && github.event_name == 'push' && github.ref == 'refs/heads/main'}}
run: ./gradlew publishAndReleaseToMavenCentral --no-configuration-cache
run: ./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository
env:
ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.CENTRAL_USERNAME }}
ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.CENTRAL_PASSWORD }}
ORG_GRADLE_PROJECT_sonatypeUsername: ${{ secrets.SONATYPE_USERNAME }}
ORG_GRADLE_PROJECT_sonatypePassword: ${{ secrets.SONATYPE_PASSWORD }}
ORG_GRADLE_PROJECT_signingKey: ${{ secrets.SIGNING_KEY }}
ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.SIGNING_PASSWORD }}
- name: Publish Snapshot
if: ${{ runner.os == 'Linux' && env.STATUS != 'release' && github.event_name == 'push' && github.ref == 'refs/heads/main' }}
run: ./gradlew publishAllPublicationsToMavenCentralRepository
run: ./gradlew publishToSonatype
env:
ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.CENTRAL_USERNAME }}
ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.CENTRAL_PASSWORD }}
ORG_GRADLE_PROJECT_sonatypeUsername: ${{ secrets.SONATYPE_USERNAME }}
ORG_GRADLE_PROJECT_sonatypePassword: ${{ secrets.SONATYPE_PASSWORD }}
- name: Publish core javadoc
if: ${{ runner.os == 'Linux' && env.STATUS == 'release' && github.event_name == 'push' && github.ref == 'refs/heads/main'}}
uses: cpina/github-action-push-to-another-repository@main

View File

@@ -25,7 +25,7 @@ jobs:
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 21
java-version: 17
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:

View File

@@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Label conflicting PRs
uses: eps1lon/actions-label-merge-conflict@v3.0.3
uses: eps1lon/actions-label-merge-conflict@v2.1.0
with:
dirtyLabel: "unresolved-merge-conflict"
repoToken: "${{ secrets.GITHUB_TOKEN }}"

View File

@@ -12,6 +12,6 @@ jobs:
if: ${{ github.event_name != 'pull_request' || github.repository != github.event.pull_request.head.repo.full_name }}
runs-on: ubuntu-latest
steps:
- uses: release-drafter/release-drafter@v6
- uses: release-drafter/release-drafter@v5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -3,7 +3,7 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
repositories {
maven {
name = "PlaceholderAPI"
url = uri("https://repo.extendedclip.com/releases/")
url = uri("https://repo.extendedclip.com/content/repositories/placeholderapi/")
}
maven {
@@ -39,10 +39,7 @@ dependencies {
}
compileOnly(libs.placeholderapi)
compileOnly(libs.luckperms)
compileOnly(libs.essentialsx) {
exclude(group = "org.spigotmc")
exclude(group = "io.papermc.paper")
}
compileOnly(libs.essentialsx)
compileOnly(libs.mvdwapi) { isTransitive = false }
// Other libraries
@@ -56,6 +53,9 @@ dependencies {
// Adventure
implementation(libs.adventureBukkit)
// Cloud
implementation(libs.cloudPaper)
}
tasks.processResources {
@@ -75,11 +75,12 @@ tasks.named<ShadowJar>("shadowJar") {
relocate("net.kyori.examination", "com.plotsquared.core.configuration.examination")
relocate("io.papermc.lib", "com.plotsquared.bukkit.paperlib")
relocate("org.bstats", "com.plotsquared.metrics")
relocate("org.enginehub.squirrelid", "com.plotsquared.squirrelid")
relocate("org.enginehub", "com.plotsquared.squirrelid")
relocate("org.khelekore.prtree", "com.plotsquared.prtree")
relocate("com.google.inject", "com.plotsquared.google")
relocate("org.aopalliance", "com.plotsquared.core.aopalliance")
relocate("cloud.commandframework.services", "com.plotsquared.core.services")
relocate("cloud.commandframework", "com.plotsquared.commands")
relocate("io.leangen.geantyref", "com.plotsquared.core.geantyref")
relocate("com.intellectualsites.arkitektonika", "com.plotsquared.core.arkitektonika")
relocate("com.intellectualsites.http", "com.plotsquared.core.http")
@@ -104,18 +105,17 @@ tasks {
withType<Javadoc> {
val isRelease = if (rootProject.version.toString().endsWith("-SNAPSHOT")) "TODO" else rootProject.version.toString()
val opt = options as StandardJavadocDocletOptions
opt.links("https://jd.papermc.io/paper/1.20.4/")
opt.links("https://jd.papermc.io/paper/1.20/")
opt.links("https://docs.enginehub.org/javadoc/com.sk89q.worldedit/worldedit-bukkit/" + libs.worldeditBukkit.get().versionConstraint.toString())
opt.links("https://intellectualsites.github.io/plotsquared-javadocs/core/")
opt.links("https://jd.advntr.dev/api/" + libs.adventureApi.get().versionConstraint.toString())
opt.links("https://jd.advntr.dev/api/4.14.0/")
opt.links("https://google.github.io/guice/api-docs/" + libs.guice.get().versionConstraint.toString() + "/javadoc/")
opt.links("https://checkerframework.org/api/")
// opt.links("https://checkerframework.org/api/")
opt.isLinkSource = true
opt.bottom(File("$rootDir/javadocfooter.html").readText())
opt.isUse = true
opt.encoding("UTF-8")
opt.keyWords()
opt.addStringOption("-since", isRelease)
opt.noTimestamp()
}
}

View File

@@ -24,9 +24,11 @@ import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Singleton;
import com.google.inject.Stage;
import com.google.inject.TypeLiteral;
import com.plotsquared.bukkit.generator.BukkitPlotGenerator;
import com.plotsquared.bukkit.inject.BackupModule;
import com.plotsquared.bukkit.inject.BukkitModule;
import com.plotsquared.bukkit.inject.CloudModule;
import com.plotsquared.bukkit.inject.PermissionModule;
import com.plotsquared.bukkit.inject.WorldManagerModule;
import com.plotsquared.bukkit.listener.BlockEventListener;
@@ -34,7 +36,6 @@ import com.plotsquared.bukkit.listener.BlockEventListener117;
import com.plotsquared.bukkit.listener.ChunkListener;
import com.plotsquared.bukkit.listener.EntityEventListener;
import com.plotsquared.bukkit.listener.EntitySpawnListener;
import com.plotsquared.bukkit.listener.HighFreqBlockEventListener;
import com.plotsquared.bukkit.listener.PaperListener;
import com.plotsquared.bukkit.listener.PlayerEventListener;
import com.plotsquared.bukkit.listener.PlayerEventListener1201;
@@ -45,6 +46,7 @@ import com.plotsquared.bukkit.listener.SpigotListener;
import com.plotsquared.bukkit.listener.WorldEvents;
import com.plotsquared.bukkit.placeholder.PAPIPlaceholders;
import com.plotsquared.bukkit.placeholder.PlaceholderFormatter;
import com.plotsquared.bukkit.player.BukkitPlayer;
import com.plotsquared.bukkit.player.BukkitPlayerManager;
import com.plotsquared.bukkit.util.BukkitUtil;
import com.plotsquared.bukkit.util.BukkitWorld;
@@ -63,6 +65,7 @@ import com.plotsquared.bukkit.uuid.SquirrelIdUUIDService;
import com.plotsquared.core.PlotPlatform;
import com.plotsquared.core.PlotSquared;
import com.plotsquared.core.backup.BackupManager;
import com.plotsquared.core.commands.PlotSquaredCommandManager;
import com.plotsquared.core.components.ComponentPresetManager;
import com.plotsquared.core.configuration.ConfigurationNode;
import com.plotsquared.core.configuration.ConfigurationSection;
@@ -82,6 +85,7 @@ import com.plotsquared.core.inject.annotations.DefaultGenerator;
import com.plotsquared.core.inject.annotations.ImpromptuPipeline;
import com.plotsquared.core.inject.annotations.WorldConfig;
import com.plotsquared.core.inject.annotations.WorldFile;
import com.plotsquared.core.inject.modules.CommandModule;
import com.plotsquared.core.inject.modules.PlotSquaredModule;
import com.plotsquared.core.listener.PlotListener;
import com.plotsquared.core.listener.WESubscriber;
@@ -252,11 +256,6 @@ public final class BukkitPlatform extends JavaPlugin implements Listener, PlotPl
return Bukkit.getVersion();
}
@Override
public @NonNull String serverBrand() {
return Bukkit.getName();
}
@Override
@SuppressWarnings("deprecation") // Paper deprecation
public void onEnable() {
@@ -297,6 +296,8 @@ public final class BukkitPlatform extends JavaPlugin implements Listener, PlotPl
new PermissionModule(),
new WorldManagerModule(),
new PlotSquaredModule(),
new CommandModule(),
new CloudModule(this),
new BukkitModule(this),
new BackupModule()
);
@@ -368,9 +369,6 @@ public final class BukkitPlatform extends JavaPlugin implements Listener, PlotPl
getServer().getPluginManager().registerEvents(injector().getInstance(PlayerEventListener1201.class), this);
}
getServer().getPluginManager().registerEvents(injector().getInstance(BlockEventListener.class), this);
if (Settings.HIGH_FREQUENCY_LISTENER) {
getServer().getPluginManager().registerEvents(injector().getInstance(HighFreqBlockEventListener.class), this);
}
if (serverVersion()[1] >= 17) {
getServer().getPluginManager().registerEvents(injector().getInstance(BlockEventListener117.class), this);
}
@@ -395,6 +393,8 @@ public final class BukkitPlatform extends JavaPlugin implements Listener, PlotPl
// Commands
if (Settings.Enabled_Components.COMMANDS) {
this.registerCommands();
// Register the commands.
this.injector().getInstance(PlotSquaredCommandManager.class).registerDefaultCommands();
}
// Permissions
@@ -786,31 +786,22 @@ public final class BukkitPlatform extends JavaPlugin implements Listener, PlotPl
Iterator<Entity> iterator = entities.iterator();
while (iterator.hasNext()) {
Entity entity = iterator.next();
//noinspection ConstantValue - getEntitySpawnReason annotated as NotNull, but is not NotNull. lol.
if (PaperLib.isPaper() && entity.getEntitySpawnReason() != null && "CUSTOM".equals(entity.getEntitySpawnReason().name())) {
continue;
}
// Fallback for Spigot not having Entity#getEntitySpawnReason
if (entity.getMetadata("ps_custom_spawned").stream().anyMatch(MetadataValue::asBoolean)) {
continue;
}
// TODO: use (type) pattern matching when targeting java 21
switch (entity.getType().toString()) {
case "EGG":
case "FISHING_HOOK", "FISHING_BOBBER":
case "ENDER_SIGNAL", "EYE_OF_ENDER":
case "FISHING_HOOK":
case "ENDER_SIGNAL":
case "AREA_EFFECT_CLOUD":
case "EXPERIENCE_ORB":
case "LEASH_HITCH", "LEASH_KNOT":
case "FIREWORK", "FIREWORK_ROCKET":
case "LIGHTNING", "LIGHTNING_BOLT":
case "LEASH_HITCH":
case "FIREWORK":
case "LIGHTNING":
case "WITHER_SKULL":
case "UNKNOWN":
case "PLAYER":
// non moving / unmovable
continue;
case "THROWN_EXP_BOTTLE", "EXPERIENCE_BOTTLE":
case "SPLASH_POTION", "POTION":
case "THROWN_EXP_BOTTLE":
case "SPLASH_POTION":
case "SNOWBALL":
case "SHULKER_BULLET":
case "SPECTRAL_ARROW":
@@ -828,26 +819,14 @@ public final class BukkitPlatform extends JavaPlugin implements Listener, PlotPl
// Temporarily classify as vehicle
case "MINECART":
case "MINECART_CHEST":
case "CHEST_MINECART":
case "MINECART_COMMAND":
case "COMMAND_BLOCK_MINECART":
case "MINECART_FURNACE":
case "FURNACE_MINECART":
case "MINECART_HOPPER":
case "HOPPER_MINECART":
case "MINECART_MOB_SPAWNER":
case "SPAWNER_MINECART":
case "END_CRYSTAL":
case "ENDER_CRYSTAL": // Backwards compatibility for 1.20.4
case "ENDER_CRYSTAL":
case "MINECART_TNT":
case "TNT_MINECART":
case "CHEST_BOAT":
case "BOAT":
case "ACACIA_BOAT", "BIRCH_BOAT", "CHERRY_BOAT", "DARK_OAK_BOAT", "JUNGLE_BOAT", "MANGROVE_BOAT",
"OAK_BOAT", "PALE_OAK_BOAT", "SPRUCE_BOAT", "BAMBOO_RAFT":
case "ACACIA_CHEST_BOAT", "BIRCH_CHEST_BOAT", "CHERRY_CHEST_BOAT", "DARK_OAK_CHEST_BOAT",
"JUNGLE_CHEST_BOAT", "MANGROVE_CHEST_BOAT", "OAK_CHEST_BOAT", "PALE_OAK_CHEST_BOAT",
"SPRUCE_CHEST_BOAT", "BAMBOO_CHEST_RAFT":
if (Settings.Enabled_Components.KILL_ROAD_VEHICLES) {
com.plotsquared.core.location.Location location = BukkitUtil.adapt(entity.getLocation());
Plot plot = location.getPlot();
@@ -876,14 +855,14 @@ public final class BukkitPlatform extends JavaPlugin implements Listener, PlotPl
case "SMALL_FIREBALL":
case "FIREBALL":
case "DRAGON_FIREBALL":
case "DROPPED_ITEM", "ITEM":
case "DROPPED_ITEM":
if (Settings.Enabled_Components.KILL_ROAD_ITEMS
&& plotArea.getOwnedPlotAbs(BukkitUtil.adapt(entity.getLocation())) == null) {
this.removeRoadEntity(entity, iterator);
}
// dropped item
continue;
case "PRIMED_TNT", "TNT":
case "PRIMED_TNT":
case "FALLING_BLOCK":
// managed elsewhere
continue;
@@ -895,7 +874,8 @@ public final class BukkitPlatform extends JavaPlugin implements Listener, PlotPl
if (livingEntity.isLeashed() && !Settings.Enabled_Components.KILL_OWNED_ROAD_MOBS) {
continue;
}
if (entity.hasMetadata("keep")) {
List<MetadataValue> keep = entity.getMetadata("keep");
if (!keep.isEmpty()) {
continue;
}
@@ -956,14 +936,12 @@ public final class BukkitPlatform extends JavaPlugin implements Listener, PlotPl
case "ENDERMITE":
case "ENDER_DRAGON":
case "GHAST":
case "HAPPY_GHAST": // 1.21.6+
case "GHASTLING": // 1.21.6+
case "GIANT":
case "GUARDIAN":
case "HORSE":
case "IRON_GOLEM":
case "MAGMA_CUBE":
case "MUSHROOM_COW", "MOOSHROOM":
case "MUSHROOM_COW":
case "OCELOT":
case "PIG":
case "PIG_ZOMBIE":
@@ -972,7 +950,7 @@ public final class BukkitPlatform extends JavaPlugin implements Listener, PlotPl
case "SILVERFISH":
case "SKELETON":
case "SLIME":
case "SNOWMAN", "SNOW_GOLEM":
case "SNOWMAN":
case "SPIDER":
case "SQUID":
case "VILLAGER":
@@ -1185,9 +1163,7 @@ public final class BukkitPlatform extends JavaPlugin implements Listener, PlotPl
@Override
public @NonNull String serverNativePackage() {
final String name = Bukkit.getServer().getClass().getPackage().getName();
String ver = name.substring(name.lastIndexOf('.') + 1);
// org.bukkit.craftbukkit is no longer suffixed by a version
return ver.equals("craftbukkit") ? "" : ver;
return name.substring(name.lastIndexOf('.') + 1);
}
@Override
@@ -1295,13 +1271,15 @@ public final class BukkitPlatform extends JavaPlugin implements Listener, PlotPl
@Override
public @NonNull PlatformWorldManager<?> worldManager() {
return this.worldManager;
return injector().getInstance(Key.get(new TypeLiteral<PlatformWorldManager<World>>() {
}));
}
@Override
@NonNull
@SuppressWarnings("unchecked")
public PlayerManager<? extends PlotPlayer<Player>, ? extends Player> playerManager() {
return this.playerManager;
return (PlayerManager<BukkitPlayer, Player>) injector().getInstance(PlayerManager.class);
}
@Override

View File

@@ -0,0 +1,50 @@
/*
* 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.commands;
import com.plotsquared.bukkit.util.BukkitUtil;
import com.plotsquared.core.player.ConsolePlayer;
import com.plotsquared.core.player.PlotPlayer;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.incendo.cloud.SenderMapper;
/**
* Mapper between {@link CommandSender} and {@link PlotPlayer}.
*/
public final class BukkitSenderMapper implements SenderMapper<CommandSender, PlotPlayer<?>> {
@Override
public @NonNull PlotPlayer<?> map(final @NonNull CommandSender base) {
if (base instanceof Player player) {
return BukkitUtil.adapt(player);
}
return ConsolePlayer.getConsole();
}
@Override
public @NonNull CommandSender reverse(final @NonNull PlotPlayer<?> mapped) {
if (mapped instanceof ConsolePlayer) {
return Bukkit.getConsoleSender();
}
return (Player) mapped.getPlatformPlayer();
}
}

View File

@@ -25,6 +25,7 @@ import org.bukkit.Art;
import org.bukkit.DyeColor;
import org.bukkit.Location;
import org.bukkit.Rotation;
import org.bukkit.TreeSpecies;
import org.bukkit.World;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.AbstractHorse;
@@ -33,7 +34,6 @@ import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Bat;
import org.bukkit.entity.Boat;
import org.bukkit.entity.Breedable;
import org.bukkit.entity.ChestBoat;
import org.bukkit.entity.ChestedHorse;
import org.bukkit.entity.EnderDragon;
import org.bukkit.entity.Entity;
@@ -44,6 +44,7 @@ import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Painting;
import org.bukkit.entity.Rabbit;
import org.bukkit.entity.Sheep;
import org.bukkit.entity.Slime;
import org.bukkit.entity.Tameable;
import org.bukkit.inventory.EntityEquipment;
import org.bukkit.inventory.InventoryHolder;
@@ -102,20 +103,12 @@ public final class ReplicatingEntityWrapper extends EntityWrapper {
this.noGravity = true;
}
switch (entity.getType().toString()) {
case "BOAT", "ACACIA_BOAT", "BIRCH_BOAT", "CHERRY_BOAT", "DARK_OAK_BOAT", "JUNGLE_BOAT", "MANGROVE_BOAT",
"OAK_BOAT", "PALE_OAK_BOAT", "SPRUCE_BOAT", "BAMBOO_RAFT" -> {
case "BOAT" -> {
Boat boat = (Boat) entity;
this.dataByte = getOrdinal(Boat.Type.values(), boat.getBoatType());
this.dataByte = getOrdinal(TreeSpecies.values(), boat.getWoodType());
return;
}
case "ACACIA_CHEST_BOAT", "BIRCH_CHEST_BOAT", "CHERRY_CHEST_BOAT", "DARK_OAK_CHEST_BOAT",
"JUNGLE_CHEST_BOAT", "MANGROVE_CHEST_BOAT", "OAK_CHEST_BOAT", "PALE_OAK_CHEST_BOAT",
"SPRUCE_CHEST_BOAT", "BAMBOO_CHEST_RAFT" -> {
ChestBoat boat = (ChestBoat) entity;
this.dataByte = getOrdinal(Boat.Type.values(), boat.getBoatType());
storeInventory(boat);
}
case "ARROW", "EGG", "END_CRYSTAL", "ENDER_CRYSTAL", "ENDER_PEARL", "ENDER_SIGNAL", "EXPERIENCE_ORB", "FALLING_BLOCK", "FIREBALL",
case "ARROW", "EGG", "ENDER_CRYSTAL", "ENDER_PEARL", "ENDER_SIGNAL", "EXPERIENCE_ORB", "FALLING_BLOCK", "FIREBALL",
"FIREWORK", "FISHING_HOOK", "LEASH_HITCH", "LIGHTNING", "MINECART", "MINECART_COMMAND", "MINECART_MOB_SPAWNER",
"MINECART_TNT", "PLAYER", "PRIMED_TNT", "SLIME", "SMALL_FIREBALL", "SNOWBALL", "MINECART_FURNACE", "SPLASH_POTION",
"THROWN_EXP_BOTTLE", "WITHER_SKULL", "UNKNOWN", "SPECTRAL_ARROW", "SHULKER_BULLET", "DRAGON_FIREBALL", "AREA_EFFECT_CLOUD",
@@ -124,7 +117,7 @@ public final class ReplicatingEntityWrapper extends EntityWrapper {
return;
}
// MISC //
case "DROPPED_ITEM", "ITEM" -> {
case "DROPPED_ITEM" -> {
Item item = (Item) entity;
this.stack = item.getItemStack();
return;
@@ -154,14 +147,14 @@ public final class ReplicatingEntityWrapper extends EntityWrapper {
}
// END MISC //
// INVENTORY HOLDER //
case "MINECART_CHEST", "CHEST_MINECART", "MINECART_HOPPER", "HOPPER_MINECART" -> {
case "MINECART_CHEST", "MINECART_HOPPER" -> {
storeInventory((InventoryHolder) entity);
return;
}
// START LIVING ENTITY //
// START AGEABLE //
// START TAMEABLE //
case "CAMEL", "HORSE", "DONKEY", "LLAMA", "TRADER_LLAMA", "MULE", "SKELETON_HORSE", "ZOMBIE_HORSE" -> {
case "HORSE", "DONKEY", "LLAMA", "MULE", "SKELETON_HORSE" -> {
AbstractHorse horse = (AbstractHorse) entity;
this.horse = new HorseStats();
this.horse.jump = horse.getJumpStrength();
@@ -179,13 +172,14 @@ public final class ReplicatingEntityWrapper extends EntityWrapper {
return;
}
// END INVENTORY HOLDER //
case "WOLF", "OCELOT", "CAT", "PARROT" -> {
case "WOLF", "OCELOT" -> {
storeTameable((Tameable) entity);
storeBreedable((Breedable) entity);
storeLiving((LivingEntity) entity);
return;
}
// END TAMEABLE //
//todo fix sheep
case "SHEEP" -> {
Sheep sheep = (Sheep) entity;
if (sheep.isSheared()) {
@@ -193,7 +187,7 @@ public final class ReplicatingEntityWrapper extends EntityWrapper {
} else {
this.dataByte = (byte) 0;
}
this.dataByte2 = getOrdinal(DyeColor.values(), sheep.getColor());
this.dataByte2 = sheep.getColor().getDyeData();
storeBreedable(sheep);
storeLiving(sheep);
return;
@@ -272,9 +266,9 @@ public final class ReplicatingEntityWrapper extends EntityWrapper {
this.dataByte = (byte) entity1.getPhase().ordinal();
return;
}
case "SKELETON", "WITHER_SKELETON", "GUARDIAN", "ELDER_GUARDIAN", "GHAST", "HAPPY_GHAST", "GHASTLING", "MAGMA_CUBE", "SQUID", "PIG_ZOMBIE", "HOGLIN",
case "SKELETON", "WITHER_SKELETON", "GUARDIAN", "ELDER_GUARDIAN", "GHAST", "MAGMA_CUBE", "SQUID", "PIG_ZOMBIE", "HOGLIN",
"ZOMBIFIED_PIGLIN", "PIGLIN", "PIGLIN_BRUTE", "ZOMBIE", "WITHER", "WITCH", "SPIDER", "CAVE_SPIDER", "SILVERFISH",
"GIANT", "ENDERMAN", "CREEPER", "BLAZE", "SHULKER", "SNOWMAN", "SNOW_GOLEM" -> {
"GIANT", "ENDERMAN", "CREEPER", "BLAZE", "SHULKER", "SNOWMAN" -> {
storeLiving((LivingEntity) entity);
return;
}
@@ -456,7 +450,7 @@ public final class ReplicatingEntityWrapper extends EntityWrapper {
}
Entity entity;
switch (this.getType().toString()) {
case "DROPPED_ITEM", "ITEM" -> {
case "DROPPED_ITEM" -> {
return world.dropItem(location, this.stack);
}
case "PLAYER", "LEASH_HITCH" -> {
@@ -492,26 +486,16 @@ public final class ReplicatingEntityWrapper extends EntityWrapper {
entity.setGravity(false);
}
switch (entity.getType().toString()) {
case "BOAT", "ACACIA_BOAT", "BIRCH_BOAT", "CHERRY_BOAT", "DARK_OAK_BOAT", "JUNGLE_BOAT", "MANGROVE_BOAT",
"OAK_BOAT", "PALE_OAK_BOAT", "SPRUCE_BOAT", "BAMBOO_RAFT" -> {
case "BOAT" -> {
Boat boat = (Boat) entity;
boat.setBoatType(Boat.Type.values()[dataByte]);
boat.setWoodType(TreeSpecies.values()[dataByte]);
return entity;
}
case "ACACIA_CHEST_BOAT", "BIRCH_CHEST_BOAT", "CHERRY_CHEST_BOAT", "DARK_OAK_CHEST_BOAT",
"JUNGLE_CHEST_BOAT", "MANGROVE_CHEST_BOAT", "OAK_CHEST_BOAT", "PALE_OAK_CHEST_BOAT",
"SPRUCE_CHEST_BOAT", "BAMBOO_CHEST_RAFT" -> {
ChestBoat boat = (ChestBoat) entity;
boat.setBoatType(Boat.Type.values()[dataByte]);
restoreInventory(boat);
return entity;
}
// SLIME is not even stored
/* case "SLIME" -> {
case "SLIME" -> {
((Slime) entity).setSize(this.dataByte);
return entity;
} */
case "ARROW", "EGG", "END_CRYSTAL", "ENDER_CRYSTAL", "ENDER_PEARL", "ENDER_SIGNAL", "DROPPED_ITEM", "EXPERIENCE_ORB", "FALLING_BLOCK",
}
case "ARROW", "EGG", "ENDER_CRYSTAL", "ENDER_PEARL", "ENDER_SIGNAL", "DROPPED_ITEM", "EXPERIENCE_ORB", "FALLING_BLOCK",
"FIREBALL", "FIREWORK", "FISHING_HOOK", "LEASH_HITCH", "LIGHTNING", "MINECART", "MINECART_COMMAND",
"MINECART_MOB_SPAWNER", "MINECART_TNT", "PLAYER", "PRIMED_TNT", "SMALL_FIREBALL", "SNOWBALL",
"SPLASH_POTION", "THROWN_EXP_BOTTLE", "SPECTRAL_ARROW", "SHULKER_BULLET", "AREA_EFFECT_CLOUD",
@@ -534,14 +518,14 @@ public final class ReplicatingEntityWrapper extends EntityWrapper {
}
// END MISC //
// INVENTORY HOLDER //
case "MINECART_CHEST", "CHEST_MINECART", "MINECART_HOPPER", "HOPPER_MINECART" -> {
case "MINECART_CHEST", "MINECART_HOPPER" -> {
restoreInventory((InventoryHolder) entity);
return entity;
}
// START LIVING ENTITY //
// START AGEABLE //
// START TAMEABLE //
case "CAMEL", "HORSE", "DONKEY", "LLAMA", "TRADER_LLAMA", "MULE", "SKELETON_HORSE", "ZOMBIE_HORSE" -> {
case "HORSE", "LLAMA", "SKELETON_HORSE", "DONKEY", "MULE" -> {
AbstractHorse horse = (AbstractHorse) entity;
horse.setJumpStrength(this.horse.jump);
if (horse instanceof ChestedHorse) {
@@ -558,7 +542,7 @@ public final class ReplicatingEntityWrapper extends EntityWrapper {
return entity;
}
// END INVENTORY HOLDER //
case "WOLF", "OCELOT", "CAT", "PARROT" -> {
case "WOLF", "OCELOT" -> {
restoreTameable((Tameable) entity);
restoreBreedable((Breedable) entity);
restoreLiving((LivingEntity) entity);
@@ -571,7 +555,7 @@ public final class ReplicatingEntityWrapper extends EntityWrapper {
sheep.setSheared(true);
}
if (this.dataByte2 != 0) {
sheep.setColor(DyeColor.values()[this.dataByte2]);
sheep.setColor(DyeColor.getByDyeData(this.dataByte2));
}
restoreBreedable(sheep);
restoreLiving(sheep);
@@ -676,7 +660,7 @@ public final class ReplicatingEntityWrapper extends EntityWrapper {
restoreLiving((LivingEntity) entity);
return entity;
}
case "ENDERMITE", "GHAST", "HAPPY_GHAST", "GHASTLING", "MAGMA_CUBE", "SQUID", "PIG_ZOMBIE", "HOGLIN", "PIGLIN", "ZOMBIFIED_PIGLIN", "PIGLIN_BRUTE", "ZOMBIE", "WITHER", "WITCH", "SPIDER", "CAVE_SPIDER", "SILVERFISH", "GIANT", "ENDERMAN", "CREEPER", "BLAZE", "SNOWMAN", "SHULKER", "GUARDIAN", "ELDER_GUARDIAN", "SKELETON", "WITHER_SKELETON" -> {
case "ENDERMITE", "GHAST", "MAGMA_CUBE", "SQUID", "PIG_ZOMBIE", "HOGLIN", "PIGLIN", "ZOMBIFIED_PIGLIN", "PIGLIN_BRUTE", "ZOMBIE", "WITHER", "WITCH", "SPIDER", "CAVE_SPIDER", "SILVERFISH", "GIANT", "ENDERMAN", "CREEPER", "BLAZE", "SNOWMAN", "SHULKER", "GUARDIAN", "ELDER_GUARDIAN", "SKELETON", "WITHER_SKELETON" -> {
restoreLiving((LivingEntity) entity);
return entity;
}

View File

@@ -52,7 +52,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.EnumSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
@@ -90,7 +90,12 @@ public class BukkitPlotGenerator extends ChunkGenerator implements GeneratorWrap
this.plotGenerator = generator;
this.platformGenerator = this;
this.populators = new ArrayList<>();
this.populators.add(new BlockStatePopulator(this.plotGenerator));
int minecraftMinorVersion = PlotSquared.platform().serverVersion()[1];
if (minecraftMinorVersion >= 17) {
this.populators.add(new BlockStatePopulator(this.plotGenerator));
} else {
this.populators.add(new LegacyBlockStatePopulator(this.plotGenerator));
}
this.full = true;
this.useNewGenerationMethods = PlotSquared.platform().serverVersion()[1] >= 19;
this.biomeProvider = new BukkitPlotBiomeProvider();
@@ -440,7 +445,7 @@ public class BukkitPlotGenerator extends ChunkGenerator implements GeneratorWrap
private static final List<Biome> BIOMES;
static {
Set<Biome> disabledBiomes = new HashSet<>(List.of(Biome.CUSTOM));
Set<Biome> disabledBiomes = EnumSet.of(Biome.CUSTOM);
if (PlotSquared.platform().serverVersion()[1] <= 19) {
final Biome cherryGrove = Registry.BIOME.get(NamespacedKey.minecraft("cherry_grove"));
if (cherryGrove != null) {

View File

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

View File

@@ -0,0 +1,99 @@
/*
* 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.inject;
import com.google.inject.AbstractModule;
import com.google.inject.Key;
import com.google.inject.TypeLiteral;
import com.plotsquared.bukkit.BukkitPlatform;
import com.plotsquared.bukkit.commands.BukkitSenderMapper;
import com.plotsquared.bukkit.util.BukkitUtil;
import com.plotsquared.core.commands.CommandRequirement;
import com.plotsquared.core.commands.PlotSquaredCaptionProvider;
import com.plotsquared.core.commands.PlotSquaredRequirementFailureHandler;
import com.plotsquared.core.configuration.caption.TranslatableCaption;
import com.plotsquared.core.player.ConsolePlayer;
import com.plotsquared.core.player.PlotPlayer;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.incendo.cloud.CommandManager;
import org.incendo.cloud.bukkit.CloudBukkitCapabilities;
import org.incendo.cloud.execution.ExecutionCoordinator;
import org.incendo.cloud.minecraft.extras.MinecraftExceptionHandler;
import org.incendo.cloud.paper.PaperCommandManager;
import org.incendo.cloud.processors.requirements.RequirementPostprocessor;
public class CloudModule extends AbstractModule {
private static final Logger LOGGER = LogManager.getLogger("PlotSquared/" + CloudModule.class.getSimpleName());
private static @NonNull CommandSender convert(final @NonNull PlotPlayer<?> player) {
if (player instanceof ConsolePlayer) {
return Bukkit.getConsoleSender();
}
return (Player) player.getPlatformPlayer();
}
private static @NonNull PlotPlayer<?> convert (final @NonNull CommandSender sender) {
if (sender instanceof Player player) {
return BukkitUtil.adapt(player);
}
return ConsolePlayer.getConsole();
}
private final BukkitPlatform bukkitPlatform;
public CloudModule(final @NonNull BukkitPlatform bukkitPlatform) {
this.bukkitPlatform = bukkitPlatform;
}
@Override
protected void configure() {
final PaperCommandManager<PlotPlayer<?>> commandManager = new PaperCommandManager<PlotPlayer<?>>(
this.bukkitPlatform,
ExecutionCoordinator.asyncCoordinator(),
new BukkitSenderMapper()
);
commandManager.captionRegistry().registerProvider(new PlotSquaredCaptionProvider());
if (commandManager.hasCapability(CloudBukkitCapabilities.NATIVE_BRIGADIER)) {
commandManager.registerBrigadier();
} else if (commandManager.hasCapability(CloudBukkitCapabilities.ASYNCHRONOUS_COMPLETION)) {
commandManager.registerAsynchronousCompletions();
}
final RequirementPostprocessor<PlotPlayer<?>, CommandRequirement> requirementPostprocessor =
RequirementPostprocessor.of(CommandRequirement.REQUIREMENTS_KEY, new PlotSquaredRequirementFailureHandler());
commandManager.registerCommandPostProcessor(requirementPostprocessor);
// TODO(City): Override parsing errors using MM parsing.
MinecraftExceptionHandler.<PlotPlayer<?>>create(PlotPlayer::getAudience)
.defaultHandlers()
.decorator((ctx, component) -> TranslatableCaption.of("core.prefix").
toComponent(ctx.context().sender())
.append(component))
.registerTo(commandManager);
bind(Key.get(new TypeLiteral<CommandManager<PlotPlayer<?>>>() {})).toInstance(commandManager);
}
}

View File

@@ -24,6 +24,7 @@ import com.plotsquared.bukkit.util.BukkitUtil;
import com.plotsquared.core.PlotSquared;
import com.plotsquared.core.configuration.Settings;
import com.plotsquared.core.configuration.caption.TranslatableCaption;
import com.plotsquared.core.database.DBFunc;
import com.plotsquared.core.location.Location;
import com.plotsquared.core.permissions.Permission;
import com.plotsquared.core.player.PlotPlayer;
@@ -47,6 +48,7 @@ import com.plotsquared.core.plot.flag.implementations.LeafDecayFlag;
import com.plotsquared.core.plot.flag.implementations.LiquidFlowFlag;
import com.plotsquared.core.plot.flag.implementations.MycelGrowFlag;
import com.plotsquared.core.plot.flag.implementations.PlaceFlag;
import com.plotsquared.core.plot.flag.implementations.RedstoneFlag;
import com.plotsquared.core.plot.flag.implementations.SnowFormFlag;
import com.plotsquared.core.plot.flag.implementations.SnowMeltFlag;
import com.plotsquared.core.plot.flag.implementations.SoilDryFlag;
@@ -60,6 +62,7 @@ import com.plotsquared.core.util.task.TaskTime;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.world.block.BlockType;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.tag.Tag;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import org.bukkit.Bukkit;
@@ -89,9 +92,11 @@ import org.bukkit.event.block.BlockFromToEvent;
import org.bukkit.event.block.BlockGrowEvent;
import org.bukkit.event.block.BlockIgniteEvent;
import org.bukkit.event.block.BlockMultiPlaceEvent;
import org.bukkit.event.block.BlockPhysicsEvent;
import org.bukkit.event.block.BlockPistonExtendEvent;
import org.bukkit.event.block.BlockPistonRetractEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.block.BlockRedstoneEvent;
import org.bukkit.event.block.BlockSpreadEvent;
import org.bukkit.event.block.CauldronLevelChangeEvent;
import org.bukkit.event.block.EntityBlockFormEvent;
@@ -106,6 +111,10 @@ import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.bukkit.Tag.CORALS;
import static org.bukkit.Tag.CORAL_BLOCKS;
@@ -113,6 +122,20 @@ import static org.bukkit.Tag.WALL_CORALS;
@SuppressWarnings("unused")
public class BlockEventListener implements Listener {
private static final Set<Material> PISTONS = Set.of(
Material.PISTON,
Material.STICKY_PISTON
);
private static final Set<Material> PHYSICS_BLOCKS = Set.of(
Material.TURTLE_EGG,
Material.TURTLE_SPAWN_EGG
);
private static final Set<Material> SNOW = Stream.of(Material.values()) // needed as Tag.SNOW isn't present in 1.16.5
.filter(material -> material.name().contains("SNOW"))
.filter(Material::isBlock)
.collect(Collectors.toUnmodifiableSet());
private final PlotAreaManager plotAreaManager;
private final WorldEdit worldEdit;
@@ -141,6 +164,114 @@ public class BlockEventListener implements Listener {
}, TaskTime.ticks(3L));
}
@EventHandler
public void onRedstoneEvent(BlockRedstoneEvent event) {
Block block = event.getBlock();
Location location = BukkitUtil.adapt(block.getLocation());
PlotArea area = location.getPlotArea();
if (area == null) {
return;
}
Plot plot = location.getOwnedPlot();
if (plot == null) {
if (PlotFlagUtil.isAreaRoadFlagsAndFlagEquals(area, RedstoneFlag.class, false)) {
event.setNewCurrent(0);
}
return;
}
if (!plot.getFlag(RedstoneFlag.class)) {
event.setNewCurrent(0);
plot.debug("Redstone event was cancelled because redstone = false");
return;
}
if (Settings.Redstone.DISABLE_OFFLINE) {
boolean disable = false;
if (!DBFunc.SERVER.equals(plot.getOwner())) {
if (plot.isMerged()) {
disable = true;
for (UUID owner : plot.getOwners()) {
if (PlotSquared.platform().playerManager().getPlayerIfExists(owner) != null) {
disable = false;
break;
}
}
} else {
disable = PlotSquared.platform().playerManager().getPlayerIfExists(plot.getOwnerAbs()) == null;
}
}
if (disable) {
for (UUID trusted : plot.getTrusted()) {
if (PlotSquared.platform().playerManager().getPlayerIfExists(trusted) != null) {
disable = false;
break;
}
}
if (disable) {
event.setNewCurrent(0);
plot.debug("Redstone event was cancelled because no trusted player was in the plot");
return;
}
}
}
if (Settings.Redstone.DISABLE_UNOCCUPIED) {
for (final PlotPlayer<?> player : PlotSquared.platform().playerManager().getPlayers()) {
if (plot.equals(player.getCurrentPlot())) {
return;
}
}
event.setNewCurrent(0);
}
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
public void onPhysicsEvent(BlockPhysicsEvent event) {
Block block = event.getBlock();
Location location = BukkitUtil.adapt(block.getLocation());
PlotArea area = location.getPlotArea();
if (area == null) {
return;
}
Plot plot = area.getOwnedPlotAbs(location);
if (plot == null) {
return;
}
if (event.getChangedType().hasGravity() && plot.getFlag(DisablePhysicsFlag.class)) {
event.setCancelled(true);
sendBlockChange(event.getBlock().getLocation(), event.getBlock().getBlockData());
plot.debug("Prevented block physics and resent block change because disable-physics = true");
return;
}
if (event.getChangedType() == Material.COMPARATOR) {
if (!plot.getFlag(RedstoneFlag.class)) {
event.setCancelled(true);
plot.debug("Prevented comparator update because redstone = false");
}
return;
}
if (PHYSICS_BLOCKS.contains(event.getChangedType())) {
if (plot.getFlag(DisablePhysicsFlag.class)) {
event.setCancelled(true);
plot.debug("Prevented block physics because disable-physics = true");
}
return;
}
if (Settings.Redstone.DETECT_INVALID_EDGE_PISTONS) {
if (PISTONS.contains(block.getType())) {
org.bukkit.block.data.Directional piston = (org.bukkit.block.data.Directional) block.getBlockData();
final BlockFace facing = piston.getFacing();
location = location.add(facing.getModX(), facing.getModY(), facing.getModZ());
Plot newPlot = area.getOwnedPlotAbs(location);
if (plot.equals(newPlot)) {
return;
}
if (!plot.isMerged() || !plot.getConnectedPlots().contains(newPlot)) {
event.setCancelled(true);
plot.debug("Prevented piston update because of invalid edge piston detection");
}
}
}
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void blockCreate(BlockPlaceEvent event) {
Location location = BukkitUtil.adapt(event.getBlock().getLocation());
@@ -154,6 +285,13 @@ public class BlockEventListener implements Listener {
if (plot != null) {
if (area.notifyIfOutsideBuildArea(pp, location.getY())) {
event.setCancelled(true);
pp.sendMessage(
TranslatableCaption.of("height.height_limit"),
TagResolver.builder()
.tag("minheight", Tag.inserting(Component.text(area.getMinBuildHeight())))
.tag("maxheight", Tag.inserting(Component.text(area.getMaxBuildHeight())))
.build()
);
return;
}
if (!plot.hasOwner()) {
@@ -245,6 +383,13 @@ public class BlockEventListener implements Listener {
}
} else if (area.notifyIfOutsideBuildArea(plotPlayer, location.getY())) {
event.setCancelled(true);
plotPlayer.sendMessage(
TranslatableCaption.of("height.height_limit"),
TagResolver.builder()
.tag("minheight", Tag.inserting(Component.text(area.getMinBuildHeight())))
.tag("maxheight", Tag.inserting(Component.text(area.getMaxBuildHeight())))
.build()
);
return;
}
if (!plot.hasOwner()) {
@@ -525,11 +670,7 @@ public class BlockEventListener implements Listener {
BlockBreakEvent call = new BlockBreakEvent(block, player);
Bukkit.getServer().getPluginManager().callEvent(call);
if (!call.isCancelled()) {
if (Settings.Flags.INSTABREAK_CONSIDER_TOOL) {
block.breakNaturally(event.getItemInHand());
} else {
block.breakNaturally();
}
event.getBlock().breakNaturally();
}
}
// == rather than <= as we only care about the "ground level" not being destroyed
@@ -1200,9 +1341,18 @@ public class BlockEventListener implements Listener {
if (pp.hasPermission(Permission.PERMISSION_ADMIN_BUILD_HEIGHT_LIMIT)) {
continue;
}
if (area.notifyIfOutsideBuildArea(pp, currentLocation.getY())) {
event.setCancelled(true);
break;
if (currentLocation.getY() >= area.getMaxBuildHeight() || currentLocation.getY() < area.getMinBuildHeight()) {
pp.sendMessage(
TranslatableCaption.of("height.height_limit"),
TagResolver.builder()
.tag("minheight", Tag.inserting(Component.text(area.getMinBuildHeight())))
.tag("maxheight", Tag.inserting(Component.text(area.getMaxBuildHeight())))
.build()
);
if (area.notifyIfOutsideBuildArea(pp, currentLocation.getY())) {
event.setCancelled(true);
break;
}
}
}

View File

@@ -28,6 +28,7 @@ import com.plotsquared.core.plot.flag.implementations.CopperOxideFlag;
import com.plotsquared.core.plot.flag.implementations.MiscInteractFlag;
import com.plotsquared.core.plot.flag.implementations.SculkSensorInteractFlag;
import com.plotsquared.core.util.PlotFlagUtil;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Item;
@@ -41,11 +42,31 @@ import org.bukkit.event.block.BlockReceiveGameEvent;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
@SuppressWarnings("unused")
public class BlockEventListener117 implements Listener {
private static final Set<Material> COPPER_OXIDIZING = Set.of(
Material.COPPER_BLOCK,
Material.EXPOSED_COPPER,
Material.WEATHERED_COPPER,
Material.OXIDIZED_COPPER,
Material.CUT_COPPER,
Material.EXPOSED_CUT_COPPER,
Material.WEATHERED_CUT_COPPER,
Material.OXIDIZED_CUT_COPPER,
Material.CUT_COPPER_STAIRS,
Material.EXPOSED_CUT_COPPER_STAIRS,
Material.WEATHERED_CUT_COPPER_STAIRS,
Material.OXIDIZED_CUT_COPPER_STAIRS,
Material.CUT_COPPER_SLAB,
Material.EXPOSED_CUT_COPPER_SLAB,
Material.WEATHERED_CUT_COPPER_SLAB,
Material.OXIDIZED_CUT_COPPER_SLAB
);
@Inject
public BlockEventListener117() {
}
@@ -113,7 +134,7 @@ public class BlockEventListener117 implements Listener {
public void onBlockFertilize(BlockFertilizeEvent event) {
Block block = event.getBlock();
List<org.bukkit.block.BlockState> blocks = event.getBlocks();
Location location = BukkitUtil.adapt(block.getLocation());
Location location = BukkitUtil.adapt(blocks.get(0).getLocation());
PlotArea area = location.getPlotArea();
if (area == null) {
@@ -163,7 +184,7 @@ public class BlockEventListener117 implements Listener {
if (plot == null) {
return;
}
if (event.getNewState().getType().name().contains("COPPER")) {
if (COPPER_OXIDIZING.contains(event.getNewState().getType())) {
if (!plot.getFlag(CopperOxideFlag.class)) {
plot.debug("Copper could not oxide because copper-oxide = false");
event.setCancelled(true);

View File

@@ -19,7 +19,6 @@
package com.plotsquared.bukkit.listener;
import com.google.inject.Inject;
import com.plotsquared.bukkit.BukkitPlatform;
import com.plotsquared.bukkit.player.BukkitPlayer;
import com.plotsquared.bukkit.util.BukkitEntityUtil;
import com.plotsquared.bukkit.util.BukkitUtil;
@@ -37,14 +36,11 @@ import com.plotsquared.core.plot.flag.implementations.EntityChangeBlockFlag;
import com.plotsquared.core.plot.flag.implementations.ExplosionFlag;
import com.plotsquared.core.plot.flag.implementations.InvincibleFlag;
import com.plotsquared.core.plot.flag.implementations.ProjectileChangeBlockFlag;
import com.plotsquared.core.plot.flag.implementations.WeavingDeathPlace;
import com.plotsquared.core.plot.world.PlotAreaManager;
import com.plotsquared.core.util.EventDispatcher;
import com.plotsquared.core.util.PlotFlagUtil;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.util.Enums;
import com.sk89q.worldedit.world.block.BlockType;
import io.papermc.lib.PaperLib;
import org.bukkit.Material;
import org.bukkit.Particle;
import org.bukkit.World;
@@ -58,8 +54,6 @@ import org.bukkit.entity.Player;
import org.bukkit.entity.Projectile;
import org.bukkit.entity.TNTPrimed;
import org.bukkit.entity.Vehicle;
import org.bukkit.entity.minecart.ExplosiveMinecart;
import org.bukkit.event.Cancellable;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
@@ -80,54 +74,56 @@ import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
@SuppressWarnings("unused")
public class EntityEventListener implements Listener {
private static final Particle EXPLOSION_HUGE = Objects.requireNonNull(Enums.findByValue(
Particle.class,
"EXPLOSION_EMITTER",
"EXPLOSION_HUGE"
));
private final BukkitPlatform platform;
private final PlotAreaManager plotAreaManager;
private final EventDispatcher eventDispatcher;
private float lastRadius;
@Inject
public EntityEventListener(
final @NonNull BukkitPlatform platform,
final @NonNull PlotAreaManager plotAreaManager,
final @NonNull EventDispatcher eventDispatcher
) {
this.platform = platform;
this.plotAreaManager = plotAreaManager;
this.eventDispatcher = eventDispatcher;
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onEntityCombustByEntity(EntityCombustByEntityEvent event) {
onEntityDamageByEntityCommon(event.getCombuster(), event.getEntity(), EntityDamageEvent.DamageCause.FIRE_TICK, event);
EntityDamageByEntityEvent eventChange =
new EntityDamageByEntityEvent(
event.getCombuster(),
event.getEntity(),
EntityDamageEvent.DamageCause.FIRE_TICK,
event.getDuration()
);
onEntityDamageByEntityEvent(eventChange);
if (eventChange.isCancelled()) {
event.setCancelled(true);
}
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onEntityDamageByEntityEvent(EntityDamageByEntityEvent event) {
onEntityDamageByEntityCommon(event.getDamager(), event.getEntity(), event.getCause(), event);
}
private void onEntityDamageByEntityCommon(
final Entity damager,
final Entity victim,
final EntityDamageEvent.DamageCause cause,
final Cancellable event
) {
Entity damager = event.getDamager();
Location location = BukkitUtil.adapt(damager.getLocation());
if (!this.plotAreaManager.hasPlotArea(location.getWorldName())) {
return;
}
if (!BukkitEntityUtil.entityDamage(damager, victim, cause)) {
Entity victim = event.getEntity();
/*
if (victim.getType().equals(EntityType.ITEM_FRAME)) {
Plot plot = BukkitUtil.getLocation(victim).getPlot();
if (plot != null && !plot.isAdded(damager.getUniqueId())) {
event.setCancelled(true);
return;
}
}
*/
if (!BukkitEntityUtil.entityDamage(damager, victim, event.getCause())) {
if (event.isCancelled()) {
if (victim instanceof Ageable ageable) {
if (ageable.getAge() == -24000) {
@@ -161,31 +157,20 @@ public class EntityEventListener implements Listener {
}
}
case "REINFORCEMENTS", "NATURAL", "MOUNT", "PATROL", "RAID", "SHEARED", "SILVERFISH_BLOCK", "ENDER_PEARL",
"TRAP", "VILLAGE_DEFENSE", "VILLAGE_INVASION", "BEEHIVE", "CHUNK_GEN", "NETHER_PORTAL",
"FROZEN", "SPELL", "DEFAULT" -> {
"TRAP", "VILLAGE_DEFENSE", "VILLAGE_INVASION", "BEEHIVE", "CHUNK_GEN", "NETHER_PORTAL",
"DUPLICATION", "FROZEN", "SPELL", "DEFAULT" -> {
if (!area.isMobSpawning()) {
event.setCancelled(true);
return;
}
}
case "BREEDING", "DUPLICATION" -> {
case "BREEDING" -> {
if (!area.isSpawnBreeding()) {
event.setCancelled(true);
return;
}
}
case "CUSTOM" -> {
if (!area.isSpawnCustom()) {
event.setCancelled(true);
return;
}
// No need to clutter metadata if running paper
if (!PaperLib.isPaper()) {
entity.setMetadata("ps_custom_spawned", new FixedMetadataValue(this.platform, true));
}
return; // Don't cancel if mob spawning is disabled
}
case "BUILD_IRONGOLEM", "BUILD_SNOWMAN", "BUILD_WITHER" -> {
case "BUILD_IRONGOLEM", "BUILD_SNOWMAN", "BUILD_WITHER", "CUSTOM" -> {
if (!area.isSpawnCustom()) {
event.setCancelled(true);
return;
@@ -253,29 +238,6 @@ public class EntityEventListener implements Listener {
}
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
public void onWeavingEffect(EntityChangeBlockEvent event) {
if (event.getTo() != Material.COBWEB) {
return;
}
Location location = BukkitUtil.adapt(event.getBlock().getLocation());
PlotArea area = location.getPlotArea();
if (area == null) {
return;
}
Plot plot = location.getOwnedPlot();
if (plot == null) {
if (PlotFlagUtil.isAreaRoadFlagsAndFlagEquals(area, WeavingDeathPlace.class, false)) {
event.setCancelled(true);
}
return;
}
if (!plot.getFlag(WeavingDeathPlace.class)) {
plot.debug(event.getTo() + " could not spawn because weaving-death-place = false");
event.setCancelled(true);
}
}
@EventHandler(priority = EventPriority.HIGH)
public void onDamage(EntityDamageEvent event) {
if (event.getEntityType() != EntityType.PLAYER) {
@@ -323,7 +285,7 @@ public class EntityEventListener implements Listener {
if (this.lastRadius != 0) {
List<Entity> nearby = event.getEntity().getNearbyEntities(this.lastRadius, this.lastRadius, this.lastRadius);
for (Entity near : nearby) {
if (near instanceof TNTPrimed || near instanceof ExplosiveMinecart) {
if (near instanceof TNTPrimed || near.getType().equals(EntityType.MINECART_TNT)) {
if (!near.hasMetadata("plot")) {
near.setMetadata("plot", new FixedMetadataValue((Plugin) PlotSquared.platform(), plot));
}
@@ -347,7 +309,7 @@ public class EntityEventListener implements Listener {
event.setCancelled(true);
//Spawn Explosion Particles when enabled in settings
if (Settings.General.ALWAYS_SHOW_EXPLOSIONS) {
event.getLocation().getWorld().spawnParticle(EXPLOSION_HUGE, event.getLocation(), 0);
event.getLocation().getWorld().spawnParticle(Particle.EXPLOSION_HUGE, event.getLocation(), 0);
}
}
@@ -434,13 +396,7 @@ public class EntityEventListener implements Listener {
}
Plot plot = area.getOwnedPlot(location);
if (plot == null) {
if (PlotFlagUtil.isAreaRoadFlagsAndFlagEquals(area, EntityChangeBlockFlag.class, false)) {
event.setCancelled(true);
}
return;
}
if (!plot.getFlag(EntityChangeBlockFlag.class)) {
if (plot != null && !plot.getFlag(EntityChangeBlockFlag.class)) {
plot.debug(e.getType() + " could not change block because entity-change-block = false");
event.setCancelled(true);
}

View File

@@ -31,10 +31,8 @@ import org.bukkit.Chunk;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.EnderCrystal;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Item;
import org.bukkit.entity.Vehicle;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
@@ -122,19 +120,13 @@ public class EntitySpawnListener implements Listener {
Entity entity = event.getEntity();
Location location = BukkitUtil.adapt(entity.getLocation());
PlotArea area = location.getPlotArea();
if (!location.isPlotArea() || area == null) {
if (!location.isPlotArea()) {
return;
}
if (PaperLib.isPaper()) {
//noinspection ConstantValue - getEntitySpawnReason annotated as NotNull, but is not NotNull. lol.
if (area.isSpawnCustom() && entity.getEntitySpawnReason() != null && "CUSTOM".equals(entity.getEntitySpawnReason().name())) {
return;
}
}
Plot plot = location.getOwnedPlotAbs();
EntityType type = entity.getType();
if (plot == null) {
if (entity instanceof Item) {
if (type == EntityType.DROPPED_ITEM) {
if (Settings.Enabled_Components.KILL_ROAD_ITEMS) {
event.setCancelled(true);
}
@@ -156,7 +148,7 @@ public class EntitySpawnListener implements Listener {
if (Settings.Done.RESTRICT_BUILDING && DoneFlag.isDone(plot)) {
event.setCancelled(true);
}
if (entity instanceof EnderCrystal || type == EntityType.ARMOR_STAND) {
if (type == EntityType.ENDER_CRYSTAL) {
if (BukkitEntityUtil.checkEntity(entity, plot)) {
event.setCancelled(true);
}

View File

@@ -1,201 +0,0 @@
/*
* PlotSquared, a land and world management plugin for Minecraft.
* Copyright (C) IntellectualSites <https://intellectualsites.com>
* Copyright (C) IntellectualSites team and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.plotsquared.bukkit.listener;
import com.google.inject.Inject;
import com.plotsquared.bukkit.player.BukkitPlayer;
import com.plotsquared.bukkit.util.BukkitUtil;
import com.plotsquared.core.PlotSquared;
import com.plotsquared.core.configuration.Settings;
import com.plotsquared.core.database.DBFunc;
import com.plotsquared.core.location.Location;
import com.plotsquared.core.player.PlotPlayer;
import com.plotsquared.core.plot.Plot;
import com.plotsquared.core.plot.PlotArea;
import com.plotsquared.core.plot.flag.implementations.DisablePhysicsFlag;
import com.plotsquared.core.plot.flag.implementations.RedstoneFlag;
import com.plotsquared.core.plot.world.PlotAreaManager;
import com.plotsquared.core.util.PlotFlagUtil;
import com.plotsquared.core.util.task.TaskManager;
import com.plotsquared.core.util.task.TaskTime;
import com.sk89q.worldedit.WorldEdit;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.data.BlockData;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockPhysicsEvent;
import org.bukkit.event.block.BlockRedstoneEvent;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Set;
import java.util.UUID;
@SuppressWarnings("unused")
public class HighFreqBlockEventListener implements Listener {
private static final Set<Material> PISTONS = Set.of(
Material.PISTON,
Material.STICKY_PISTON
);
private static final Set<Material> PHYSICS_BLOCKS = Set.of(
Material.TURTLE_EGG,
Material.TURTLE_SPAWN_EGG
);
private final PlotAreaManager plotAreaManager;
private final WorldEdit worldEdit;
@Inject
public HighFreqBlockEventListener(final @NonNull PlotAreaManager plotAreaManager, final @NonNull WorldEdit worldEdit) {
this.plotAreaManager = plotAreaManager;
this.worldEdit = worldEdit;
}
public static void sendBlockChange(final org.bukkit.Location bloc, final BlockData data) {
TaskManager.runTaskLater(() -> {
String world = bloc.getWorld().getName();
int x = bloc.getBlockX();
int z = bloc.getBlockZ();
int distance = Bukkit.getViewDistance() * 16;
for (final PlotPlayer<?> player : PlotSquared.platform().playerManager().getPlayers()) {
Location location = player.getLocation();
if (location.getWorldName().equals(world)) {
if (16 * Math.abs(location.getX() - x) / 16 > distance || 16 * Math.abs(location.getZ() - z) / 16 > distance) {
continue;
}
((BukkitPlayer) player).player.sendBlockChange(bloc, data);
}
}
}, TaskTime.ticks(3L));
}
@EventHandler
public void onRedstoneEvent(BlockRedstoneEvent event) {
Block block = event.getBlock();
Location location = BukkitUtil.adapt(block.getLocation());
PlotArea area = location.getPlotArea();
if (area == null) {
return;
}
Plot plot = location.getOwnedPlot();
if (plot == null) {
if (PlotFlagUtil.isAreaRoadFlagsAndFlagEquals(area, RedstoneFlag.class, false)) {
event.setNewCurrent(0);
}
return;
}
if (!plot.getFlag(RedstoneFlag.class)) {
event.setNewCurrent(0);
plot.debug("Redstone event was cancelled because redstone = false");
return;
}
if (Settings.Redstone.DISABLE_OFFLINE) {
boolean disable = false;
if (!DBFunc.SERVER.equals(plot.getOwner())) {
if (plot.isMerged()) {
disable = true;
for (UUID owner : plot.getOwners()) {
if (PlotSquared.platform().playerManager().getPlayerIfExists(owner) != null) {
disable = false;
break;
}
}
} else {
disable = PlotSquared.platform().playerManager().getPlayerIfExists(plot.getOwnerAbs()) == null;
}
}
if (disable) {
for (UUID trusted : plot.getTrusted()) {
if (PlotSquared.platform().playerManager().getPlayerIfExists(trusted) != null) {
disable = false;
break;
}
}
if (disable) {
event.setNewCurrent(0);
plot.debug("Redstone event was cancelled because no trusted player was in the plot");
return;
}
}
}
if (Settings.Redstone.DISABLE_UNOCCUPIED) {
for (final PlotPlayer<?> player : PlotSquared.platform().playerManager().getPlayers()) {
if (plot.equals(player.getCurrentPlot())) {
return;
}
}
event.setNewCurrent(0);
}
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
public void onPhysicsEvent(BlockPhysicsEvent event) {
Block block = event.getBlock();
Location location = BukkitUtil.adapt(block.getLocation());
PlotArea area = location.getPlotArea();
if (area == null) {
return;
}
Plot plot = area.getOwnedPlotAbs(location);
if (plot == null) {
return;
}
if (event.getChangedType().hasGravity() && plot.getFlag(DisablePhysicsFlag.class)) {
event.setCancelled(true);
sendBlockChange(event.getBlock().getLocation(), event.getBlock().getBlockData());
plot.debug("Prevented block physics and resent block change because disable-physics = true");
return;
}
if (event.getChangedType() == Material.COMPARATOR) {
if (!plot.getFlag(RedstoneFlag.class)) {
event.setCancelled(true);
plot.debug("Prevented comparator update because redstone = false");
}
return;
}
if (PHYSICS_BLOCKS.contains(event.getChangedType())) {
if (plot.getFlag(DisablePhysicsFlag.class)) {
event.setCancelled(true);
plot.debug("Prevented block physics because disable-physics = true");
}
return;
}
if (Settings.Redstone.DETECT_INVALID_EDGE_PISTONS) {
if (PISTONS.contains(block.getType())) {
org.bukkit.block.data.Directional piston = (org.bukkit.block.data.Directional) block.getBlockData();
final BlockFace facing = piston.getFacing();
location = location.add(facing.getModX(), facing.getModY(), facing.getModZ());
Plot newPlot = area.getOwnedPlotAbs(location);
if (plot.equals(newPlot)) {
return;
}
if (!plot.isMerged() || !plot.getConnectedPlots().contains(newPlot)) {
event.setCancelled(true);
plot.debug("Prevented piston update because of invalid edge piston detection");
}
}
}
}
}

View File

@@ -19,7 +19,6 @@
package com.plotsquared.bukkit.listener;
import com.destroystokyo.paper.event.block.BeaconEffectEvent;
import com.destroystokyo.paper.event.block.BlockDestroyEvent;
import com.destroystokyo.paper.event.entity.EntityPathfindEvent;
import com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent;
import com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent;
@@ -29,7 +28,6 @@ import com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent;
import com.destroystokyo.paper.event.server.AsyncTabCompleteEvent;
import com.google.inject.Inject;
import com.plotsquared.bukkit.util.BukkitUtil;
import com.plotsquared.core.PlotSquared;
import com.plotsquared.core.command.Command;
import com.plotsquared.core.command.MainCommand;
import com.plotsquared.core.configuration.Settings;
@@ -39,23 +37,17 @@ import com.plotsquared.core.permissions.Permission;
import com.plotsquared.core.player.PlotPlayer;
import com.plotsquared.core.plot.Plot;
import com.plotsquared.core.plot.PlotArea;
import com.plotsquared.core.plot.PlotAreaType;
import com.plotsquared.core.plot.flag.FlagContainer;
import com.plotsquared.core.plot.flag.implementations.BeaconEffectsFlag;
import com.plotsquared.core.plot.flag.implementations.DoneFlag;
import com.plotsquared.core.plot.flag.implementations.FishingFlag;
import com.plotsquared.core.plot.flag.implementations.ProjectilesFlag;
import com.plotsquared.core.plot.flag.implementations.TileDropFlag;
import com.plotsquared.core.plot.flag.types.BooleanFlag;
import com.plotsquared.core.plot.world.PlotAreaManager;
import com.plotsquared.core.util.PlotFlagUtil;
import io.papermc.paper.event.entity.EntityMoveEvent;
import io.papermc.paper.event.world.StructuresLocateEvent;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.tag.Tag;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import org.bukkit.Chunk;
import org.bukkit.NamespacedKey;
import org.bukkit.block.Block;
import org.bukkit.block.TileState;
import org.bukkit.entity.Entity;
@@ -63,7 +55,6 @@ import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.entity.Projectile;
import org.bukkit.entity.Slime;
import org.bukkit.event.Cancellable;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
@@ -84,9 +75,6 @@ import java.util.regex.Pattern;
@SuppressWarnings("unused")
public class PaperListener implements Listener {
private static final NamespacedKey ITEM = NamespacedKey.minecraft("item");
private static final NamespacedKey FISHING_BOBBER = NamespacedKey.minecraft("fishing_bobber");
private final PlotAreaManager plotAreaManager;
private Chunk lastChunk;
@@ -95,56 +83,13 @@ public class PaperListener implements Listener {
this.plotAreaManager = plotAreaManager;
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onBlockDestroy(final BlockDestroyEvent event) {
Location location = BukkitUtil.adapt(event.getBlock().getLocation());
PlotArea area = location.getPlotArea();
if (area == null) {
return;
}
Plot plot = area.getPlot(location);
if (plot != null) {
event.setWillDrop(plot.getFlag(TileDropFlag.class));
}
}
@EventHandler
public void onEntityPathfind(EntityPathfindEvent event) {
if (!Settings.Paper_Components.ENTITY_PATHING) {
return;
}
handleEntityMovement(event, event.getEntity().getLocation(), event.getLoc());
}
@EventHandler
public void onEntityPathfind(SlimePathfindEvent event) {
if (!Settings.Paper_Components.ENTITY_PATHING) {
return;
}
Slime slime = event.getEntity();
Block b = slime.getTargetBlockExact(4);
if (b == null) {
return;
}
handleEntityMovement(event, event.getEntity().getLocation(), b.getLocation());
}
@EventHandler
public void onEntityMove(EntityMoveEvent event) {
if (!Settings.Paper_Components.ENTITY_MOVEMENT) {
return;
}
if (!event.hasExplicitlyChangedBlock()) {
return;
}
handleEntityMovement(event, event.getFrom(), event.getTo());
}
private static void handleEntityMovement(Cancellable event, org.bukkit.Location from, org.bukkit.Location target) {
Location toLoc = BukkitUtil.adapt(target);
Location fromLoc = BukkitUtil.adapt(from);
Location toLoc = BukkitUtil.adapt(event.getLoc());
Location fromLoc = BukkitUtil.adapt(event.getEntity().getLocation());
PlotArea tarea = toLoc.getPlotArea();
if (tarea == null) {
return;
@@ -163,10 +108,52 @@ public class PaperListener implements Listener {
event.setCancelled(true);
return;
}
if (tplot == null || tplot.getId().equals(fplot.getId())) {
if (tplot == null || tplot.getId().hashCode() == fplot.getId().hashCode()) {
return;
}
if (fplot.isMerged() && fplot.getConnectedPlots().contains(tplot)) {
if (fplot.isMerged() && fplot.getConnectedPlots().contains(fplot)) {
return;
}
event.setCancelled(true);
}
@EventHandler
public void onEntityPathfind(SlimePathfindEvent event) {
if (!Settings.Paper_Components.ENTITY_PATHING) {
return;
}
Slime slime = event.getEntity();
Block b = slime.getTargetBlockExact(4);
if (b == null) {
return;
}
Location toLoc = BukkitUtil.adapt(b.getLocation());
Location fromLoc = BukkitUtil.adapt(event.getEntity().getLocation());
PlotArea tarea = toLoc.getPlotArea();
if (tarea == null) {
return;
}
PlotArea farea = fromLoc.getPlotArea();
if (farea == null) {
return;
}
if (tarea != farea) {
event.setCancelled(true);
return;
}
Plot tplot = toLoc.getPlot();
Plot fplot = fromLoc.getPlot();
if (tplot == null ^ fplot == null) {
event.setCancelled(true);
return;
}
if (tplot == null || tplot.getId().hashCode() == fplot.getId().hashCode()) {
return;
}
if (fplot.isMerged() && fplot.getConnectedPlots().contains(fplot)) {
return;
}
event.setCancelled(true);
@@ -235,7 +222,7 @@ public class PaperListener implements Listener {
if (plot == null) {
EntityType type = event.getType();
// PreCreatureSpawnEvent **should** not be called for DROPPED_ITEM, just for the sake of consistency
if (type.getKey().equals(ITEM)) {
if (type == EntityType.DROPPED_ITEM) {
if (Settings.Enabled_Components.KILL_ROAD_ITEMS) {
event.setCancelled(true);
}
@@ -361,11 +348,6 @@ public class PaperListener implements Listener {
event.setCancelled(true);
}
} else if (!plot.isAdded(pp.getUUID())) {
if (entity.getType().getKey().equals(FISHING_BOBBER)) {
if (plot.getFlag(FishingFlag.class)) {
return;
}
}
if (!plot.getFlag(ProjectilesFlag.class)) {
if (!pp.hasPermission(Permission.PERMISSION_ADMIN_PROJECTILE_OTHER)) {
pp.sendMessage(
@@ -461,21 +443,6 @@ public class PaperListener implements Listener {
}
}
/**
* Don't let the server die when populating cartographers (villager offering maps) in classic plot worlds
* (as those don't generate POIs)
*/
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
public void onStructuresLocate(StructuresLocateEvent event) {
if (!PlotSquared.get().getPlotAreaManager().hasPlotArea(event.getWorld().getName())) {
return;
}
final PlotArea area = PlotSquared.get().getPlotAreaManager().getPlotAreaByString(event.getWorld().getName());
if (area != null && area.getType() == PlotAreaType.NORMAL) {
event.setCancelled(true);
}
}
private boolean getBooleanFlagValue(
@NonNull FlagContainer container,
@NonNull Class<? extends BooleanFlag<?>> flagClass,

View File

@@ -54,7 +54,6 @@ import com.plotsquared.core.plot.flag.implementations.EditSignFlag;
import com.plotsquared.core.plot.flag.implementations.HangingBreakFlag;
import com.plotsquared.core.plot.flag.implementations.HangingPlaceFlag;
import com.plotsquared.core.plot.flag.implementations.HostileInteractFlag;
import com.plotsquared.core.plot.flag.implementations.InteractionInteractFlag;
import com.plotsquared.core.plot.flag.implementations.ItemDropFlag;
import com.plotsquared.core.plot.flag.implementations.KeepInventoryFlag;
import com.plotsquared.core.plot.flag.implementations.LecternReadBookFlag;
@@ -62,13 +61,10 @@ import com.plotsquared.core.plot.flag.implementations.MiscInteractFlag;
import com.plotsquared.core.plot.flag.implementations.PlayerInteractFlag;
import com.plotsquared.core.plot.flag.implementations.PreventCreativeCopyFlag;
import com.plotsquared.core.plot.flag.implementations.TamedInteractFlag;
import com.plotsquared.core.plot.flag.implementations.TileDropFlag;
import com.plotsquared.core.plot.flag.implementations.UntrustedVisitFlag;
import com.plotsquared.core.plot.flag.implementations.UseFlag;
import com.plotsquared.core.plot.flag.implementations.VehicleBreakFlag;
import com.plotsquared.core.plot.flag.implementations.VehicleUseFlag;
import com.plotsquared.core.plot.flag.implementations.VillagerInteractFlag;
import com.plotsquared.core.plot.flag.types.BlockTypeWrapper;
import com.plotsquared.core.plot.world.PlotAreaManager;
import com.plotsquared.core.util.EventDispatcher;
import com.plotsquared.core.util.MathMan;
@@ -79,9 +75,7 @@ import com.plotsquared.core.util.task.TaskManager;
import com.plotsquared.core.util.task.TaskTime;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.util.Enums;
import com.sk89q.worldedit.world.block.BlockType;
import com.sk89q.worldedit.world.block.BlockTypes;
import io.papermc.lib.PaperLib;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
@@ -92,8 +86,10 @@ import org.bukkit.Bukkit;
import org.bukkit.FluidCollisionMode;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.BlockState;
import org.bukkit.block.Sign;
import org.bukkit.block.data.Waterlogged;
import org.bukkit.command.PluginCommand;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Boat;
@@ -111,7 +107,6 @@ import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.entity.EntityPickupItemEvent;
import org.bukkit.event.entity.EntityPlaceEvent;
import org.bukkit.event.entity.EntityPotionEffectEvent;
@@ -156,12 +151,9 @@ import org.bukkit.util.Vector;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
@@ -185,17 +177,7 @@ public class PlayerEventListener implements Listener {
Material.WRITABLE_BOOK,
Material.WRITTEN_BOOK
);
/**
* The correct EntityType for End Crystal, determined once at class loading time.
* Tries END_CRYSTAL first (1.21+), falls back to ENDER_CRYSTAL (1.20.4 and older).
*/
private static final EntityType END_CRYSTAL_ENTITY_TYPE = Objects.requireNonNull(
Enums.findByValue(EntityType.class, "END_CRYSTAL", "ENDER_CRYSTAL")
);
private static final Set<String> DYES;
static {
Set<String> mutableDyes = new HashSet<>(Set.of(
"WHITE_DYE",
@@ -217,76 +199,11 @@ public class PlayerEventListener implements Listener {
"GLOW_INK_SAC"
));
int[] version = PlotSquared.platform().serverVersion();
if (version[1] >= 20) {
if (version[1] >= 20 && version[2] >= 1) {
mutableDyes.add("HONEYCOMB");
}
DYES = Set.copyOf(mutableDyes);
}
private static final Set<String> INTERACTABLE_MATERIALS;
static {
// @formatter:off
// "temporary" fix for https://hub.spigotmc.org/jira/browse/SPIGOT-7813
// can (and should) be removed when 1.21 support is dropped
// List of all interactable 1.21 materials
INTERACTABLE_MATERIALS = Material.CHEST.isInteractable() ? null : Set.of(
"REDSTONE_ORE", "DEEPSLATE_REDSTONE_ORE", "CHISELED_BOOKSHELF", "DECORATED_POT", "CHEST", "CRAFTING_TABLE",
"FURNACE", "JUKEBOX", "OAK_FENCE", "SPRUCE_FENCE", "BIRCH_FENCE", "JUNGLE_FENCE", "ACACIA_FENCE", "CHERRY_FENCE",
"DARK_OAK_FENCE", "MANGROVE_FENCE", "BAMBOO_FENCE", "CRIMSON_FENCE", "WARPED_FENCE", "PUMPKIN",
"NETHER_BRICK_FENCE", "ENCHANTING_TABLE", "DRAGON_EGG", "ENDER_CHEST", "COMMAND_BLOCK", "BEACON", "ANVIL",
"CHIPPED_ANVIL", "DAMAGED_ANVIL", "LIGHT", "REPEATING_COMMAND_BLOCK", "CHAIN_COMMAND_BLOCK", "SHULKER_BOX",
"WHITE_SHULKER_BOX", "ORANGE_SHULKER_BOX", "MAGENTA_SHULKER_BOX", "LIGHT_BLUE_SHULKER_BOX", "YELLOW_SHULKER_BOX",
"LIME_SHULKER_BOX", "PINK_SHULKER_BOX", "GRAY_SHULKER_BOX", "LIGHT_GRAY_SHULKER_BOX", "CYAN_SHULKER_BOX",
"PURPLE_SHULKER_BOX", "BLUE_SHULKER_BOX", "BROWN_SHULKER_BOX", "GREEN_SHULKER_BOX", "RED_SHULKER_BOX",
"BLACK_SHULKER_BOX", "REPEATER", "COMPARATOR", "HOPPER", "DISPENSER", "DROPPER", "LECTERN", "LEVER",
"DAYLIGHT_DETECTOR", "TRAPPED_CHEST", "TNT", "NOTE_BLOCK", "STONE_BUTTON", "POLISHED_BLACKSTONE_BUTTON",
"OAK_BUTTON", "SPRUCE_BUTTON", "BIRCH_BUTTON", "JUNGLE_BUTTON", "ACACIA_BUTTON", "CHERRY_BUTTON",
"DARK_OAK_BUTTON", "MANGROVE_BUTTON", "BAMBOO_BUTTON", "CRIMSON_BUTTON", "WARPED_BUTTON", "IRON_DOOR", "OAK_DOOR",
"SPRUCE_DOOR", "BIRCH_DOOR", "JUNGLE_DOOR", "ACACIA_DOOR", "CHERRY_DOOR", "DARK_OAK_DOOR", "MANGROVE_DOOR",
"BAMBOO_DOOR", "CRIMSON_DOOR", "WARPED_DOOR", "COPPER_DOOR", "EXPOSED_COPPER_DOOR", "WEATHERED_COPPER_DOOR",
"OXIDIZED_COPPER_DOOR", "WAXED_COPPER_DOOR", "WAXED_EXPOSED_COPPER_DOOR", "WAXED_WEATHERED_COPPER_DOOR",
"WAXED_OXIDIZED_COPPER_DOOR", "IRON_TRAPDOOR", "OAK_TRAPDOOR", "SPRUCE_TRAPDOOR", "BIRCH_TRAPDOOR",
"JUNGLE_TRAPDOOR", "ACACIA_TRAPDOOR", "CHERRY_TRAPDOOR", "DARK_OAK_TRAPDOOR", "MANGROVE_TRAPDOOR",
"BAMBOO_TRAPDOOR", "CRIMSON_TRAPDOOR", "WARPED_TRAPDOOR", "COPPER_TRAPDOOR", "EXPOSED_COPPER_TRAPDOOR",
"WEATHERED_COPPER_TRAPDOOR", "OXIDIZED_COPPER_TRAPDOOR", "WAXED_COPPER_TRAPDOOR", "WAXED_EXPOSED_COPPER_TRAPDOOR",
"WAXED_WEATHERED_COPPER_TRAPDOOR", "WAXED_OXIDIZED_COPPER_TRAPDOOR", "OAK_FENCE_GATE", "SPRUCE_FENCE_GATE",
"BIRCH_FENCE_GATE", "JUNGLE_FENCE_GATE", "ACACIA_FENCE_GATE", "CHERRY_FENCE_GATE", "DARK_OAK_FENCE_GATE",
"MANGROVE_FENCE_GATE", "BAMBOO_FENCE_GATE", "CRIMSON_FENCE_GATE", "WARPED_FENCE_GATE", "STRUCTURE_BLOCK",
"JIGSAW", "OAK_SIGN", "SPRUCE_SIGN", "BIRCH_SIGN", "JUNGLE_SIGN", "ACACIA_SIGN", "CHERRY_SIGN", "DARK_OAK_SIGN",
"MANGROVE_SIGN", "BAMBOO_SIGN", "CRIMSON_SIGN", "WARPED_SIGN", "OAK_HANGING_SIGN", "SPRUCE_HANGING_SIGN",
"BIRCH_HANGING_SIGN", "JUNGLE_HANGING_SIGN", "ACACIA_HANGING_SIGN", "CHERRY_HANGING_SIGN",
"DARK_OAK_HANGING_SIGN", "MANGROVE_HANGING_SIGN", "BAMBOO_HANGING_SIGN", "CRIMSON_HANGING_SIGN",
"WARPED_HANGING_SIGN", "CAKE", "WHITE_BED", "ORANGE_BED", "MAGENTA_BED", "LIGHT_BLUE_BED", "YELLOW_BED",
"LIME_BED", "PINK_BED", "GRAY_BED", "LIGHT_GRAY_BED", "CYAN_BED", "PURPLE_BED", "BLUE_BED", "BROWN_BED",
"GREEN_BED", "RED_BED", "BLACK_BED", "CRAFTER", "BREWING_STAND", "CAULDRON", "FLOWER_POT", "LOOM", "COMPOSTER",
"BARREL", "SMOKER", "BLAST_FURNACE", "CARTOGRAPHY_TABLE", "FLETCHING_TABLE", "GRINDSTONE", "SMITHING_TABLE",
"STONECUTTER", "BELL", "CAMPFIRE", "SOUL_CAMPFIRE", "BEE_NEST", "BEEHIVE", "RESPAWN_ANCHOR", "CANDLE",
"WHITE_CANDLE", "ORANGE_CANDLE", "MAGENTA_CANDLE", "LIGHT_BLUE_CANDLE", "YELLOW_CANDLE", "LIME_CANDLE",
"PINK_CANDLE", "GRAY_CANDLE", "LIGHT_GRAY_CANDLE", "CYAN_CANDLE", "PURPLE_CANDLE", "BLUE_CANDLE", "BROWN_CANDLE",
"GREEN_CANDLE", "RED_CANDLE", "BLACK_CANDLE", "VAULT", "MOVING_PISTON", "REDSTONE_WIRE", "OAK_WALL_SIGN",
"SPRUCE_WALL_SIGN", "BIRCH_WALL_SIGN", "ACACIA_WALL_SIGN", "CHERRY_WALL_SIGN", "JUNGLE_WALL_SIGN",
"DARK_OAK_WALL_SIGN", "MANGROVE_WALL_SIGN", "BAMBOO_WALL_SIGN", "OAK_WALL_HANGING_SIGN",
"SPRUCE_WALL_HANGING_SIGN", "BIRCH_WALL_HANGING_SIGN", "ACACIA_WALL_HANGING_SIGN",
"CHERRY_WALL_HANGING_SIGN", "JUNGLE_WALL_HANGING_SIGN", "DARK_OAK_WALL_HANGING_SIGN",
"MANGROVE_WALL_HANGING_SIGN", "CRIMSON_WALL_HANGING_SIGN", "WARPED_WALL_HANGING_SIGN", "BAMBOO_WALL_HANGING_SIGN",
"WATER_CAULDRON", "LAVA_CAULDRON", "POWDER_SNOW_CAULDRON", "POTTED_TORCHFLOWER", "POTTED_OAK_SAPLING",
"POTTED_SPRUCE_SAPLING", "POTTED_BIRCH_SAPLING", "POTTED_JUNGLE_SAPLING", "POTTED_ACACIA_SAPLING",
"POTTED_CHERRY_SAPLING", "POTTED_DARK_OAK_SAPLING", "POTTED_MANGROVE_PROPAGULE", "POTTED_FERN",
"POTTED_DANDELION", "POTTED_POPPY", "POTTED_BLUE_ORCHID", "POTTED_ALLIUM", "POTTED_AZURE_BLUET",
"POTTED_RED_TULIP", "POTTED_ORANGE_TULIP", "POTTED_WHITE_TULIP", "POTTED_PINK_TULIP", "POTTED_OXEYE_DAISY",
"POTTED_CORNFLOWER", "POTTED_LILY_OF_THE_VALLEY", "POTTED_WITHER_ROSE", "POTTED_RED_MUSHROOM",
"POTTED_BROWN_MUSHROOM", "POTTED_DEAD_BUSH", "POTTED_CACTUS", "POTTED_BAMBOO", "SWEET_BERRY_BUSH",
"CRIMSON_WALL_SIGN", "WARPED_WALL_SIGN", "POTTED_CRIMSON_FUNGUS", "POTTED_WARPED_FUNGUS", "POTTED_CRIMSON_ROOTS",
"POTTED_WARPED_ROOTS", "CANDLE_CAKE", "WHITE_CANDLE_CAKE", "ORANGE_CANDLE_CAKE", "MAGENTA_CANDLE_CAKE",
"LIGHT_BLUE_CANDLE_CAKE", "YELLOW_CANDLE_CAKE", "LIME_CANDLE_CAKE", "PINK_CANDLE_CAKE", "GRAY_CANDLE_CAKE",
"LIGHT_GRAY_CANDLE_CAKE", "CYAN_CANDLE_CAKE", "PURPLE_CANDLE_CAKE", "BLUE_CANDLE_CAKE", "BROWN_CANDLE_CAKE",
"GREEN_CANDLE_CAKE", "RED_CANDLE_CAKE", "BLACK_CANDLE_CAKE", "CAVE_VINES", "CAVE_VINES_PLANT",
"POTTED_AZALEA_BUSH", "POTTED_FLOWERING_AZALEA_BUSH"
);
// @formatter:on
}
private final EventDispatcher eventDispatcher;
private final WorldEdit worldEdit;
private final PlotAreaManager plotAreaManager;
@@ -319,19 +236,6 @@ public class PlayerEventListener implements Listener {
this.plotListener = plotListener;
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onBlockBreak(final BlockBreakEvent event) {
Location location = BukkitUtil.adapt(event.getBlock().getLocation());
PlotArea area = location.getPlotArea();
if (area == null) {
return;
}
Plot plot = area.getPlot(location);
if (plot != null) {
event.setDropItems(plot.getFlag(TileDropFlag.class));
}
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onPlayerDyeSign(PlayerInteractEvent event) {
ItemStack itemStack = event.getItem();
@@ -391,7 +295,7 @@ public class PlayerEventListener implements Listener {
@EventHandler
public void onVehicleEntityCollision(VehicleEntityCollisionEvent e) {
if (e.getVehicle() instanceof Boat) {
if (e.getVehicle().getType() == EntityType.BOAT) {
Location location = BukkitUtil.adapt(e.getEntity().getLocation());
if (location.isPlotArea()) {
if (e.getEntity() instanceof Player) {
@@ -548,14 +452,12 @@ public class PlayerEventListener implements Listener {
// Delayed
// Async
TaskManager.runTaskLaterAsync(
() -> {
if (!player.hasPlayedBefore() && player.isOnline()) {
player.saveData();
}
this.eventDispatcher.doJoinTask(pp);
}, TaskTime.seconds(1L)
);
TaskManager.runTaskLaterAsync(() -> {
if (!player.hasPlayedBefore() && player.isOnline()) {
player.saveData();
}
this.eventDispatcher.doJoinTask(pp);
}, TaskTime.seconds(1L));
if (pp.hasPermission(Permission.PERMISSION_ADMIN_UPDATE_NOTIFICATION.toString()) && Settings.Enabled_Components.UPDATE_NOTIFICATIONS
&& PremiumVerification.isPremium() && UpdateUtility.hasUpdate) {
@@ -610,21 +512,15 @@ public class PlayerEventListener implements Listener {
return;
}
Plot plot = area.getPlot(location);
if (plot != null && !plot.equals(lastPlot)) {
if (plot != null) {
final boolean result = DenyTeleportFlag.allowsTeleport(pp, plot);
// there is one possibility to still allow teleportation:
// to is identical to the plot's home location, and untrusted-visit is true
// i.e. untrusted-visit can override deny-teleport
// this is acceptable, because otherwise it wouldn't make sense to have both flags set
if (result || (plot.getFlag(UntrustedVisitFlag.class) && plot
if (!result && !(plot.getFlag(UntrustedVisitFlag.class) && plot
.getHomeSynchronous()
.equals(BukkitUtil.adaptComplete(to)))) {
// returns false if the player is not allowed to enter the plot (if they are denied, for example)
// don't let the move event cancel the entry after teleport, but rather catch and cancel early (#4647)
if (!plotListener.plotEntry(pp, plot)) {
event.setCancelled(true);
}
} else {
pp.sendMessage(
TranslatableCaption.of("deny.no_enter"),
TagResolver.resolver("plot", Tag.inserting(Component.text(plot.toString())))
@@ -637,19 +533,6 @@ public class PlayerEventListener implements Listener {
playerMove(event);
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onWorldChanged(PlayerChangedWorldEvent event) {
Player player = event.getPlayer();
BukkitPlayer pp = BukkitUtil.adapt(player);
if (this.worldEdit != null) {
if (!pp.hasPermission(Permission.PERMISSION_WORLDEDIT_BYPASS)) {
if (pp.getAttribute("worldedit")) {
pp.removeAttribute("worldedit");
}
}
}
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void vehicleMove(VehicleMoveEvent event)
throws IllegalAccessException {
@@ -962,15 +845,12 @@ public class PlayerEventListener implements Listener {
builder.tag("plot_id", Tag.inserting(Component.text(id.toString())));
builder.tag("sender", Tag.inserting(Component.text(sender)));
if (plotPlayer.hasPermission("plots.chat.color")) {
builder.tag(
"msg", Tag.inserting(MiniMessage.miniMessage().deserialize(
message,
TagResolver.resolver(
StandardTags.color(), StandardTags.gradient(),
StandardTags.rainbow(), StandardTags.decorations()
)
))
);
builder.tag("msg", Tag.inserting(MiniMessage.miniMessage().deserialize(
message,
TagResolver.resolver(StandardTags.color(), StandardTags.gradient(),
StandardTags.rainbow(), StandardTags.decorations()
)
)));
} else {
builder.tag("msg", Tag.inserting(Component.text(message)));
}
@@ -992,6 +872,40 @@ public class PlayerEventListener implements Listener {
}
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onWorldChanged(PlayerChangedWorldEvent event) {
Player player = event.getPlayer();
BukkitPlayer pp = BukkitUtil.adapt(player);
// Delete last location
Plot plot;
try (final MetaDataAccess<Plot> lastPlotAccess =
pp.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_LAST_PLOT)) {
plot = lastPlotAccess.remove();
}
try (final MetaDataAccess<Location> lastLocationAccess =
pp.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_LOCATION)) {
lastLocationAccess.remove();
}
if (plot != null) {
plotListener.plotExit(pp, plot);
}
if (this.worldEdit != null) {
if (!pp.hasPermission(Permission.PERMISSION_WORLDEDIT_BYPASS)) {
if (pp.getAttribute("worldedit")) {
pp.removeAttribute("worldedit");
}
}
}
Location location = pp.getLocation();
PlotArea area = location.getPlotArea();
if (location.isPlotArea()) {
plot = location.getPlot();
if (plot != null) {
plotListener.plotEntry(pp, plot);
}
}
}
@SuppressWarnings("deprecation")
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onInventoryClick(InventoryClickEvent event) {
@@ -1279,9 +1193,7 @@ public class PlayerEventListener implements Listener {
eventType = PlayerBlockEventType.INTERACT_BLOCK;
blocktype1 = BukkitAdapter.asBlockType(block.getType());
if (INTERACTABLE_MATERIALS != null
? INTERACTABLE_MATERIALS.contains(blockType.name())
: blockType.isInteractable()) {
if (blockType.isInteractable()) {
if (!player.isSneaking()) {
break;
}
@@ -1299,7 +1211,7 @@ public class PlayerEventListener implements Listener {
// in the following, lb needs to have the material of the item in hand i.e. type
switch (type.toString()) {
case "REDSTONE", "STRING", "PUMPKIN_SEEDS", "MELON_SEEDS", "COCOA_BEANS", "WHEAT_SEEDS", "BEETROOT_SEEDS",
"SWEET_BERRIES", "GLOW_BERRIES" -> {
"SWEET_BERRIES", "GLOW_BERRIES" -> {
return;
}
default -> {
@@ -1324,17 +1236,6 @@ public class PlayerEventListener implements Listener {
//Allow all players to eat while also allowing the block place event to be fired
return;
}
// Process creature spawning of armor stands & end crystals here if spawned by the player in order to be able to
// reset the player's hand item if spawning needs to be cancelled.
if (type == Material.ARMOR_STAND || type == Material.END_CRYSTAL) {
Plot plot = location.getOwnedPlotAbs();
EntityType entityType = type == Material.ARMOR_STAND ? EntityType.ARMOR_STAND : END_CRYSTAL_ENTITY_TYPE;
if (BukkitEntityUtil.checkEntity(entityType, plot)) {
event.setCancelled(true);
break;
}
}
// Continue with normal place event checks
if (type == Material.ARMOR_STAND) {
location = BukkitUtil.adapt(block.getRelative(event.getBlockFace()).getLocation());
eventType = PlayerBlockEventType.PLACE_MISC;
@@ -1409,7 +1310,22 @@ public class PlayerEventListener implements Listener {
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onBucketEmpty(PlayerBucketEmptyEvent event) {
final Block block = event.getBlock();
BlockFace bf = event.getBlockFace();
// Note: a month after Bukkit 1.14.4 released, they added the API method
// PlayerBucketEmptyEvent#getBlock(), which returns the block the
// bucket contents is going to be placed at. Currently we determine this
// block ourselves to retain compatibility with 1.13.
final Block block;
// if the block can be waterlogged, the event might waterlog the block
// sometimes
if (event.getBlockClicked().getBlockData() instanceof Waterlogged waterlogged
&& !waterlogged.isWaterlogged() && event.getBucket() != Material.LAVA_BUCKET) {
block = event.getBlockClicked();
} else {
block = event.getBlockClicked().getLocation()
.add(bf.getModX(), bf.getModY(), bf.getModZ())
.getBlock();
}
Location location = BukkitUtil.adapt(block.getLocation());
PlotArea area = location.getPlotArea();
if (area == null) {
@@ -1417,16 +1333,6 @@ public class PlayerEventListener implements Listener {
}
BukkitPlayer pp = BukkitUtil.adapt(event.getPlayer());
Plot plot = area.getPlot(location);
final List<BlockTypeWrapper> use =
Optional.ofNullable(plot).map(p -> p.getFlag(UseFlag.class)).orElse(area.isRoadFlags() ?
area.getFlag(UseFlag.class) : Collections.emptyList());
BlockType type = BukkitAdapter.asBlockType(block.getType());
for (final BlockTypeWrapper blockTypeWrapper : use) {
if (blockTypeWrapper.accepts(BlockTypes.AIR) || blockTypeWrapper
.accepts(type)) {
return;
}
}
if (plot == null) {
if (pp.hasPermission(Permission.PERMISSION_ADMIN_BUILD_ROAD)) {
return;
@@ -1498,16 +1404,6 @@ public class PlayerEventListener implements Listener {
Player player = event.getPlayer();
BukkitPlayer plotPlayer = BukkitUtil.adapt(player);
Plot plot = area.getPlot(location);
final List<BlockTypeWrapper> use =
Optional.ofNullable(plot).map(p -> p.getFlag(UseFlag.class)).orElse(area.isRoadFlags() ?
area.getFlag(UseFlag.class) : Collections.emptyList());
BlockType type = BukkitAdapter.asBlockType(blockClicked.getType());
for (final BlockTypeWrapper blockTypeWrapper : use) {
if (blockTypeWrapper.accepts(BlockTypes.AIR) || blockTypeWrapper
.accepts(type)) {
return;
}
}
if (plot == null) {
if (plotPlayer.hasPermission(Permission.PERMISSION_ADMIN_BUILD_ROAD)) {
return;
@@ -1782,11 +1678,6 @@ public class PlayerEventListener implements Listener {
return;
}
if (EntityCategories.INTERACTION.contains(entityType) && flagContainer
.getFlag(InteractionInteractFlag.class).getValue()) {
return;
}
if (EntityCategories.VILLAGER.contains(entityType) && flagContainer
.getFlag(VillagerInteractFlag.class).getValue()) {
return;
@@ -2030,9 +1921,7 @@ public class PlayerEventListener implements Listener {
@EventHandler
public void onPlayerTakeLecternBook(PlayerTakeLecternBookEvent event) {
Player player = event.getPlayer();
BukkitPlayer pp = BukkitUtil.adapt(player);
Location location = pp.getLocation();
Location location = BukkitUtil.adapt(event.getPlayer().getLocation());
PlotArea area = location.getPlotArea();
if (area == null) {
return;
@@ -2044,11 +1933,9 @@ public class PlayerEventListener implements Listener {
}
return;
}
if (!plot.isAdded(pp.getUUID())) {
if (plot.getFlag(LecternReadBookFlag.class)) {
plot.debug(event.getPlayer().getName() + " could not take the book because of lectern-read-book = true");
event.setCancelled(true);
}
if (plot.getFlag(LecternReadBookFlag.class)) {
plot.debug(event.getPlayer().getName() + " could not take the book because of lectern-read-book = true");
event.setCancelled(true);
}
}

View File

@@ -28,14 +28,12 @@ import com.plotsquared.core.player.PlotPlayer;
import com.plotsquared.core.plot.Plot;
import com.plotsquared.core.plot.PlotArea;
import com.plotsquared.core.plot.PlotHandler;
import com.plotsquared.core.plot.flag.implementations.FishingFlag;
import com.plotsquared.core.plot.flag.implementations.ProjectilesFlag;
import com.plotsquared.core.plot.world.PlotAreaManager;
import com.plotsquared.core.util.PlotFlagUtil;
import net.kyori.adventure.text.minimessage.tag.Tag;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import org.bukkit.entity.Entity;
import org.bukkit.entity.FishHook;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.entity.Projectile;
@@ -134,11 +132,6 @@ public class ProjectileEventListener implements Listener {
event.setCancelled(true);
}
} else if (!plot.isAdded(pp.getUUID())) {
if (entity instanceof FishHook) {
if (plot.getFlag(FishingFlag.class)) {
return;
}
}
if (!plot.getFlag(ProjectilesFlag.class)) {
if (!pp.hasPermission(Permission.PERMISSION_ADMIN_PROJECTILE_OTHER)) {
pp.sendMessage(
@@ -194,8 +187,7 @@ public class ProjectileEventListener implements Listener {
return;
}
if (plot.isAdded(pp.getUUID()) || pp.hasPermission(Permission.PERMISSION_ADMIN_PROJECTILE_OTHER) || plot.getFlag(
ProjectilesFlag.class) || (entity instanceof FishHook && plot.getFlag(
FishingFlag.class))) {
ProjectilesFlag.class)) {
return;
}
entity.remove();

View File

@@ -49,14 +49,9 @@ public class SingleWorldListener implements Listener {
this.methodGetHandleChunk = classCraftChunk.getMethod("getHandle").getRealMethod();
} catch (NoSuchMethodException ignored) {
try {
String chunkStatus = PlotSquared.platform().serverVersion()[1] < 21
? "net.minecraft.world.level.chunk" + ".ChunkStatus"
: "net.minecraft.world.level.chunk.status.ChunkStatus";
ReflectionUtils.RefClass classChunkStatus = getRefClass(chunkStatus);
ReflectionUtils.RefClass classChunkStatus = getRefClass("net.minecraft.world.level.chunk.ChunkStatus");
this.objChunkStatusFull = classChunkStatus.getRealClass().getField("n").get(null);
this.methodGetHandleChunk = classCraftChunk
.getMethod("getHandle", classChunkStatus.getRealClass())
.getRealMethod();
this.methodGetHandleChunk = classCraftChunk.getMethod("getHandle", classChunkStatus.getRealClass()).getRealMethod();
} catch (NoSuchMethodException ex) {
throw new RuntimeException(ex);
}
@@ -100,8 +95,7 @@ public class SingleWorldListener implements Listener {
@EventHandler(priority = EventPriority.LOWEST)
public void onChunkLoad(ChunkLoadEvent event) {
// disable this for now, should address https://github.com/IntellectualSites/PlotSquared/issues/4413
// handle(event);
handle(event);
}
}

View File

@@ -20,8 +20,6 @@ package com.plotsquared.bukkit.placeholder;
import com.plotsquared.core.PlotSquared;
import com.plotsquared.core.player.PlotPlayer;
import com.plotsquared.core.plot.flag.implementations.DoneFlag;
import com.plotsquared.core.util.query.PlotQuery;
import me.clip.placeholderapi.PlaceholderAPIPlugin;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import org.bukkit.entity.Player;
@@ -85,20 +83,6 @@ public class PAPIPlaceholders extends PlaceholderExpansion {
return String.valueOf(pl.getPlotCount(identifier));
}
if (identifier.startsWith("base_plot_count_")) {
identifier = identifier.substring("base_plot_count_".length());
if (identifier.isEmpty()) {
return "";
}
return String.valueOf(PlotQuery.newQuery()
.ownedBy(pl)
.inWorld(identifier)
.whereBasePlot()
.thatPasses(plot -> !DoneFlag.isDone(plot))
.count());
}
// PlotSquared placeholders
return PlotSquared.platform().placeholderRegistry().getPlaceholderValue(identifier, pl);
}

View File

@@ -32,7 +32,6 @@ import com.plotsquared.core.plot.PlotWeather;
import com.plotsquared.core.plot.world.PlotAreaManager;
import com.plotsquared.core.util.EventDispatcher;
import com.plotsquared.core.util.MathMan;
import com.plotsquared.core.util.WorldUtil;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.world.item.ItemType;
@@ -41,7 +40,6 @@ import io.papermc.lib.PaperLib;
import net.kyori.adventure.audience.Audience;
import org.bukkit.GameMode;
import org.bukkit.Sound;
import org.bukkit.SoundCategory;
import org.bukkit.WeatherType;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
@@ -53,6 +51,7 @@ import org.bukkit.potion.PotionEffectType;
import org.checkerframework.checker.index.qual.NonNegative;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Arrays;
import java.util.Set;
import java.util.UUID;
@@ -121,9 +120,6 @@ public class BukkitPlayer extends PlotPlayer<Player> {
@Override
public boolean canTeleport(final @NonNull Location location) {
if (!WorldUtil.isValidLocation(location)) {
return false;
}
final org.bukkit.Location to = BukkitUtil.adapt(location);
final org.bukkit.Location from = player.getLocation();
PlayerTeleportEvent event = new PlayerTeleportEvent(player, from, to);
@@ -225,7 +221,7 @@ public class BukkitPlayer extends PlotPlayer<Player> {
@Override
public void teleport(final @NonNull Location location, final @NonNull TeleportCause cause) {
if (!WorldUtil.isValidLocation(location)) {
if (Math.abs(location.getX()) >= 30000000 || Math.abs(location.getZ()) >= 30000000) {
return;
}
final org.bukkit.Location bukkitLocation =
@@ -313,21 +309,18 @@ public class BukkitPlayer extends PlotPlayer<Player> {
@Override
public void playMusic(final @NonNull Location location, final @NonNull ItemType id) {
if (id == ItemTypes.AIR) {
if (PlotSquared.platform().serverVersion()[1] >= 19) {
player.stopSound(SoundCategory.MUSIC);
return;
// Let's just stop all the discs because why not?
for (final Sound sound : Arrays.stream(Sound.values())
.filter(sound -> sound.name().contains("DISC")).toList()) {
player.stopSound(sound);
}
// 1.18 and downwards require a specific Sound to stop (even tho the packet does not??)
for (final Sound sound : Sound.values()) {
if (sound.name().startsWith("MUSIC_DISC")) {
this.player.stopSound(sound, SoundCategory.MUSIC);
}
}
return;
// this.player.playEffect(BukkitUtil.getLocation(location), Effect.RECORD_PLAY, Material.AIR);
} else {
// this.player.playEffect(BukkitUtil.getLocation(location), Effect.RECORD_PLAY, id.to(Material.class));
this.player.playSound(BukkitUtil.adapt(location),
Sound.valueOf(BukkitAdapter.adapt(id).name()), Float.MAX_VALUE, 1f
);
}
this.player.playSound(BukkitUtil.adapt(location), Sound.valueOf(BukkitAdapter.adapt(id).name()),
SoundCategory.MUSIC, Float.MAX_VALUE, 1f
);
}
@SuppressWarnings("deprecation") // Needed for Spigot compatibility

View File

@@ -30,8 +30,6 @@ import com.plotsquared.core.util.task.TaskTime;
import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.world.World;
import io.papermc.lib.PaperLib;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.plugin.Plugin;
@@ -43,8 +41,6 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
@@ -59,8 +55,6 @@ import java.util.function.Consumer;
**/
public final class BukkitChunkCoordinator extends ChunkCoordinator {
private static final Logger LOGGER = LogManager.getLogger("PlotSquared/" + BukkitChunkCoordinator.class.getSimpleName());
private final List<ProgressSubscriber> progressSubscribers = new LinkedList<>();
private final Queue<BlockVector2> requestedChunks;
@@ -76,7 +70,6 @@ public final class BukkitChunkCoordinator extends ChunkCoordinator {
private final AtomicInteger expectedSize;
private final AtomicInteger loadingChunks = new AtomicInteger();
private final boolean forceSync;
private final boolean shouldGen;
private int batchSize;
private PlotSquaredTask task;
@@ -94,8 +87,7 @@ public final class BukkitChunkCoordinator extends ChunkCoordinator {
@Assisted final @NonNull Consumer<Throwable> throwableConsumer,
@Assisted("unloadAfter") final boolean unloadAfter,
@Assisted final @NonNull Collection<ProgressSubscriber> progressSubscribers,
@Assisted("forceSync") final boolean forceSync,
@Assisted("shouldGen") final boolean shouldGen
@Assisted("forceSync") final boolean forceSync
) {
this.requestedChunks = new LinkedBlockingQueue<>(requestedChunks);
this.availableChunks = new LinkedBlockingQueue<>();
@@ -111,7 +103,6 @@ public final class BukkitChunkCoordinator extends ChunkCoordinator {
this.bukkitWorld = Bukkit.getWorld(world.getName());
this.progressSubscribers.addAll(progressSubscribers);
this.forceSync = forceSync;
this.shouldGen = shouldGen;
}
@Override
@@ -221,28 +212,18 @@ public final class BukkitChunkCoordinator extends ChunkCoordinator {
* Requests a batch of chunks to be loaded
*/
private void requestBatch() {
for (int i = 0; i < this.batchSize && this.requestedChunks.peek() != null; i++) {
BlockVector2 chunk;
for (int i = 0; i < this.batchSize && (chunk = this.requestedChunks.poll()) != null; i++) {
// This required PaperLib to be bumped to version 1.0.4 to mark the request as urgent
final BlockVector2 chunk = this.requestedChunks.poll();
loadingChunks.incrementAndGet();
PaperLib
.getChunkAtAsync(this.bukkitWorld, chunk.getX(), chunk.getZ(), shouldGen, true)
.orTimeout(10L, TimeUnit.SECONDS)
.getChunkAtAsync(this.bukkitWorld, chunk.getX(), chunk.getZ(), true, true)
.whenComplete((chunkObject, throwable) -> {
loadingChunks.decrementAndGet();
if (throwable != null) {
if (throwable instanceof TimeoutException) {
LOGGER.warn("Timed out awaiting chunk load {}", chunk);
this.requestedChunks.offer(chunk);
} else {
LOGGER.error("Failed to load chunk {}", chunk, throwable);
// We want one less because this couldn't be processed
this.expectedSize.decrementAndGet();
}
} else if (chunkObject == null) {
if (shouldGen) {
LOGGER.error("Null chunk returned for chunk at {}", chunk);
}
throwable.printStackTrace();
// We want one less because this couldn't be processed
this.expectedSize.decrementAndGet();
} else if (PlotSquared.get().isMainThread(Thread.currentThread())) {
this.processChunk(chunkObject);
} else {

View File

@@ -62,28 +62,19 @@ public class BukkitQueueCoordinator extends BasicQueueCoordinator {
private static final SideEffectSet EDGE_LIGHTING_SIDE_EFFECT_SET;
static {
NO_SIDE_EFFECT_SET = enableNetworkIfNeeded()
.with(SideEffect.LIGHTING, SideEffect.State.OFF)
.with(SideEffect.NEIGHBORS, SideEffect.State.OFF);
EDGE_SIDE_EFFECT_SET = NO_SIDE_EFFECT_SET
.with(SideEffect.UPDATE, SideEffect.State.ON)
.with(SideEffect.NEIGHBORS, SideEffect.State.ON);
LIGHTING_SIDE_EFFECT_SET = NO_SIDE_EFFECT_SET
.with(SideEffect.NEIGHBORS, SideEffect.State.OFF);
EDGE_LIGHTING_SIDE_EFFECT_SET = NO_SIDE_EFFECT_SET
.with(SideEffect.UPDATE, SideEffect.State.ON)
.with(SideEffect.NEIGHBORS, SideEffect.State.ON);
}
// make sure block changes are sent
private static SideEffectSet enableNetworkIfNeeded() {
SideEffect network;
try {
network = SideEffect.valueOf("NETWORK");
} catch (IllegalArgumentException ignored) {
return SideEffectSet.none();
}
return SideEffectSet.none().with(network, SideEffect.State.ON);
NO_SIDE_EFFECT_SET = SideEffectSet.none().with(SideEffect.LIGHTING, SideEffect.State.OFF).with(
SideEffect.NEIGHBORS,
SideEffect.State.OFF
);
EDGE_SIDE_EFFECT_SET = SideEffectSet.none().with(SideEffect.UPDATE, SideEffect.State.ON).with(
SideEffect.NEIGHBORS,
SideEffect.State.ON
);
LIGHTING_SIDE_EFFECT_SET = SideEffectSet.none().with(SideEffect.NEIGHBORS, SideEffect.State.OFF);
EDGE_LIGHTING_SIDE_EFFECT_SET = SideEffectSet.none().with(SideEffect.UPDATE, SideEffect.State.ON).with(
SideEffect.NEIGHBORS,
SideEffect.State.ON
);
}
private org.bukkit.World bukkitWorld;
@@ -238,7 +229,6 @@ public class BukkitQueueCoordinator extends BasicQueueCoordinator {
.unloadAfter(isUnloadAfter())
.withProgressSubscribers(getProgressSubscribers())
.forceSync(isForceSync())
.shouldGen(isShouldGen())
.build();
return super.enqueue();
}

View File

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

View File

@@ -49,7 +49,6 @@ import org.bukkit.entity.Arrow;
import org.bukkit.entity.Creature;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Firework;
import org.bukkit.entity.Player;
import org.bukkit.entity.Projectile;
import org.bukkit.event.entity.EntityDamageEvent;
@@ -342,7 +341,8 @@ public class BukkitEntityUtil {
}
//disable the firework damage. too much of a headache to support at the moment.
if (vplot != null) {
if (EntityDamageEvent.DamageCause.ENTITY_EXPLOSION == cause && damager instanceof Firework) {
if (EntityDamageEvent.DamageCause.ENTITY_EXPLOSION == cause
&& damager.getType() == EntityType.FIREWORK) {
return false;
}
}
@@ -354,17 +354,13 @@ public class BukkitEntityUtil {
}
public static boolean checkEntity(Entity entity, Plot plot) {
return checkEntity(entity.getType(), plot);
}
public static boolean checkEntity(EntityType type, Plot plot) {
if (plot == null || !plot.hasOwner() || plot.getFlags().isEmpty() && plot.getArea()
.getFlagContainer().getFlagMap().isEmpty()) {
return false;
}
final com.sk89q.worldedit.world.entity.EntityType entityType =
BukkitAdapter.adapt(type);
BukkitAdapter.adapt(entity.getType());
if (EntityCategories.PLAYER.contains(entityType)) {
return false;

View File

@@ -75,7 +75,6 @@ import org.bukkit.entity.FallingBlock;
import org.bukkit.entity.Firework;
import org.bukkit.entity.Ghast;
import org.bukkit.entity.Hanging;
import org.bukkit.entity.Interaction;
import org.bukkit.entity.IronGolem;
import org.bukkit.entity.Item;
import org.bukkit.entity.LightningStrike;
@@ -262,11 +261,6 @@ public class BukkitUtil extends WorldUtil {
});
}
@Override
public boolean isSmallBlock(Location location) {
return adapt(location).getBlock().getBoundingBox().getHeight() < 0.25;
}
@Override
@NonNegative
public int getHighestBlockSynchronous(final @NonNull String world, final int x, final int z) {
@@ -438,7 +432,6 @@ public class BukkitUtil extends WorldUtil {
@Override
public @NonNull Set<com.sk89q.worldedit.world.entity.EntityType> getTypesInCategory(final @NonNull String category) {
final Collection<Class<?>> allowedInterfaces = new HashSet<>();
final int[] version = PlotSquared.platform().serverVersion();
switch (category) {
case "animal" -> {
allowedInterfaces.add(IronGolem.class);
@@ -446,7 +439,7 @@ public class BukkitUtil extends WorldUtil {
allowedInterfaces.add(Animals.class);
allowedInterfaces.add(WaterMob.class);
allowedInterfaces.add(Ambient.class);
if (version[1] >= 19) {
if (PlotSquared.platform().serverVersion()[1] >= 19) {
allowedInterfaces.add(Allay.class);
}
}
@@ -477,11 +470,6 @@ public class BukkitUtil extends WorldUtil {
allowedInterfaces.add(Firework.class);
}
case "player" -> allowedInterfaces.add(Player.class);
case "interaction" -> {
if ((version[1] > 19) || (version[1] == 19 && version[2] >= 4)) {
allowedInterfaces.add(Interaction.class);
}
}
default -> LOGGER.error("Unknown entity category requested: {}", category);
}
final Set<com.sk89q.worldedit.world.entity.EntityType> types = new HashSet<>();

View File

@@ -23,15 +23,11 @@ import com.plotsquared.core.location.World;
import org.bukkit.Bukkit;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.Map;
public class BukkitWorld implements World<org.bukkit.World> {
// Upon world unload we should probably have the P2 BukkitWorld be GCed relatively eagerly, thus freeing the bukkit world.
// We also want to avoid circumstances where a bukkit world has been GCed, but a P2 BukkitWorld has not
private static final Map<String, WeakReference<BukkitWorld>> worldMap = Maps.newHashMap();
private static final Map<String, BukkitWorld> worldMap = Maps.newHashMap();
private static final boolean HAS_MIN_Y;
static {
@@ -45,11 +41,10 @@ public class BukkitWorld implements World<org.bukkit.World> {
HAS_MIN_Y = temp;
}
// We want to allow GC to remove bukkit worlds, but not too eagerly
private final SoftReference<org.bukkit.World> world;
private final org.bukkit.World world;
private BukkitWorld(final org.bukkit.World world) {
this.world = new SoftReference<>(world);
this.world = world;
}
/**
@@ -73,13 +68,12 @@ public class BukkitWorld implements World<org.bukkit.World> {
* @return World instance
*/
public static @NonNull BukkitWorld of(final org.bukkit.World world) {
WeakReference<BukkitWorld> bukkitWorldRef = worldMap.get(world.getName());
BukkitWorld bukkitWorld;
if (bukkitWorldRef != null && (bukkitWorld = bukkitWorldRef.get()) != null && world.equals(bukkitWorld.world.get())) {
BukkitWorld bukkitWorld = worldMap.get(world.getName());
if (bukkitWorld != null && bukkitWorld.getPlatformWorld().equals(world)) {
return bukkitWorld;
}
bukkitWorld = new BukkitWorld(world);
worldMap.put(world.getName(), new WeakReference<>(bukkitWorld));
worldMap.put(world.getName(), bukkitWorld);
return bukkitWorld;
}
@@ -103,26 +97,22 @@ public class BukkitWorld implements World<org.bukkit.World> {
@Override
public org.bukkit.World getPlatformWorld() {
org.bukkit.World world = this.world.get();
if (world == null) {
throw new IllegalStateException("Bukkit platform world was unloaded from memory");
}
return world;
return this.world;
}
@Override
public @NonNull String getName() {
return this.getPlatformWorld().getName();
return this.world.getName();
}
@Override
public int getMinHeight() {
return getMinWorldHeight(getPlatformWorld());
return getMinWorldHeight(world);
}
@Override
public int getMaxHeight() {
return getMaxWorldHeight(getPlatformWorld()) - 1;
return getMaxWorldHeight(world) - 1;
}
@Override

View File

@@ -37,9 +37,7 @@ import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.world.biome.BiomeType;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
import java.util.Set;
public class FaweRegionManager extends BukkitRegionManager {
@@ -61,10 +59,7 @@ public class FaweRegionManager extends BukkitRegionManager {
@Nullable PlotPlayer<?> actor,
@Nullable QueueCoordinator queue
) {
return delegate.setCuboids(
area, regions, blocks, minY, maxY,
Objects.requireNonNullElseGet(queue, area::getQueue).getCompleteTask()
);
return delegate.setCuboids(area, regions, blocks, minY, maxY, queue.getCompleteTask());
}
@Override
@@ -116,7 +111,7 @@ public class FaweRegionManager extends BukkitRegionManager {
}
@Override
public boolean regenerateRegion(final @NotNull Location pos1, final @NotNull Location pos2, boolean ignore, final Runnable whenDone) {
public boolean regenerateRegion(final Location pos1, final Location pos2, boolean ignore, final Runnable whenDone) {
return delegate.regenerateRegion(pos1, pos2, ignore, whenDone);
}

View File

@@ -15,6 +15,11 @@ dependencies {
api(libs.adventureApi)
api(libs.adventureMiniMessage)
// Cloud
api(libs.cloud)
api(libs.cloudMinecraftExtras)
api(libs.cloudRequirements)
// Guice
api(libs.guice) {
exclude(group = "com.google.guava")
@@ -68,16 +73,15 @@ tasks {
val isRelease = if (rootProject.version.toString().endsWith("-SNAPSHOT")) "TODO" else rootProject.version.toString()
val opt = options as StandardJavadocDocletOptions
opt.links("https://docs.enginehub.org/javadoc/com.sk89q.worldedit/worldedit-core/" + libs.worldeditCore.get().versionConstraint.toString())
opt.links("https://jd.advntr.dev/api/" + libs.adventureApi.get().versionConstraint.toString())
opt.links("https://jd.advntr.dev/text-minimessage/" + libs.adventureApi.get().versionConstraint.toString())
opt.links("https://jd.advntr.dev/api/4.14.0/")
opt.links("https://jd.advntr.dev/text-minimessage/4.14.0/")
opt.links("https://google.github.io/guice/api-docs/" + libs.guice.get().versionConstraint.toString() + "/javadoc/")
opt.links("https://checkerframework.org/api/")
// opt.links("https://checkerframework.org/api/")
opt.isLinkSource = true
opt.bottom(File("$rootDir/javadocfooter.html").readText())
opt.isUse = true
opt.encoding("UTF-8")
opt.keyWords()
opt.addStringOption("-since", isRelease)
opt.noTimestamp()
}
}

View File

@@ -18,7 +18,6 @@
*/
package com.plotsquared.core;
import cloud.commandframework.services.ServicePipeline;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.TypeLiteral;
@@ -47,6 +46,7 @@ import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.text.Component;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.incendo.cloud.services.ServicePipeline;
import java.io.File;
@@ -120,14 +120,6 @@ public interface PlotPlatform<P> extends LocaleHolder {
*/
@NonNull String serverImplementation();
/**
* Gets the server brand name
*
* @return server brand
* @since 7.5.3
*/
@NonNull String serverBrand();
/**
* Gets the native server code package prefix.
*

View File

@@ -206,8 +206,7 @@ public class PlotSquared {
GlobalFlagContainer.setup();
try {
String ver = this.platform.serverNativePackage();
new ReflectionUtils(ver.isEmpty() ? null : ver);
new ReflectionUtils(this.platform.serverNativePackage());
try {
URL logurl = PlotSquared.class.getProtectionDomain().getCodeSource().getLocation();
this.jarFile = new File(
@@ -215,7 +214,7 @@ public class PlotSquared {
logurl.toURI().toString().split("\\!")[0].replaceAll("jar:file", "file"))
.getPath());
} catch (URISyntaxException | SecurityException e) {
LOGGER.error(e);
e.printStackTrace();
this.jarFile = new File(this.platform.getDirectory().getParentFile(), "PlotSquared.jar");
if (!this.jarFile.exists()) {
this.jarFile = new File(
@@ -239,7 +238,7 @@ public class PlotSquared {
copyFile("skyblock.template", Settings.Paths.TEMPLATES);
showDebug();
} catch (Throwable e) {
LOGGER.error(e);
e.printStackTrace();
}
}
@@ -796,8 +795,9 @@ public class PlotSquared {
if (world.equals("CheckingPlotSquaredGenerator")) {
return;
}
// Don't check the return result -> breaks runtime loading of single plot areas on creation
this.getPlotAreaManager().addWorld(world);
if (!this.getPlotAreaManager().addWorld(world)) {
return;
}
Set<String> worlds;
if (this.worldConfiguration.contains("worlds")) {
worlds = this.worldConfiguration.getConfigurationSection("worlds").getKeys(false);

View File

@@ -184,7 +184,6 @@ public class Area extends SubCommand {
CuboidRegion.makeCuboid(playerSelectedRegion)
).length != 0) {
player.sendMessage(TranslatableCaption.of("single.single_area_overlapping"));
return false;
}
// Alter the region
final BlockVector3 playerSelectionMin = playerSelectedRegion.getMinimumPoint();

View File

@@ -18,7 +18,6 @@
*/
package com.plotsquared.core.command;
import cloud.commandframework.services.ServicePipeline;
import com.google.inject.Inject;
import com.plotsquared.core.PlotSquared;
import com.plotsquared.core.configuration.Settings;
@@ -49,6 +48,7 @@ import net.kyori.adventure.text.minimessage.tag.Tag;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.incendo.cloud.services.ServicePipeline;
import java.util.Collections;
import java.util.Iterator;
@@ -332,7 +332,7 @@ public class Auto extends SubCommand {
List<Plot> plots = this.servicePipeline
.pump(new AutoQuery(player, null, sizeX, sizeZ, plotarea))
.through(AutoService.class)
.getResult();
.complete();
plots = this.eventDispatcher.callAutoPlotsChosen(player, plots).getPlots();

View File

@@ -21,9 +21,8 @@ package com.plotsquared.core.command;
import com.google.inject.Inject;
import com.plotsquared.core.PlotSquared;
import com.plotsquared.core.configuration.caption.TranslatableCaption;
import com.plotsquared.core.events.PlayerBuyPlotEvent;
import com.plotsquared.core.events.PlotFlagRemoveEvent;
import com.plotsquared.core.events.Result;
import com.plotsquared.core.player.OfflinePlotPlayer;
import com.plotsquared.core.player.PlotPlayer;
import com.plotsquared.core.plot.Plot;
import com.plotsquared.core.plot.PlotArea;
@@ -89,30 +88,22 @@ public class Buy extends Command {
TranslatableCaption.of("permission.cant_claim_more_plots"),
TagResolver.resolver("amount", Tag.inserting(Component.text(player.getAllowedPlots())))
);
double priceFlag = plot.getFlag(PriceFlag.class);
if (priceFlag <= 0) {
double price = plot.getFlag(PriceFlag.class);
if (price <= 0) {
throw new CommandException(TranslatableCaption.of("economy.not_for_sale"));
}
checkTrue(
this.econHandler.isSupported(),
TranslatableCaption.of("economy.vault_or_consumer_null")
);
PlayerBuyPlotEvent event = this.eventDispatcher.callPlayerBuyPlot(player, plot, priceFlag);
if (event.getEventResult() == Result.DENY) {
throw new CommandException(TranslatableCaption.of("economy.cannot_buy_blocked"));
}
double price = event.getEventResult() == Result.FORCE ? 0 : event.price();
if (this.econHandler.getMoney(player) < price) {
throw new CommandException(
TranslatableCaption.of("economy.cannot_afford_plot"),
TagResolver.builder()
.tag("money", Tag.inserting(Component.text(this.econHandler.format(price))))
.tag("balance", Tag.inserting(Component.text(this.econHandler.format(this.econHandler.getMoney(player)))))
.build()
);
}
checkTrue(
this.econHandler.getMoney(player) >= price,
TranslatableCaption.of("economy.cannot_afford_plot"),
TagResolver.builder()
.tag("money", Tag.inserting(Component.text(this.econHandler.format(price))))
.tag("balance", Tag.inserting(Component.text(this.econHandler.format(this.econHandler.getMoney(player)))))
.build()
);
this.econHandler.withdrawMoney(player, price);
// Failure
// Success
@@ -122,8 +113,7 @@ public class Buy extends Command {
TagResolver.resolver("money", Tag.inserting(Component.text(this.econHandler.format(price))))
);
OfflinePlotPlayer previousOwner = PlotSquared.platform().playerManager().getOfflinePlayer(plot.getOwnerAbs());
this.econHandler.depositMoney(previousOwner, price);
this.econHandler.depositMoney(PlotSquared.platform().playerManager().getOfflinePlayer(plot.getOwnerAbs()), price);
PlotPlayer<?> owner = PlotSquared.platform().playerManager().getPlayerIfExists(plot.getOwnerAbs());
if (owner != null) {
@@ -137,17 +127,16 @@ public class Buy extends Command {
);
}
PlotFlag<?, ?> plotFlag = plot.getFlagContainer().getFlag(PriceFlag.class);
if (this.eventDispatcher.callFlagRemove(plotFlag, plot).getEventResult() != Result.DENY) {
plot.removeFlag(plotFlag);
PlotFlagRemoveEvent event = this.eventDispatcher.callFlagRemove(plotFlag, plot);
if (event.getEventResult() != Result.DENY) {
plot.removeFlag(event.getFlag());
}
plot.setOwner(player.getUUID());
plot.getPlotModificationManager().setSign(player.getName());
player.sendMessage(
TranslatableCaption.of("working.claimed"),
TagResolver.resolver("world", Tag.inserting(Component.text(plot.getArea().getWorldName()))),
TagResolver.resolver("plot", Tag.inserting(Component.text(plot.getId().toString())))
);
this.eventDispatcher.callPostPlayerBuyPlot(player, previousOwner, plot, price);
whenDone.run(Buy.this, CommandResult.SUCCESS);
}, () -> {
this.econHandler.depositMoney(player, price);

View File

@@ -131,12 +131,10 @@ public class Clear extends Command {
player.sendMessage(
TranslatableCaption.of("working.clearing_done"),
TagResolver.builder()
.tag("world", Tag.inserting(Component.text(plot.getArea().getWorldName())))
.tag("amount", Tag.inserting(Component.text(System.currentTimeMillis() - start)))
.tag("plot", Tag.inserting(Component.text(plot.getId().toString())))
.build()
);
this.eventDispatcher.callPostPlotClear(player, plot);
}));
if (!result) {
player.sendMessage(TranslatableCaption.of("errors.wait_for_timer"));

View File

@@ -24,7 +24,6 @@ import com.plotsquared.core.configuration.caption.TranslatableCaption;
import com.plotsquared.core.player.PlotPlayer;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.jetbrains.annotations.NotNull;
@@ -99,14 +98,6 @@ public enum CommandCategory implements Caption {
return MiniMessage.miniMessage().deserialize(getComponent(localeHolder));
}
@Override
public @NonNull Component toComponent(
@NonNull final LocaleHolder localeHolder,
final @NonNull TagResolver @NonNull ... tagResolvers
) {
return MiniMessage.miniMessage().deserialize(getComponent(localeHolder));
}
/**
* Checks if a player has access to this command category
*

View File

@@ -86,8 +86,7 @@ public class DebugPaste extends SubCommand {
b.append("# WorldEdit implementation:\n");
b.append(PlotSquared.platform().worldEditImplementations()).append("\n\n");
b.append("# Server Information\n");
b.append("Server Version: ").append(PlotSquared.platform().serverBrand()).append(": ")
.append(PlotSquared.platform().serverImplementation()).append("\n")
b.append("Server Version: ").append(PlotSquared.platform().serverImplementation())
.append("\n");
b.append("online_mode: ").append(!Settings.UUID.OFFLINE).append(';')
.append(!Settings.UUID.OFFLINE).append('\n');

View File

@@ -124,7 +124,6 @@ public class Delete extends SubCommand {
"amount",
Tag.inserting(Component.text(String.valueOf(System.currentTimeMillis() - start)))
),
TagResolver.resolver("world", Tag.inserting(Component.text(plotArea.getWorldName()))),
TagResolver.resolver("plot", Tag.inserting(Component.text(plot.getId().toString())))
);
eventDispatcher.callPostDelete(plot);

View File

@@ -20,6 +20,7 @@ package com.plotsquared.core.command;
import com.google.inject.Inject;
import com.plotsquared.core.configuration.Settings;
import com.plotsquared.core.configuration.caption.StaticCaption;
import com.plotsquared.core.configuration.caption.TranslatableCaption;
import com.plotsquared.core.permissions.Permission;
import com.plotsquared.core.player.PlotPlayer;
@@ -201,6 +202,7 @@ public class Download extends SubCommand {
.tag("delete", Tag.preProcessParsed("Not available"))
.build()
);
player.sendMessage(StaticCaption.of(value.toString()));
}
}
));

View File

@@ -101,10 +101,6 @@ public class Grant extends Command {
);
} else {
access.set(access.get().orElse(0) + 1);
player.sendMessage(
TranslatableCaption.of("grants.added"),
TagResolver.resolver("grants", Tag.inserting(Component.text(access.get().orElse(0))))
);
}
}
} else {
@@ -177,14 +173,8 @@ public class Grant extends Command {
commands.addAll(TabCompletions.completePlayers(player, args[0], Collections.emptyList()));
}
return commands;
} else if (args.length == 2) {
final String subcommand = args[0].toLowerCase();
if ((subcommand.equals("add") && player.hasPermission(Permission.PERMISSION_GRANT_ADD)) ||
(subcommand.equals("check") && player.hasPermission(Permission.PERMISSION_GRANT_CHECK))) {
return TabCompletions.completePlayers(player, args[1], Collections.emptyList());
}
}
return Collections.emptyList();
return TabCompletions.completePlayers(player, String.join(",", args).trim(), Collections.emptyList());
}
}

View File

@@ -113,34 +113,38 @@ public class Help extends Command {
}
if (cat == null && page == 0) {
TextComponent.Builder builder = Component.text();
builder.append(TranslatableCaption.of("help.help_header").toComponent(player));
builder.append(MINI_MESSAGE.deserialize(TranslatableCaption.of("help.help_header").getComponent(player)));
for (CommandCategory c : CommandCategory.values()) {
if (!c.canAccess(player)) {
continue;
}
builder.append(Component.newline());
builder.append(TranslatableCaption.of("help.help_info_item").toComponent(
player, TagResolver.builder()
.tag("command", Tag.inserting(Component.text("/plot help")))
.tag("category", Tag.inserting(Component.text(c.name().toLowerCase())))
.tag("category_desc", Tag.inserting(c.toComponent(player)))
.build()
));
builder.append(Component.newline()).append(MINI_MESSAGE
.deserialize(
TranslatableCaption.of("help.help_info_item").getComponent(player),
TagResolver.builder()
.tag("command", Tag.inserting(Component.text("/plot help")))
.tag("category", Tag.inserting(Component.text(c.name().toLowerCase())))
.tag("category_desc", Tag.inserting(c.toComponent(player)))
.build()
));
}
builder.append(Component.newline());
builder.append(TranslatableCaption.of("help.help_info_item").toComponent(
player, TagResolver.builder()
.tag("command", Tag.inserting(Component.text("/plot help")))
.tag("category", Tag.inserting(Component.text("all")))
.tag(
"category_desc", Tag.inserting(TranslatableCaption
.of("help.help_display_all_commands")
.toComponent(player))
)
.build()
));
builder.append(Component.newline());
builder.append(TranslatableCaption.of("help.help_footer").toComponent(player));
builder.append(Component.newline()).append(MINI_MESSAGE
.deserialize(
TranslatableCaption.of("help.help_info_item").getComponent(player),
TagResolver.builder()
.tag("command", Tag.inserting(Component.text("/plot help")))
.tag("category", Tag.inserting(Component.text("all")))
.tag(
"category_desc",
Tag.inserting(TranslatableCaption
.of("help.help_display_all_commands")
.toComponent(player))
)
.build()
));
builder.append(Component.newline()).append(MINI_MESSAGE.deserialize(TranslatableCaption
.of("help.help_footer")
.getComponent(player)));
player.sendMessage(StaticCaption.of(MINI_MESSAGE.serialize(builder.asComponent())));
return true;
}

View File

@@ -20,6 +20,7 @@ package com.plotsquared.core.command;
import com.plotsquared.core.configuration.Settings;
import com.plotsquared.core.configuration.caption.Caption;
import com.plotsquared.core.configuration.caption.StaticCaption;
import com.plotsquared.core.configuration.caption.TranslatableCaption;
import com.plotsquared.core.database.DBFunc;
import com.plotsquared.core.permissions.Permission;
@@ -130,9 +131,13 @@ public class Info extends SubCommand {
info = getCaption(arg);
if (info == null) {
if (Settings.Ratings.USE_LIKES) {
player.sendMessage(TranslatableCaption.of("info.plot_info_categories.use_likes"));
player.sendMessage(StaticCaption.of(
"&6Categories&7: &amembers&7, &aalias&7, &abiome&7, &aseen&7, &adenied&7, &aflags&7, &aid&7, &asize&7, &atrusted&7, "
+ "&aowner&7, " + " &alikes"));
} else {
player.sendMessage(TranslatableCaption.of("info.plot_info_categories.use_rating"));
player.sendMessage(StaticCaption.of(
"&6Categories&7: &amembers&7, &aalias&7, &abiome&7, &aseen&7, &adenied&7, &aflags&7, &aid&7, &asize&7, &atrusted&7, "
+ "&aowner&7, " + " &arating"));
}
return false;
}

View File

@@ -153,7 +153,7 @@ public class Kick extends SubCommand {
if (plot == null) {
return Collections.emptyList();
}
return TabCompletions.completePlayersInPlot(player, plot, String.join(",", args).trim(),
return TabCompletions.completePlayersInPlot(plot, String.join(",", args).trim(),
Collections.singletonList(player.getName())
);
}

View File

@@ -44,13 +44,8 @@ import org.apache.logging.log4j.Logger;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* PlotSquared command class.
*/
@@ -152,7 +147,8 @@ public class MainCommand extends Command {
try {
injector.getInstance(command);
} catch (final Exception e) {
LOGGER.error("Failed to register command {}", command.getCanonicalName(), e);
LOGGER.error("Failed to register command {}", command.getCanonicalName());
e.printStackTrace();
}
}
@@ -240,180 +236,110 @@ public class MainCommand extends Command {
RunnableVal3<Command, Runnable, Runnable> confirm,
RunnableVal2<Command, CommandResult> whenDone
) {
prepareArguments(new CommandExecutionData(player, args, confirm, whenDone, null))
.thenCompose(executionData -> {
if (executionData.isEmpty()) {
return CompletableFuture.completedFuture(false);
// Optional command scope //
Location location = null;
Plot plot = null;
boolean tp = false;
if (args.length >= 2) {
PlotArea area = player.getApplicablePlotArea();
Plot newPlot = Plot.fromString(area, args[0]);
if (newPlot != null && (player instanceof ConsolePlayer || newPlot.getArea()
.equals(area) || player.hasPermission(Permission.PERMISSION_ADMIN)
|| player.hasPermission(Permission.PERMISSION_ADMIN_AREA_SUDO))
&& !newPlot.isDenied(player.getUUID())) {
final Location newLoc;
if (newPlot.getArea() instanceof SinglePlotArea) {
newLoc = newPlot.isLoaded() ? newPlot.getCenterSynchronous() : Location.at("", 0, 0, 0);
} else {
newLoc = newPlot.getCenterSynchronous();
}
if (player.canTeleport(newLoc)) {
// Save meta
try (final MetaDataAccess<Location> locationMetaDataAccess
= player.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_LOCATION)) {
location = locationMetaDataAccess.get().orElse(null);
locationMetaDataAccess.set(newLoc);
}
var data = executionData.get();
try {
return super.execute(data.player(), data.args(), data.confirm(), data.whenDone());
} catch (CommandException e) {
throw e;
} catch (Throwable e) {
LOGGER.error("A error occurred while executing plot command", e);
String message = e.getMessage();
if (message != null) {
data.player().sendMessage(
TranslatableCaption.of("errors.error"),
TagResolver.resolver("value", Tag.inserting(Component.text(message)))
);
} else {
data.player().sendMessage(
TranslatableCaption.of("errors.error_console"));
}
} finally {
if (data.postCommandData() != null) {
resetCommandScope(data.player(), data.postCommandData());
}
try (final MetaDataAccess<Plot> plotMetaDataAccess
= player.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_LAST_PLOT)) {
plot = plotMetaDataAccess.get().orElse(null);
plotMetaDataAccess.set(newPlot);
}
return CompletableFuture.completedFuture(true);
});
tp = true;
} else {
player.sendMessage(TranslatableCaption.of("border.denied"));
}
// Trim command
args = Arrays.copyOfRange(args, 1, args.length);
}
if (args.length >= 2 && !args[0].isEmpty() && args[0].charAt(0) == '-') {
if ("f".equals(args[0].substring(1))) {
confirm = new RunnableVal3<>() {
@Override
public void run(Command cmd, Runnable success, Runnable failure) {
if (area != null && PlotSquared.platform().econHandler().isEnabled(area)) {
PlotExpression priceEval =
area.getPrices().get(cmd.getFullId());
double price = priceEval != null ? priceEval.evaluate(0d) : 0d;
if (price != 0d
&& PlotSquared.platform().econHandler().getMoney(player) < price) {
if (failure != null) {
failure.run();
}
return;
}
}
if (success != null) {
success.run();
}
}
};
args = Arrays.copyOfRange(args, 1, args.length);
} else {
player.sendMessage(TranslatableCaption.of("errors.invalid_command_flag"));
return CompletableFuture.completedFuture(false);
}
}
}
try {
super.execute(player, args, confirm, whenDone);
} catch (CommandException e) {
throw e;
} catch (Throwable e) {
e.printStackTrace();
String message = e.getMessage();
if (message != null) {
player.sendMessage(
TranslatableCaption.of("errors.error"),
TagResolver.resolver("value", Tag.inserting(Component.text(message)))
);
} else {
player.sendMessage(
TranslatableCaption.of("errors.error_console"));
}
}
// Reset command scope //
if (tp && !(player instanceof ConsolePlayer)) {
try (final MetaDataAccess<Location> locationMetaDataAccess
= player.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_LOCATION)) {
if (location == null) {
locationMetaDataAccess.remove();
} else {
locationMetaDataAccess.set(location);
}
}
try (final MetaDataAccess<Plot> plotMetaDataAccess
= player.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_LAST_PLOT)) {
if (plot == null) {
plotMetaDataAccess.remove();
} else {
plotMetaDataAccess.set(plot);
}
}
}
return CompletableFuture.completedFuture(true);
}
private CompletableFuture<Optional<CommandExecutionData>> prepareArguments(CommandExecutionData data) {
if (data.args().length >= 2) {
PlotArea area = data.player().getApplicablePlotArea();
Plot newPlot = Plot.fromString(area, data.args()[0], data.player());
return preparePlotArgument(newPlot, data, area)
.thenApply(d -> d.flatMap(x -> prepareFlagArgument(x, area)));
} else {
return CompletableFuture.completedFuture(Optional.of(data));
}
}
private CompletableFuture<Optional<CommandExecutionData>> preparePlotArgument(
@Nullable Plot newPlot,
@Nonnull CommandExecutionData data,
@Nullable PlotArea area
) {
if (newPlot == null) {
return CompletableFuture.completedFuture(Optional.of(data));
}
final PlotPlayer<?> player = data.player();
final boolean isAdmin = player instanceof ConsolePlayer || player.hasPermission(Permission.PERMISSION_ADMIN);
final boolean isDenied = newPlot.isDenied(player.getUUID());
if (!isAdmin) {
if (isDenied) {
throw new CommandException(TranslatableCaption.of("deny.cannot_interact"));
}
if (area != null && area.equals(newPlot.getArea()) && !player.hasPermission(Permission.PERMISSION_ADMIN_AREA_SUDO)) {
return CompletableFuture.completedFuture(Optional.of(data));
}
}
return fetchPlotCenterLocation(newPlot)
.thenApply(newLoc -> {
if (!player.canTeleport(newLoc)) {
player.sendMessage(TranslatableCaption.of("border.denied"));
return Optional.empty();
}
// Save meta
var originalCommandMeta = setCommandScope(player, new TemporaryCommandMeta(newLoc, newPlot));
return Optional.of(new CommandExecutionData(
player,
Arrays.copyOfRange(data.args(), 1, data.args().length), // Trimmed command
data.confirm(),
data.whenDone(),
originalCommandMeta
));
});
}
private Optional<CommandExecutionData> prepareFlagArgument(@Nonnull CommandExecutionData data, @Nonnull PlotArea area) {
if (data.args().length >= 2 && !data.args()[0].isEmpty() && data.args()[0].charAt(0) == '-') {
if ("f".equals(data.args()[0].substring(1))) {
return Optional.of(new CommandExecutionData(
data.player(),
Arrays.copyOfRange(data.args(), 1, data.args().length), // Trimmed command
createForcedConfirmation(data.player(), area),
data.whenDone(),
data.postCommandData()
));
} else {
data.player().sendMessage(TranslatableCaption.of("errors.invalid_command_flag"));
return Optional.empty();
}
}
return Optional.of(data);
}
private CompletableFuture<Location> fetchPlotCenterLocation(Plot plot) {
if (plot.getArea() instanceof SinglePlotArea && !plot.isLoaded()) {
return CompletableFuture.completedFuture(Location.at("", 0, 0, 0));
}
CompletableFuture<Location> future = new CompletableFuture<>();
plot.getCenter(future::complete);
return future;
}
private @Nonnull RunnableVal3<Command, Runnable, Runnable> createForcedConfirmation(@Nonnull PlotPlayer<?> player,
@Nullable PlotArea area) {
return new RunnableVal3<>() {
@Override
public void run(Command cmd, Runnable success, Runnable failure) {
if (area != null && PlotSquared.platform().econHandler().isEnabled(area)
&& Optional.of(area.getPrices().get(cmd.getFullId()))
.map(priceEval -> priceEval.evaluate(0d))
.filter(price -> price != 0d)
.filter(price -> PlotSquared.platform().econHandler().getMoney(player) < price)
.isPresent()) {
if (failure != null) {
failure.run();
}
return;
}
if (success != null) {
success.run();
}
}
};
}
private @Nonnull TemporaryCommandMeta setCommandScope(@Nonnull PlotPlayer<?> player, @Nonnull TemporaryCommandMeta commandMeta) {
Objects.requireNonNull(commandMeta.location());
Objects.requireNonNull(commandMeta.plot());
Location location;
Plot plot;
try (final MetaDataAccess<Location> locationMetaDataAccess
= player.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_LOCATION)) {
location = locationMetaDataAccess.get().orElse(null);
locationMetaDataAccess.set(commandMeta.location());
}
try (final MetaDataAccess<Plot> plotMetaDataAccess
= player.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_LAST_PLOT)) {
plot = plotMetaDataAccess.get().orElse(null);
plotMetaDataAccess.set(commandMeta.plot());
}
return new TemporaryCommandMeta(location, plot);
}
private void resetCommandScope(@Nonnull PlotPlayer<?> player, @Nonnull TemporaryCommandMeta commandMeta) {
try (final MetaDataAccess<Location> locationMetaDataAccess
= player.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_LOCATION)) {
if (commandMeta.location() == null) {
locationMetaDataAccess.remove();
} else {
locationMetaDataAccess.set(commandMeta.location());
}
}
try (final MetaDataAccess<Plot> plotMetaDataAccess
= player.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_LAST_PLOT)) {
if (commandMeta.plot() == null) {
plotMetaDataAccess.remove();
} else {
plotMetaDataAccess.set(commandMeta.plot());
}
}
}
private record CommandExecutionData(@Nonnull PlotPlayer<?> player, @Nonnull String[] args,
@Nonnull RunnableVal3<Command, Runnable, Runnable> confirm,
@Nonnull RunnableVal2<Command, CommandResult> whenDone,
@Nullable TemporaryCommandMeta postCommandData) {}
private record TemporaryCommandMeta(@Nullable Location location, @Nullable Plot plot) {}
@Override
public boolean canExecute(PlotPlayer<?> player, boolean message) {
return true;

View File

@@ -56,13 +56,9 @@ public class Music extends SubCommand {
.asList("music_disc_13", "music_disc_cat", "music_disc_blocks", "music_disc_chirp",
"music_disc_far", "music_disc_mall", "music_disc_mellohi", "music_disc_stal",
"music_disc_strad", "music_disc_ward", "music_disc_11", "music_disc_wait", "music_disc_otherside",
"music_disc_pigstep", "music_disc_5", "music_disc_relic", "music_disc_creator",
"music_disc_creator_music_box", "music_disc_precipice", "music_disc_tears", "music_disc_lava_chicken"
"music_disc_pigstep", "music_disc_5", "music_disc_relic"
);
// make sure all discs and the bedrock ("cancel") fit into the inventory
private static final int INVENTORY_ROWS = (int) Math.ceil((DISCS.size() + 1) / 9.0);
private final InventoryUtil inventoryUtil;
private final EventDispatcher eventDispatcher;
@@ -97,7 +93,7 @@ public class Music extends SubCommand {
PlotInventory inv = new PlotInventory(
this.inventoryUtil,
player,
INVENTORY_ROWS,
2,
TranslatableCaption.of("plotjukebox.jukebox_header").getComponent(player)
) {
@Override

View File

@@ -117,8 +117,8 @@ public class Remove extends SubCommand {
}
if (count == 0) {
player.sendMessage(
TranslatableCaption.of("member.player_not_removed"),
TagResolver.resolver("player", Tag.inserting(Component.text(args[0])))
TranslatableCaption.of("errors.invalid_player"),
TagResolver.resolver("value", Tag.inserting(Component.text(args[0])))
);
} else {
player.sendMessage(

View File

@@ -77,7 +77,6 @@ public class Visit extends Command {
query.whereBasePlot();
}
// without specified argument
if (page == Integer.MIN_VALUE) {
page = 1;
}
@@ -95,15 +94,10 @@ public class Visit extends Command {
final List<Plot> plots = query.asList();
// Conversion of reversed page argument
if (page < 0) {
page = (plots.size() + 1) + page;
}
if (plots.isEmpty()) {
player.sendMessage(TranslatableCaption.of("invalid.found_no_plots"));
return;
} else if (page > plots.size() || page < 1) {
} else if (plots.size() < page || page < 1) {
player.sendMessage(
TranslatableCaption.of("invalid.number_not_in_range"),
TagResolver.builder()
@@ -194,22 +188,34 @@ public class Visit extends Command {
int page = Integer.MIN_VALUE;
switch (args.length) {
// /p v <player> <area> <page>
// /p v <user> <area> <page>
case 3:
if (isInvalidPageNr(args[2])) {
sendInvalidPageNrMsg(player);
if (!MathMan.isInteger(args[2])) {
player.sendMessage(
TranslatableCaption.of("invalid.not_valid_number"),
TagResolver.resolver("value", Tag.inserting(Component.text("(1, ∞)")))
);
player.sendMessage(
TranslatableCaption.of("commandconfig.command_syntax"),
TagResolver.resolver("value", Tag.inserting(Component.text(getUsage())))
);
return CompletableFuture.completedFuture(false);
}
page = getPageNr(args[2]);
// /p v <player> <area> [page]
// /p v <player> [page]
page = Integer.parseInt(args[2]);
// /p v <name> <area> [page]
// /p v <name> [page]
case 2:
// If "case 3" is already through or the argument is not a page number:
// -> /p v <player> <area> [page]
if (page != Integer.MIN_VALUE || isInvalidPageNr(args[1])) {
if (page != Integer.MIN_VALUE || !MathMan.isInteger(args[1])) {
sortByArea = this.plotAreaManager.getPlotAreaByString(args[1]);
if (sortByArea == null) {
sendInvalidPageNrMsg(player);
player.sendMessage(
TranslatableCaption.of("invalid.not_valid_number"),
TagResolver.resolver("value", Tag.inserting(Component.text("(1, ∞)")))
);
player.sendMessage(
TranslatableCaption.of("commandconfig.command_syntax"),
TagResolver.resolver("value", Tag.inserting(Component.text(getUsage())))
);
return CompletableFuture.completedFuture(false);
}
@@ -243,13 +249,16 @@ public class Visit extends Command {
});
break;
}
// -> /p v <player> <page>
if (isInvalidPageNr(args[1])) {
sendInvalidPageNrMsg(player);
try {
page = Integer.parseInt(args[1]);
} catch (NumberFormatException ignored) {
player.sendMessage(
TranslatableCaption.of("invalid.not_a_number"),
TagResolver.resolver("value", Tag.inserting(Component.text(args[1])))
);
return CompletableFuture.completedFuture(false);
}
page = getPageNr(args[1]);
// /p v <player> [page]
// /p v <name> [page]
// /p v <uuid> [page]
// /p v <plot> [page]
// /p v <alias>
@@ -317,35 +326,6 @@ public class Visit extends Command {
return CompletableFuture.completedFuture(true);
}
private boolean isInvalidPageNr(String arg) {
if (MathMan.isInteger(arg)) {
return false;
} else if (arg.equals("last") || arg.equals("n")) {
return false;
}
return true;
}
private int getPageNr(String arg) {
if (MathMan.isInteger(arg)) {
return Integer.parseInt(arg);
} else if (arg.equals("last") || arg.equals("n")) {
return -1;
}
return Integer.MIN_VALUE;
}
private void sendInvalidPageNrMsg(PlotPlayer<?> player) {
player.sendMessage(
TranslatableCaption.of("invalid.not_valid_number"),
TagResolver.resolver("value", Tag.inserting(Component.text("(1, ∞)")))
);
player.sendMessage(
TranslatableCaption.of("commandconfig.command_syntax"),
TagResolver.resolver("value", Tag.inserting(Component.text(getUsage())))
);
}
@Override
public Collection<Command> tab(PlotPlayer<?> player, String[] args, boolean space) {
final List<Command> completions = new ArrayList<>();
@@ -354,7 +334,6 @@ public class Visit extends Command {
case 1 -> {
completions.addAll(
TabCompletions.completeAreas(args[1]));
completions.addAll(TabCompletions.asCompletions("last"));
if (args[1].isEmpty()) {
// if no input is given, only suggest 1 - 3
completions.addAll(
@@ -365,7 +344,6 @@ public class Visit extends Command {
TabCompletions.completeNumbers(args[1], 10, 999));
}
case 2 -> {
completions.addAll(TabCompletions.asCompletions("last"));
if (args[2].isEmpty()) {
// if no input is given, only suggest 1 - 3
completions.addAll(

View File

@@ -0,0 +1,98 @@
/*
* 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.commands;
import com.plotsquared.core.configuration.caption.TranslatableCaption;
import com.plotsquared.core.permissions.Permission;
import com.plotsquared.core.player.PlotPlayer;
import io.leangen.geantyref.TypeToken;
import net.kyori.adventure.text.minimessage.tag.Tag;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.incendo.cloud.context.CommandContext;
import org.incendo.cloud.key.CloudKey;
import org.incendo.cloud.processors.requirements.Requirement;
import org.incendo.cloud.processors.requirements.Requirements;
import java.util.List;
/**
* Something that is required for a command to be executed.
*/
public interface CommandRequirement extends Requirement<PlotPlayer<?>, CommandRequirement> {
/**
* The key used to store the requirements in the {@link org.incendo.cloud.meta.CommandMeta}.
*/
CloudKey<Requirements<PlotPlayer<?>, CommandRequirement>> REQUIREMENTS_KEY = CloudKey.of(
"requirements",
new TypeToken<Requirements<PlotPlayer<?>, CommandRequirement>>() {
}
);
/**
* Returns the caption sent when the requirement is not met.
*
* @return the caption
*/
@NonNull TranslatableCaption failureCaption();
/**
* Returns the placeholder values.
*
* @return placeholder values
*/
default @NonNull TagResolver @NonNull[] tagResolvers() {
return new TagResolver[0];
}
/**
* Returns a requirement that evaluates to {@code true} if the sender has the given {@code permission} or if
* this requirement evaluates to {@code true}.
*
* @param permission the override permission
* @return the new requirement
*/
default @NonNull CommandRequirement withPermissionOverride(final @NonNull Permission permission) {
final CommandRequirement thisRequirement = this;
return new CommandRequirement() {
@Override
public @NonNull TranslatableCaption failureCaption() {
return TranslatableCaption.of("permission.no_permission");
}
@Override
public @NonNull TagResolver @NonNull [] tagResolvers() {
return new TagResolver[] {
TagResolver.resolver("node", Tag.inserting(Permission.PERMISSION_SET_FLAG_OTHER))
};
}
@Override
public @NonNull List<@NonNull CommandRequirement> parents() {
return thisRequirement.parents();
}
@Override
public boolean evaluateRequirement(final @NonNull CommandContext<PlotPlayer<?>> context) {
return context.sender().hasPermission(permission) || thisRequirement.evaluateRequirement(context);
}
};
}
}

View File

@@ -0,0 +1,81 @@
/*
* 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.commands;
import com.plotsquared.core.configuration.caption.TranslatableCaption;
import com.plotsquared.core.player.PlotPlayer;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.incendo.cloud.context.CommandContext;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
/**
* Common {@link CommandRequirement command requirements}.
*/
public enum CommonCommandRequirement implements CommandRequirement {
/**
* Requires that the command sender is currently in a plot.
*/
REQUIRES_PLOT(TranslatableCaption.of("errors.not_in_plot"), ctx -> ctx.sender().getCurrentPlot() != null),
/**
* Requires that the command sender is in a claimed plot.
*/
REQUIRES_OWNER(TranslatableCaption.of("working.plot_not_claimed"),
ctx -> ctx.sender().getCurrentPlot().hasOwner(),
REQUIRES_PLOT
),
/**
* Requires that the command sender is the plot owner.
*/
IS_OWNER(TranslatableCaption.of("permission.no_plot_perms"),
ctx -> ctx.sender().getCurrentPlot().isOwner(ctx.sender().getUUID()),
REQUIRES_OWNER
)
;
private final TranslatableCaption failureCaption;
private final Predicate<CommandContext<PlotPlayer<?>>> predicate;
private final List<@NonNull CommandRequirement> parents;
CommonCommandRequirement(
final @NonNull TranslatableCaption failureCaption,
final @NonNull Predicate<CommandContext<PlotPlayer<?>>> predicate,
final @NonNull CommandRequirement @NonNull... parents
) {
this.failureCaption = failureCaption;
this.predicate = predicate;
this.parents = Arrays.asList(parents);
}
public @NonNull TranslatableCaption failureCaption() {
return this.failureCaption;
}
@Override
public @NonNull List<@NonNull CommandRequirement> parents() {
return this.parents;
}
@Override
public boolean evaluateRequirement(final @NonNull CommandContext<PlotPlayer<?>> context) {
return this.predicate.test(context);
}
}

View File

@@ -0,0 +1,50 @@
/*
* 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.commands;
import com.plotsquared.core.PlotSquared;
import com.plotsquared.core.configuration.caption.CaptionMap;
import com.plotsquared.core.configuration.caption.TranslatableCaption;
import com.plotsquared.core.player.PlotPlayer;
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.Nullable;
import org.incendo.cloud.caption.Caption;
import org.incendo.cloud.caption.CaptionProvider;
/**
* {@link CaptionProvider} that retrieves caption values from the {@link CaptionMap caption map}.
*/
public final class PlotSquaredCaptionProvider implements CaptionProvider<PlotPlayer<?>> {
private static final Logger LOGGER = LogManager.getLogger("PlotSquared/" + PlotSquaredCaptionProvider.class.getSimpleName());
@Override
public @Nullable String provide(final @NonNull Caption caption, final @NonNull PlotPlayer<?> recipient) {
try {
return PlotSquared.get()
.getCaptionMap(TranslatableCaption.DEFAULT_NAMESPACE)
.getMessage(TranslatableCaption.of(caption.key()), recipient);
} catch (final CaptionMap.NoSuchCaptionException ignored) {
LOGGER.warn("Missing caption '{}', will attempt to fall back on Cloud defaults", caption.key());
return null;
}
}
}

View File

@@ -0,0 +1,78 @@
/*
* 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.commands;
import com.plotsquared.core.command.CommandCategory;
import com.plotsquared.core.player.PlotPlayer;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.incendo.cloud.Command;
import org.incendo.cloud.bean.CommandBean;
import org.incendo.cloud.bean.CommandProperties;
import org.incendo.cloud.processors.requirements.RequirementApplicable;
import org.incendo.cloud.processors.requirements.Requirements;
import java.util.List;
public abstract class PlotSquaredCommandBean extends CommandBean<PlotPlayer<?>> {
private final RequirementApplicable.RequirementApplicableFactory<PlotPlayer<?>, CommandRequirement>
requirementApplicableFactory = RequirementApplicable.factory(CommandRequirement.REQUIREMENTS_KEY);
/**
* Returns the category of the command.
*
* @return the category
*/
public abstract @NonNull CommandCategory category();
/**
* Returns the requirements for the command to be executable.
*
* @return the requirements
*/
public abstract @NonNull List<@NonNull CommandRequirement> requirements();
/**
* Prepares the given {@code builder}.
*
* <p>This should be implemented by abstract classes that extend {@link PlotSquaredCommandBean} to offer shared behavior
* for a subset of plot commands.</p>
*
* @param builder the builder
* @return the prepared builder
*/
protected Command.@NonNull Builder<PlotPlayer<?>> prepare(final Command.@NonNull Builder<PlotPlayer<?>> builder) {
return builder;
}
@Override
protected final @NonNull CommandProperties properties() {
return CommandProperties.of("platsquared", "plat");
}
@Override
protected final Command.@NonNull Builder<PlotPlayer<?>> configure(final Command.@NonNull Builder<PlotPlayer<?>> builder) {
return this.configurePlotCommand(this.prepare(builder.meta(PlotSquaredCommandMeta.META_CATEGORY, this.category())))
.apply(this.requirementApplicableFactory.create(Requirements.of(this.requirements())));
}
protected abstract Command.@NonNull Builder<PlotPlayer<?>> configurePlotCommand(
Command.@NonNull Builder<PlotPlayer<?>> builder
);
}

View File

@@ -0,0 +1,74 @@
/*
* 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.commands;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Singleton;
import com.google.inject.TypeLiteral;
import com.plotsquared.core.commands.injection.PlotInjector;
import com.plotsquared.core.player.PlotPlayer;
import com.plotsquared.core.plot.Plot;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.incendo.cloud.CommandManager;
import org.incendo.cloud.injection.GuiceInjectionService;
import java.util.Set;
@Singleton
public final class PlotSquaredCommandManager {
private final CommandManager<PlotPlayer<?>> commandManager;
private final Injector injector;
@Inject
public PlotSquaredCommandManager(
final @NonNull CommandManager<PlotPlayer<?>> commandManager,
final @NonNull Injector injector
) {
this.commandManager = commandManager;
this.injector = injector;
this.registerInjectors();
}
/**
* Registers the commands that are shipped with PlotSquared.
*/
public void registerDefaultCommands() {
final Set<PlotSquaredCommandBean> commands =
this.injector.getInstance(Key.get(new TypeLiteral<Set<PlotSquaredCommandBean>>() {}));
commands.forEach(command -> this.commandManager().command(command));
}
/**
* Returns the command manager.
*
* @return the command manager
*/
public @NonNull CommandManager<PlotPlayer<?>> commandManager() {
return this.commandManager;
}
private void registerInjectors() {
this.commandManager.parameterInjectorRegistry().registerInjector(Plot.class,
this.injector.getInstance(PlotInjector.class));
this.commandManager.parameterInjectorRegistry().registerInjectionService(GuiceInjectionService.create(this.injector));
}
}

View File

@@ -16,28 +16,21 @@
* 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.events.post;
package com.plotsquared.core.commands;
import com.plotsquared.core.events.PlotPlayerEvent;
import com.plotsquared.core.player.PlotPlayer;
import com.plotsquared.core.plot.Plot;
import com.plotsquared.core.command.CommandCategory;
import org.incendo.cloud.key.CloudKey;
/**
* Called after a {@link Plot} was cleared.
*
* @since 7.3.2
* Shared {@link org.incendo.cloud.meta.CommandMeta command meta} keys.
*/
public class PostPlotClearEvent extends PlotPlayerEvent {
public final class PlotSquaredCommandMeta {
/**
* Instantiate a new PostPlotClearEvent.
*
* @param plotPlayer The {@link PlotPlayer} that initiated the clear.
* @param plot The clearing plot.
* Key that determines what {@link CommandCategory category} a command belongs to.
*/
public PostPlotClearEvent(final PlotPlayer<?> plotPlayer, final Plot plot) {
super(plotPlayer, plot);
}
public static final CloudKey<CommandCategory> META_CATEGORY = CloudKey.of("category", CommandCategory.class);
private PlotSquaredCommandMeta() {
}
}

View File

@@ -16,24 +16,20 @@
* 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.plot.flag.implementations;
package com.plotsquared.core.commands;
import com.plotsquared.core.configuration.caption.TranslatableCaption;
import com.plotsquared.core.plot.flag.types.BooleanFlag;
import com.plotsquared.core.player.PlotPlayer;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.incendo.cloud.context.CommandContext;
import org.incendo.cloud.processors.requirements.RequirementFailureHandler;
public class FishingFlag extends BooleanFlag<FishingFlag> {
public static final FishingFlag FISHING_TRUE = new FishingFlag(true);
public static final FishingFlag FISHING_FALSE = new FishingFlag(false);
private FishingFlag(boolean value) {
super(value, TranslatableCaption.of("flags.flag_description_fishing"));
}
public final class PlotSquaredRequirementFailureHandler implements RequirementFailureHandler<PlotPlayer<?>, CommandRequirement> {
@Override
protected FishingFlag flagOf(@NonNull final Boolean value) {
return value ? FISHING_TRUE : FISHING_FALSE;
public void handleFailure(
final @NonNull CommandContext<PlotPlayer<?>> context,
final @NonNull CommandRequirement requirement
) {
context.sender().sendMessage(requirement.failureCaption(), requirement.tagResolvers());
}
}

View File

@@ -0,0 +1,120 @@
/*
* 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.commands.command.setting.flag;
import com.google.inject.Inject;
import com.plotsquared.core.commands.parser.PlotFlagParser;
import com.plotsquared.core.commands.suggestions.FlagValueSuggestionProvider;
import com.plotsquared.core.configuration.caption.CaptionUtility;
import com.plotsquared.core.configuration.caption.TranslatableCaption;
import com.plotsquared.core.events.PlotFlagAddEvent;
import com.plotsquared.core.events.Result;
import com.plotsquared.core.player.PlotPlayer;
import com.plotsquared.core.plot.Plot;
import com.plotsquared.core.plot.flag.FlagParseException;
import com.plotsquared.core.plot.flag.PlotFlag;
import com.plotsquared.core.util.EventDispatcher;
import io.leangen.geantyref.TypeToken;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.tag.Tag;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.incendo.cloud.Command;
import org.incendo.cloud.context.CommandContext;
import org.incendo.cloud.key.CloudKey;
import static com.plotsquared.core.commands.parser.PlotFlagParser.plotFlagParser;
import static org.incendo.cloud.parser.standard.StringParser.greedyStringParser;
public final class FlagAddCommand extends FlagCommandBean {
private static final CloudKey<PlotFlag<?, ?>> COMPONENT_FLAG = CloudKey.of("flag", new TypeToken<PlotFlag<?, ?>>() {});
private static final CloudKey<String> COMPONENT_VALUE = CloudKey.of("value", String.class);
private final EventDispatcher eventDispatcher;
@Inject
public FlagAddCommand(final @NonNull EventDispatcher eventDispatcher) {
this.eventDispatcher = eventDispatcher;
}
@Override
protected Command.@NonNull Builder<PlotPlayer<?>> configurePlotCommand(
final Command.@NonNull Builder<PlotPlayer<?>> builder
) {
return builder.literal("add")
.required(COMPONENT_FLAG, plotFlagParser(PlotFlagParser.FlagSource.GLOBAL))
.required(COMPONENT_VALUE, greedyStringParser(), new FlagValueSuggestionProvider(COMPONENT_FLAG));
}
@Override
public void execute(final @NonNull CommandContext<PlotPlayer<?>> commandContext) {
final PlotPlayer<?> player = commandContext.sender();
final Plot plot = commandContext.inject(Plot.class).orElseThrow();
final PlotFlag<?, ?> flag = commandContext.get(COMPONENT_FLAG);
final String flagValue = commandContext.get(COMPONENT_VALUE);
final PlotFlagAddEvent event = this.eventDispatcher.callFlagAdd(flag, plot);
if (event.getEventResult() == Result.DENY) {
player.sendMessage(
TranslatableCaption.of("events.event_denied"),
TagResolver.resolver("value", Tag.inserting(Component.text("Flag set")))
);
return;
}
if (event.getEventResult() != Result.FORCE) {
final String[] split = flagValue.split(",");
for (final String entry : split) {
if (!checkPermValue(player, flag, flag.getName(), entry)) {
return;
}
}
}
final String sanitizedValue = CaptionUtility.stripClickEvents(flag, flagValue);
final PlotFlag<?, ?> parsedFlag;
try {
parsedFlag = flag.parse(flagValue);
} catch (final FlagParseException e) {
player.sendMessage(
TranslatableCaption.of("flag.flag_parse_error"),
TagResolver.builder()
.tag("flag_name", Tag.inserting(Component.text(flag.getName())))
.tag("flag_value", Tag.inserting(Component.text(e.getValue())))
.tag("error", Tag.inserting(e.getErrorMessage().toComponent(player)))
.build()
);
return;
}
final boolean result = plot.setFlag(plot.getFlagContainer().getFlag(flag.getClass()).merge(parsedFlag.getValue()));
if (!result) {
player.sendMessage(TranslatableCaption.of("flag.flag_not_added"));
return;
}
player.sendMessage(
TranslatableCaption.of("flag.flag_added"),
TagResolver.builder()
.tag("flag", Tag.inserting(Component.text(flag.getName())))
.tag("value", Tag.inserting(Component.text(parsedFlag.toString())))
.build()
);
}
}

View File

@@ -0,0 +1,139 @@
/*
* 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.commands.command.setting.flag;
import com.plotsquared.core.PlotSquared;
import com.plotsquared.core.command.CommandCategory;
import com.plotsquared.core.commands.CommandRequirement;
import com.plotsquared.core.commands.CommonCommandRequirement;
import com.plotsquared.core.commands.PlotSquaredCommandBean;
import com.plotsquared.core.configuration.Settings;
import com.plotsquared.core.configuration.caption.TranslatableCaption;
import com.plotsquared.core.permissions.Permission;
import com.plotsquared.core.player.PlotPlayer;
import com.plotsquared.core.plot.flag.FlagParseException;
import com.plotsquared.core.plot.flag.PlotFlag;
import com.plotsquared.core.plot.flag.types.IntegerFlag;
import com.plotsquared.core.plot.flag.types.ListFlag;
import com.plotsquared.core.util.MathMan;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.tag.Tag;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.incendo.cloud.Command;
import java.util.List;
public abstract class FlagCommandBean extends PlotSquaredCommandBean {
protected static boolean checkPermValue(
final @NonNull PlotPlayer<?> player,
final @NonNull PlotFlag<?, ?> flag, @NonNull String key, @NonNull String value
) {
key = key.toLowerCase();
value = value.toLowerCase();
String perm = Permission.PERMISSION_SET_FLAG_KEY_VALUE.format(key.toLowerCase(), value.toLowerCase());
if (flag instanceof IntegerFlag && MathMan.isInteger(value)) {
try {
int numeric = Integer.parseInt(value);
// Getting full permission without ".<amount>" at the end
perm = perm.substring(0, perm.length() - value.length() - 1);
boolean result = false;
if (numeric >= 0) {
int checkRange = PlotSquared.get().getPlatform().equalsIgnoreCase("bukkit") ?
numeric :
Settings.Limit.MAX_PLOTS;
result = player.hasPermissionRange(perm, checkRange) >= numeric;
}
if (!result) {
player.sendMessage(
TranslatableCaption.of("permission.no_permission"),
TagResolver.resolver(
"node",
Tag.inserting(Component.text(perm + "." + numeric))
)
);
}
return result;
} catch (NumberFormatException ignore) {
}
} else if (flag instanceof final ListFlag<?, ?> listFlag) {
try {
PlotFlag<? extends List<?>, ?> parsedFlag = listFlag.parse(value);
for (final Object entry : parsedFlag.getValue()) {
final String permission = Permission.PERMISSION_SET_FLAG_KEY_VALUE.format(
key.toLowerCase(),
entry.toString().toLowerCase()
);
final boolean result = player.hasPermission(permission);
if (!result) {
player.sendMessage(
TranslatableCaption.of("permission.no_permission"),
TagResolver.resolver("node", Tag.inserting(Component.text(permission)))
);
return false;
}
}
} catch (final FlagParseException e) {
player.sendMessage(
TranslatableCaption.of("flag.flag_parse_error"),
TagResolver.builder()
.tag("flag_name", Tag.inserting(Component.text(flag.getName())))
.tag("flag_value", Tag.inserting(Component.text(e.getValue())))
.tag("error", Tag.inserting(e.getErrorMessage().toComponent(player)))
.build()
);
return false;
} catch (final Exception e) {
return false;
}
return true;
}
boolean result;
String basePerm = Permission.PERMISSION_SET_FLAG_KEY.format(key.toLowerCase());
if (flag.isValuedPermission()) {
result = player.hasKeyedPermission(basePerm, value);
} else {
result = player.hasPermission(basePerm);
perm = basePerm;
}
if (!result) {
player.sendMessage(
TranslatableCaption.of("permission.no_permission"),
TagResolver.resolver("node", Tag.inserting(Component.text(perm)))
);
}
return result;
}
@Override
public final @NonNull CommandCategory category() {
return CommandCategory.SETTINGS;
}
@Override
public @NonNull List<@NonNull CommandRequirement> requirements() {
return List.of(CommonCommandRequirement.IS_OWNER.withPermissionOverride(Permission.PERMISSION_SET_FLAG_OTHER));
}
@Override
protected final Command.@NonNull Builder<PlotPlayer<?>> prepare(final Command.@NonNull Builder<PlotPlayer<?>> builder) {
return builder.literal("flag");
}
}

View File

@@ -0,0 +1,88 @@
/*
* 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.commands.command.setting.flag;
import com.plotsquared.core.commands.parser.PlotFlagParser;
import com.plotsquared.core.configuration.caption.TranslatableCaption;
import com.plotsquared.core.player.PlotPlayer;
import com.plotsquared.core.plot.flag.PlotFlag;
import io.leangen.geantyref.TypeToken;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.tag.Tag;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.incendo.cloud.Command;
import org.incendo.cloud.context.CommandContext;
import org.incendo.cloud.key.CloudKey;
import static com.plotsquared.core.commands.parser.PlotFlagParser.plotFlagParser;
public final class FlagInfoCommand extends FlagCommandBean {
private static final CloudKey<PlotFlag<?, ?>> COMPONENT_FLAG = CloudKey.of("flag", new TypeToken<PlotFlag<?, ?>>() {});
@Override
protected Command.@NonNull Builder<PlotPlayer<?>> configurePlotCommand(final Command.@NonNull Builder<PlotPlayer<?>> builder) {
return builder.literal("info")
.required(COMPONENT_FLAG, plotFlagParser(PlotFlagParser.FlagSource.GLOBAL));
}
@Override
public void execute(final @NonNull CommandContext<PlotPlayer<?>> commandContext) {
final PlotFlag<?, ?> plotFlag = commandContext.get(COMPONENT_FLAG);
final PlotPlayer<?> player = commandContext.sender();
player.sendMessage(TranslatableCaption.of("flag.flag_info_header"));
// Flag name
player.sendMessage(
TranslatableCaption.of("flag.flag_info_name"),
TagResolver.resolver("flag", Tag.inserting(Component.text(plotFlag.getName())))
);
// Flag category
player.sendMessage(
TranslatableCaption.of("flag.flag_info_category"),
TagResolver.resolver(
"value",
Tag.inserting(plotFlag.getFlagCategory().toComponent(player))
)
);
// Flag description
// TODO maybe merge and \n instead?
player.sendMessage(TranslatableCaption.of("flag.flag_info_description"));
player.sendMessage(plotFlag.getFlagDescription());
// Flag example
player.sendMessage(
TranslatableCaption.of("flag.flag_info_example"),
TagResolver.builder()
.tag("command", Tag.preProcessParsed("/plot flag set"))
.tag("flag", Tag.preProcessParsed(plotFlag.getName()))
.tag("value", Tag.preProcessParsed(plotFlag.getExample()))
.build()
);
// Default value
final String defaultValue = player.getLocation().getPlotArea().getFlagContainer()
.getFlagErased(plotFlag.getClass()).toString();
player.sendMessage(
TranslatableCaption.of("flag.flag_info_default_value"),
TagResolver.resolver("value", Tag.inserting(Component.text(defaultValue)))
);
// Footer. Done this way to prevent the duplicate-message-thingy from catching it
player.sendMessage(TranslatableCaption.of("flag.flag_info_footer"));
}
}

View File

@@ -0,0 +1,93 @@
/*
* 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.commands.command.setting.flag;
import com.plotsquared.core.configuration.caption.StaticCaption;
import com.plotsquared.core.configuration.caption.TranslatableCaption;
import com.plotsquared.core.player.PlotPlayer;
import com.plotsquared.core.plot.flag.GlobalFlagContainer;
import com.plotsquared.core.plot.flag.InternalFlag;
import com.plotsquared.core.plot.flag.PlotFlag;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.Tag;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.incendo.cloud.Command;
import org.incendo.cloud.context.CommandContext;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public final class FlagListCommand extends FlagCommandBean {
private static final MiniMessage MINI_MESSAGE = MiniMessage.builder().build();
@Override
protected Command.@NonNull Builder<PlotPlayer<?>> configurePlotCommand(
final Command.@NonNull Builder<PlotPlayer<?>> builder
) {
return builder.literal("list");
}
@Override
public void execute(final @NonNull CommandContext<PlotPlayer<?>> commandContext) {
final PlotPlayer<?> player = commandContext.sender();
final Map<Component, ArrayList<String>> flags = new HashMap<>();
for (PlotFlag<?, ?> plotFlag : GlobalFlagContainer.getInstance().getRecognizedPlotFlags()) {
if (plotFlag instanceof InternalFlag) {
continue;
}
final Component category = plotFlag.getFlagCategory().toComponent(player);
final Collection<String> flagList = flags.computeIfAbsent(category, k -> new ArrayList<>());
flagList.add(plotFlag.getName());
}
for (final Map.Entry<Component, ArrayList<String>> entry : flags.entrySet()) {
Collections.sort(entry.getValue());
Component category =
MINI_MESSAGE.deserialize(
TranslatableCaption.of("flag.flag_list_categories").getComponent(player),
TagResolver.resolver("category", Tag.inserting(entry.getKey().style(Style.empty())))
);
TextComponent.Builder builder = Component.text().append(category);
final Iterator<String> flagIterator = entry.getValue().iterator();
while (flagIterator.hasNext()) {
final String flag = flagIterator.next();
builder.append(MINI_MESSAGE
.deserialize(
TranslatableCaption.of("flag.flag_list_flag").getComponent(player),
TagResolver.builder()
.tag("command", Tag.preProcessParsed("/plat flag info " + flag))
.tag("flag", Tag.inserting(Component.text(flag)))
.tag("suffix", Tag.inserting(Component.text(flagIterator.hasNext() ? ", " : "")))
.build()
));
}
player.sendMessage(StaticCaption.of(MINI_MESSAGE.serialize(builder.build())));
}
}
}

View File

@@ -0,0 +1,171 @@
/*
* 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.commands.command.setting.flag;
import com.google.inject.Inject;
import com.plotsquared.core.commands.parser.PlotFlagParser;
import com.plotsquared.core.commands.suggestions.FlagValueSuggestionProvider;
import com.plotsquared.core.configuration.caption.TranslatableCaption;
import com.plotsquared.core.events.PlotFlagAddEvent;
import com.plotsquared.core.events.PlotFlagRemoveEvent;
import com.plotsquared.core.events.Result;
import com.plotsquared.core.permissions.Permission;
import com.plotsquared.core.player.PlotPlayer;
import com.plotsquared.core.plot.Plot;
import com.plotsquared.core.plot.flag.FlagParseException;
import com.plotsquared.core.plot.flag.PlotFlag;
import com.plotsquared.core.plot.flag.types.ListFlag;
import com.plotsquared.core.util.EventDispatcher;
import io.leangen.geantyref.TypeToken;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.tag.Tag;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.incendo.cloud.Command;
import org.incendo.cloud.context.CommandContext;
import org.incendo.cloud.key.CloudKey;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import static com.plotsquared.core.commands.parser.PlotFlagParser.plotFlagParser;
import static org.incendo.cloud.parser.standard.StringParser.greedyStringParser;
public final class FlagRemoveCommand extends FlagCommandBean {
private static final CloudKey<PlotFlag<?, ?>> COMPONENT_FLAG = CloudKey.of("flag", new TypeToken<PlotFlag<?, ?>>() {});
private static final CloudKey<String> COMPONENT_VALUE = CloudKey.of("value", String.class);
private final EventDispatcher eventDispatcher;
@Inject
public FlagRemoveCommand(final @NonNull EventDispatcher eventDispatcher) {
this.eventDispatcher = eventDispatcher;
}
@Override
protected Command.@NonNull Builder<PlotPlayer<?>> configurePlotCommand(
final Command.@NonNull Builder<PlotPlayer<?>> builder
) {
return builder.literal("remove")
.required(COMPONENT_FLAG, plotFlagParser(PlotFlagParser.FlagSource.PLOT))
.optional(COMPONENT_VALUE, greedyStringParser(), new FlagValueSuggestionProvider(COMPONENT_FLAG));
}
@Override
public void execute(final @NonNull CommandContext<PlotPlayer<?>> commandContext) {
final PlotPlayer<?> player = commandContext.sender();
final Plot plot = commandContext.inject(Plot.class).orElseThrow();
final PlotFlag<?, ?> flag = commandContext.get(COMPONENT_FLAG);
final String flagValue = commandContext.getOrDefault(COMPONENT_VALUE, null);
final PlotFlagRemoveEvent event = this.eventDispatcher.callFlagRemove(flag, plot);
if (event.getEventResult() == Result.DENY) {
player.sendMessage(
TranslatableCaption.of("events.event_denied"),
TagResolver.resolver("value", Tag.inserting(Component.text("Flag set")))
);
return;
}
final String flagKey = flag.getName().toLowerCase(Locale.ENGLISH);
if (event.getEventResult() != Result.FORCE
&& !player.hasPermission(Permission.PERMISSION_SET_FLAG_KEY.format(flagKey))) {
if (flagValue == null) {
player.sendMessage(
TranslatableCaption.of("permission.no_permission"),
TagResolver.resolver(
"node",
Tag.inserting(Component.text(Permission.PERMISSION_SET_FLAG_KEY.format(flagKey)))
)
);
return;
}
}
if (flagValue != null && flag instanceof ListFlag<?,?> listFlag) {
final List<?> list = new ArrayList<>(plot.getFlag(listFlag));
final PlotFlag parsedFlag;
try {
parsedFlag = listFlag.parse(flagValue);
} catch (final FlagParseException e) {
player.sendMessage(
TranslatableCaption.of("flag.flag_parse_error"),
TagResolver.builder()
.tag("flag_name", Tag.inserting(Component.text(flag.getName())))
.tag("flag_value", Tag.inserting(Component.text(e.getValue())))
.tag("error", Tag.inserting(e.getErrorMessage().toComponent(player)))
.build()
);
return;
}
if (((List<?>) parsedFlag.getValue()).isEmpty()) {
player.sendMessage(TranslatableCaption.of("flag.flag_not_removed"));
return;
}
if (list.removeAll((List) parsedFlag.getValue())) {
if (list.isEmpty()) {
if (plot.removeFlag(flag)) {
player.sendMessage(
TranslatableCaption.of("flag.flag_removed"),
TagResolver.builder()
.tag("flag", Tag.inserting(Component.text(flagKey)))
.tag("value", Tag.inserting(Component.text(flag.toString())))
.build()
);
return;
} else {
player.sendMessage(TranslatableCaption.of("flag.flag_not_removed"));
return;
}
} else {
PlotFlag<?, ?> plotFlag = parsedFlag.createFlagInstance(list);
PlotFlagAddEvent addEvent = eventDispatcher.callFlagAdd(plotFlag, plot);
if (addEvent.getEventResult() == Result.DENY) {
player.sendMessage(
TranslatableCaption.of("events.event_denied"),
TagResolver.resolver(
"value",
Tag.inserting(Component.text("Re-addition of " + plotFlag.getName()))
)
);
return;
}
if (plot.setFlag(addEvent.getFlag())) {
player.sendMessage(TranslatableCaption.of("flag.flag_partially_removed"));
return;
} else {
player.sendMessage(TranslatableCaption.of("flag.flag_not_removed"));
return;
}
}
}
} else if (!plot.removeFlag(flag)) {
player.sendMessage(TranslatableCaption.of("flag.flag_not_removed"));
return;
}
player.sendMessage(
TranslatableCaption.of("flag.flag_removed"),
TagResolver.builder()
.tag("flag", Tag.inserting(Component.text(flagKey)))
.tag("value", Tag.inserting(Component.text(flag.toString())))
.build()
);
}
}

View File

@@ -0,0 +1,110 @@
/*
* 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.commands.command.setting.flag;
import com.google.inject.Inject;
import com.plotsquared.core.commands.parser.PlotFlagParser;
import com.plotsquared.core.commands.suggestions.FlagValueSuggestionProvider;
import com.plotsquared.core.configuration.caption.CaptionUtility;
import com.plotsquared.core.configuration.caption.TranslatableCaption;
import com.plotsquared.core.events.PlotFlagAddEvent;
import com.plotsquared.core.events.Result;
import com.plotsquared.core.player.PlotPlayer;
import com.plotsquared.core.plot.Plot;
import com.plotsquared.core.plot.flag.FlagParseException;
import com.plotsquared.core.plot.flag.PlotFlag;
import com.plotsquared.core.util.EventDispatcher;
import io.leangen.geantyref.TypeToken;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.tag.Tag;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.incendo.cloud.Command;
import org.incendo.cloud.context.CommandContext;
import org.incendo.cloud.key.CloudKey;
import static com.plotsquared.core.commands.parser.PlotFlagParser.plotFlagParser;
import static org.incendo.cloud.parser.standard.StringParser.greedyStringParser;
public final class FlagSetCommand extends FlagCommandBean {
private static final CloudKey<PlotFlag<?, ?>> COMPONENT_FLAG = CloudKey.of("flag", new TypeToken<PlotFlag<?, ?>>() {});
private static final CloudKey<String> COMPONENT_VALUE = CloudKey.of("value", String.class);
private final EventDispatcher eventDispatcher;
@Inject
public FlagSetCommand(final @NonNull EventDispatcher eventDispatcher) {
this.eventDispatcher = eventDispatcher;
}
@Override
protected Command.@NonNull Builder<PlotPlayer<?>> configurePlotCommand(
final Command.@NonNull Builder<PlotPlayer<?>> builder
) {
return builder.literal("set")
.required(COMPONENT_FLAG, plotFlagParser(PlotFlagParser.FlagSource.GLOBAL))
.required(COMPONENT_VALUE, greedyStringParser(), new FlagValueSuggestionProvider(COMPONENT_FLAG));
}
@Override
public void execute(final @NonNull CommandContext<PlotPlayer<?>> commandContext) {
final PlotPlayer<?> player = commandContext.sender();
final Plot plot = commandContext.inject(Plot.class).orElseThrow();
final PlotFlag<?, ?> flag = commandContext.get(COMPONENT_FLAG);
final String flagValue = commandContext.get(COMPONENT_VALUE);
final PlotFlagAddEvent event = this.eventDispatcher.callFlagAdd(flag, plot);
if (event.getEventResult() == Result.DENY) {
player.sendMessage(
TranslatableCaption.of("events.event_denied"),
TagResolver.resolver("value", Tag.inserting(Component.text("Flag set")))
);
return;
}
if (event.getEventResult() != Result.FORCE && !checkPermValue(player, flag, flag.getName(), flagValue)) {
return;
}
final String sanitizedValue = CaptionUtility.stripClickEvents(flag, flagValue);
final PlotFlag<?, ?> parsedFlag;
try {
parsedFlag = flag.parse(flagValue);
} catch (final FlagParseException e) {
player.sendMessage(
TranslatableCaption.of("flag.flag_parse_error"),
TagResolver.builder()
.tag("flag_name", Tag.inserting(Component.text(flag.getName())))
.tag("flag_value", Tag.inserting(Component.text(e.getValue())))
.tag("error", Tag.inserting(e.getErrorMessage().toComponent(player)))
.build()
);
return;
}
plot.setFlag(parsedFlag);
player.sendMessage(
TranslatableCaption.of("flag.flag_added"),
TagResolver.builder()
.tag("flag", Tag.inserting(Component.text(flag.getName())))
.tag("value", Tag.inserting(Component.text(parsedFlag.toString())))
.build()
);
}
}

View File

@@ -16,28 +16,27 @@
* 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.plot.flag.implementations;
package com.plotsquared.core.commands.injection;
import com.plotsquared.core.configuration.caption.TranslatableCaption;
import com.plotsquared.core.plot.flag.types.BooleanFlag;
import com.plotsquared.core.player.PlotPlayer;
import com.plotsquared.core.plot.Plot;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.incendo.cloud.context.CommandContext;
import org.incendo.cloud.injection.ParameterInjector;
import org.incendo.cloud.util.annotation.AnnotationAccessor;
/**
* @since 7.3.7
* {@link ParameterInjector} that returns the current plot of the player.
*/
public class TileDropFlag extends BooleanFlag<TileDropFlag> {
public static final TileDropFlag TILE_DROP_TRUE = new TileDropFlag(true);
public static final TileDropFlag TILE_DROP_FALSE = new TileDropFlag(false);
private TileDropFlag(boolean value) {
super(value, TranslatableCaption.of("flags.flag_description_tile_drop"));
}
public final class PlotInjector implements ParameterInjector<PlotPlayer<?>, Plot> {
@Override
protected TileDropFlag flagOf(@NonNull Boolean value) {
return value ? TILE_DROP_TRUE : TILE_DROP_FALSE;
public @Nullable Plot create(
final @NonNull CommandContext<PlotPlayer<?>> context,
final @NonNull AnnotationAccessor annotationAccessor
) {
// TODO: Allow for overriding for console.
return context.sender().getCurrentPlot();
}
}

View File

@@ -0,0 +1,158 @@
/*
* 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.commands.parser;
import com.plotsquared.core.configuration.caption.LocaleHolder;
import com.plotsquared.core.configuration.caption.TranslatableCaption;
import com.plotsquared.core.player.PlotPlayer;
import com.plotsquared.core.plot.Plot;
import com.plotsquared.core.plot.flag.FlagContainer;
import com.plotsquared.core.plot.flag.GlobalFlagContainer;
import com.plotsquared.core.plot.flag.InternalFlag;
import com.plotsquared.core.plot.flag.PlotFlag;
import io.leangen.geantyref.TypeToken;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.util.ComponentMessageThrowable;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.incendo.cloud.context.CommandContext;
import org.incendo.cloud.context.CommandInput;
import org.incendo.cloud.exception.parsing.ParserException;
import org.incendo.cloud.parser.ArgumentParseResult;
import org.incendo.cloud.parser.ArgumentParser;
import org.incendo.cloud.parser.ParserDescriptor;
import org.incendo.cloud.suggestion.BlockingSuggestionProvider;
import org.incendo.cloud.suggestion.Suggestion;
import java.util.Collection;
import java.util.function.Function;
/**
* Parser that parses and suggests {@link PlotFlag plot flags}.
*/
public final class PlotFlagParser implements ArgumentParser<PlotPlayer<?>, PlotFlag<?, ?>>,
BlockingSuggestionProvider<PlotPlayer<?>> {
/**
* Returns a new parser that parses {@link PlotFlag plot flags}.
*
* @param source the source of available flag values
* @return the parser
*/
public static @NonNull ParserDescriptor<PlotPlayer<?>, PlotFlag<?, ?>> plotFlagParser(final @NonNull FlagSource source) {
return ParserDescriptor.of(new PlotFlagParser(source), new TypeToken<PlotFlag<?, ?>>() {
});
}
private final FlagSource flagSource;
private PlotFlagParser(final @NonNull FlagSource flagSource) {
this.flagSource = flagSource;
}
@Override
public @NonNull ArgumentParseResult<@NonNull PlotFlag<?, ?>> parse(
final @NonNull CommandContext<@NonNull PlotPlayer<?>> commandContext,
final @NonNull CommandInput commandInput
) {
final String flagName = commandInput.readString();
final PlotFlag<?, ?> flag = GlobalFlagContainer.getInstance().getFlagFromString(flagName);
if (flag == null) {
return ArgumentParseResult.failure(new PlotFlagParseException(commandContext));
}
return ArgumentParseResult.success(flag);
}
@Override
public @NonNull Iterable<@NonNull Suggestion> suggestions(
final @NonNull CommandContext<PlotPlayer<?>> context,
final @NonNull CommandInput input
) {
return this.flagSource.flags(context.sender())
.stream()
.filter(flag -> (!(flag instanceof InternalFlag)))
.map(PlotFlag::getName)
.map(Suggestion::simple)
.toList();
}
public enum FlagSource {
/**
* All recognized flags.
*/
GLOBAL(player -> GlobalFlagContainer.getInstance(), false),
/**
* All flags that have been configured in the current plot.
*/
PLOT(player -> {
final Plot plot = player.getCurrentPlot();
if (plot == null) {
return GlobalFlagContainer.getInstance();
}
return plot.getFlagContainer();
}, true);
private final Function<PlotPlayer<?>, FlagContainer> containerFunction;
private final boolean storedOnly;
FlagSource(final @NonNull Function<PlotPlayer<?>, FlagContainer> containerFunction, final boolean storedOnly) {
this.containerFunction = containerFunction;
this.storedOnly = storedOnly;
}
/**
* Returns the flag container.
*
* @param player the player to get the container for
* @return the container
*/
public @NonNull FlagContainer flagContainer(final @NonNull PlotPlayer<?> player) {
return this.containerFunction.apply(player);
}
/**
* Returns the flags from this source.
*
* @param player the player to get the flags for
* @return the flags
*/
public @NonNull Collection<@NonNull PlotFlag<?, ?>> flags(final @NonNull PlotPlayer<?> player) {
final FlagContainer container = this.flagContainer(player);
if (this.storedOnly) {
return container.getFlagMap().values();
}
return container.getRecognizedPlotFlags();
}
}
/**
* Exception thrown when an invalid flag name is supplied.
*/
public static final class PlotFlagParseException extends ParserException implements ComponentMessageThrowable {
private PlotFlagParseException(final @NonNull CommandContext<?> context) {
super(PlotFlagParser.class, context, TranslatableCaption.of("flag.not_valid_flag"));
}
@Override
public @NonNull Component componentMessage() {
// TODO(City): This sucks...
return ((TranslatableCaption) this.errorCaption()).toComponent(LocaleHolder.console());
}
}
}

View File

@@ -0,0 +1,86 @@
/*
* 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.commands.suggestions;
import com.plotsquared.core.player.PlotPlayer;
import com.plotsquared.core.plot.flag.PlotFlag;
import com.plotsquared.core.plot.flag.types.ListFlag;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.incendo.cloud.context.CommandContext;
import org.incendo.cloud.context.CommandInput;
import org.incendo.cloud.key.CloudKey;
import org.incendo.cloud.suggestion.BlockingSuggestionProvider;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* Suggestion provider that provides context-aware {@link PlotFlag plot flag} value suggestions using
* {@link PlotFlag#getTabCompletions()}.
*/
public final class FlagValueSuggestionProvider implements BlockingSuggestionProvider.Strings<PlotPlayer<?>> {
private final CloudKey<PlotFlag<?, ?>> flagKey;
/**
* Creates a new suggestion provider.
*
* @param flagKey the key of the argument that contains the flag to provide value suggestions for
*/
public FlagValueSuggestionProvider(final @NonNull CloudKey<PlotFlag<?, ?>> flagKey) {
this.flagKey = Objects.requireNonNull(flagKey, "flagKey");
}
@Override
public @NonNull Iterable<@NonNull String> stringSuggestions(
@NonNull final CommandContext<PlotPlayer<?>> context,
@NonNull final CommandInput input
) {
final PlotFlag<?, ?> plotFlag = context.getOrDefault(this.flagKey, null);
if (plotFlag == null) {
return List.of();
}
final Collection<String> completions = plotFlag.getTabCompletions();
if (plotFlag instanceof ListFlag<?,?> && input.peekString().contains(",")) {
final String[] split = input.peekString().split(",");
final List<String> existingValues = new ArrayList<>(Arrays.asList(split));
final String completingValue;
if (!input.peekString().endsWith(",")) {
// In this case we want to complete the value we're currently typing.
completingValue = split[split.length - 1];
existingValues.remove(existingValues.size() - 1);
} else {
completingValue = null;
}
final String prefix = existingValues.stream().collect(Collectors.joining(",", "", ","));
return completions.stream()
.filter(value -> !existingValues.contains(value))
.filter(value -> completingValue == null || value.startsWith(completingValue))
.map(value -> prefix + value)
.toList();
}
return completions;
}
}

View File

@@ -26,7 +26,6 @@ import org.apache.logging.log4j.Logger;
import java.io.File;
import java.io.PrintWriter;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -373,7 +372,6 @@ public class Config {
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.TYPE})
@Documented
public @interface Comment {
String[] value();

View File

@@ -43,11 +43,6 @@ public class Settings extends Config {
"Leave it off if you don't need it, it can spam your console."})
public static boolean DEBUG = true;
@Comment({"The activity of high-frequency event listener can be deactivated here to improve the server performance. ",
"Affected settings: 'redstone' settings here below. Affected flags: 'disable-physics', 'redstone'. ",
"Only deactivate this setting if you do not need any of the mentioned settings or flags."})
public static boolean HIGH_FREQUENCY_LISTENER = true;
@Create // This value will be generated automatically
public static ConfigBlock<Auto_Clear> AUTO_CLEAR = null;
// A ConfigBlock is a section that can have multiple instances e.g. multiple expiry tasks
@@ -582,8 +577,6 @@ public class Settings extends Config {
public static boolean PER_WORLD_VISIT = false;
@Comment("Search merged plots for having multiple owners when using the visit command")
public static boolean VISIT_MERGED_OWNERS = true;
@Comment("Allows to teleport based on block size instead to spawn on the highest block at the home command")
public static boolean SIZED_BASED = true;
}
@@ -653,8 +646,6 @@ public class Settings extends Config {
public static boolean PAPER_LISTENERS = true;
@Comment("Prevent entities from leaving plots")
public static boolean ENTITY_PATHING = true;
@Comment("Prevent entities from leaving plots, even by pushing or pulling")
public static boolean ENTITY_MOVEMENT = false;
@Comment(
"Cancel entity spawns when the chunk is loaded if the PlotArea's mob spawning is off")
public static boolean CANCEL_CHUNK_SPAWN = true;
@@ -732,12 +723,6 @@ public class Settings extends Config {
}
@Comment("Settings related to flags")
public static final class Flags {
@Comment("If \"instabreak\" should consider the used tool.")
public static boolean INSTABREAK_CONSIDER_TOOL = false;
}
@Comment({"Enable or disable parts of the plugin",
"Note: A cache will use some memory if enabled"})

View File

@@ -46,7 +46,7 @@ public class Storage extends Config {
public static String PASSWORD = "password";
public static String DATABASE = "plot_db";
@Comment("Set additional properties: https://dev.mysql.com/doc/connector-j/en/connector-j-reference-configuration-properties.html")
@Comment("Set additional properties: https://goo.gl/wngtN8")
public static List<String>
PROPERTIES = new ArrayList<>(Collections.singletonList("useSSL=false"));

View File

@@ -20,7 +20,6 @@ package com.plotsquared.core.configuration.caption;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.ComponentLike;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import org.checkerframework.checker.nullness.qual.NonNull;
/**
@@ -45,16 +44,6 @@ public interface Caption {
*/
@NonNull Component toComponent(@NonNull LocaleHolder localeHolder);
/**
* Get the Adventure {@link ComponentLike} for this caption while applying custom {@link TagResolver}
* (apart from the default {@code core.prefix})
* @param localeHolder Local holder
* @param tagResolvers custom tag resolvers to replace placeholders / parameters
* @return {@link ComponentLike}
* @since 7.5.4
*/
@NonNull Component toComponent(@NonNull LocaleHolder localeHolder, @NonNull TagResolver @NonNull... tagResolvers);
@NonNull String toString();
}

View File

@@ -21,7 +21,6 @@ package com.plotsquared.core.configuration.caption;
import com.google.common.base.Preconditions;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import org.checkerframework.checker.nullness.qual.NonNull;
public final class StaticCaption implements Caption {
@@ -52,14 +51,6 @@ public final class StaticCaption implements Caption {
return MiniMessage.miniMessage().deserialize(this.value);
}
@Override
public @NonNull Component toComponent(
@NonNull final LocaleHolder localeHolder,
final @NonNull TagResolver @NonNull ... tagResolvers
) {
return MiniMessage.miniMessage().deserialize(this.value, tagResolvers);
}
@Override
public @NonNull String toString() {
return "StaticCaption(" + value + ")";

View File

@@ -25,16 +25,16 @@ import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.Tag;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.incendo.cloud.caption.Caption;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.Locale;
import java.util.regex.Pattern;
/**
* Caption that is user modifiable
*/
public final class TranslatableCaption implements NamespacedCaption {
public final class TranslatableCaption implements NamespacedCaption, Caption {
/**
* Default caption namespace
@@ -73,6 +73,11 @@ public final class TranslatableCaption implements NamespacedCaption {
);
}
@Override
public @NonNull String key() {
return this.getKey();
}
/**
* Get a new {@link TranslatableCaption} instance
*
@@ -97,23 +102,13 @@ public final class TranslatableCaption implements NamespacedCaption {
@Override
public @NonNull Component toComponent(@NonNull final LocaleHolder localeHolder) {
return this.toComponent(localeHolder, new TagResolver[0]);
}
@Override
public @NonNull Component toComponent(
@NonNull final LocaleHolder localeHolder,
final @NonNull TagResolver @NonNull ... tagResolvers
) {
if (getKey().equals("core.prefix")) {
return MiniMessage.miniMessage().deserialize(getComponent(localeHolder));
}
TagResolver[] finalResolvers = Arrays.copyOf(tagResolvers, tagResolvers.length + 1);
finalResolvers[finalResolvers.length - 1] = TagResolver.resolver(
return MiniMessage.miniMessage().deserialize(getComponent(localeHolder), TagResolver.resolver(
"prefix",
Tag.inserting(TranslatableCaption.of("core.prefix").toComponent(localeHolder))
);
return MiniMessage.miniMessage().deserialize(getComponent(localeHolder), finalResolvers);
));
}
@Override

View File

@@ -2401,8 +2401,7 @@ public class SQLManager implements AbstractDB {
addPlotTask(plot, new UniqueStatement("setPosition") {
@Override
public void set(PreparedStatement statement) throws SQLException {
// Please see the table creation statement. There is the default value of "default"
statement.setString(1, position == null ? "DEFAULT" : position);
statement.setString(1, position == null ? "" : position);
statement.setInt(2, getId(plot));
}

View File

@@ -18,34 +18,17 @@
*/
package com.plotsquared.core.events;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
* PlotSquared event with {@link Result} to cancel, force, or allow.
*/
public interface CancellablePlotEvent {
/**
* The currently set {@link Result} for this event (as set by potential previous event listeners).
*
* @return the current result.
*/
@Nullable Result getEventResult();
Result getEventResult();
/**
* Set the {@link Result} for this event.
*
* @param eventResult the new result.
*/
void setEventResult(@Nullable Result eventResult);
void setEventResult(Result eventResult);
/**
* @deprecated No usage and not null-safe
*/
@Deprecated(since = "7.3.2")
default int getEventResultRaw() {
return getEventResult() != null ? getEventResult().getValue() : -1;
return getEventResult().getValue();
}
}

View File

@@ -1,88 +0,0 @@
/*
* PlotSquared, a land and world management plugin for Minecraft.
* Copyright (C) IntellectualSites <https://intellectualsites.com>
* Copyright (C) IntellectualSites team and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.plotsquared.core.events;
import com.plotsquared.core.player.PlotPlayer;
import com.plotsquared.core.plot.Plot;
import org.checkerframework.checker.index.qual.NonNegative;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
* Called when a user attempts to buy a plot.
* <p>
* Setting the {@link #setEventResult(Result) Result} to {@link Result#FORCE} ignores the price and players account balance and does not charge the
* player anything. {@link Result#DENY} blocks the purchase completely, {@link Result#ACCEPT} and {@code null} do not modify
* the behaviour.
* <p>
* Setting the {@link #setPrice(double) price} to {@code 0} makes the plot practically free.
*
* @since 7.3.2
*/
public class PlayerBuyPlotEvent extends PlotPlayerEvent implements CancellablePlotEvent {
private Result result;
private double price;
public PlayerBuyPlotEvent(final PlotPlayer<?> plotPlayer, final Plot plot, @NonNegative final double price) {
super(plotPlayer, plot);
this.price = price;
}
/**
* Sets the price required to buy the plot.
*
* @param price the new price.
* @since 7.3.2
*/
public void setPrice(@NonNegative final double price) {
//noinspection ConstantValue - the annotation does not ensure a non-negative runtime value
if (price < 0) {
throw new IllegalArgumentException("price must be non-negative");
}
this.price = price;
}
/**
* Returns the currently set price required to buy the plot.
*
* @return the price.
* @since 7.3.2
*/
public @NonNegative double price() {
return price;
}
/**
* {@inheritDoc}
*/
@Override
public void setEventResult(@Nullable final Result eventResult) {
this.result = eventResult;
}
/**
* {@inheritDoc}
*/
@Override
public @Nullable Result getEventResult() {
return this.result;
}
}

View File

@@ -24,7 +24,7 @@ import com.plotsquared.core.plot.Plot;
public class PlayerEnterPlotEvent extends PlotPlayerEvent {
/**
* PlayerEnterPlotEvent: Called when a player enters a plot
* Called when a player leaves a plot.
*
* @param player Player that entered the plot
* @param plot Plot that was entered

View File

@@ -1,64 +0,0 @@
/*
* PlotSquared, a land and world management plugin for Minecraft.
* Copyright (C) IntellectualSites <https://intellectualsites.com>
* Copyright (C) IntellectualSites team and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.plotsquared.core.events.post;
import com.plotsquared.core.events.PlotPlayerEvent;
import com.plotsquared.core.player.OfflinePlotPlayer;
import com.plotsquared.core.player.PlotPlayer;
import com.plotsquared.core.plot.Plot;
import org.checkerframework.checker.index.qual.NonNegative;
/**
* Called after a player has successfully bought a plot.
*
* @since 7.3.2
*/
public class PostPlayerBuyPlotEvent extends PlotPlayerEvent {
private final OfflinePlotPlayer previousOwner;
private final double price;
public PostPlayerBuyPlotEvent(
final PlotPlayer<?> plotPlayer, final OfflinePlotPlayer previousOwner, final Plot plot,
@NonNegative final double price
) {
super(plotPlayer, plot);
this.previousOwner = previousOwner;
this.price = price;
}
/**
* The previous owner of the bought plot.
*
* @return the previous owner.
*/
public OfflinePlotPlayer previousOwner() {
return previousOwner;
}
/**
* Returns the price after potential modifications by {@link com.plotsquared.core.events.PlayerBuyPlotEvent}.
*
* @return the price the player had to pay to buy the plot.
*/
public double price() {
return price;
}
}

View File

@@ -143,7 +143,7 @@ public class ClassicPlotManager extends SquarePlotManager {
classicPlotWorld,
plot.getRegions(),
blocks,
classicPlotWorld.getMinComponentHeight(),
classicPlotWorld.getMinBuildHeight(),
classicPlotWorld.getMaxBuildHeight() - 1,
actor,
queue
@@ -204,7 +204,7 @@ public class ClassicPlotManager extends SquarePlotManager {
classicPlotWorld,
plot.getRegions(),
blocks,
classicPlotWorld.getMinComponentHeight(),
classicPlotWorld.getMinBuildHeight(),
classicPlotWorld.PLOT_HEIGHT - 1,
actor,
queue
@@ -379,7 +379,7 @@ public class ClassicPlotManager extends SquarePlotManager {
}
}
int yStart = classicPlotWorld.getMinComponentHeight();
int yStart = classicPlotWorld.getMinBuildHeight() + (classicPlotWorld.PLOT_BEDROCK ? 1 : 0);
if (!plot.isMerged(Direction.NORTH)) {
int z = bot.getZ();
for (int x = bot.getX(); x < top.getX(); x++) {

View File

@@ -52,7 +52,6 @@ public abstract class ClassicPlotWorld extends SquarePlotWorld {
public BlockBucket ROAD_BLOCK = new BlockBucket(BlockTypes.QUARTZ_BLOCK);
public boolean PLOT_BEDROCK = true;
public boolean PLACE_TOP_BLOCK = true;
public boolean COMPONENT_BELOW_BEDROCK = false;
public ClassicPlotWorld(
final @NonNull String worldName,
@@ -130,9 +129,6 @@ public abstract class ClassicPlotWorld extends SquarePlotWorld {
),
new ConfigurationNode("plot.bedrock", this.PLOT_BEDROCK, TranslatableCaption.of("setup.bedrock_boolean"),
ConfigurationUtil.BOOLEAN
),
new ConfigurationNode("world.component_below_bedrock", this.COMPONENT_BELOW_BEDROCK, TranslatableCaption.of(
"setup.component_below_bedrock_boolean"), ConfigurationUtil.BOOLEAN
)};
}
@@ -154,14 +150,6 @@ public abstract class ClassicPlotWorld extends SquarePlotWorld {
this.PLACE_TOP_BLOCK = config.getBoolean("wall.place_top_block");
this.WALL_HEIGHT = Math.min(getMaxGenHeight() - (PLACE_TOP_BLOCK ? 1 : 0), config.getInt("wall.height"));
this.CLAIMED_WALL_BLOCK = createCheckedBlockBucket(config.getString("wall.block_claimed"), CLAIMED_WALL_BLOCK);
this.COMPONENT_BELOW_BEDROCK = config.getBoolean("world.component_below_bedrock");
}
@Override
public int getMinComponentHeight() {
return COMPONENT_BELOW_BEDROCK && getMinGenHeight() >= getMinBuildHeight()
? getMinGenHeight() + (PLOT_BEDROCK ? 1 : 0)
: getMinBuildHeight();
}
int schematicStartHeight() {

View File

@@ -432,7 +432,6 @@ public class HybridUtils {
if (!UPDATE) {
Iterator<BlockVector2> iter = chunks.iterator();
QueueCoordinator queue = blockQueue.getNewQueue(worldUtil.getWeWorld(area.getWorldName()));
queue.setShouldGen(false);
while (iter.hasNext()) {
BlockVector2 chunk = iter.next();
iter.remove();
@@ -475,7 +474,6 @@ public class HybridUtils {
Iterator<BlockVector2> iterator = chunks.iterator();
if (chunks.size() >= 32) {
QueueCoordinator queue = blockQueue.getNewQueue(worldUtil.getWeWorld(area.getWorldName()));
queue.setShouldGen(false);
for (int i = 0; i < 32; i++) {
final BlockVector2 chunk = iterator.next();
iterator.remove();
@@ -489,7 +487,6 @@ public class HybridUtils {
return null;
}
QueueCoordinator queue = blockQueue.getNewQueue(worldUtil.getWeWorld(area.getWorldName()));
queue.setShouldGen(false);
while (!chunks.isEmpty()) {
final BlockVector2 chunk = iterator.next();
iterator.remove();
@@ -505,6 +502,7 @@ public class HybridUtils {
return;
}
} catch (Exception e) {
e.printStackTrace();
Iterator<BlockVector2> iterator = HybridUtils.regions.iterator();
BlockVector2 loc = iterator.next();
iterator.remove();
@@ -512,8 +510,7 @@ public class HybridUtils {
"Error! Could not update '{}/region/r.{}.{}.mca' (Corrupt chunk?)",
area.getWorldHash(),
loc.getX(),
loc.getZ(),
e
loc.getZ()
);
}
TaskManager.runTaskLater(task, TaskTime.seconds(1L));
@@ -561,7 +558,7 @@ public class HybridUtils {
try {
plotworld.setupSchematics();
} catch (SchematicHandler.UnsupportedFormatException e) {
LOGGER.error(e);
e.printStackTrace();
}
});
});

View File

@@ -40,8 +40,7 @@ public interface ChunkCoordinatorFactory {
final @NonNull Consumer<Throwable> throwableConsumer,
@Assisted("unloadAfter") final boolean unloadAfter,
final @NonNull Collection<ProgressSubscriber> progressSubscribers,
@Assisted("forceSync") final boolean forceSync,
@Assisted("shouldGen") final boolean shouldGen
@Assisted("forceSync") final boolean forceSync
);
}

View File

@@ -0,0 +1,46 @@
/*
* 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.inject.modules;
import com.google.inject.AbstractModule;
import com.google.inject.Scopes;
import com.google.inject.multibindings.Multibinder;
import com.plotsquared.core.commands.PlotSquaredCommandBean;
import com.plotsquared.core.commands.command.setting.flag.FlagAddCommand;
import com.plotsquared.core.commands.command.setting.flag.FlagInfoCommand;
import com.plotsquared.core.commands.command.setting.flag.FlagListCommand;
import com.plotsquared.core.commands.command.setting.flag.FlagRemoveCommand;
import com.plotsquared.core.commands.command.setting.flag.FlagSetCommand;
public final class CommandModule extends AbstractModule {
@Override
protected void configure() {
final Multibinder<PlotSquaredCommandBean> commands = Multibinder.newSetBinder(
this.binder(),
PlotSquaredCommandBean.class
);
commands.addBinding().to(FlagAddCommand.class).in(Scopes.SINGLETON);
commands.addBinding().to(FlagInfoCommand.class).in(Scopes.SINGLETON);
commands.addBinding().to(FlagListCommand.class).in(Scopes.SINGLETON);
commands.addBinding().to(FlagRemoveCommand.class).in(Scopes.SINGLETON);
commands.addBinding().to(FlagSetCommand.class).in(Scopes.SINGLETON);
}
}

View File

@@ -18,7 +18,6 @@
*/
package com.plotsquared.core.inject.modules;
import cloud.commandframework.services.ServicePipeline;
import com.google.inject.AbstractModule;
import com.plotsquared.core.PlotSquared;
import com.plotsquared.core.configuration.file.YamlConfiguration;
@@ -31,6 +30,7 @@ import com.plotsquared.core.listener.PlotListener;
import com.plotsquared.core.util.EventDispatcher;
import com.plotsquared.core.uuid.UUIDPipeline;
import com.sk89q.worldedit.WorldEdit;
import org.incendo.cloud.services.ServicePipeline;
import java.io.File;

View File

@@ -106,10 +106,6 @@ public class PlotListener {
iterator.remove();
continue;
}
// Don't attempt to heal dead players - they will get stuck in the abyss (#4406)
if (PlotSquared.platform().worldUtil().getHealth(player) <= 0) {
continue;
}
double level = PlotSquared.platform().worldUtil().getHealth(player);
if (level != value.max) {
PlotSquared.platform().worldUtil().setHealth(player, Math.min(level + value.amount, value.max));

View File

@@ -444,7 +444,7 @@ public abstract class PlotPlayer<P> implements CommandCaller, OfflinePlotPlayer,
/**
* Get this player's UUID.
* <p>=== !IMPORTANT ===</p>
* === !IMPORTANT ===<br>
* The UUID is dependent on the mode chosen in the settings.yml and may not be the same as Bukkit has
* (especially if using an old version of Bukkit that does not support UUIDs)
*

View File

@@ -321,8 +321,7 @@ public class Plot {
}
/**
* Get the plot from a string. Performs a check to ensure Plot#getBottomAbs is not outside world bounds
* (x/z +/- 30,000,000) to prevent crashes
* Get the plot from a string.
*
* @param player Provides a context for what world to search in. Prefixing the term with 'world_name;' will override this context.
* @param arg The search term
@@ -333,31 +332,6 @@ public class Plot {
final @Nullable PlotPlayer<?> player,
final @Nullable String arg,
final boolean message
) {
Plot plot = getPlotFromStringUnchecked(player, arg, message);
if (plot != null && !WorldUtil.isValidLocation(plot.getBottomAbs())) {
if (message) {
(player == null ? ConsolePlayer.getConsole() : player).sendMessage(TranslatableCaption.of(
"invalid.world_location_plot"));
}
return null;
}
return plot;
}
/**
* Get the plot from a string. Does not perform a check on world bounds.
*
* @param player Provides a context for what world to search in. Prefixing the term with 'world_name;' will override this context.
* @param arg The search term
* @param message If a message should be sent to the player if a plot cannot be found
* @return The plot if only 1 result is found, or null
* @since 7.5.5
*/
public static @Nullable Plot getPlotFromStringUnchecked(
final @Nullable PlotPlayer<?> player,
final @Nullable String arg,
final boolean message
) {
if (arg == null) {
if (player == null) {
@@ -415,51 +389,13 @@ public class Plot {
}
/**
* Gets a plot from a string e.g. [area];[id]. Performs a check to ensure Plot#getBottomAbs is not outside world bounds
* (x/z +/- 30,000,000) to prevent crashes
* Gets a plot from a string e.g. [area];[id]
*
* @param defaultArea if no area is specified
* @param string plot id/area + id
* @return New or existing plot object
*/
public static @Nullable Plot fromString(final @Nullable PlotArea defaultArea, final @NonNull String string) {
return fromString(defaultArea, string, null);
}
/**
* Gets a plot from a string e.g. [area];[id]. Performs a check to ensure Plot#getBottomAbs is not outside world bounds
* (x/z +/- 30,000,000) to prevent crashes
*
* @param defaultArea if no area is specified
* @param string plot id/area + id
* @param player {@link PlotPlayer} player to notify if plot is invalid (outside bounds)
* @return New or existing plot object
* @since 7.5.5
*/
public static @Nullable Plot fromString(
final @Nullable PlotArea defaultArea,
final @NonNull String string,
final @Nullable PlotPlayer<?> player
) {
Plot plot = fromStringUnchecked(defaultArea, string);
if (plot != null && !WorldUtil.isValidLocation(plot.getBottomAbs())) {
if (player != null) {
player.sendMessage(TranslatableCaption.of("invalid.world_location_plot"));
}
return null;
}
return plot;
}
/**
* Gets a plot from a string e.g. [area];[id]. Does not perform a check on world bounds.
*
* @param defaultArea if no area is specified
* @param string plot id/area + id
* @return New or existing plot object
* @since 7.5.5
*/
public static @Nullable Plot fromStringUnchecked(final @Nullable PlotArea defaultArea, final @NonNull String string) {
final String[] split = string.split("[;,]");
if (split.length == 2) {
if (defaultArea != null) {
@@ -483,8 +419,7 @@ public class Plot {
}
/**
* Return a new/cached plot object at a given location. Does not check world bounds for potential crashes, these should be
* performed before (or after) this method is used.
* Return a new/cached plot object at a given location.
*
* <p>
* Use {@link PlotPlayer#getCurrentPlot()} if a player is expected here.
@@ -708,22 +643,35 @@ public class Plot {
}
/**
* Gets an immutable set of owner UUIDs for a plot (supports multi-owner mega-plots).
* Gets a immutable set of owner UUIDs for a plot (supports multi-owner mega-plots).
* <p>
* This method cannot be used to add or remove owners from a plot.
* </p>
*
* @return Immutable set of plot owners
* @return Immutable view of plot owners
*/
public @NonNull Set<UUID> getOwners() {
ImmutableSet.Builder<UUID> owners = ImmutableSet.builder();
for (Plot plot : getConnectedPlots()) {
UUID owner = plot.getOwner();
if (owner != null) {
owners.add(owner);
}
if (this.getOwner() == null) {
return ImmutableSet.of();
}
return owners.build();
if (isMerged()) {
Set<Plot> plots = getConnectedPlots();
Plot[] array = plots.toArray(new Plot[0]);
ImmutableSet.Builder<UUID> owners = ImmutableSet.builder();
UUID last = this.getOwner();
owners.add(this.getOwner());
for (final Plot current : array) {
if (current.getOwner() == null) {
continue;
}
if (last == null || current.getOwner().getMostSignificantBits() != last.getMostSignificantBits()) {
owners.add(current.getOwner());
last = current.getOwner();
}
}
return owners.build();
}
return ImmutableSet.of(this.getOwner());
}
/**
@@ -1472,9 +1420,6 @@ public class Plot {
);
}
Location location = toHomeLocation(bottom, home);
if (Settings.Teleport.SIZED_BASED && this.worldUtil.isSmallBlock(location) && this.worldUtil.isSmallBlock(location.add(0,1,0))) {
return location;
}
if (!this.worldUtil.getBlockSynchronous(location).getBlockType().getMaterial().isAir()) {
location = location.withY(
Math.max(1 + this.worldUtil.getHighestBlockSynchronous(
@@ -1508,21 +1453,15 @@ public class Plot {
}
Location bottom = this.getBottomAbs();
Location location = toHomeLocation(bottom, home);
if (Settings.Teleport.SIZED_BASED && this.worldUtil.isSmallBlock(location) && this.worldUtil.isSmallBlock(location.add(0,1,0))) {
result.accept(location);
} else {
this.worldUtil.getBlock(location, block -> {
if (!block.getBlockType().getMaterial().isAir()) {
this.worldUtil.getHighestBlock(this.getWorldName(), location.getX(), location.getZ(),
y -> result.accept(location.withY(Math.max(1 + y, bottom.getY())))
);
} else {
result.accept(location);
}
});
}
this.worldUtil.getBlock(location, block -> {
if (!block.getBlockType().getMaterial().isAir()) {
this.worldUtil.getHighestBlock(this.getWorldName(), location.getX(), location.getZ(),
y -> result.accept(location.withY(Math.max(1 + y, bottom.getY())))
);
} else {
result.accept(location);
}
});
}
}
@@ -1544,7 +1483,7 @@ public class Plot {
*/
public void setHome(BlockLoc location) {
Plot plot = this.getBasePlot(false);
if (location != null && (BlockLoc.ZERO.equals(location) || BlockLoc.MINY.equals(location))) {
if (BlockLoc.ZERO.equals(location) || BlockLoc.MINY.equals(location)) {
return;
}
plot.getSettings().setPosition(location);
@@ -1782,7 +1721,6 @@ public class Plot {
}
player.sendMessage(
TranslatableCaption.of("working.claimed"),
TagResolver.resolver("world", Tag.inserting(Component.text(this.getWorldName()))),
TagResolver.resolver("plot", Tag.inserting(Component.text(this.getId().toString())))
);
if (teleport) {
@@ -2257,9 +2195,6 @@ public class Plot {
* @return if the given player can claim the plot
*/
public boolean canClaim(@NonNull PlotPlayer<?> player) {
if (!WorldUtil.isValidLocation(getBottomAbs())) {
return false;
}
PlotCluster cluster = this.getCluster();
if (cluster != null) {
if (!cluster.isAdded(player.getUUID()) && !player.hasPermission("plots.admin.command.claim")) {
@@ -2639,12 +2574,6 @@ public class Plot {
*/
public void teleportPlayer(final PlotPlayer<?> player, TeleportCause cause, Consumer<Boolean> resultConsumer) {
Plot plot = this.getBasePlot(false);
if ((getArea() == null || !(getArea() instanceof SinglePlotArea)) && !WorldUtil.isValidLocation(plot.getBottomAbs())) {
// prevent from teleporting into unsafe regions
player.sendMessage(TranslatableCaption.of("border.denied"));
resultConsumer.accept(false);
return;
}
PlayerTeleportToPlotEvent event = this.eventDispatcher.callTeleport(player, player.getLocation(), plot, cause);
if (event.getEventResult() == Result.DENY) {

View File

@@ -679,8 +679,10 @@ public abstract class PlotArea implements ComponentLike {
TranslatableCaption.of("height.height_limit"),
TagResolver.builder()
.tag("minheight", Tag.inserting(Component.text(minBuildHeight)))
.tag("maxheight", Tag.inserting(Component.text(maxBuildHeight)))
.build()
.tag(
"maxheight",
Tag.inserting(Component.text(maxBuildHeight))
).build()
);
// Return true if "failed" as the method will always be inverted otherwise
return true;
@@ -1450,24 +1452,6 @@ public abstract class PlotArea implements ComponentLike {
this.defaultHome = defaultHome;
}
/**
* Get the maximum height that changes to plot components (wall filling, air, all etc.) may operate to
*
* @since 7.3.4
*/
public int getMaxComponentHeight() {
return this.maxBuildHeight;
}
/**
* Get the minimum height that changes to plot components (wall filling, air, all etc.) may operate to
*
* @since 7.3.4
*/
public int getMinComponentHeight() {
return this.minBuildHeight;
}
/**
* Get the maximum height players may build in. Exclusive.
*/

View File

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

View File

@@ -63,18 +63,4 @@ public class PlotTitle {
return subtitle;
}
/**
* Provides a string representation of this plot title value (used in placeholders).
*
* @return the plot title representation in the format {@code "<title>" "<subtitle>"}
* @since 7.5.5
*/
@Override
public String toString() {
return "\"%s\" \"%s\"".formatted(
this.title != null ? this.title : "",
this.subtitle != null ? this.subtitle : ""
);
}
}

View File

@@ -48,7 +48,6 @@ import com.plotsquared.core.plot.flag.implementations.EntityChangeBlockFlag;
import com.plotsquared.core.plot.flag.implementations.ExplosionFlag;
import com.plotsquared.core.plot.flag.implementations.FarewellFlag;
import com.plotsquared.core.plot.flag.implementations.FeedFlag;
import com.plotsquared.core.plot.flag.implementations.FishingFlag;
import com.plotsquared.core.plot.flag.implementations.FlyFlag;
import com.plotsquared.core.plot.flag.implementations.ForcefieldFlag;
import com.plotsquared.core.plot.flag.implementations.GamemodeFlag;
@@ -65,7 +64,6 @@ import com.plotsquared.core.plot.flag.implementations.HostileInteractFlag;
import com.plotsquared.core.plot.flag.implementations.IceFormFlag;
import com.plotsquared.core.plot.flag.implementations.IceMeltFlag;
import com.plotsquared.core.plot.flag.implementations.InstabreakFlag;
import com.plotsquared.core.plot.flag.implementations.InteractionInteractFlag;
import com.plotsquared.core.plot.flag.implementations.InvincibleFlag;
import com.plotsquared.core.plot.flag.implementations.ItemDropFlag;
import com.plotsquared.core.plot.flag.implementations.KeepFlag;
@@ -93,7 +91,6 @@ import com.plotsquared.core.plot.flag.implementations.PreventCreativeCopyFlag;
import com.plotsquared.core.plot.flag.implementations.PriceFlag;
import com.plotsquared.core.plot.flag.implementations.ProjectileChangeBlockFlag;
import com.plotsquared.core.plot.flag.implementations.ProjectilesFlag;
import com.plotsquared.core.plot.flag.implementations.WeavingDeathPlace;
import com.plotsquared.core.plot.flag.implementations.PveFlag;
import com.plotsquared.core.plot.flag.implementations.PvpFlag;
import com.plotsquared.core.plot.flag.implementations.RedstoneFlag;
@@ -104,7 +101,6 @@ import com.plotsquared.core.plot.flag.implementations.SnowMeltFlag;
import com.plotsquared.core.plot.flag.implementations.SoilDryFlag;
import com.plotsquared.core.plot.flag.implementations.TamedAttackFlag;
import com.plotsquared.core.plot.flag.implementations.TamedInteractFlag;
import com.plotsquared.core.plot.flag.implementations.TileDropFlag;
import com.plotsquared.core.plot.flag.implementations.TimeFlag;
import com.plotsquared.core.plot.flag.implementations.TitlesFlag;
import com.plotsquared.core.plot.flag.implementations.UntrustedVisitFlag;
@@ -162,7 +158,6 @@ public final class GlobalFlagContainer extends FlagContainer {
this.addFlag(EditSignFlag.EDIT_SIGN_FALSE);
this.addFlag(EntityChangeBlockFlag.ENTITY_CHANGE_BLOCK_FALSE);
this.addFlag(ExplosionFlag.EXPLOSION_FALSE);
this.addFlag(FishingFlag.FISHING_FALSE);
this.addFlag(ForcefieldFlag.FORCEFIELD_FALSE);
this.addFlag(GrassGrowFlag.GRASS_GROW_TRUE);
this.addFlag(HangingBreakFlag.HANGING_BREAK_FALSE);
@@ -173,7 +168,6 @@ public final class GlobalFlagContainer extends FlagContainer {
this.addFlag(IceFormFlag.ICE_FORM_FALSE);
this.addFlag(IceMeltFlag.ICE_MELT_FALSE);
this.addFlag(InstabreakFlag.INSTABREAK_FALSE);
this.addFlag(InteractionInteractFlag.INTERACTION_INTERACT_FALSE);
this.addFlag(InvincibleFlag.INVINCIBLE_FALSE);
this.addFlag(ItemDropFlag.ITEM_DROP_TRUE);
this.addFlag(KeepInventoryFlag.KEEP_INVENTORY_FALSE);
@@ -202,7 +196,6 @@ public final class GlobalFlagContainer extends FlagContainer {
this.addFlag(SoilDryFlag.SOIL_DRY_FALSE);
this.addFlag(TamedAttackFlag.TAMED_ATTACK_FALSE);
this.addFlag(TamedInteractFlag.TAMED_INTERACT_FALSE);
this.addFlag(TileDropFlag.TILE_DROP_TRUE);
this.addFlag(UntrustedVisitFlag.UNTRUSTED_VISIT_FLAG_TRUE);
this.addFlag(VehicleBreakFlag.VEHICLE_BREAK_FALSE);
this.addFlag(VehiclePlaceFlag.VEHICLE_PLACE_FALSE);
@@ -210,7 +203,6 @@ public final class GlobalFlagContainer extends FlagContainer {
this.addFlag(VillagerInteractFlag.VILLAGER_INTERACT_FALSE);
this.addFlag(VineGrowFlag.VINE_GROW_TRUE);
this.addFlag(ProjectilesFlag.PROJECTILES_FALSE);
this.addFlag(WeavingDeathPlace.WEAVING_DEATH_PLACE_FALSE);
// Double flags
this.addFlag(PriceFlag.PRICE_NOT_BUYABLE);

View File

@@ -1,39 +0,0 @@
/*
* PlotSquared, a land and world management plugin for Minecraft.
* Copyright (C) IntellectualSites <https://intellectualsites.com>
* Copyright (C) IntellectualSites team and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.plotsquared.core.plot.flag.implementations;
import com.plotsquared.core.configuration.caption.TranslatableCaption;
import com.plotsquared.core.plot.flag.types.BooleanFlag;
import org.checkerframework.checker.nullness.qual.NonNull;
public class InteractionInteractFlag extends BooleanFlag<InteractionInteractFlag> {
public static final InteractionInteractFlag INTERACTION_INTERACT_TRUE = new InteractionInteractFlag(true);
public static final InteractionInteractFlag INTERACTION_INTERACT_FALSE = new InteractionInteractFlag(false);
private InteractionInteractFlag(boolean value) {
super(value, TranslatableCaption.of("flags.flag_description_interaction_interact"));
}
@Override
protected InteractionInteractFlag flagOf(@NonNull Boolean value) {
return value ? INTERACTION_INTERACT_TRUE : INTERACTION_INTERACT_FALSE;
}
}

View File

@@ -1,39 +0,0 @@
/*
* PlotSquared, a land and world management plugin for Minecraft.
* Copyright (C) IntellectualSites <https://intellectualsites.com>
* Copyright (C) IntellectualSites team and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.plotsquared.core.plot.flag.implementations;
import com.plotsquared.core.configuration.caption.TranslatableCaption;
import com.plotsquared.core.plot.flag.types.BooleanFlag;
import org.checkerframework.checker.nullness.qual.NonNull;
public class WeavingDeathPlace extends BooleanFlag<WeavingDeathPlace> {
public static final WeavingDeathPlace WEAVING_DEATH_PLACE_TRUE = new WeavingDeathPlace(true);
public static final WeavingDeathPlace WEAVING_DEATH_PLACE_FALSE = new WeavingDeathPlace(false);
private WeavingDeathPlace(boolean value) {
super(value, TranslatableCaption.of("flags.flag_description_weaving_death_place"));
}
@Override
protected WeavingDeathPlace flagOf(@NonNull Boolean value) {
return value ? WEAVING_DEATH_PLACE_TRUE : WEAVING_DEATH_PLACE_FALSE;
}
}

View File

@@ -37,7 +37,7 @@ import java.util.List;
public class SinglePlotManager extends PlotManager {
private static final int MAX_COORDINATE = 20000000;
private static final int MAX_COORDINATE = 30000000;
public SinglePlotManager(final @NonNull PlotArea plotArea) {
super(plotArea);

View File

@@ -52,7 +52,6 @@ public class ChunkCoordinatorBuilder {
private int initialBatchSize = Settings.QUEUE.INITIAL_BATCH_SIZE;
private boolean unloadAfter = true;
private boolean forceSync = false;
private boolean shouldGen = true;
@Inject
public ChunkCoordinatorBuilder(@NonNull ChunkCoordinatorFactory chunkCoordinatorFactory) {
@@ -204,19 +203,6 @@ public class ChunkCoordinatorBuilder {
return this;
}
/**
* Set whether chunks should be generated as part of this operation. Default is true. Disabling this may not be supported
* depending on server implementation. (i.e. setting to false may not actually disable generation as part of this operation
* - this is just a catch-all in case of future differing server implementations; the option will work on Spigot/Paper).
*
* @param shouldGen should generate new chunks or not
* @since 7.5.0
*/
public @NonNull ChunkCoordinatorBuilder shouldGen(final boolean shouldGen) {
this.shouldGen = shouldGen;
return this;
}
public @NonNull ChunkCoordinatorBuilder withProgressSubscriber(ProgressSubscriber progressSubscriber) {
this.progressSubscribers.add(progressSubscriber);
return this;
@@ -248,8 +234,7 @@ public class ChunkCoordinatorBuilder {
this.throwableConsumer,
this.unloadAfter,
this.progressSubscribers,
this.forceSync,
this.shouldGen
this.forceSync
);
}

View File

@@ -51,7 +51,6 @@ public class DelegateQueueCoordinator extends QueueCoordinator {
if (parent != null) {
this.setForceSync(parent.isForceSync());
this.setShouldGen(parent.isShouldGen());
}
}

View File

@@ -45,7 +45,6 @@ public abstract class QueueCoordinator {
private final AtomicBoolean enqueued = new AtomicBoolean();
private boolean forceSync = false;
private boolean shouldGen = true;
@Nullable
private Object chunkObject;
@SuppressWarnings({"unused", "FieldCanBeLocal"})
@@ -111,30 +110,6 @@ public abstract class QueueCoordinator {
this.forceSync = forceSync;
}
/**
* Get whether chunks should be generated as part of this operation. Default is true. Disabling this may not be supported
* depending on server implementation. (i.e. setting to false may not actually disable generation as part of this operation
* - this is just a catch-all in case of future differing server implementations; the option will work on Spigot/Paper).
*
* @since 7.5.0
*/
public boolean isShouldGen() {
return shouldGen;
}
/**
* Set whether chunks should be generated as part of this operation. Default is true. Disabling this may not be supported
* depending on server implementation. (i.e. setting to false may not actually disable generation as part of this operation
* - this is just a catch-all in case of future differing server implementations; the option will work on Spigot/Paper).
*
* @param shouldGen should generate new chunks or not
* @since 7.5.0
*/
public void setShouldGen(boolean shouldGen) {
this.shouldGen = shouldGen;
}
/**
* Get the Chunk Object set to the queue
*

View File

@@ -18,7 +18,6 @@
*/
package com.plotsquared.core.services.plots;
import cloud.commandframework.services.types.Service;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.plotsquared.core.plot.Plot;
@@ -26,6 +25,7 @@ import com.plotsquared.core.plot.PlotAreaType;
import com.plotsquared.core.plot.PlotId;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.incendo.cloud.services.type.Service;
import java.util.Collections;
import java.util.List;

Some files were not shown because too many files have changed in this diff Show More