mirror of
https://github.com/PlayPro/CoreProtect.git
synced 2026-11-04 16:40:13 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bf0e9cf661 | ||
|
|
71b673756f | ||
|
|
2f794d6398 | ||
|
|
f3a1d95b77 | ||
|
|
8c79b3a867 | ||
|
|
d87ecaee42 | ||
|
|
dc4e9f8d60 | ||
|
|
c145fca68c | ||
|
|
b0856b51a5 | ||
|
|
7386f4bd50 | ||
|
|
0f0ceccb7b | ||
|
|
893ce9af46 | ||
|
|
609990e344 | ||
|
|
38166fd381 |
No files matched your search
@@ -13,16 +13,11 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
|
||||
- name: Set up JDK 21
|
||||
- name: Set up JDK 25
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: '21'
|
||||
java-version: '25'
|
||||
distribution: 'temurin'
|
||||
- name: Cache Maven packages
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.m2
|
||||
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
|
||||
restore-keys: ${{ runner.os }}-m2
|
||||
cache: 'maven'
|
||||
- name: Build
|
||||
run: mvn -B verify
|
||||
@@ -18,7 +18,7 @@ For a detailed description of the plugin, please visit [coreprotect.net](https:/
|
||||
| CoreProtect Discord: | [discord.gg/b4DZ4jy](https://discord.gg/b4DZ4jy) |
|
||||
| CoreProtect Patreon: | [patreon.com/coreprotect](https://www.patreon.com/coreprotect) |
|
||||
| CoreProtect Documentation: | [docs.coreprotect.net](https://docs.coreprotect.net) |
|
||||
| Downloads for MC 1.14 - 1.21: | [coreprotect.net/latest](https://coreprotect.net/latest/) |
|
||||
| Downloads for MC 1.14 - 26.1: | [coreprotect.net/latest](https://coreprotect.net/latest/) |
|
||||
| Downloads for MC 1.8 - 1.12: | [coreprotect.net/legacy](https://coreprotect.net/legacy/) |
|
||||
|
||||
bStats
|
||||
@@ -41,7 +41,7 @@ Maven
|
||||
<dependency>
|
||||
<groupId>net.coreprotect</groupId>
|
||||
<artifactId>coreprotect</artifactId>
|
||||
<version>23.1</version>
|
||||
<version>23.2</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
+3
-3
@@ -4,8 +4,8 @@ The CoreProtect API enables you to log your own block changes, perform lookups,
|
||||
|
||||
| API Details | |
|
||||
| --- | --- |
|
||||
| **API Version:** | 10 |
|
||||
| **Plugin Version:** | v22.4+ |
|
||||
| **API Version:** | 11 |
|
||||
| **Plugin Version:** | v23.1+ |
|
||||
| **Maven:** | [maven.playpro.com](https://maven.playpro.com) |
|
||||
|
||||
*Documentation for the API version 10 can be found [here](/api/version/v10/).*
|
||||
*Documentation for the API version 10 can be found [here](/api/version/v11/).*
|
||||
@@ -5,7 +5,7 @@ The CoreProtect API enables you to log your own block changes, perform lookups,
|
||||
| API Details | |
|
||||
| --- | --- |
|
||||
| **API Version:** | 11 |
|
||||
| **Plugin Version:** | v24.0+ |
|
||||
| **Plugin Version:** | v23.1+ |
|
||||
| **Maven:** | [maven.playpro.com](https://maven.playpro.com) |
|
||||
|
||||
---
|
||||
@@ -28,8 +28,8 @@ CoreProtectPreLogEvent(String user)
|
||||
|
||||
## Getting Started
|
||||
|
||||
Ensure you're using CoreProtect 24.0 or higher. Add it as an external jar to your plugin in your IDE.
|
||||
Alternatively, if using Maven, you can add it via the repository https://maven.playpro.com (net.coreprotect, 24.0).
|
||||
Ensure you're using CoreProtect 23.1 or higher. Add it as an external jar to your plugin in your IDE.
|
||||
Alternatively, if using Maven, you can add it via the repository https://maven.playpro.com (net.coreprotect, 23.1).
|
||||
|
||||
The first thing you need to do is get access to CoreProtect. You can do this by using code similar to the following:
|
||||
|
||||
|
||||
@@ -46,6 +46,8 @@ The following permissions can be used to restrict functionality within the plugi
|
||||
|
||||
## Child Permissions
|
||||
|
||||
> **Note:** Child permissions are not presently supported by CoreProtect Hytale Edition.
|
||||
|
||||
* **coreprotect.lookup.block** *(default: op)*
|
||||
Can be optionally used as a negative permission to prevent block lookups.
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>net.coreprotect</groupId>
|
||||
<artifactId>CoreProtect</artifactId>
|
||||
<version>23.1</version>
|
||||
<version>23.2</version>
|
||||
<properties>
|
||||
<project.branch></project.branch>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
@@ -26,7 +26,7 @@
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<version>3.15.0</version>
|
||||
<configuration>
|
||||
<source>11</source>
|
||||
<target>11</target>
|
||||
@@ -47,7 +47,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>3.3.0</version>
|
||||
<version>3.5.0</version>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifestEntries>
|
||||
@@ -59,7 +59,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.2.4</version>
|
||||
<version>3.6.2</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
@@ -105,7 +105,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>3.2.3</version>
|
||||
<version>3.5.5</version>
|
||||
<configuration>
|
||||
<systemPropertyVariables>
|
||||
<plugin.version>${project.version}</plugin.version>
|
||||
@@ -116,7 +116,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-enforcer-plugin</artifactId>
|
||||
<version>3.6.1</version>
|
||||
<version>3.6.2</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>validate</phase>
|
||||
@@ -152,7 +152,7 @@
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-antrun-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<version>3.2.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>install-validation</id>
|
||||
@@ -209,32 +209,32 @@
|
||||
<dependencies>
|
||||
<!--
|
||||
paper-api: io.papermc.paper
|
||||
paper-api (<= 1.16): com.destroystokyo.paper
|
||||
paper-api (<= 1.16): com.destroystokyo.paper / 1.16.1-R0.1-SNAPSHOT
|
||||
spigot-api: org.spigotmc
|
||||
bukkit: org.bukkit
|
||||
-->
|
||||
<dependency>
|
||||
<groupId>io.papermc.paper</groupId>
|
||||
<artifactId>paper-api</artifactId>
|
||||
<version>1.21.11-R0.1-SNAPSHOT</version>
|
||||
<version>26.1.2.build.9-alpha</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bstats</groupId>
|
||||
<artifactId>bstats-bukkit</artifactId>
|
||||
<version>3.0.2</version>
|
||||
<version>3.2.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fastasyncworldedit</groupId>
|
||||
<artifactId>FastAsyncWorldEdit-Core</artifactId>
|
||||
<version>2.13.1</version>
|
||||
<version>2.15.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fastasyncworldedit</groupId>
|
||||
<artifactId>FastAsyncWorldEdit-Bukkit</artifactId>
|
||||
<version>2.13.1</version>
|
||||
<version>2.15.0</version>
|
||||
<scope>provided</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
@@ -246,22 +246,22 @@
|
||||
<dependency>
|
||||
<groupId>com.zaxxer</groupId>
|
||||
<artifactId>HikariCP</artifactId>
|
||||
<version>5.0.1</version>
|
||||
<version>7.0.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.oshi</groupId>
|
||||
<artifactId>oshi-core</artifactId>
|
||||
<version>6.6.2</version>
|
||||
<version>6.12.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-core</artifactId>
|
||||
<version>2.19.0</version>
|
||||
<version>2.25.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-api</artifactId>
|
||||
<version>2.19.0</version>
|
||||
<version>2.25.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.DeadSilenceIV</groupId>
|
||||
@@ -273,31 +273,31 @@
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-api</artifactId>
|
||||
<version>5.10.1</version>
|
||||
<version>5.14.3</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<version>5.10.1</version>
|
||||
<version>5.14.3</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-params</artifactId>
|
||||
<version>5.10.1</version>
|
||||
<version>5.14.3</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>5.16.1</version>
|
||||
<version>5.23.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-junit-jupiter</artifactId>
|
||||
<version>5.16.1</version>
|
||||
<version>5.23.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -309,25 +309,25 @@
|
||||
<dependency>
|
||||
<groupId>org.xerial</groupId>
|
||||
<artifactId>sqlite-jdbc</artifactId>
|
||||
<version>3.45.1.0</version>
|
||||
<version>3.53.0.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-simple</artifactId>
|
||||
<version>2.0.12</version>
|
||||
<version>2.0.17</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.bytebuddy</groupId>
|
||||
<artifactId>byte-buddy</artifactId>
|
||||
<version>1.16.1</version>
|
||||
<version>1.18.8</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.bytebuddy</groupId>
|
||||
<artifactId>byte-buddy-agent</artifactId>
|
||||
<version>1.16.1</version>
|
||||
<version>1.18.8</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
@@ -22,6 +22,8 @@ import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.ItemFrame;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.block.BlockExplodeEvent;
|
||||
import org.bukkit.event.block.SignChangeEvent;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
@@ -316,6 +318,17 @@ public class BukkitAdapter implements BukkitInterface {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldLogExplosion(Event event){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Material getExplodedBlock(BlockExplodeEvent event){
|
||||
// accoding to the Bukkit docs this will always return air
|
||||
return event.getBlock().getType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGlowing(Sign sign, boolean isFront, boolean isGlowing) {
|
||||
// Base implementation does nothing
|
||||
@@ -361,13 +374,29 @@ public class BukkitAdapter implements BukkitInterface {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isBundle(Material material) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCopperChest(Material material) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShelf(Material material){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Material> copperChestMaterials() {
|
||||
return EMPTY_SET;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Material> shelfMaterials() {
|
||||
return EMPTY_SET;
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,8 @@ import org.bukkit.entity.Arrow;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.block.BlockExplodeEvent;
|
||||
import org.bukkit.event.block.SignChangeEvent;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
@@ -132,6 +134,17 @@ public interface BukkitInterface {
|
||||
*/
|
||||
boolean isChiseledBookshelf(Material material);
|
||||
|
||||
|
||||
/**
|
||||
* Checks if a material is a shelf of any wood kind.
|
||||
*
|
||||
* @param material
|
||||
* The material to check
|
||||
* @return true if the material is a shelf, false otherwise
|
||||
*/
|
||||
boolean isShelf(Material material);
|
||||
|
||||
|
||||
/**
|
||||
* Checks if a material is a bookshelf book.
|
||||
*
|
||||
@@ -141,6 +154,17 @@ public interface BukkitInterface {
|
||||
*/
|
||||
boolean isBookshelfBook(Material material);
|
||||
|
||||
|
||||
/**
|
||||
* Checks if a material is a bundle.
|
||||
*
|
||||
* @param material
|
||||
* The material to check
|
||||
* @return true if the material is a bundle, false otherwise
|
||||
*/
|
||||
boolean isBundle(Material material);
|
||||
|
||||
|
||||
/**
|
||||
* Gets the seeds material for a plant material.
|
||||
*
|
||||
@@ -402,6 +426,27 @@ public interface BukkitInterface {
|
||||
*/
|
||||
boolean isSignFront(SignChangeEvent event);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Checks whether an explosion event should be logged or not. (i.e. wind charge explosions)
|
||||
*
|
||||
* @param event
|
||||
* The explosion event (Block or Entity ExplodeEvent)
|
||||
* @return true if the explosion should affect blocks
|
||||
*/
|
||||
boolean shouldLogExplosion(Event event);
|
||||
|
||||
|
||||
/**
|
||||
* Gets the material of the block that exploded
|
||||
*
|
||||
* @param event
|
||||
* The block explosion event
|
||||
* @return the material of the block that caused the explosion
|
||||
*/
|
||||
Material getExplodedBlock(BlockExplodeEvent event);
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// Registry methods
|
||||
// --------------------------------------------------------------------------
|
||||
@@ -441,4 +486,6 @@ public interface BukkitInterface {
|
||||
|
||||
Set<Material> copperChestMaterials();
|
||||
|
||||
Set<Material> shelfMaterials();
|
||||
|
||||
}
|
||||
@@ -152,9 +152,15 @@ public class Bukkit_v1_17 extends BukkitAdapter {
|
||||
return super.getItemMeta(itemMeta, list, metadata, slot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBundle(Material material) {
|
||||
return material == Material.BUNDLE;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean setItemMeta(Material rowType, ItemStack itemstack, List<Map<String, Object>> map) {
|
||||
if (rowType == Material.BUNDLE) {
|
||||
if (BukkitAdapter.ADAPTER.isBundle(rowType)) {
|
||||
BundleMeta meta = (BundleMeta) itemstack.getItemMeta();
|
||||
for (Map<String, Object> itemData : map) {
|
||||
ItemStack itemStack = ItemUtils.unserializeItemStack(itemData);
|
||||
|
||||
@@ -13,6 +13,7 @@ import org.bukkit.block.ChiseledBookshelf;
|
||||
import org.bukkit.block.Sign;
|
||||
import org.bukkit.block.sign.Side;
|
||||
import org.bukkit.entity.Arrow;
|
||||
import org.bukkit.event.block.BlockExplodeEvent;
|
||||
import org.bukkit.event.block.SignChangeEvent;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
@@ -297,4 +298,10 @@ public class Bukkit_v1_20 extends Bukkit_v1_19 {
|
||||
return super.getArrowMeta(arrow, itemStack);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Material getExplodedBlock(BlockExplodeEvent event){
|
||||
// accoding to the Bukkit docs this will always return air
|
||||
return event.getExplodedBlockState().getType();
|
||||
}
|
||||
}
|
||||
@@ -6,13 +6,18 @@ import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ExplosionResult;
|
||||
import org.bukkit.Keyed;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.Tag;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.block.BlockExplodeEvent;
|
||||
import org.bukkit.event.entity.EntityExplodeEvent;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
|
||||
|
||||
import net.coreprotect.model.BlockGroup;
|
||||
|
||||
/**
|
||||
@@ -23,9 +28,11 @@ import net.coreprotect.model.BlockGroup;
|
||||
* - Registry handling for named objects
|
||||
* - Updated interaction blocks
|
||||
*/
|
||||
public class Bukkit_v1_21 extends Bukkit_v1_20 implements BukkitInterface {
|
||||
public class Bukkit_v1_21 extends Bukkit_v1_20 {
|
||||
|
||||
public static Set<Material> COPPER_CHESTS = new HashSet<>(Arrays.asList());
|
||||
public static Set<Material> SHELVES = new HashSet<>(Arrays.asList());
|
||||
public static Set<Material> BUNDLES = new HashSet<>(Arrays.asList());
|
||||
|
||||
/**
|
||||
* Initializes the Bukkit_v1_21 adapter with 1.21-specific block groups and mappings.
|
||||
@@ -34,9 +41,11 @@ public class Bukkit_v1_21 extends Bukkit_v1_20 implements BukkitInterface {
|
||||
public Bukkit_v1_21() {
|
||||
initializeBlockGroups();
|
||||
initializeTrapdoorBlocks();
|
||||
initializeBundles();
|
||||
BlockGroup.INTERACT_BLOCKS.addAll(copperChestMaterials());
|
||||
BlockGroup.CONTAINERS.addAll(copperChestMaterials());
|
||||
BlockGroup.UPDATE_STATE.addAll(copperChestMaterials());
|
||||
BlockGroup.CONTAINERS.addAll(shelfMaterials());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,6 +75,20 @@ public class Bukkit_v1_21 extends Bukkit_v1_20 implements BukkitInterface {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the bundles group to enable the ability to roll back dyed bundles correctly.
|
||||
* It needs to check whether dyed bundles exist because they were added in 1.21.2.
|
||||
*/
|
||||
public void initializeBundles(){
|
||||
if (BUNDLES.isEmpty()) {
|
||||
Material bundle = Material.getMaterial("RED_BUNDLE");
|
||||
if (bundle != null) {
|
||||
BUNDLES.addAll(Tag.ITEMS_BUNDLES.getValues());
|
||||
}
|
||||
BUNDLES.add(Material.BUNDLE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to add a block to a block group if it's not already present.
|
||||
*
|
||||
@@ -111,6 +134,7 @@ public class Bukkit_v1_21 extends Bukkit_v1_20 implements BukkitInterface {
|
||||
return ((Keyed) value).getKey().toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets a registry value from a key string and class.
|
||||
* Used for deserializing registry objects.
|
||||
@@ -177,6 +201,17 @@ public class Bukkit_v1_21 extends Bukkit_v1_20 implements BukkitInterface {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShelf(Material material) {
|
||||
return SHELVES.contains(material);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBundle(Material material) {
|
||||
return Tag.ITEMS_BUNDLES.getValues().contains(material);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Set<Material> copperChestMaterials() {
|
||||
if (COPPER_CHESTS.isEmpty()) {
|
||||
@@ -198,4 +233,30 @@ public class Bukkit_v1_21 extends Bukkit_v1_20 implements BukkitInterface {
|
||||
|
||||
return COPPER_CHESTS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Material> shelfMaterials() {
|
||||
if (SHELVES.isEmpty()) {
|
||||
Material shelf = Material.getMaterial("OAK_SHELF");
|
||||
if (shelf != null) {
|
||||
SHELVES.addAll(Tag.WOODEN_SHELVES.getValues());
|
||||
}
|
||||
}
|
||||
|
||||
return SHELVES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldLogExplosion(Event event){
|
||||
ExplosionResult result = null;
|
||||
|
||||
if (event instanceof EntityExplodeEvent){
|
||||
result = ((EntityExplodeEvent)event).getExplosionResult();
|
||||
} else if (event instanceof BlockExplodeEvent){
|
||||
result = ((BlockExplodeEvent)event).getExplosionResult();
|
||||
}
|
||||
return !(result == ExplosionResult.KEEP ||
|
||||
result == ExplosionResult.TRIGGER_BLOCK
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -52,8 +52,8 @@ public class ConfigHandler extends Queue {
|
||||
public static final String COMMUNITY_EDITION = "Community Edition";
|
||||
public static final String JAVA_VERSION = "11.0";
|
||||
public static final String MINECRAFT_VERSION = "1.16";
|
||||
public static final String PATCH_VERSION = "23.1";
|
||||
public static final String LATEST_VERSION = "1.21.11";
|
||||
public static final String PATCH_VERSION = "23.2";
|
||||
public static final String LATEST_VERSION = "26.1";
|
||||
public static String path = "plugins/CoreProtect/";
|
||||
public static String sqlite = "database.db";
|
||||
public static String host = "127.0.0.1";
|
||||
|
||||
@@ -75,7 +75,7 @@ public final class ListenerHandler {
|
||||
|
||||
try {
|
||||
Class.forName("io.papermc.paper.event.entity.ItemTransportingEntityValidateTargetEvent"); // Paper 1.21.10+
|
||||
// pluginManager.registerEvents(new CopperGolemChestListener(plugin), plugin);
|
||||
pluginManager.registerEvents(new CopperGolemChestListener(plugin), plugin);
|
||||
}
|
||||
catch (Exception e) {
|
||||
// Ignore registration failures to remain compatible with older servers.
|
||||
|
||||
@@ -146,18 +146,25 @@ public final class BlockExplodeListener extends Queue implements Listener {
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
protected void onBlockExplode(BlockExplodeEvent event) {
|
||||
Block eventBlock = event.getBlock();
|
||||
World world = eventBlock.getLocation().getWorld();
|
||||
Material eventMaterial = BukkitAdapter.ADAPTER.getExplodedBlock(event);
|
||||
World world = event.getBlock().getLocation().getWorld();
|
||||
|
||||
if (!BukkitAdapter.ADAPTER.shouldLogExplosion(event)){
|
||||
return;
|
||||
}
|
||||
|
||||
String user = "";
|
||||
if (!eventBlock.getType().equals(Material.AIR) && !eventBlock.getType().equals(Material.CAVE_AIR)) {
|
||||
user = eventBlock.getType().name().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
if (user.contains("tnt")) {
|
||||
user = "#tnt";
|
||||
}
|
||||
else if (user.contains("end_crystal")) {
|
||||
user = "#end_crystal";
|
||||
if (!eventMaterial.equals(Material.AIR) && !eventMaterial.equals(Material.CAVE_AIR)) {
|
||||
user = eventMaterial.name().toLowerCase(Locale.ROOT);
|
||||
|
||||
if (user.contains("respawn_anchor")) {
|
||||
user = "#respawn_anchor";
|
||||
}
|
||||
else if (user.contains("_bed")) {
|
||||
user = "#bed";
|
||||
}
|
||||
}
|
||||
|
||||
if (!user.startsWith("#")) {
|
||||
user = "#explosion";
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package net.coreprotect.listener.entity;
|
||||
|
||||
import org.bukkit.ExplosionResult;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Creeper;
|
||||
import org.bukkit.entity.EnderCrystal;
|
||||
@@ -15,6 +16,7 @@ import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.entity.EntityExplodeEvent;
|
||||
|
||||
import net.coreprotect.bukkit.BukkitAdapter;
|
||||
import net.coreprotect.config.Config;
|
||||
import net.coreprotect.consumer.Queue;
|
||||
import net.coreprotect.listener.block.BlockExplodeListener;
|
||||
@@ -24,7 +26,8 @@ public final class EntityExplodeListener extends Queue implements Listener {
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
protected void onEntityExplode(EntityExplodeEvent event) {
|
||||
Entity entity = event.getEntity();
|
||||
if (entity.getType().name().equals("WIND_CHARGE") || entity.getType().name().equals("BREEZE_WIND_CHARGE")) {
|
||||
|
||||
if (!BukkitAdapter.ADAPTER.shouldLogExplosion(event)){
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,12 +18,14 @@ import org.bukkit.block.Jukebox;
|
||||
import org.bukkit.block.Sign;
|
||||
import org.bukkit.block.data.Bisected;
|
||||
import org.bukkit.block.data.Bisected.Half;
|
||||
import org.bukkit.block.data.SideChaining.ChainPart;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.block.data.Lightable;
|
||||
import org.bukkit.block.data.Waterlogged;
|
||||
import org.bukkit.block.data.type.Bed;
|
||||
import org.bukkit.block.data.type.Bed.Part;
|
||||
import org.bukkit.block.data.type.Cake;
|
||||
import org.bukkit.block.data.type.Shelf;
|
||||
import org.bukkit.entity.EnderCrystal;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
@@ -37,6 +39,7 @@ import org.bukkit.inventory.BlockInventoryHolder;
|
||||
import org.bukkit.inventory.EquipmentSlot;
|
||||
import org.bukkit.inventory.InventoryHolder;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import net.coreprotect.CoreProtect;
|
||||
import net.coreprotect.bukkit.BukkitAdapter;
|
||||
@@ -465,6 +468,49 @@ public final class PlayerInteractListener extends Queue implements Listener {
|
||||
InventoryChangeListener.inventoryTransaction(player.getName(), blockState.getLocation(), null);
|
||||
}
|
||||
}
|
||||
} else if (BukkitAdapter.ADAPTER.isShelf(type) ){
|
||||
BlockData blockState = block.getBlockData();
|
||||
if (blockState instanceof Shelf){
|
||||
Shelf shelf = (Shelf) blockState;
|
||||
|
||||
// ignore clicking on the back face
|
||||
if (event.getBlockFace() != shelf.getFacing()){
|
||||
return;
|
||||
}
|
||||
|
||||
if (shelf.getSideChain() == ChainPart.UNCONNECTED){
|
||||
InventoryChangeListener.inventoryTransaction(player.getName(), block.getLocation(), null);
|
||||
} else {
|
||||
Block center = block;
|
||||
Vector direction = shelf.getFacing().getDirection();
|
||||
|
||||
if (shelf.getSideChain() == ChainPart.LEFT){
|
||||
center = center.getRelative(direction.getBlockZ(), 0, -direction.getBlockX());
|
||||
} else if (shelf.getSideChain() == ChainPart.RIGHT){
|
||||
center = center.getRelative(-direction.getBlockZ(), 0, direction.getBlockX());
|
||||
}
|
||||
|
||||
BlockData centerBlockData = center.getBlockData();
|
||||
if (centerBlockData instanceof Shelf){
|
||||
// log center
|
||||
InventoryChangeListener.inventoryTransaction(player.getName(), center.getLocation(), null);
|
||||
|
||||
if (((Shelf)centerBlockData).getSideChain() != ChainPart.CENTER){
|
||||
// if it's not the center it's just a chain of 2
|
||||
InventoryChangeListener.inventoryTransaction(player.getName(), block.getLocation(), null);
|
||||
} else {
|
||||
Block left = center.getRelative(-direction.getBlockZ(), 0, direction.getBlockX());
|
||||
InventoryChangeListener.inventoryTransaction(player.getName(), left.getLocation(), null);
|
||||
|
||||
Block right = center.getRelative(direction.getBlockZ(), 0, -direction.getBlockX());
|
||||
InventoryChangeListener.inventoryTransaction(player.getName(), right.getLocation(), null);
|
||||
}
|
||||
} else {
|
||||
// fallback if invalid block is found just log clicked shelf
|
||||
InventoryChangeListener.inventoryTransaction(player.getName(), block.getLocation(), null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (BukkitAdapter.ADAPTER.isDecoratedPot(type)) {
|
||||
BlockState blockState = block.getState();
|
||||
|
||||
@@ -1,133 +1,574 @@
|
||||
package net.coreprotect.paper.listener;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.bukkit.GameEvent;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.block.DoubleChest;
|
||||
import org.bukkit.entity.CopperGolem;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.world.GenericGameEvent;
|
||||
import org.bukkit.inventory.EntityEquipment;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.InventoryHolder;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
|
||||
import io.papermc.paper.event.entity.ItemTransportingEntityValidateTargetEvent;
|
||||
import net.coreprotect.CoreProtect;
|
||||
import net.coreprotect.bukkit.BukkitAdapter;
|
||||
import net.coreprotect.config.Config;
|
||||
import net.coreprotect.config.ConfigHandler;
|
||||
import net.coreprotect.listener.player.InventoryChangeListener;
|
||||
import net.coreprotect.thread.Scheduler;
|
||||
import net.coreprotect.utility.ItemUtils;
|
||||
|
||||
public final class CopperGolemChestListener implements Listener {
|
||||
|
||||
private static final String COPPER_GOLEM_NAME = "COPPER_GOLEM";
|
||||
private static final String USERNAME = "#copper_golem";
|
||||
private static final long INITIAL_DELAY_TICKS = 5L;
|
||||
private static final long POLL_INTERVAL_TICKS = 15L;
|
||||
private static final long MAX_POLL_DURATION_TICKS = 600L;
|
||||
private static final long MIN_THROTTLE_MILLIS = 2800L;
|
||||
private static final long OPEN_INTERACTION_TIMEOUT_MILLIS = 20000L;
|
||||
private static final long CLEANUP_INTERVAL_MILLIS = 60000L;
|
||||
private static final long EMPTY_COPPER_CHEST_SKIP_TTL_MILLIS = 6000L;
|
||||
private static final long CLOSE_FINALIZE_DELAY_TICKS = 1L;
|
||||
private static final int CLOSE_FINALIZE_MAX_ATTEMPTS = 3;
|
||||
private static final int CLOSE_FALLBACK_MAX_ATTEMPTS = 2;
|
||||
|
||||
private final CoreProtect plugin;
|
||||
private final Map<TransactionKey, PendingTransaction> pendingTransactions = new ConcurrentHashMap<>();
|
||||
private final Map<TransactionKey, Long> throttleUntil = new ConcurrentHashMap<>();
|
||||
private final Map<UUID, OpenInteraction> openInteractions = new ConcurrentHashMap<>();
|
||||
private final Map<UUID, RecentEmptyCopperChestSkip> recentEmptyCopperChestSkips = new ConcurrentHashMap<>();
|
||||
private final Map<TransactionKey, OpenInteractionIndex> openInteractionIndexByContainerKey = new ConcurrentHashMap<>();
|
||||
private final Map<TransactionKey, Set<UUID>> emptySkipGolemsByContainerKey = new ConcurrentHashMap<>();
|
||||
private volatile long lastCleanupMillis;
|
||||
|
||||
public CopperGolemChestListener(CoreProtect plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onValidate(ItemTransportingEntityValidateTargetEvent event) {
|
||||
if (!event.isAllowed()) {
|
||||
public void onGenericGameEvent(GenericGameEvent event) {
|
||||
if (event == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
GameEvent gameEvent = event.getEvent();
|
||||
if (gameEvent != GameEvent.CONTAINER_OPEN && gameEvent != GameEvent.CONTAINER_CLOSE) {
|
||||
return;
|
||||
}
|
||||
|
||||
Entity entity = event.getEntity();
|
||||
if (entity == null || entity.getType() == null || !COPPER_GOLEM_NAME.equals(entity.getType().name())) {
|
||||
return;
|
||||
if (gameEvent == GameEvent.CONTAINER_OPEN) {
|
||||
if (!(entity instanceof CopperGolem)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (entity != null && !(entity instanceof CopperGolem)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
CopperGolem copperGolem = (CopperGolem) entity;
|
||||
Material heldMaterial = getHeldItemMaterial(copperGolem);
|
||||
|
||||
Block block = event.getBlock();
|
||||
if (block == null) {
|
||||
Location eventLocation = event.getLocation();
|
||||
if (eventLocation == null || eventLocation.getWorld() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
BlockState blockState = block.getState();
|
||||
if (!Config.getConfig(eventLocation.getWorld()).ITEM_TRANSACTIONS) {
|
||||
return;
|
||||
}
|
||||
|
||||
BlockState blockState = eventLocation.getBlock().getState();
|
||||
if (!(blockState instanceof InventoryHolder)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Location location = blockState.getLocation();
|
||||
if (location == null || location.getWorld() == null) {
|
||||
Location containerLocation = blockState.getLocation();
|
||||
if (containerLocation == null || containerLocation.getWorld() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Config.getConfig(location.getWorld()).ITEM_TRANSACTIONS) {
|
||||
Material containerType = blockState.getType();
|
||||
boolean isCopperChest = isCopperChest(containerType);
|
||||
boolean isStandardChest = containerType == Material.CHEST || containerType == Material.TRAPPED_CHEST;
|
||||
if (!isCopperChest && !isStandardChest) {
|
||||
return;
|
||||
}
|
||||
|
||||
scheduleTransaction(copperGolem, blockState, heldMaterial);
|
||||
}
|
||||
|
||||
private void scheduleTransaction(CopperGolem copperGolem, BlockState blockState, Material heldMaterial) {
|
||||
Location location = blockState.getLocation();
|
||||
if (location == null || copperGolem == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
TransactionKey transactionKey = TransactionKey.of(location);
|
||||
Location targetLocation = location.clone();
|
||||
ItemStack[] baselineState = captureInventoryState(targetLocation);
|
||||
if (baselineState == null) {
|
||||
return;
|
||||
}
|
||||
if (!shouldMonitorInteraction(blockState.getType(), baselineState, heldMaterial)) {
|
||||
return;
|
||||
}
|
||||
|
||||
PendingTransaction existing = pendingTransactions.get(transactionKey);
|
||||
if (existing != null) {
|
||||
existing.refresh(baselineState);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isThrottled(transactionKey)) {
|
||||
return;
|
||||
}
|
||||
|
||||
PendingTransaction scheduled = new PendingTransaction(transactionKey, targetLocation, baselineState);
|
||||
pendingTransactions.put(transactionKey, scheduled);
|
||||
throttleUntil.put(transactionKey, System.currentTimeMillis() + MIN_THROTTLE_MILLIS);
|
||||
scheduled.start();
|
||||
}
|
||||
|
||||
private ItemStack[] captureInventoryState(Location location) {
|
||||
if (location == null || location.getWorld() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
BlockState blockState = location.getBlock().getState();
|
||||
if (!(blockState instanceof InventoryHolder)) {
|
||||
return null;
|
||||
}
|
||||
long now = System.currentTimeMillis();
|
||||
cleanupOpenInteractions(now);
|
||||
|
||||
InventoryHolder inventoryHolder = (InventoryHolder) blockState;
|
||||
Inventory inventory = inventoryHolder.getInventory();
|
||||
Location canonicalLocation = getCanonicalContainerLocation(containerLocation, inventory);
|
||||
TransactionKey containerKey = TransactionKey.of(canonicalLocation);
|
||||
|
||||
if (gameEvent == GameEvent.CONTAINER_OPEN) {
|
||||
handleContainerOpen((CopperGolem) entity, canonicalLocation, containerKey, containerType, inventoryHolder, now);
|
||||
}
|
||||
else {
|
||||
if (entity instanceof CopperGolem) {
|
||||
handleContainerClose((CopperGolem) entity, canonicalLocation, containerKey, containerType, inventoryHolder, now);
|
||||
}
|
||||
else if (entity == null) {
|
||||
handleContainerCloseWithoutEntity(containerKey, containerType, now);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Location getCanonicalContainerLocation(Location containerLocation, Inventory inventory) {
|
||||
if (containerLocation == null || containerLocation.getWorld() == null || inventory == null) {
|
||||
return containerLocation;
|
||||
}
|
||||
|
||||
InventoryHolder holder = inventory.getHolder();
|
||||
if (!(holder instanceof DoubleChest)) {
|
||||
return containerLocation;
|
||||
}
|
||||
|
||||
Location doubleChestLocation = ((DoubleChest) holder).getLocation();
|
||||
if (doubleChestLocation == null) {
|
||||
return containerLocation;
|
||||
}
|
||||
|
||||
Location canonical = new Location(containerLocation.getWorld(), doubleChestLocation.getBlockX(), doubleChestLocation.getBlockY(), doubleChestLocation.getBlockZ());
|
||||
if (canonical.getWorld() == null) {
|
||||
return containerLocation;
|
||||
}
|
||||
|
||||
return canonical;
|
||||
}
|
||||
|
||||
private static boolean isCopperChest(Material material) {
|
||||
return BukkitAdapter.ADAPTER != null && BukkitAdapter.ADAPTER.isCopperChest(material);
|
||||
}
|
||||
|
||||
private void handleContainerOpen(CopperGolem golem, Location containerLocation, TransactionKey containerKey, Material containerType, InventoryHolder inventoryHolder, long nowMillis) {
|
||||
Inventory inventory = inventoryHolder.getInventory();
|
||||
if (inventory == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
HeldItemSnapshot held = getHeldItemSnapshot(golem);
|
||||
boolean isCopperChest = isCopperChest(containerType);
|
||||
if (isCopperChest) {
|
||||
if (held.material != null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (held.material == null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ItemStack[] contents = inventory.getContents();
|
||||
if (contents == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isCopperChest) {
|
||||
if (isInventoryEmpty(contents)) {
|
||||
UUID golemId = golem.getUniqueId();
|
||||
RecentEmptyCopperChestSkip previous = recentEmptyCopperChestSkips.put(golemId, new RecentEmptyCopperChestSkip(containerKey, nowMillis));
|
||||
if (previous != null) {
|
||||
unindexEmptySkip(previous.containerKey, golemId);
|
||||
}
|
||||
indexEmptySkip(containerKey, golemId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!isInventoryEmpty(contents) && !containsOnlyMaterial(contents, held.material)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hasSpaceForMaterial(contents, held.material)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ItemStack[] baseline = ItemUtils.getContainerState(contents);
|
||||
if (baseline == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Material heldMaterial = isCopperChest ? null : held.material;
|
||||
int heldAmount = isCopperChest ? 0 : held.amount;
|
||||
OpenInteraction interaction = new OpenInteraction(containerKey, containerLocation.clone(), containerType, baseline, heldMaterial, heldAmount, nowMillis);
|
||||
UUID golemId = golem.getUniqueId();
|
||||
RecentEmptyCopperChestSkip removedSkip = recentEmptyCopperChestSkips.remove(golemId);
|
||||
if (removedSkip != null) {
|
||||
unindexEmptySkip(removedSkip.containerKey, golemId);
|
||||
}
|
||||
|
||||
OpenInteraction previous = openInteractions.put(golemId, interaction);
|
||||
if (previous != null) {
|
||||
openInteractionIndexByContainerKey.remove(previous.containerKey, new OpenInteractionIndex(golemId, previous.openedAtMillis));
|
||||
}
|
||||
openInteractionIndexByContainerKey.put(containerKey, new OpenInteractionIndex(golemId, nowMillis));
|
||||
}
|
||||
|
||||
private void handleContainerClose(CopperGolem golem, Location containerLocation, TransactionKey containerKey, Material containerType, InventoryHolder inventoryHolder, long nowMillis) {
|
||||
UUID golemId = golem.getUniqueId();
|
||||
OpenInteraction interaction = openInteractions.get(golemId);
|
||||
if (interaction == null) {
|
||||
if (isCopperChest(containerType)) {
|
||||
RecentEmptyCopperChestSkip emptySkip = recentEmptyCopperChestSkips.get(golemId);
|
||||
if (emptySkip != null && emptySkip.containerKey.equals(containerKey) && (nowMillis - emptySkip.skippedAtMillis) <= EMPTY_COPPER_CHEST_SKIP_TTL_MILLIS) {
|
||||
if (recentEmptyCopperChestSkips.remove(golemId, emptySkip)) {
|
||||
unindexEmptySkip(emptySkip.containerKey, golemId);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
scheduleUntrackedCopperChestCloseFinalize(golemId, containerLocation.clone(), containerType, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (nowMillis - interaction.openedAtMillis > OPEN_INTERACTION_TIMEOUT_MILLIS) {
|
||||
if (openInteractions.remove(golemId, interaction)) {
|
||||
openInteractionIndexByContainerKey.remove(interaction.containerKey, new OpenInteractionIndex(golemId, interaction.openedAtMillis));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!interaction.containerKey.equals(containerKey)) {
|
||||
return;
|
||||
}
|
||||
|
||||
openInteractions.remove(golemId, interaction);
|
||||
openInteractionIndexByContainerKey.remove(interaction.containerKey, new OpenInteractionIndex(golemId, interaction.openedAtMillis));
|
||||
scheduleCloseFinalize(golemId, interaction, containerKey, 1);
|
||||
}
|
||||
|
||||
private void handleContainerCloseWithoutEntity(TransactionKey containerKey, Material containerType, long nowMillis) {
|
||||
if (containerKey == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isCopperChest(containerType)) {
|
||||
clearRecentEmptyCopperChestSkipsByKey(containerKey);
|
||||
}
|
||||
|
||||
OpenInteractionIndex index = openInteractionIndexByContainerKey.get(containerKey);
|
||||
if (index == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
UUID golemId = index.golemId;
|
||||
OpenInteraction interaction = openInteractions.get(golemId);
|
||||
if (interaction == null) {
|
||||
openInteractionIndexByContainerKey.remove(containerKey, index);
|
||||
return;
|
||||
}
|
||||
if (interaction.openedAtMillis != index.openedAtMillis || !interaction.containerKey.equals(containerKey)) {
|
||||
openInteractionIndexByContainerKey.remove(containerKey, index);
|
||||
return;
|
||||
}
|
||||
if (nowMillis - interaction.openedAtMillis > OPEN_INTERACTION_TIMEOUT_MILLIS) {
|
||||
if (openInteractions.remove(golemId, interaction)) {
|
||||
openInteractionIndexByContainerKey.remove(containerKey, index);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (openInteractions.remove(golemId, interaction)) {
|
||||
openInteractionIndexByContainerKey.remove(containerKey, index);
|
||||
scheduleCloseFinalize(golemId, interaction, containerKey, 1);
|
||||
}
|
||||
}
|
||||
|
||||
private void clearRecentEmptyCopperChestSkipsByKey(TransactionKey containerKey) {
|
||||
Set<UUID> golemIds = emptySkipGolemsByContainerKey.remove(containerKey);
|
||||
if (golemIds == null || golemIds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (UUID golemId : golemIds) {
|
||||
RecentEmptyCopperChestSkip skip = recentEmptyCopperChestSkips.get(golemId);
|
||||
if (skip != null && skip.containerKey.equals(containerKey)) {
|
||||
recentEmptyCopperChestSkips.remove(golemId, skip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void indexEmptySkip(TransactionKey containerKey, UUID golemId) {
|
||||
emptySkipGolemsByContainerKey.computeIfAbsent(containerKey, key -> ConcurrentHashMap.newKeySet()).add(golemId);
|
||||
}
|
||||
|
||||
private void unindexEmptySkip(TransactionKey containerKey, UUID golemId) {
|
||||
if (containerKey == null || golemId == null) {
|
||||
return;
|
||||
}
|
||||
Set<UUID> golemIds = emptySkipGolemsByContainerKey.get(containerKey);
|
||||
if (golemIds == null) {
|
||||
return;
|
||||
}
|
||||
golemIds.remove(golemId);
|
||||
if (golemIds.isEmpty()) {
|
||||
emptySkipGolemsByContainerKey.remove(containerKey, golemIds);
|
||||
}
|
||||
}
|
||||
|
||||
private void scheduleCloseFinalize(UUID golemId, OpenInteraction interaction, TransactionKey containerKey, int attempt) {
|
||||
Scheduler.scheduleSyncDelayedTask(plugin, () -> finalizeContainerClose(golemId, interaction, containerKey, attempt), interaction.location, (int) CLOSE_FINALIZE_DELAY_TICKS);
|
||||
}
|
||||
|
||||
private void scheduleUntrackedCopperChestCloseFinalize(UUID golemId, Location containerLocation, Material containerType, int attempt) {
|
||||
Scheduler.scheduleSyncDelayedTask(plugin, () -> finalizeUntrackedCopperChestClose(golemId, containerLocation, containerType, attempt), containerLocation, (int) CLOSE_FINALIZE_DELAY_TICKS);
|
||||
}
|
||||
|
||||
private void finalizeContainerClose(UUID golemId, OpenInteraction interaction, TransactionKey containerKey, int attempt) {
|
||||
Entity entity = plugin.getServer().getEntity(golemId);
|
||||
if (!(entity instanceof CopperGolem)) {
|
||||
return;
|
||||
}
|
||||
|
||||
BlockState blockState = interaction.location.getBlock().getState();
|
||||
if (!(blockState instanceof InventoryHolder)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Inventory inventory = ((InventoryHolder) blockState).getInventory();
|
||||
if (inventory == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ItemStack[] currentContents = inventory.getContents();
|
||||
if (currentContents == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean changed = hasInventoryChanged(interaction.baselineState, currentContents);
|
||||
if (!changed) {
|
||||
if (attempt < CLOSE_FINALIZE_MAX_ATTEMPTS) {
|
||||
scheduleCloseFinalize(golemId, interaction, containerKey, attempt + 1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
CopperGolem golem = (CopperGolem) entity;
|
||||
if (!isAttributableToGolem(golem, interaction, currentContents)) {
|
||||
return;
|
||||
}
|
||||
|
||||
recordForcedContainerState(interaction.location, currentContents);
|
||||
InventoryChangeListener.inventoryTransaction(USERNAME, interaction.location, interaction.baselineState);
|
||||
}
|
||||
|
||||
private void finalizeUntrackedCopperChestClose(UUID golemId, Location containerLocation, Material containerType, int attempt) {
|
||||
Entity entity = plugin.getServer().getEntity(golemId);
|
||||
if (!(entity instanceof CopperGolem)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isCopperChest(containerType)) {
|
||||
return;
|
||||
}
|
||||
|
||||
CopperGolem golem = (CopperGolem) entity;
|
||||
ItemStack heldNowStack = getHeldItemStack(golem);
|
||||
if (isEmptyItem(heldNowStack) || heldNowStack == null || heldNowStack.getAmount() <= 0) {
|
||||
if (attempt < CLOSE_FALLBACK_MAX_ATTEMPTS) {
|
||||
scheduleUntrackedCopperChestCloseFinalize(golemId, containerLocation, containerType, attempt + 1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int heldAmount = heldNowStack.getAmount();
|
||||
if (heldAmount > 16) {
|
||||
return;
|
||||
}
|
||||
|
||||
BlockState blockState = containerLocation.getBlock().getState();
|
||||
if (!(blockState instanceof InventoryHolder) || !isCopperChest(blockState.getType())) {
|
||||
return;
|
||||
}
|
||||
|
||||
Inventory inventory = ((InventoryHolder) blockState).getInventory();
|
||||
if (inventory == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ItemStack[] currentContents = inventory.getContents();
|
||||
if (currentContents == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ItemStack[] baselineCandidate = reconstructCopperChestBaseline(currentContents, heldNowStack);
|
||||
if (baselineCandidate == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
recordForcedContainerState(containerLocation, currentContents);
|
||||
InventoryChangeListener.inventoryTransaction(USERNAME, containerLocation, baselineCandidate);
|
||||
}
|
||||
|
||||
private boolean isAttributableToGolem(CopperGolem golem, OpenInteraction interaction, ItemStack[] currentContents) {
|
||||
boolean isCopperChest = isCopperChest(interaction.containerType);
|
||||
HeldItemSnapshot heldNow = getHeldItemSnapshot(golem);
|
||||
|
||||
if (isCopperChest) {
|
||||
if (heldNow.material == null || heldNow.amount <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int baselineTotal = sumAllItems(interaction.baselineState);
|
||||
int currentTotal = sumAllItems(currentContents);
|
||||
int totalRemoved = baselineTotal - currentTotal;
|
||||
if (totalRemoved != heldNow.amount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int removedSameMaterial = sumMaterialAmount(interaction.baselineState, heldNow.material) - sumMaterialAmount(currentContents, heldNow.material);
|
||||
return removedSameMaterial == heldNow.amount;
|
||||
}
|
||||
|
||||
Material heldMaterial = interaction.heldMaterial;
|
||||
if (heldMaterial == null || interaction.heldAmount <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!containsOnlyMaterial(currentContents, heldMaterial)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int baselineCount = sumMaterialAmount(interaction.baselineState, heldMaterial);
|
||||
int currentCount = sumMaterialAmount(currentContents, heldMaterial);
|
||||
int added = currentCount - baselineCount;
|
||||
if (added <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int heldAfter = (heldNow.material == heldMaterial ? heldNow.amount : 0);
|
||||
int removedFromGolem = interaction.heldAmount - heldAfter;
|
||||
return added == removedFromGolem;
|
||||
}
|
||||
|
||||
private ItemStack getHeldItemStack(CopperGolem copperGolem) {
|
||||
if (copperGolem == null) {
|
||||
return null;
|
||||
}
|
||||
EntityEquipment equipment = copperGolem.getEquipment();
|
||||
if (equipment == null) {
|
||||
return null;
|
||||
}
|
||||
ItemStack mainHand = equipment.getItemInMainHand();
|
||||
if (isEmptyItem(mainHand)) {
|
||||
return null;
|
||||
}
|
||||
return mainHand.clone();
|
||||
}
|
||||
|
||||
private ItemStack[] reconstructCopperChestBaseline(ItemStack[] currentContents, ItemStack heldNowStack) {
|
||||
if (currentContents == null || heldNowStack == null || isEmptyItem(heldNowStack) || heldNowStack.getAmount() <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return ItemUtils.getContainerState(inventory.getContents());
|
||||
ItemStack[] baseline = ItemUtils.getContainerState(currentContents);
|
||||
if (baseline == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int addAmount = heldNowStack.getAmount();
|
||||
int maxStack = heldNowStack.getMaxStackSize();
|
||||
|
||||
for (int i = 0; i < baseline.length; i++) {
|
||||
ItemStack item = baseline[i];
|
||||
if (item == null) {
|
||||
continue;
|
||||
}
|
||||
if (item.isSimilar(heldNowStack)) {
|
||||
if (item.getAmount() + addAmount > maxStack) {
|
||||
return null;
|
||||
}
|
||||
item.setAmount(item.getAmount() + addAmount);
|
||||
return baseline;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < baseline.length; i++) {
|
||||
ItemStack item = baseline[i];
|
||||
if (isEmptyItem(item)) {
|
||||
ItemStack placed = heldNowStack.clone();
|
||||
placed.setAmount(addAmount);
|
||||
baseline[i] = placed;
|
||||
return baseline;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void recordForcedContainerState(Location location, ItemStack[] contents) {
|
||||
if (location == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ItemStack[] snapshot = ItemUtils.getContainerState(contents);
|
||||
if (snapshot == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String loggingContainerId = USERNAME + "." + location.getBlockX() + "." + location.getBlockY() + "." + location.getBlockZ();
|
||||
|
||||
List<ItemStack[]> forceList = ConfigHandler.forceContainer.get(loggingContainerId);
|
||||
List<ItemStack[]> oldList = ConfigHandler.oldContainer.get(loggingContainerId);
|
||||
|
||||
boolean hasPendingBaseline = oldList != null && !oldList.isEmpty();
|
||||
boolean hasStaleForceSnapshots = forceList != null && !forceList.isEmpty() && (forceList.get(0) == null || forceList.get(0).length != snapshot.length);
|
||||
|
||||
if (!hasPendingBaseline || hasStaleForceSnapshots) {
|
||||
ConfigHandler.forceContainer.remove(loggingContainerId);
|
||||
forceList = null;
|
||||
}
|
||||
|
||||
if (forceList == null) {
|
||||
forceList = Collections.synchronizedList(new ArrayList<>());
|
||||
ConfigHandler.forceContainer.put(loggingContainerId, forceList);
|
||||
}
|
||||
|
||||
forceList.add(snapshot);
|
||||
}
|
||||
|
||||
private void cleanupOpenInteractions(long nowMillis) {
|
||||
long last = lastCleanupMillis;
|
||||
if (nowMillis - last < CLEANUP_INTERVAL_MILLIS) {
|
||||
return;
|
||||
}
|
||||
lastCleanupMillis = nowMillis;
|
||||
|
||||
for (Map.Entry<UUID, OpenInteraction> entry : openInteractions.entrySet()) {
|
||||
UUID golemId = entry.getKey();
|
||||
OpenInteraction interaction = entry.getValue();
|
||||
if (interaction == null || nowMillis - interaction.openedAtMillis > OPEN_INTERACTION_TIMEOUT_MILLIS || plugin.getServer().getEntity(golemId) == null) {
|
||||
if (openInteractions.remove(golemId, interaction) && interaction != null) {
|
||||
openInteractionIndexByContainerKey.remove(interaction.containerKey, new OpenInteractionIndex(golemId, interaction.openedAtMillis));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Map.Entry<UUID, RecentEmptyCopperChestSkip> entry : recentEmptyCopperChestSkips.entrySet()) {
|
||||
UUID golemId = entry.getKey();
|
||||
RecentEmptyCopperChestSkip skip = entry.getValue();
|
||||
if (skip == null || nowMillis - skip.skippedAtMillis > EMPTY_COPPER_CHEST_SKIP_TTL_MILLIS || plugin.getServer().getEntity(golemId) == null) {
|
||||
if (recentEmptyCopperChestSkips.remove(golemId, skip) && skip != null) {
|
||||
unindexEmptySkip(skip.containerKey, golemId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasInventoryChanged(ItemStack[] previousState, ItemStack[] currentState) {
|
||||
@@ -158,30 +599,6 @@ public final class CopperGolemChestListener implements Listener {
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean shouldMonitorInteraction(Material blockType, ItemStack[] baselineState, Material heldMaterial) {
|
||||
boolean isCopperChest = BukkitAdapter.ADAPTER.isCopperChest(blockType);
|
||||
boolean isStandardChest = blockType == Material.CHEST || blockType == Material.TRAPPED_CHEST;
|
||||
boolean golemHoldingItem = heldMaterial != null && heldMaterial != Material.AIR;
|
||||
|
||||
if (!isCopperChest && !isStandardChest) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isCopperChest) {
|
||||
return !golemHoldingItem && !isInventoryEmpty(baselineState);
|
||||
}
|
||||
|
||||
if (!golemHoldingItem) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isInventoryEmpty(baselineState)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return containsMaterial(baselineState, heldMaterial);
|
||||
}
|
||||
|
||||
private boolean isInventoryEmpty(ItemStack[] state) {
|
||||
if (state == null) {
|
||||
return true;
|
||||
@@ -200,106 +617,122 @@ public final class CopperGolemChestListener implements Listener {
|
||||
return item == null || item.getType().isAir() || item.getAmount() <= 0;
|
||||
}
|
||||
|
||||
private Material getHeldItemMaterial(CopperGolem copperGolem) {
|
||||
if (copperGolem == null) {
|
||||
return null;
|
||||
}
|
||||
EntityEquipment equipment = copperGolem.getEquipment();
|
||||
if (equipment == null) {
|
||||
return null;
|
||||
}
|
||||
ItemStack mainHand = equipment.getItemInMainHand();
|
||||
if (isEmptyItem(mainHand)) {
|
||||
return null;
|
||||
}
|
||||
return mainHand.getType();
|
||||
}
|
||||
|
||||
private boolean containsMaterial(ItemStack[] state, Material material) {
|
||||
private boolean containsOnlyMaterial(ItemStack[] state, Material material) {
|
||||
if (state == null || material == null) {
|
||||
return false;
|
||||
}
|
||||
for (ItemStack item : state) {
|
||||
if (item != null && item.getType() == material && item.getAmount() > 0) {
|
||||
return true;
|
||||
if (isEmptyItem(item)) {
|
||||
continue;
|
||||
}
|
||||
if (item.getType() != material) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isThrottled(TransactionKey transactionKey) {
|
||||
Long eligibleAt = throttleUntil.get(transactionKey);
|
||||
if (eligibleAt == null) {
|
||||
return false;
|
||||
}
|
||||
long now = System.currentTimeMillis();
|
||||
if (eligibleAt <= now) {
|
||||
throttleUntil.remove(transactionKey, eligibleAt);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private final class PendingTransaction implements Runnable {
|
||||
private boolean hasSpaceForMaterial(ItemStack[] state, Material material) {
|
||||
if (state == null || material == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
private final TransactionKey transactionKey;
|
||||
private final Location targetLocation;
|
||||
private ItemStack[] baselineState;
|
||||
private BukkitTask task;
|
||||
private long ticksElapsed;
|
||||
private long ticksSinceLastTrigger;
|
||||
int maxStackSize = material.getMaxStackSize();
|
||||
for (ItemStack item : state) {
|
||||
if (isEmptyItem(item)) {
|
||||
return true;
|
||||
}
|
||||
if (item.getType() == material && item.getAmount() < maxStackSize) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private PendingTransaction(TransactionKey transactionKey, Location targetLocation, ItemStack[] baselineState) {
|
||||
this.transactionKey = transactionKey;
|
||||
this.targetLocation = targetLocation;
|
||||
return false;
|
||||
}
|
||||
|
||||
private int sumMaterialAmount(ItemStack[] state, Material material) {
|
||||
if (state == null || material == null) {
|
||||
return 0;
|
||||
}
|
||||
int total = 0;
|
||||
for (ItemStack item : state) {
|
||||
if (!isEmptyItem(item) && item.getType() == material) {
|
||||
total += item.getAmount();
|
||||
}
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
private int sumAllItems(ItemStack[] state) {
|
||||
if (state == null) {
|
||||
return 0;
|
||||
}
|
||||
int total = 0;
|
||||
for (ItemStack item : state) {
|
||||
if (!isEmptyItem(item)) {
|
||||
total += item.getAmount();
|
||||
}
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
private HeldItemSnapshot getHeldItemSnapshot(CopperGolem copperGolem) {
|
||||
if (copperGolem == null) {
|
||||
return HeldItemSnapshot.EMPTY;
|
||||
}
|
||||
EntityEquipment equipment = copperGolem.getEquipment();
|
||||
if (equipment == null) {
|
||||
return HeldItemSnapshot.EMPTY;
|
||||
}
|
||||
ItemStack mainHand = equipment.getItemInMainHand();
|
||||
if (isEmptyItem(mainHand)) {
|
||||
return HeldItemSnapshot.EMPTY;
|
||||
}
|
||||
return new HeldItemSnapshot(mainHand.getType(), mainHand.getAmount());
|
||||
}
|
||||
|
||||
private static final class OpenInteraction {
|
||||
|
||||
private final TransactionKey containerKey;
|
||||
private final Location location;
|
||||
private final Material containerType;
|
||||
private final ItemStack[] baselineState;
|
||||
private final Material heldMaterial;
|
||||
private final int heldAmount;
|
||||
private final long openedAtMillis;
|
||||
|
||||
private OpenInteraction(TransactionKey containerKey, Location location, Material containerType, ItemStack[] baselineState, Material heldMaterial, int heldAmount, long openedAtMillis) {
|
||||
this.containerKey = containerKey;
|
||||
this.location = location;
|
||||
this.containerType = containerType;
|
||||
this.baselineState = baselineState;
|
||||
this.heldMaterial = heldMaterial;
|
||||
this.heldAmount = heldAmount;
|
||||
this.openedAtMillis = openedAtMillis;
|
||||
}
|
||||
}
|
||||
|
||||
private void start() {
|
||||
task = plugin.getServer().getScheduler().runTaskTimer(plugin, this, INITIAL_DELAY_TICKS, POLL_INTERVAL_TICKS);
|
||||
private static final class RecentEmptyCopperChestSkip {
|
||||
|
||||
private final TransactionKey containerKey;
|
||||
private final long skippedAtMillis;
|
||||
|
||||
private RecentEmptyCopperChestSkip(TransactionKey containerKey, long skippedAtMillis) {
|
||||
this.containerKey = containerKey;
|
||||
this.skippedAtMillis = skippedAtMillis;
|
||||
}
|
||||
}
|
||||
|
||||
private void refresh(ItemStack[] newBaselineState) {
|
||||
ticksSinceLastTrigger = 0L;
|
||||
if (newBaselineState != null) {
|
||||
baselineState = newBaselineState;
|
||||
}
|
||||
}
|
||||
private static final class HeldItemSnapshot {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
long increment = ticksElapsed == 0L ? INITIAL_DELAY_TICKS : POLL_INTERVAL_TICKS;
|
||||
ticksElapsed += increment;
|
||||
ticksSinceLastTrigger += increment;
|
||||
if (ticksSinceLastTrigger > MAX_POLL_DURATION_TICKS) {
|
||||
cancelAndRemove();
|
||||
return;
|
||||
}
|
||||
private static final HeldItemSnapshot EMPTY = new HeldItemSnapshot(null, 0);
|
||||
|
||||
ItemStack[] currentState = captureInventoryState(targetLocation);
|
||||
if (currentState == null) {
|
||||
cancelAndRemove();
|
||||
return;
|
||||
}
|
||||
private final Material material;
|
||||
private final int amount;
|
||||
|
||||
boolean stateChanged = hasInventoryChanged(baselineState, currentState);
|
||||
if (stateChanged) {
|
||||
InventoryChangeListener.inventoryTransaction(USERNAME, targetLocation, baselineState);
|
||||
baselineState = ItemUtils.getContainerState(currentState);
|
||||
// cancelAndRemove();
|
||||
// return;
|
||||
}
|
||||
}
|
||||
|
||||
private void cancel() {
|
||||
if (task != null) {
|
||||
task.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
private void cancelAndRemove() {
|
||||
cancel();
|
||||
pendingTransactions.remove(transactionKey, this);
|
||||
private HeldItemSnapshot(Material material, int amount) {
|
||||
this.material = material;
|
||||
this.amount = amount;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -342,4 +775,32 @@ public final class CopperGolemChestListener implements Listener {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class OpenInteractionIndex {
|
||||
|
||||
private final UUID golemId;
|
||||
private final long openedAtMillis;
|
||||
|
||||
private OpenInteractionIndex(UUID golemId, long openedAtMillis) {
|
||||
this.golemId = golemId;
|
||||
this.openedAtMillis = openedAtMillis;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof OpenInteractionIndex)) {
|
||||
return false;
|
||||
}
|
||||
OpenInteractionIndex other = (OpenInteractionIndex) obj;
|
||||
return openedAtMillis == other.openedAtMillis && golemId.equals(other.golemId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(golemId, openedAtMillis);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,14 +26,23 @@ public class VersionCheckService {
|
||||
public static boolean performVersionChecks() {
|
||||
try {
|
||||
// Check Minecraft version compatibility
|
||||
String[] bukkitVersion = Bukkit.getServer().getBukkitVersion().split("[-.]");
|
||||
if (VersionUtils.newVersion(bukkitVersion[0] + "." + bukkitVersion[1], ConfigHandler.MINECRAFT_VERSION)) {
|
||||
String numericVersion = Bukkit.getServer().getBukkitVersion().split("-", 2)[0];
|
||||
String[] bukkitVersion = numericVersion.split("\\.");
|
||||
if (bukkitVersion.length < 2 || !bukkitVersion[0].matches("\\d+") || !bukkitVersion[1].matches("\\d+")) {
|
||||
Chat.console(Phrase.build(Phrase.VERSION_INCOMPATIBLE, "Minecraft", numericVersion));
|
||||
return false;
|
||||
}
|
||||
|
||||
String minimumVersion = bukkitVersion[0] + "." + bukkitVersion[1];
|
||||
String currentVersion = minimumVersion + (bukkitVersion.length > 2 && bukkitVersion[2].matches("\\d+") ? "." + bukkitVersion[2] : "");
|
||||
|
||||
if (VersionUtils.newVersion(minimumVersion, ConfigHandler.MINECRAFT_VERSION)) {
|
||||
Chat.console(Phrase.build(Phrase.VERSION_REQUIRED, "Minecraft", ConfigHandler.MINECRAFT_VERSION));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (VersionUtils.newVersion(ConfigHandler.LATEST_VERSION, bukkitVersion[0] + "." + bukkitVersion[1] + (bukkitVersion.length > 2 && bukkitVersion[2].matches("\\d+") ? "." + bukkitVersion[2] : "")) && VersionUtils.isCommunityEdition()) {
|
||||
Chat.console(Phrase.build(Phrase.VERSION_INCOMPATIBLE, "Minecraft", bukkitVersion[0] + "." + bukkitVersion[1] + (bukkitVersion.length > 2 ? "." + bukkitVersion[2] : "")));
|
||||
if (VersionUtils.newVersion(ConfigHandler.LATEST_VERSION, currentVersion) && VersionUtils.isCommunityEdition()) {
|
||||
Chat.console(Phrase.build(Phrase.VERSION_INCOMPATIBLE, "Minecraft", currentVersion));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -60,7 +69,9 @@ public class VersionCheckService {
|
||||
}
|
||||
|
||||
// Store Minecraft server version for later use
|
||||
ConfigHandler.SERVER_VERSION = Integer.parseInt(bukkitVersion[1]);
|
||||
int major = Integer.parseInt(bukkitVersion[0]);
|
||||
int minor = Integer.parseInt(bukkitVersion[1]);
|
||||
ConfigHandler.SERVER_VERSION = (major == 1 ? minor : major);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
|
||||
@@ -2,7 +2,6 @@ package net.coreprotect.thread;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
@@ -21,10 +20,10 @@ public class Scheduler {
|
||||
if (regionData instanceof Location) {
|
||||
Location location = (Location) regionData;
|
||||
if (delay == 0) {
|
||||
Bukkit.getServer().getRegionScheduler().run(plugin, location, value -> task.run());
|
||||
plugin.getServer().getRegionScheduler().run(plugin, location, value -> task.run());
|
||||
}
|
||||
else {
|
||||
Bukkit.getServer().getRegionScheduler().runDelayed(plugin, location, value -> task.run(), delay);
|
||||
plugin.getServer().getRegionScheduler().runDelayed(plugin, location, value -> task.run(), delay);
|
||||
}
|
||||
}
|
||||
else if (regionData instanceof Entity) {
|
||||
@@ -38,19 +37,19 @@ public class Scheduler {
|
||||
}
|
||||
else {
|
||||
if (delay == 0) {
|
||||
Bukkit.getServer().getGlobalRegionScheduler().run(plugin, value -> task.run());
|
||||
plugin.getServer().getGlobalRegionScheduler().run(plugin, value -> task.run());
|
||||
}
|
||||
else {
|
||||
Bukkit.getServer().getGlobalRegionScheduler().runDelayed(plugin, value -> task.run(), delay);
|
||||
plugin.getServer().getGlobalRegionScheduler().runDelayed(plugin, value -> task.run(), delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (delay == 0) {
|
||||
Bukkit.getServer().getScheduler().runTask(plugin, task);
|
||||
plugin.getServer().getScheduler().runTask(plugin, task);
|
||||
}
|
||||
else {
|
||||
Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, task, delay);
|
||||
plugin.getServer().getScheduler().runTaskLater(plugin, task, delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -59,36 +58,36 @@ public class Scheduler {
|
||||
if (ConfigHandler.isFolia) {
|
||||
if (regionData instanceof Location) {
|
||||
Location location = (Location) regionData;
|
||||
return Bukkit.getServer().getRegionScheduler().runAtFixedRate(plugin, location, value -> task.run(), delay, period);
|
||||
return plugin.getServer().getRegionScheduler().runAtFixedRate(plugin, location, value -> task.run(), delay, period);
|
||||
}
|
||||
else if (regionData instanceof Entity) {
|
||||
Entity entity = (Entity) regionData;
|
||||
return entity.getScheduler().runAtFixedRate(plugin, value -> task.run(), task, delay, period);
|
||||
}
|
||||
else {
|
||||
return Bukkit.getServer().getGlobalRegionScheduler().runAtFixedRate(plugin, value -> task.run(), delay, period);
|
||||
return plugin.getServer().getGlobalRegionScheduler().runAtFixedRate(plugin, value -> task.run(), delay, period);
|
||||
}
|
||||
}
|
||||
else {
|
||||
return Bukkit.getServer().getScheduler().scheduleSyncRepeatingTask(plugin, task, delay, period);
|
||||
return plugin.getServer().getScheduler().scheduleSyncRepeatingTask(plugin, task, delay, period);
|
||||
}
|
||||
}
|
||||
|
||||
public static void scheduleAsyncDelayedTask(CoreProtect plugin, Runnable task, int delay) {
|
||||
if (ConfigHandler.isFolia) {
|
||||
if (delay == 0) {
|
||||
Bukkit.getServer().getAsyncScheduler().runNow(plugin, value -> task.run());
|
||||
plugin.getServer().getAsyncScheduler().runNow(plugin, value -> task.run());
|
||||
}
|
||||
else {
|
||||
Bukkit.getServer().getAsyncScheduler().runDelayed(plugin, value -> task.run(), (delay * 50L), TimeUnit.MILLISECONDS);
|
||||
plugin.getServer().getAsyncScheduler().runDelayed(plugin, value -> task.run(), (delay * 50L), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (delay == 0) {
|
||||
Bukkit.getServer().getScheduler().runTaskAsynchronously(plugin, task);
|
||||
plugin.getServer().getScheduler().runTaskAsynchronously(plugin, task);
|
||||
}
|
||||
else {
|
||||
Bukkit.getServer().getScheduler().runTaskLaterAsynchronously(plugin, task, delay);
|
||||
plugin.getServer().getScheduler().runTaskLaterAsynchronously(plugin, task, delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user