Compare commits

..

78 Commits

Author SHA1 Message Date
Will FP
a55407be51 Added 1.21.6 support 2025-06-20 10:20:03 +01:00
Will FP
1ffadf8927 Merge pull request #399
Fix FabledSkyBlock errors on new versions of the plugin
2025-06-07 14:58:54 +01:00
xsmeths
12f8117252 Update stripped FSB 4.2.2 lib jar (compile only) 2025-06-07 09:40:53 +01:00
xsmeths
57c365c158 Add stripped FSB 4.2.2 lib jar (compile only) 2025-06-07 08:30:00 +01:00
smeths
7bd6e4be90 Update AntigriefFabledSkyBlock.kt
change import to match newest versions of FSB
2025-06-07 03:49:59 +01:00
Will FP
ba9210e203 Updated cache 2025-06-03 15:17:00 +01:00
Will FP
a2a3ade0ae Cleaned up PacketWindowItems.kt 2025-06-03 15:15:42 +01:00
Will FP
08455b3694 Fixed 1.21.5 support 2025-06-02 15:01:33 +01:00
Will FP
76144013d5 Added 1.21.5 support, bumped version 2025-06-02 13:52:11 +01:00
Will FP
bde705dbf5 Updated to 6.75.2 2025-02-01 16:10:24 +00:00
Will FP
48ca42211d Merge remote-tracking branch 'origin/master'
# Conflicts:
#	eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/integrations/antigrief/AntigriefHuskClaims.kt
2025-02-01 16:10:17 +00:00
Will FP
af5a325527 Fixed Entity lookup throwing errors when passing in 'unknown' 2025-02-01 16:10:07 +00:00
Will FP
eb0d6b2d27 Optimised imports 2025-02-01 16:09:54 +00:00
Will FP
69d21550b3 Merge pull request #386 from Exanthiax/husktowns
Updated HuskClaims/HuskTowns Integration
2025-01-31 11:40:38 +00:00
Will FP
1e3053c0a6 Updated to 6.75.1 2025-01-31 11:13:20 +00:00
Will FP
2d46a6c392 Fixed enchantment registry issues 2025-01-31 11:13:16 +00:00
Exanthiax
0bcd688345 fix oopsie 2025-01-26 18:43:51 +00:00
Exanthiax
25de30142b Updated HuskClaims/HuskTowns Integration
Updated API versions and classes to newest versions.
2025-01-25 21:12:29 +00:00
Will FP
5e4ae1f932 Removed uses of Sound.valueOf 2025-01-21 14:23:45 +00:00
Will FP
e1849fe307 Updated paperweight userdev 2025-01-20 14:05:51 +00:00
Will FP
afd794e443 Fixed version compatibility issues on FastItemStack 2025-01-20 13:38:03 +00:00
Will FP
91eff08c25 Updated gradle 2024-12-23 12:46:32 +00:00
Will FP
8aef6e90b0 Updated to 6.75.0 2024-12-23 11:34:43 +00:00
Will FP
ce334cb3fd Added cross-version sound fix 2024-12-23 11:34:32 +00:00
Will FP
f45b510036 Added 1.21.4 support 2024-12-06 13:55:16 +00:00
Will FP
64672904e5 Revert "Janky fix for third party plugin"
This reverts commit 8ce07d772e.
2024-12-06 13:48:28 +00:00
Will FP
810aa75143 Revert "Janky thing with Items#getCustomItem"
This reverts commit 7502355e15.
2024-12-06 13:48:28 +00:00
Will FP
7502355e15 Janky thing with Items#getCustomItem 2024-12-05 22:23:01 +00:00
Will FP
2a85c5ae6b Updated to 6.74.5 2024-12-05 22:19:58 +00:00
Will FP
8ce07d772e Janky fix for third party plugin 2024-12-05 22:19:50 +00:00
Will FP
22fd12a23f Added .kotlin to gitignore 2024-12-05 17:12:43 +00:00
Will FP
d0b7e1d7a4 Enabled parallel compilation 2024-12-05 17:10:50 +00:00
Will FP
d87fcc96bb Trying async cache for DelegatedExpressionHandler 2024-12-05 16:31:30 +00:00
Will FP
2158be40cd Updated gradle compilation config 2024-12-05 16:29:08 +00:00
Will FP
1e2d87c9fa Updated to Kotlin 2.1.0 2024-12-05 16:22:00 +00:00
Will FP
fa6753c7c1 Updated build configuration 2024-12-05 15:33:23 +00:00
Will FP
be970bd5a0 Disabled java-ci artifact temporarily 2024-11-04 17:25:22 +00:00
Will FP
b47b3834a4 Added 1.21.3 support 2024-11-04 17:23:14 +00:00
Will FP
81de46a1c1 Fixed skull on 1.21 2024-09-22 16:49:08 +01:00
Auxilor
9814e594e8 Updated to 6.74.1 2024-09-02 11:52:55 +01:00
Auxilor
5b9fffe14a Fixed nearest_attackable target goal 2024-09-02 11:52:48 +01:00
Auxilor
4e6960fab5 Updated withRetries for MySQL 2024-08-27 16:03:55 +01:00
Auxilor
c95c1f032d Updated mysql connector 2024-08-26 22:28:54 +01:00
Auxilor
1d9fc3413d PersistentDataHandler#serializeProfile now runs in parallel for all keys 2024-08-26 16:54:08 +01:00
Auxilor
19c099e2ef Changed exception type 2024-08-26 16:29:19 +01:00
Auxilor
91e224020b Changed MySQL column names 2024-08-26 16:27:11 +01:00
Auxilor
19fc168034 Added legacy mongodb handler for migration 2024-08-26 16:16:31 +01:00
Auxilor
a11815af82 Another reserved keyword 2024-08-25 21:57:57 +01:00
Auxilor
2a63c62800 Another reserved keyword 2024-08-25 21:36:41 +01:00
Auxilor
fd806621cf Removed reserved keyword 2024-08-25 20:01:30 +01:00
Auxilor
f1b831bfb4 Updated to 6.74.0 2024-08-25 17:57:10 +01:00
Will FP
aa3dae1d4e Merge pull request #375 from Auxilor/new-data-handlers
Rewrote persistent data system
2024-08-25 18:56:40 +02:00
Auxilor
413ae4e94d Persistent data tweaks and improvements 2024-08-25 17:29:40 +01:00
Auxilor
1a816b0f14 Fixed list write deadlock for mysql 2024-08-25 17:20:15 +01:00
Auxilor
c2935a45dc Tweaked MySQL retries 2024-08-25 17:09:58 +01:00
Auxilor
1338c0fadc Small cleanup 2024-08-25 17:05:15 +01:00
Auxilor
2ccdbe4bc2 Removed bson-kotlinx 2024-08-25 16:58:47 +01:00
Auxilor
3d78bad4b1 Implemented new MongoDB handler 2024-08-25 16:45:38 +01:00
Auxilor
84d481d753 Fixed several bugs with the new data system 2024-08-24 19:39:54 +01:00
Auxilor
e87b7ceb77 Implemented new data backend 2024-08-24 17:46:31 +01:00
Auxilor
fd031e21f5 Added new data handlers 2024-08-24 16:14:20 +01:00
Auxilor
449bcc1ff8 Merge branch 'refs/heads/master' into new-data-handlers 2024-08-24 14:03:17 +01:00
Auxilor
93bcf6ce44 Updated to 6.73.7 2024-08-24 14:01:20 +01:00
Auxilor
afcbcfa527 Fixed PacketAutoRecipe 2024-08-24 14:01:02 +01:00
Auxilor
2ed1f2bb2f Updated to 6.73.6 2024-08-23 15:18:30 +01:00
Auxilor
9cb596e746 Fixed FastItemStack#getEnchants(true) giving incorrect results when the enchantment component was removed from an item 2024-08-23 15:18:21 +01:00
Auxilor
763a3e9a87 Merge branch 'refs/heads/develop' 2024-08-21 18:23:59 +01:00
Auxilor
f01691663a Fixed ProtocolLib 2024-08-21 18:23:33 +01:00
Auxilor
6c91b4e41f Updated to 6.73.5 2024-08-21 18:08:50 +01:00
Auxilor
06fdb25925 Patch for ArgParserEnchantment 2024-08-21 18:08:39 +01:00
Auxilor
f1b71c2ac9 Started work on new persistent data 2024-08-21 18:07:09 +01:00
Will FP
665857a00f Merge pull request #371 from MCCasper/master
Update config.yml
2024-08-16 00:14:13 +02:00
Nikolai Connolly
f18016b2de Update config.yml 2024-08-15 14:53:23 -05:00
Auxilor
88633f94cb Updated to 6.73.4 2024-08-12 21:48:27 +01:00
Auxilor
903084e574 Added 1.21.1 support 2024-08-12 21:48:04 +01:00
Auxilor
dab0ce2ed2 Updated to 6.73.3 2024-07-30 15:58:04 +01:00
Will FP
f01e18950c Merge pull request #365 from Exanthiax/develop 2024-07-24 10:44:03 +01:00
Exanthiax
d4a6bf105b Update ParticleFactoryRGB.kt 2024-07-24 01:55:20 +01:00
182 changed files with 5545 additions and 1764 deletions

View File

@@ -21,7 +21,7 @@ jobs:
java-version: 21 java-version: 21
- name: Setup build cache - name: Setup build cache
uses: actions/cache@v2.1.6 uses: actions/cache@v4
with: with:
path: ~/.gradle/caches path: ~/.gradle/caches
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle.kts') }} key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle.kts') }}
@@ -30,7 +30,7 @@ jobs:
- run: ./gradlew build --full-stacktrace - run: ./gradlew build --full-stacktrace
- uses: actions/upload-artifact@v2 # - uses: actions/upload-artifact@v2
with: # with:
name: eco-dev-${{ steps.vars.outputs.sha_short }} # name: eco-dev-${{ steps.vars.outputs.sha_short }}
path: build/libs # path: build/libs

View File

@@ -19,7 +19,7 @@ jobs:
java-version: 21 java-version: 21
- name: Setup build cache - name: Setup build cache
uses: actions/cache@v2.1.6 uses: actions/cache@v4
with: with:
path: ~/.gradle/caches path: ~/.gradle/caches
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle.kts') }} key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle.kts') }}

3
.gitignore vendored
View File

@@ -22,3 +22,6 @@ gradle-app.setting
# Mac OSX # Mac OSX
.DS_Store .DS_Store
# Kotlin
.kotlin

View File

@@ -1,20 +1,21 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
buildscript { buildscript {
repositories { repositories {
mavenCentral() mavenCentral()
} }
dependencies { dependencies {
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.21") classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:2.1.0")
} }
} }
plugins { plugins {
id("java-library") id("java-library")
id("io.github.goooler.shadow") version "8.1.7" id("com.gradleup.shadow") version "8.3.5"
id("maven-publish") id("maven-publish")
id("java") id("java")
kotlin("jvm") version "1.9.21" kotlin("jvm") version "2.1.0"
kotlin("plugin.serialization") version "1.9.21"
} }
dependencies { dependencies {
@@ -33,23 +34,31 @@ dependencies {
implementation(project(path = ":eco-core:core-nms:v1_20_R2", configuration = "reobf")) implementation(project(path = ":eco-core:core-nms:v1_20_R2", configuration = "reobf"))
implementation(project(path = ":eco-core:core-nms:v1_20_R3", configuration = "reobf")) implementation(project(path = ":eco-core:core-nms:v1_20_R3", configuration = "reobf"))
implementation(project(path = ":eco-core:core-nms:v1_21", configuration = "reobf")) implementation(project(path = ":eco-core:core-nms:v1_21", configuration = "reobf"))
implementation(project(path = ":eco-core:core-nms:v1_21_3", configuration = "reobf"))
implementation(project(path = ":eco-core:core-nms:v1_21_4", configuration = "reobf"))
implementation(project(path = ":eco-core:core-nms:v1_21_5", configuration = "reobf"))
implementation(project(path = ":eco-core:core-nms:v1_21_6", configuration = "reobf"))
} }
allprojects { allprojects {
apply(plugin = "java") apply(plugin = "java")
apply(plugin = "java-library") apply(plugin = "java-library")
apply(plugin = "maven-publish") apply(plugin = "maven-publish")
apply(plugin = "io.github.goooler.shadow") apply(plugin = "com.gradleup.shadow")
apply(plugin = "kotlin") apply(plugin = "kotlin")
apply(plugin = "org.jetbrains.kotlin.plugin.serialization")
repositories { repositories {
mavenCentral() mavenCentral()
maven("https://repo.auxilor.io/repository/maven-public/") maven("https://repo.auxilor.io/repository/maven-public/")
maven("https://jitpack.io") { maven("https://jitpack.io") {
content { includeGroupByRegex("com\\.github\\..*") } content { includeGroupByRegex("com\\.github\\..*") }
} }
// Paper
maven("https://repo.papermc.io/repository/maven-public/")
// SuperiorSkyblock2 // SuperiorSkyblock2
maven("https://repo.bg-software.com/repository/api/") maven("https://repo.bg-software.com/repository/api/")
@@ -63,7 +72,7 @@ allprojects {
maven("https://repo.extendedclip.com/content/repositories/placeholderapi/") maven("https://repo.extendedclip.com/content/repositories/placeholderapi/")
// ProtocolLib // ProtocolLib
//maven("https://repo.dmulloy2.net/nexus/repository/public/") maven("https://repo.dmulloy2.net/nexus/repository/public/")
// WorldGuard // WorldGuard
maven("https://maven.enginehub.org/repo/") maven("https://maven.enginehub.org/repo/")
@@ -104,12 +113,12 @@ allprojects {
dependencies { dependencies {
// Kotlin // Kotlin
implementation(kotlin("stdlib", version = "1.9.21")) implementation(kotlin("stdlib", version = "2.1.0"))
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")
// Included in spigot jar, no need to move to implementation // Included in spigot jar, no need to move to implementation
compileOnly("org.jetbrains:annotations:23.0.0") compileOnly("org.jetbrains:annotations:23.0.0")
compileOnly("com.google.guava:guava:31.1-jre") compileOnly("com.google.guava:guava:32.0.0-jre")
// Test // Test
testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.2") testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.2")
@@ -153,8 +162,8 @@ allprojects {
} }
compileKotlin { compileKotlin {
kotlinOptions { compilerOptions {
jvmTarget = "17" jvmTarget.set(JvmTarget.JVM_17)
} }
} }
@@ -177,14 +186,14 @@ allprojects {
} }
withType<JavaCompile>().configureEach { withType<JavaCompile>().configureEach {
options.release = 17 options.release.set(17)
} }
} }
java { java {
withSourcesJar() withSourcesJar()
toolchain { toolchain {
languageVersion = JavaLanguageVersion.of(21) languageVersion.set(JavaLanguageVersion.of(21))
} }
} }
} }
@@ -212,7 +221,6 @@ tasks {
//relocate("com.mysql", "com.willfp.eco.libs.mysql") //relocate("com.mysql", "com.willfp.eco.libs.mysql")
relocate("com.mongodb", "com.willfp.eco.libs.mongodb") relocate("com.mongodb", "com.willfp.eco.libs.mongodb")
relocate("org.bson", "com.willfp.eco.libs.bson") relocate("org.bson", "com.willfp.eco.libs.bson")
relocate("org.litote", "com.willfp.eco.libs.litote")
relocate("org.reactivestreams", "com.willfp.eco.libs.reactivestreams") relocate("org.reactivestreams", "com.willfp.eco.libs.reactivestreams")
relocate("reactor.", "com.willfp.eco.libs.reactor.") // Dot in name to be safe relocate("reactor.", "com.willfp.eco.libs.reactor.") // Dot in name to be safe
relocate("com.moandjiezana.toml", "com.willfp.eco.libs.toml") relocate("com.moandjiezana.toml", "com.willfp.eco.libs.toml")

View File

@@ -37,8 +37,6 @@ import org.jetbrains.annotations.Nullable;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;

View File

@@ -38,15 +38,23 @@ public class Prerequisite {
); );
/** /**
* Requires the server to be running 1.21. * Requires the server to be running at least 1.21.3.
*/
public static final Prerequisite HAS_1_21_3 = new Prerequisite(
() -> ProxyConstants.NMS_VERSION.contains("1_21_3"),
"Requires server to be running 1.21.3+"
);
/**
* Requires the server to be running at least 1.21.
*/ */
public static final Prerequisite HAS_1_21 = new Prerequisite( public static final Prerequisite HAS_1_21 = new Prerequisite(
() -> ProxyConstants.NMS_VERSION.contains("1_21"), () -> ProxyConstants.NMS_VERSION.contains("1_21") || HAS_1_21_3.isMet(),
"Requires server to be running 1.21+" "Requires server to be running 1.21+"
); );
/** /**
* Requires the server to be running 1.20.5. * Requires the server to be running at least 1.20.5.
*/ */
public static final Prerequisite HAS_1_20_5 = new Prerequisite( public static final Prerequisite HAS_1_20_5 = new Prerequisite(
() -> (ProxyConstants.NMS_VERSION.contains("1_20_") && !ProxyConstants.NMS_VERSION.contains("R")) () -> (ProxyConstants.NMS_VERSION.contains("1_20_") && !ProxyConstants.NMS_VERSION.contains("R"))
@@ -55,7 +63,7 @@ public class Prerequisite {
); );
/** /**
* Requires the server to be running 1.20.3. * Requires the server to be running at least 1.20.3.
*/ */
public static final Prerequisite HAS_1_20_3 = new Prerequisite( public static final Prerequisite HAS_1_20_3 = new Prerequisite(
() -> ProxyConstants.NMS_VERSION.contains("20_R3") || HAS_1_20_5.isMet(), () -> ProxyConstants.NMS_VERSION.contains("20_R3") || HAS_1_20_5.isMet(),
@@ -63,7 +71,7 @@ public class Prerequisite {
); );
/** /**
* Requires the server to be running 1.20. * Requires the server to be running at least 1.20.
*/ */
public static final Prerequisite HAS_1_20 = new Prerequisite( public static final Prerequisite HAS_1_20 = new Prerequisite(
() -> ProxyConstants.NMS_VERSION.contains("20") || HAS_1_20_3.isMet(), () -> ProxyConstants.NMS_VERSION.contains("20") || HAS_1_20_3.isMet(),
@@ -71,7 +79,7 @@ public class Prerequisite {
); );
/** /**
* Requires the server to be running 1.19.4. * Requires the server to be running at least 1.19.4.
*/ */
public static final Prerequisite HAS_1_19_4 = new Prerequisite( public static final Prerequisite HAS_1_19_4 = new Prerequisite(
() -> ProxyConstants.NMS_VERSION.contains("19_R3") || HAS_1_20.isMet(), () -> ProxyConstants.NMS_VERSION.contains("19_R3") || HAS_1_20.isMet(),
@@ -79,7 +87,7 @@ public class Prerequisite {
); );
/** /**
* Requires the server to be running 1.19. * Requires the server to be running at least 1.19.
*/ */
public static final Prerequisite HAS_1_19 = new Prerequisite( public static final Prerequisite HAS_1_19 = new Prerequisite(
() -> ProxyConstants.NMS_VERSION.contains("19") || HAS_1_20.isMet(), () -> ProxyConstants.NMS_VERSION.contains("19") || HAS_1_20.isMet(),
@@ -87,7 +95,7 @@ public class Prerequisite {
); );
/** /**
* Requires the server to be running 1.18. * Requires the server to be running at least 1.18.
*/ */
public static final Prerequisite HAS_1_18 = new Prerequisite( public static final Prerequisite HAS_1_18 = new Prerequisite(
() -> ProxyConstants.NMS_VERSION.contains("18") || HAS_1_19.isMet(), () -> ProxyConstants.NMS_VERSION.contains("18") || HAS_1_19.isMet(),

View File

@@ -1,149 +0,0 @@
package com.willfp.eco.core.blocks;
import com.willfp.eco.core.blocks.impl.EmptyTestableBlock;
import com.willfp.eco.core.blocks.impl.MaterialTestableBlock;
import com.willfp.eco.core.blocks.impl.UnrestrictedMaterialTestableBlock;
import com.willfp.eco.core.blocks.provider.BlockProvider;
import com.willfp.eco.util.NamespacedKeyUtils;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.block.Block;
import org.jetbrains.annotations.NotNull;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* Class to manage all custom and vanilla blocks.
*/
public final class Blocks {
/**
* All entities.
*/
private static final Map<NamespacedKey, TestableBlock> REGISTRY = new ConcurrentHashMap<>();
/**
* All block providers.
*/
private static final Map<String, BlockProvider> PROVIDERS = new ConcurrentHashMap<>();
/**
* The lookup handler.
*/
private static final BlocksLookupHandler BLOCKS_LOOKUP_HANDLER = new BlocksLookupHandler(Blocks::doParse);
/**
* Register a new custom block.
*
* @param key The key of the block.
* @param block The block.
*/
public static void registerCustomBlock(@NotNull final NamespacedKey key,
@NotNull final TestableBlock block) {
REGISTRY.put(key, block);
}
/**
* Register a new block provider.
*
* @param provider The provider.
*/
public static void registerBlockProvider(@NotNull final BlockProvider provider) {
PROVIDERS.put(provider.getNamespace(), provider);
}
/**
* Remove a block.
*
* @param key The key of the block.
*/
public static void removeCustomBlock(@NotNull final NamespacedKey key) {
REGISTRY.remove(key);
}
/**
* This is the backbone of the eco block system.
* <p>
* You can look up a TestableBlock for any material or custom block,
* and it will return it.
* <p>
* If you want to get a Block instance from this, then just call
* {@link TestableBlock#place(Location)}.
*
* @param key The lookup string.
* @return The testable block, or an empty testable block if not found.
*/
@NotNull
public static TestableBlock lookup(@NotNull final String key) {
return BLOCKS_LOOKUP_HANDLER.parseKey(key);
}
@NotNull
private static TestableBlock doParse(@NotNull final String[] args) {
if (args.length == 0) {
return new EmptyTestableBlock();
}
String[] split = args[0].toLowerCase().split(":");
if (split.length == 1) {
if (args[0].startsWith("*")) {
Material type = Material.getMaterial(args[0].substring(1));
return (type == null) ? new EmptyTestableBlock() : new UnrestrictedMaterialTestableBlock(type);
} else {
Material type = Material.getMaterial(args[0].toUpperCase());
return (type == null) ? new EmptyTestableBlock() : new MaterialTestableBlock(type);
}
}
NamespacedKey namespacedKey = NamespacedKeyUtils.create(split[0], split[1]);
TestableBlock block = REGISTRY.get(namespacedKey);
if (block != null) {
return block;
}
BlockProvider provider = PROVIDERS.get(split[0]);
if (provider == null) {
return new EmptyTestableBlock();
}
block = provider.provideForKey(split[1]);
if (block == null) {
return new EmptyTestableBlock();
}
registerCustomBlock(namespacedKey, block);
return block;
}
/**
* Get if block is a custom block.
*
* @param block The block to check.
* @return If is custom.
*/
public static boolean isCustomBlock(@NotNull final Block block) {
for (TestableBlock testable : REGISTRY.values()) {
if (testable.matches(block)) {
return true;
}
}
return false;
}
/**
* Get all registered custom blocks.
*
* @return A set of all blocks.
*/
public static Set<TestableBlock> getCustomBlocks() {
return new HashSet<>(REGISTRY.values());
}
private Blocks() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
}
}

View File

@@ -1,48 +0,0 @@
package com.willfp.eco.core.blocks;
import com.willfp.eco.core.blocks.impl.EmptyTestableBlock;
import com.willfp.eco.core.blocks.impl.GroupedTestableBlocks;
import com.willfp.eco.core.lookup.LookupHandler;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.function.Function;
/**
* Handle block lookup strings.
*/
public class BlocksLookupHandler implements LookupHandler<TestableBlock> {
/**
* The parser.
*/
private final Function<String[], @NotNull TestableBlock> parser;
/**
* Create new lookup handler.
*
* @param parser The parser.
*/
public BlocksLookupHandler(@NotNull final Function<String[], @NotNull TestableBlock> parser) {
this.parser = parser;
}
@Override
public @NotNull TestableBlock parse(@NotNull final String[] args) {
return parser.apply(args);
}
@Override
public boolean validate(@NotNull final TestableBlock object) {
return !(object instanceof EmptyTestableBlock);
}
@Override
public @NotNull TestableBlock getFailsafe() {
return new EmptyTestableBlock();
}
@Override
public @NotNull TestableBlock join(@NotNull final Collection<TestableBlock> options) {
return new GroupedTestableBlocks(options);
}
}

View File

@@ -1,84 +0,0 @@
package com.willfp.eco.core.blocks;
import org.apache.commons.lang.Validate;
import org.bukkit.Location;
import org.bukkit.NamespacedKey;
import org.bukkit.block.Block;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.function.Function;
import java.util.function.Predicate;
/**
* A custom block has 3 components.
*
* <ul>
* <li>The key to identify it</li>
* <li>The test to check if any block is this custom block</li>
* <li>The supplier to spawn the custom {@link Block}</li>
* </ul>
*/
public class CustomBlock implements TestableBlock {
/**
* The key.
*/
private final NamespacedKey key;
/**
* The test for block to pass.
*/
private final Predicate<@NotNull Block> test;
/**
* The provider to spawn the block.
*/
private final Function<Location, Block> provider;
/**
* Create a new custom block.
*
* @param key The block key.
* @param test The test.
* @param provider The provider to spawn the block.
*/
public CustomBlock(@NotNull final NamespacedKey key,
@NotNull final Predicate<@NotNull Block> test,
@NotNull final Function<Location, Block> provider) {
this.key = key;
this.test = test;
this.provider = provider;
}
@Override
public boolean matches(@Nullable final Block other) {
if (other == null) {
return false;
}
return test.test(other);
}
@Override
public @NotNull Block place(@NotNull final Location location) {
Validate.notNull(location.getWorld());
return provider.apply(location);
}
/**
* Register the block.
*/
public void register() {
Blocks.registerCustomBlock(this.getKey(), this);
}
/**
* Get the key.
*
* @return The key.
*/
public NamespacedKey getKey() {
return this.key;
}
}

View File

@@ -1,31 +0,0 @@
package com.willfp.eco.core.blocks;
import com.willfp.eco.core.lookup.Testable;
import org.bukkit.Location;
import org.bukkit.block.Block;
import org.bukkit.entity.Entity;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* A block with a test.
*/
public interface TestableBlock extends Testable<Block> {
/**
* If a Block matches the test.
*
* @param other The other block.
* @return If the block matches.
*/
@Override
boolean matches(@Nullable Block other);
/**
* Place the block.
*
* @param location The location.
* @return The block.
*/
@NotNull
Block place(@NotNull Location location);
}

View File

@@ -1,29 +0,0 @@
package com.willfp.eco.core.blocks.impl;
import com.willfp.eco.core.blocks.TestableBlock;
import org.bukkit.Location;
import org.bukkit.block.Block;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Empty block.
*/
public class EmptyTestableBlock implements TestableBlock {
/**
* Create a new empty testable block.
*/
public EmptyTestableBlock() {
}
@Override
public boolean matches(@Nullable final Block other) {
return false;
}
@Override
public @NotNull Block place(@NotNull final Location location) {
return location.getBlock();
}
}

View File

@@ -1,60 +0,0 @@
package com.willfp.eco.core.blocks.impl;
import com.willfp.eco.core.blocks.TestableBlock;
import com.willfp.eco.util.NumberUtils;
import org.apache.commons.lang.Validate;
import org.bukkit.Location;
import org.bukkit.block.Block;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collection;
/**
* A group of testable blocks.
*/
public class GroupedTestableBlocks implements TestableBlock {
/**
* The children.
*/
private final Collection<TestableBlock> children;
/**
* Create a new group of testable blocks.
*
* @param children The children.
*/
public GroupedTestableBlocks(@NotNull final Collection<TestableBlock> children) {
Validate.isTrue(!children.isEmpty(), "Group must have at least one child!");
this.children = children;
}
@Override
public boolean matches(@Nullable final Block other) {
for (TestableBlock child : children) {
if (child.matches(other)) {
return true;
}
}
return false;
}
@Override
public @NotNull Block place(@NotNull final Location location) {
return new ArrayList<>(children)
.get(NumberUtils.randInt(0, children.size() - 1))
.place(location);
}
/**
* Get the children.
*
* @return The children.
*/
public Collection<TestableBlock> getChildren() {
return new ArrayList<>(children);
}
}

View File

@@ -1,59 +0,0 @@
package com.willfp.eco.core.blocks.impl;
import com.willfp.eco.core.blocks.Blocks;
import com.willfp.eco.core.blocks.TestableBlock;
import org.apache.commons.lang.Validate;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* A testable block for vanilla materials.
*/
public class MaterialTestableBlock implements TestableBlock {
/**
* The block type.
*/
private final Material material;
/**
* Create a new unrestricted material testable block.
*
* @param material The material.
*/
public MaterialTestableBlock(@NotNull final Material material) {
this.material = material;
}
@Override
public boolean matches(@Nullable final Block block) {
boolean simpleMatches = block != null && block.getType() == material;
if (!simpleMatches) {
return false;
}
return !Blocks.isCustomBlock(block);
}
@Override
public @NotNull Block place(@NotNull Location location) {
Validate.notNull(location.getWorld());
Block block = location.getWorld().getBlockAt(location);
block.setType(material);
return block;
}
/**
* Get the material.
*
* @return The material.
*/
public Material getMaterial() {
return this.material;
}
}

View File

@@ -1,52 +0,0 @@
package com.willfp.eco.core.blocks.impl;
import com.willfp.eco.core.blocks.TestableBlock;
import org.apache.commons.lang.Validate;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* A testable block for materials regardless of data.
*/
public class UnrestrictedMaterialTestableBlock implements TestableBlock {
/**
* The block type.
*/
private final Material material;
/**
* Create a new unrestricted material testable block.
*
* @param material The material.
*/
public UnrestrictedMaterialTestableBlock(@NotNull final Material material) {
this.material = material;
}
@Override
public boolean matches(@Nullable final Block other) {
return other != null && other.getType() == material;
}
@Override
public @NotNull Block place(@NotNull Location location) {
Validate.notNull(location.getWorld());
Block block = location.getWorld().getBlockAt(location);
block.setType(material);
return block;
}
/**
* Get the material.
*
* @return The material.
*/
public Material getMaterial() {
return this.material;
}
}

View File

@@ -1,49 +0,0 @@
package com.willfp.eco.core.blocks.provider;
import com.willfp.eco.core.blocks.TestableBlock;
import com.willfp.eco.core.registry.Registry;
import org.apache.commons.lang.Validate;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Block providers are call-site registrations for blocks. In other words,
* they only register their blocks when a request is made. This is marginally
* slower, however it is required for certain plugins, and fixes bugs related to
* loading orders.
*
* @see TestableBlock
*/
public abstract class BlockProvider {
/**
* The namespace.
*/
private final String namespace;
/**
* Create a new BlockProvider for a specific namespace.
*
* @param namespace The namespace.
*/
protected BlockProvider(@NotNull final String namespace) {
this.namespace = namespace;
}
/**
* Provide a TestableBlock for a given key.
*
* @param key The block ID.
* @return The TestableBlock, or null if not found.
*/
@Nullable
public abstract TestableBlock provideForKey(@NotNull String key);
/**
* Get the namespace.
*
* @return The namespace.
*/
public String getNamespace() {
return this.namespace;
}
}

View File

@@ -0,0 +1,44 @@
package com.willfp.eco.core.data.handlers;
import com.willfp.eco.core.data.keys.PersistentDataKey;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.UUID;
/**
* Handles data read/write for a {@link com.willfp.eco.core.data.keys.PersistentDataKeyType} for a specific
* data handler.
*
* @param <T> The type of data.
*/
public abstract class DataTypeSerializer<T> {
/**
* Create a new data type serializer.
*/
protected DataTypeSerializer() {
}
/**
* Read a value.
*
* @param uuid The uuid.
* @param key The key.
* @return The value.
*/
@Nullable
public abstract T readAsync(@NotNull final UUID uuid,
@NotNull final PersistentDataKey<T> key);
/**
* Write a value.
*
* @param uuid The uuid.
* @param key The key.
* @param value The value.
*/
public abstract void writeAsync(@NotNull final UUID uuid,
@NotNull final PersistentDataKey<T> key,
@NotNull final T value);
}

View File

@@ -0,0 +1,179 @@
package com.willfp.eco.core.data.handlers;
import com.willfp.eco.core.data.keys.PersistentDataKey;
import com.willfp.eco.core.registry.Registrable;
import com.willfp.eco.core.tuples.Pair;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
* Handles persistent data.
*/
public abstract class PersistentDataHandler implements Registrable {
/**
* The id.
*/
private final String id;
/**
* The executor.
*/
private final ExecutorService executor = Executors.newCachedThreadPool();
/**
* Create a new persistent data handler.
*
* @param id The id.
*/
protected PersistentDataHandler(@NotNull final String id) {
this.id = id;
}
/**
* Get all UUIDs with saved data.
* <p>
* This is a blocking operation.
*
* @return All saved UUIDs.
*/
public abstract Set<UUID> getSavedUUIDs();
/**
* Save to disk.
* <p>
* If write commits to disk, this method does not need to be overridden.
* <p>
* This method is called asynchronously.
*/
protected void doSave() {
// Save to disk
}
/**
* If the handler should autosave.
*
* @return If the handler should autosave.
*/
public boolean shouldAutosave() {
return true;
}
/**
* Save the data.
*/
public final void save() {
executor.submit(this::doSave);
}
/**
* Read a key from persistent data.
*
* @param uuid The uuid.
* @param key The key.
* @param <T> The type of the key.
* @return The value, or null if not found.
*/
@Nullable
public final <T> T read(@NotNull final UUID uuid,
@NotNull final PersistentDataKey<T> key) {
DataTypeSerializer<T> serializer = key.getType().getSerializer(this);
Future<T> future = executor.submit(() -> serializer.readAsync(uuid, key));
try {
return future.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
return null;
}
}
/**
* Write a key to persistent data.
*
* @param uuid The uuid.
* @param key The key.
* @param value The value.
* @param <T> The type of the key.
*/
public final <T> void write(@NotNull final UUID uuid,
@NotNull final PersistentDataKey<T> key,
@NotNull final T value) {
DataTypeSerializer<T> serializer = key.getType().getSerializer(this);
executor.submit(() -> serializer.writeAsync(uuid, key, value));
}
/**
* Serialize profile.
*
* @param uuid The uuid to serialize.
* @param keys The keys to serialize.
* @return The serialized data.
*/
@NotNull
public final SerializedProfile serializeProfile(@NotNull final UUID uuid,
@NotNull final Set<PersistentDataKey<?>> keys) {
Map<PersistentDataKey<?>, CompletableFuture<Object>> futures = keys.stream()
.collect(Collectors.toMap(
key -> key,
key -> CompletableFuture.supplyAsync(() -> read(uuid, key), executor)
));
Map<PersistentDataKey<?>, Object> data = futures.entrySet().stream()
.map(entry -> new Pair<PersistentDataKey<?>, Object>(entry.getKey(), entry.getValue().join()))
.filter(entry -> entry.getSecond() != null)
.collect(Collectors.toMap(Pair::getFirst, Pair::getSecond));
return new SerializedProfile(uuid, data);
}
/**`
* Load profile data.
*
* @param profile The profile.
*/
@SuppressWarnings("unchecked")
public final void loadSerializedProfile(@NotNull final SerializedProfile profile) {
for (Map.Entry<PersistentDataKey<?>, Object> entry : profile.data().entrySet()) {
PersistentDataKey<?> key = entry.getKey();
Object value = entry.getValue();
// This cast is safe because the data is serialized
write(profile.uuid(), (PersistentDataKey<? super Object>) key, value);
}
}
/**
* Save and shutdown the handler.
*
* @throws InterruptedException If the writes could not be awaited.
*/
public final void shutdown() throws InterruptedException {
doSave();
if (executor.isShutdown()) {
return;
}
executor.shutdown();
while (!executor.awaitTermination(2, TimeUnit.MINUTES)) {
// Wait
}
}
@Override
@NotNull
public final String getID() {
return id;
}
}

View File

@@ -0,0 +1,20 @@
package com.willfp.eco.core.data.handlers;
import com.willfp.eco.core.data.keys.PersistentDataKey;
import org.jetbrains.annotations.NotNull;
import java.util.Map;
import java.util.UUID;
/**
* Serialized profile.
*
* @param uuid The uuid.
* @param data The data.
*/
public record SerializedProfile(
@NotNull UUID uuid,
@NotNull Map<PersistentDataKey<?>, Object> data
) {
}

View File

@@ -34,6 +34,19 @@ public final class PersistentDataKey<T> {
*/ */
private final boolean isLocal; private final boolean isLocal;
/**
* Create a new Persistent Data Key.
*
* @param key The key.
* @param type The data type.
* @param defaultValue The default value.
*/
public PersistentDataKey(@NotNull final NamespacedKey key,
@NotNull final PersistentDataKeyType<T> type,
@NotNull final T defaultValue) {
this(key, type, defaultValue, false);
}
/** /**
* Create a new Persistent Data Key. * Create a new Persistent Data Key.
* *
@@ -54,24 +67,6 @@ public final class PersistentDataKey<T> {
Eco.get().registerPersistentKey(this); Eco.get().registerPersistentKey(this);
} }
/**
* Create a new Persistent Data Key.
*
* @param key The key.
* @param type The data type.
* @param defaultValue The default value.
*/
public PersistentDataKey(@NotNull final NamespacedKey key,
@NotNull final PersistentDataKeyType<T> type,
@NotNull final T defaultValue) {
this.key = key;
this.defaultValue = defaultValue;
this.type = type;
this.isLocal = false;
Eco.get().registerPersistentKey(this);
}
@Override @Override
public String toString() { public String toString() {
return "PersistentDataKey{" return "PersistentDataKey{"

View File

@@ -1,12 +1,17 @@
package com.willfp.eco.core.data.keys; package com.willfp.eco.core.data.keys;
import com.willfp.eco.core.config.interfaces.Config; import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.data.handlers.DataTypeSerializer;
import com.willfp.eco.core.data.handlers.PersistentDataHandler;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects; import java.util.Objects;
/** /**
@@ -61,13 +66,9 @@ public final class PersistentDataKeyType<T> {
private final String name; private final String name;
/** /**
* Get the name of the key type. * The serializers for this key type.
*
* @return The name.
*/ */
public String name() { private final Map<PersistentDataHandler, DataTypeSerializer<T>> serializers = new HashMap<>();
return name;
}
/** /**
* Create new PersistentDataKeyType. * Create new PersistentDataKeyType.
@@ -80,6 +81,44 @@ public final class PersistentDataKeyType<T> {
this.name = name; this.name = name;
} }
/**
* Get the name of the key type.
*
* @return The name.
*/
@NotNull
public String name() {
return name;
}
/**
* Register a serializer for this key type.
*
* @param handler The handler.
* @param serializer The serializer.
*/
public void registerSerializer(@NotNull final PersistentDataHandler handler,
@NotNull final DataTypeSerializer<T> serializer) {
this.serializers.put(handler, serializer);
}
/**
* Get the serializer for a handler.
*
* @param handler The handler.
* @return The serializer.
*/
@NotNull
public DataTypeSerializer<T> getSerializer(@NotNull final PersistentDataHandler handler) {
DataTypeSerializer<T> serializer = this.serializers.get(handler);
if (serializer == null) {
throw new NoSuchElementException("No serializer for handler: " + handler);
}
return serializer;
}
@Override @Override
public boolean equals(@Nullable final Object that) { public boolean equals(@Nullable final Object that) {
if (this == that) { if (this == that) {

View File

@@ -111,6 +111,11 @@ public final class Entities {
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
return new EmptyTestableEntity(); return new EmptyTestableEntity();
} }
if (type.getEntityClass() == null) {
return new EmptyTestableEntity();
}
entity = new SimpleTestableEntity(type); entity = new SimpleTestableEntity(type);
} else { } else {
String namespace = split[0]; String namespace = split[0];

View File

@@ -6,6 +6,7 @@ import com.willfp.eco.core.entities.TestableEntity;
import com.willfp.eco.core.entities.ai.EntityGoal; import com.willfp.eco.core.entities.ai.EntityGoal;
import com.willfp.eco.core.items.Items; import com.willfp.eco.core.items.Items;
import com.willfp.eco.core.serialization.KeyedDeserializer; import com.willfp.eco.core.serialization.KeyedDeserializer;
import com.willfp.eco.util.SoundUtils;
import org.bukkit.NamespacedKey; import org.bukkit.NamespacedKey;
import org.bukkit.Sound; import org.bukkit.Sound;
import org.bukkit.entity.LivingEntity; import org.bukkit.entity.LivingEntity;
@@ -50,9 +51,15 @@ public record EntityGoalUseItem(
TestableEntity filter = Entities.lookup(config.getString("condition")); TestableEntity filter = Entities.lookup(config.getString("condition"));
Sound sound = SoundUtils.getSound(config.getString("sound"));
if (sound == null) {
return null;
}
return new EntityGoalUseItem( return new EntityGoalUseItem(
Items.lookup(config.getString("item")).getItem(), Items.lookup(config.getString("item")).getItem(),
Sound.valueOf(config.getString("sound").toUpperCase()), sound,
filter::matches filter::matches
); );
} }

View File

@@ -11,19 +11,21 @@ import org.bukkit.entity.Raider;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.Set;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Collectors;
/** /**
* Allows an entity to attack the closest target within a given subset of specific target types. * Allows an entity to attack the closest target within a given subset of specific target types.
* *
* @param target The type of entities to attack. * @param targets The type of entities to attack.
* @param checkVisibility If visibility should be checked. * @param checkVisibility If visibility should be checked.
* @param checkCanNavigate If navigation should be checked. * @param checkCanNavigate If navigation should be checked.
* @param reciprocalChance 1 in reciprocalChance chance of not activating on any tick. * @param reciprocalChance 1 in reciprocalChance chance of not activating on any tick.
* @param targetFilter The filter for targets to match. * @param targetFilter The filter for targets to match.
*/ */
public record TargetGoalNearestAttackable( public record TargetGoalNearestAttackable(
@NotNull TestableEntity target, @NotNull Set<TestableEntity> targets,
boolean checkVisibility, boolean checkVisibility,
boolean checkCanNavigate, boolean checkCanNavigate,
int reciprocalChance, int reciprocalChance,
@@ -32,16 +34,16 @@ public record TargetGoalNearestAttackable(
/** /**
* Create a new target goal. * Create a new target goal.
* *
* @param target The type of entities to attack. * @param targets The type of entities to attack.
* @param checkVisibility If visibility should be checked. * @param checkVisibility If visibility should be checked.
* @param checkCanNavigate If navigation should be checked. * @param checkCanNavigate If navigation should be checked.
* @param reciprocalChance 1 in reciprocalChance chance of not activating on any tick. * @param reciprocalChance 1 in reciprocalChance chance of not activating on any tick.
*/ */
public TargetGoalNearestAttackable(@NotNull final TestableEntity target, public TargetGoalNearestAttackable(@NotNull final Set<TestableEntity> targets,
final boolean checkVisibility, final boolean checkVisibility,
final boolean checkCanNavigate, final boolean checkCanNavigate,
final int reciprocalChance) { final int reciprocalChance) {
this(target, checkVisibility, checkCanNavigate, reciprocalChance, it -> true); this(targets, checkVisibility, checkCanNavigate, reciprocalChance, it -> true);
} }
/** /**
@@ -65,11 +67,15 @@ public record TargetGoalNearestAttackable(
return null; return null;
} }
Set<TestableEntity> targets = config.getStrings("target").stream()
.map(Entities::lookup)
.collect(Collectors.toSet());
if (config.has("targetFilter")) { if (config.has("targetFilter")) {
TestableEntity filter = Entities.lookup(config.getString("targetFilter")); TestableEntity filter = Entities.lookup(config.getString("targetFilter"));
return new TargetGoalNearestAttackable( return new TargetGoalNearestAttackable(
Entities.lookup(config.getString("target")), targets,
config.getBool("checkVisibility"), config.getBool("checkVisibility"),
config.getBool("checkCanNavigate"), config.getBool("checkCanNavigate"),
config.getInt("reciprocalChance"), config.getInt("reciprocalChance"),
@@ -77,7 +83,7 @@ public record TargetGoalNearestAttackable(
); );
} else { } else {
return new TargetGoalNearestAttackable( return new TargetGoalNearestAttackable(
Entities.lookup(config.getString("target")), targets,
config.getBool("checkVisibility"), config.getBool("checkVisibility"),
config.getBool("checkCanNavigate"), config.getBool("checkCanNavigate"),
config.getInt("reciprocalChance") config.getInt("reciprocalChance")

View File

@@ -7,9 +7,6 @@ import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.HashSet;
import java.util.Set;
/** /**
* Class to handle antigrief integrations. * Class to handle antigrief integrations.
*/ */

View File

@@ -1,24 +0,0 @@
package com.willfp.eco.core.integrations.customblocks;
import com.willfp.eco.core.integrations.Integration;
/**
* Wrapper class for custom block integrations.
*/
public interface CustomBlocksIntegration extends Integration {
/**
* Register all the custom block for a specific plugin into eco.
*
* @see com.willfp.eco.core.blocks.Blocks
*/
default void registerAllBlocks() {
// Override when needed.
}
/**
* Register {@link com.willfp.eco.core.blocks.provider.BlockProvider}s.
*/
default void registerProvider() {
// Override when needed.
}
}

View File

@@ -1,45 +0,0 @@
package com.willfp.eco.core.integrations.customblocks;
import com.willfp.eco.core.integrations.IntegrationRegistry;
import org.jetbrains.annotations.NotNull;
/**
* Class to handle custom block integrations.
*/
public final class CustomBlocksManager {
/**
* A set of all registered integrations.
*/
private static final IntegrationRegistry<CustomBlocksIntegration> REGISTRY = new IntegrationRegistry<>();
/**
* Register a new integration.
*
* @param integration The integration to register.
*/
public static void register(@NotNull final CustomBlocksIntegration integration) {
REGISTRY.register(integration);
}
/**
* Register all the custom block for a specific plugin into eco.
*
* @see com.willfp.eco.core.blocks.Blocks
*/
public static void registerAllBlocks() {
REGISTRY.forEachSafely(CustomBlocksIntegration::registerAllBlocks);
}
/**
* Register all the custom blocks for a specific plugin into eco.
*
* @see com.willfp.eco.core.blocks.Blocks
*/
public static void registerProviders() {
REGISTRY.forEachSafely(CustomBlocksIntegration::registerProvider);
}
private CustomBlocksManager() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
}
}

View File

@@ -3,9 +3,6 @@ package com.willfp.eco.core.integrations.customitems;
import com.willfp.eco.core.integrations.IntegrationRegistry; import com.willfp.eco.core.integrations.IntegrationRegistry;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.HashSet;
import java.util.Set;
/** /**
* Class to handle custom item integrations. * Class to handle custom item integrations.
*/ */

View File

@@ -5,9 +5,6 @@ import org.bukkit.block.Block;
import org.bukkit.event.Event; import org.bukkit.event.Event;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.HashSet;
import java.util.Set;
/** /**
* Class to handle mcmmo integrations. * Class to handle mcmmo integrations.
*/ */

View File

@@ -6,10 +6,51 @@ import org.jetbrains.annotations.NotNull;
/** /**
* Represents a packet. * Represents a packet.
*
* @param handle The NMS handle.
*/ */
public record Packet(@NotNull Object handle) { public class Packet {
/**
* The packet handle.
*/
private Object handle;
/**
* Create a new packet.
*
* @param handle The packet handle.
*/
public Packet(@NotNull final Object handle) {
this.handle = handle;
}
/**
* Get the packet handle.
*
* @return The packet handle.
*/
public Object getHandle() {
return handle;
}
/**
* Set the packet handle.
*
* @param handle The packet handle.
*/
public void setHandle(@NotNull final Object handle) {
this.handle = handle;
}
/**
* Get the packet handle, compatible with the old record-based packet system.
*
* @return The packet handle.
* @deprecated Use {@link #getHandle()} instead.
*/
@Deprecated
public Object handle() {
return handle;
}
/** /**
* Send to a player. * Send to a player.
* *

View File

@@ -2,6 +2,7 @@ package com.willfp.eco.core.proxy;
import com.willfp.eco.core.version.Version; import com.willfp.eco.core.version.Version;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
@@ -28,13 +29,25 @@ public final class ProxyConstants {
"v1_20_R1", "v1_20_R1",
"v1_20_R2", "v1_20_R2",
"v1_20_R3", "v1_20_R3",
"v1_21" "v1_21",
"v1_21_3",
"v1_21_4",
"v1_21_5",
"v1_21_6"
); );
private ProxyConstants() { private ProxyConstants() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
} }
private static String convertVersion(@NotNull final String version) {
return switch (version) {
case "v1_21_1" -> "v1_21";
case "v1_21_2" -> "v1_21_3";
default -> version;
};
}
static { static {
String currentMinecraftVersion = Bukkit.getServer().getBukkitVersion().split("-")[0]; String currentMinecraftVersion = Bukkit.getServer().getBukkitVersion().split("-")[0];
String nmsVersion; String nmsVersion;
@@ -45,6 +58,6 @@ public final class ProxyConstants {
nmsVersion = "v" + currentMinecraftVersion.replace(".", "_"); nmsVersion = "v" + currentMinecraftVersion.replace(".", "_");
} }
NMS_VERSION = nmsVersion; NMS_VERSION = convertVersion(nmsVersion);
} }
} }

View File

@@ -2,6 +2,7 @@ package com.willfp.eco.core.sound;
import com.willfp.eco.core.config.interfaces.Config; import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.serialization.ConfigDeserializer; import com.willfp.eco.core.serialization.ConfigDeserializer;
import com.willfp.eco.util.SoundUtils;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Sound; import org.bukkit.Sound;
import org.bukkit.World; import org.bukkit.World;
@@ -82,8 +83,11 @@ public record PlayableSound(@NotNull Sound sound,
return null; return null;
} }
try { Sound sound = SoundUtils.getSound(config.getString("sound"));
Sound sound = Sound.valueOf(config.getString("sound").toUpperCase());
if (sound == null) {
return null;
}
double pitch = Objects.requireNonNullElse(config.getDoubleOrNull("pitch"), 1.0); double pitch = Objects.requireNonNullElse(config.getDoubleOrNull("pitch"), 1.0);
double volume = Objects.requireNonNullElse(config.getDoubleOrNull("volume"), 1.0); double volume = Objects.requireNonNullElse(config.getDoubleOrNull("volume"), 1.0);
@@ -93,9 +97,6 @@ public record PlayableSound(@NotNull Sound sound,
pitch, pitch,
volume volume
); );
} catch (IllegalArgumentException e) {
return null;
}
} }
} }
} }

View File

@@ -0,0 +1,50 @@
package com.willfp.eco.util;
import com.willfp.eco.core.Prerequisite;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.Sound;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Field;
/**
* Utilities / API methods for sounds.
*/
public final class SoundUtils {
/**
* Get a sound in a version-compatible way.
*
* @param name The name of the sound, case-insensitive.
* @return The sound, or null if not found.
*/
@Nullable
public static Sound getSound(@NotNull final String name) {
if (!Prerequisite.HAS_1_21_3.isMet()) {
try {
return Sound.valueOf(name.toUpperCase());
} catch (IllegalArgumentException e) {
return null;
}
}
// First try from registry (preferred)
Sound fromRegistry = Registry.SOUNDS.get(NamespacedKey.minecraft(name.toLowerCase()));
if (fromRegistry != null) {
return fromRegistry;
}
// Next try using reflection (for legacy enum names)
try {
Field field = Sound.class.getDeclaredField(name.toUpperCase());
return (Sound) field.get(null);
} catch (ReflectiveOperationException e) {
return null;
}
}
private SoundUtils() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
}
}

View File

@@ -1,19 +1,21 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
group = "com.willfp" group = "com.willfp"
version = rootProject.version version = rootProject.version
dependencies { dependencies {
compileOnly(project(":eco-core:core-backend")) compileOnly(project(":eco-core:core-backend"))
compileOnly("io.papermc.paper:paper-api:1.21-R0.1-SNAPSHOT") compileOnly("io.papermc.paper:paper-api:1.21.1-R0.1-SNAPSHOT")
} }
tasks { tasks {
compileJava { compileJava {
options.release = 21 options.release.set(21)
} }
compileKotlin { compileKotlin {
kotlinOptions { compilerOptions {
jvmTarget = "21" jvmTarget.set(JvmTarget.JVM_21)
} }
} }
} }

View File

@@ -4,8 +4,6 @@ import com.willfp.eco.core.entities.args.EntityArgParseResult
import com.willfp.eco.core.entities.args.EntityArgParser import com.willfp.eco.core.entities.args.EntityArgParser
import org.bukkit.attribute.Attribute import org.bukkit.attribute.Attribute
import org.bukkit.entity.LivingEntity import org.bukkit.entity.LivingEntity
import org.bukkit.entity.Phantom
import org.bukkit.entity.Slime
object EntityArgParserScale : EntityArgParser { object EntityArgParserScale : EntityArgParser {
override fun parseArguments(args: Array<out String>): EntityArgParseResult? { override fun parseArguments(args: Array<out String>): EntityArgParseResult? {

View File

@@ -1,3 +1,5 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
group = "com.willfp" group = "com.willfp"
version = rootProject.version version = rootProject.version
@@ -7,7 +9,7 @@ dependencies {
implementation("org.objenesis:objenesis:3.2") implementation("org.objenesis:objenesis:3.2")
compileOnly("io.papermc.paper:paper-api:1.20.2-R0.1-SNAPSHOT") compileOnly("io.papermc.paper:paper-api:1.20.2-R0.1-SNAPSHOT")
compileOnly("me.clip:placeholderapi:2.11.4") compileOnly("me.clip:placeholderapi:2.11.6")
compileOnly("net.kyori:adventure-text-minimessage:4.10.0") compileOnly("net.kyori:adventure-text-minimessage:4.10.0")
compileOnly("net.kyori:adventure-platform-bukkit:4.1.0") compileOnly("net.kyori:adventure-platform-bukkit:4.1.0")
compileOnly("org.yaml:snakeyaml:1.33") compileOnly("org.yaml:snakeyaml:1.33")
@@ -16,12 +18,12 @@ dependencies {
tasks { tasks {
compileJava { compileJava {
options.release = 17 options.release.set(17)
} }
compileKotlin { compileKotlin {
kotlinOptions { compilerOptions {
jvmTarget = "17" jvmTarget.set(JvmTarget.JVM_17)
} }
} }
} }

View File

@@ -9,7 +9,6 @@ import com.willfp.eco.core.packet.PacketPriority
import org.bukkit.Bukkit import org.bukkit.Bukkit
import org.bukkit.event.HandlerList import org.bukkit.event.HandlerList
import org.bukkit.event.Listener import org.bukkit.event.Listener
import java.lang.Exception
private class RegisteredPacketListener( private class RegisteredPacketListener(
@@ -80,9 +79,11 @@ class EcoEventManager(private val plugin: EcoPlugin) : EventManager {
} }
override fun registerPacketListener(listener: PacketListener) { override fun registerPacketListener(listener: PacketListener) {
listeners[listener.priority] += RegisteredPacketListener( listeners[listener.priority].add(
RegisteredPacketListener(
plugin, plugin,
listener listener
) )
)
} }
} }

View File

@@ -23,7 +23,7 @@ class EcoSlotBuilder(private val provider: SlotProvider) : SlotBuilder {
private var notCaptiveFor: (Player) -> Boolean = { _ -> false} private var notCaptiveFor: (Player) -> Boolean = { _ -> false}
override fun onClick(type: ClickType, action: SlotHandler): SlotBuilder { override fun onClick(type: ClickType, action: SlotHandler): SlotBuilder {
handlers[type] += action handlers[type].add(action)
return this return this
} }

View File

@@ -14,12 +14,16 @@ object ArgParserEnchantment : LookupArgParser {
val enchants = mutableMapOf<Enchantment, Int>() val enchants = mutableMapOf<Enchantment, Int>()
for (arg in args) { for (arg in args) {
try {
val argSplit = arg.split(":") val argSplit = arg.split(":")
val enchant = Enchantment.getByKey(NamespacedKey.minecraft(argSplit[0].lowercase())) ?: continue val enchant = Enchantment.getByKey(NamespacedKey.minecraft(argSplit[0].lowercase())) ?: continue
val level = argSplit.getOrNull(1)?.toIntOrNull() ?: enchant.maxLevel val level = argSplit.getOrNull(1)?.toIntOrNull() ?: enchant.maxLevel
enchants[enchant] = level enchants[enchant] = level
} catch (e: IllegalArgumentException) {
continue
}
} }
if (enchants.isEmpty()) { if (enchants.isEmpty()) {

View File

@@ -2,9 +2,7 @@ package com.willfp.eco.internal.items
import com.willfp.eco.internal.items.templates.ValueArgParser import com.willfp.eco.internal.items.templates.ValueArgParser
import com.willfp.eco.util.StringUtils import com.willfp.eco.util.StringUtils
import org.bukkit.inventory.ItemStack
import org.bukkit.inventory.meta.ItemMeta import org.bukkit.inventory.meta.ItemMeta
import java.util.function.Predicate
object ArgParserName : ValueArgParser<String>("name") { object ArgParserName : ValueArgParser<String>("name") {
override fun parse(arg: String): String { override fun parse(arg: String): String {

View File

@@ -1,7 +1,6 @@
package com.willfp.eco.internal.items.templates package com.willfp.eco.internal.items.templates
import com.willfp.eco.core.items.args.LookupArgParser import com.willfp.eco.core.items.args.LookupArgParser
import com.willfp.eco.util.StringUtils
import org.bukkit.inventory.ItemStack import org.bukkit.inventory.ItemStack
import org.bukkit.inventory.meta.ItemMeta import org.bukkit.inventory.meta.ItemMeta
import java.util.function.Predicate import java.util.function.Predicate

View File

@@ -12,7 +12,7 @@ object ParticleFactoryRGB : ParticleFactory {
if (Prerequisite.HAS_1_20_5.isMet) { if (Prerequisite.HAS_1_20_5.isMet) {
Particle.valueOf("DUST") Particle.valueOf("DUST")
} else { } else {
Particle.valueOf("REDSTONE_DUST") Particle.valueOf("REDSTONE")
} }
}.getOrNull() }.getOrNull()

View File

@@ -50,8 +50,7 @@ class EcoProxyFactory(
) )
} else { } else {
ProxyError( ProxyError(
"Could not initialize proxy. If you're seeing this error message" "Could not initialize proxy. Are you running a supported server version?",
+ ", something has gone badly wrong. This almost definitely isn't user error, blame the developer.",
e e
) )
} }

View File

@@ -1,5 +1,5 @@
plugins { plugins {
id("io.papermc.paperweight.userdev") version "1.7.1" apply false id("io.papermc.paperweight.userdev") version "2.0.0-beta.17" apply false
} }

View File

@@ -48,7 +48,7 @@ var SkullMeta.texture: String?
* at java.lang.String.checkBoundsBeginEnd(String.java:4604) ~[?:?] * at java.lang.String.checkBoundsBeginEnd(String.java:4604) ~[?:?]
* at java.lang.String.substring(String.java:2707) ~[?:?] * at java.lang.String.substring(String.java:2707) ~[?:?]
* at java.lang.String.substring(String.java:2680) ~[?:?] * at java.lang.String.substring(String.java:2680) ~[?:?]
* at com.willfp.eco.internal.spigot.proxy.v1_19_R1.common.SkullKt.setTexture(Skull.kt:36) * at com.willfp.eco.internal.spigot.proxy.v1_19_R1.common.SkullKt.setTexture(ModernSkull.kt:36)
* *
if (base64.length < 20) { if (base64.length < 20) {
return return

View File

@@ -54,8 +54,6 @@ class EcoEntityController<T : Mob>(
priority, goal.getGoalFactory()?.create(goal, nms) ?: return this priority, goal.getGoalFactory()?.create(goal, nms) ?: return this
) )
nms.targetSelector
return this return this
} }

View File

@@ -10,7 +10,7 @@ import net.minecraft.world.entity.monster.RangedAttackMob
object RangedBowAttackGoalFactory : EntityGoalFactory<EntityGoalRangedBowAttack> { object RangedBowAttackGoalFactory : EntityGoalFactory<EntityGoalRangedBowAttack> {
override fun create(apiGoal: EntityGoalRangedBowAttack, entity: PathfinderMob): Goal? { override fun create(apiGoal: EntityGoalRangedBowAttack, entity: PathfinderMob): Goal? {
(if (entity !is Monster) return null) if (entity !is Monster) return null
if (entity !is RangedAttackMob) return null if (entity !is RangedAttackMob) return null
return RangedBowAttackGoal( return RangedBowAttackGoal(

View File

@@ -11,7 +11,7 @@ import net.minecraft.world.entity.monster.RangedAttackMob
object RangedCrossbowAttackGoalFactory : EntityGoalFactory<EntityGoalRangedCrossbowAttack> { object RangedCrossbowAttackGoalFactory : EntityGoalFactory<EntityGoalRangedCrossbowAttack> {
override fun create(apiGoal: EntityGoalRangedCrossbowAttack, entity: PathfinderMob): Goal? { override fun create(apiGoal: EntityGoalRangedCrossbowAttack, entity: PathfinderMob): Goal? {
(if (entity !is Monster) return null) if (entity !is Monster) return null
if (entity !is RangedAttackMob) return null if (entity !is RangedAttackMob) return null
if (entity !is CrossbowAttackMob) return null if (entity !is CrossbowAttackMob) return null

View File

@@ -17,7 +17,9 @@ object NearestAttackableGoalFactory : TargetGoalFactory<TargetGoalNearestAttacka
apiGoal.checkVisibility, apiGoal.checkVisibility,
apiGoal.checkCanNavigate, apiGoal.checkCanNavigate,
) { ) {
apiGoal.targetFilter.test(it.toBukkitEntity()) && apiGoal.target.matches(it.toBukkitEntity()) val bukkit = it.toBukkitEntity()
apiGoal.targetFilter.test(bukkit) && apiGoal.targets.any { t -> t.matches(bukkit) }
} }
} }

View File

@@ -23,7 +23,7 @@ class EcoChannelDuplexHandler(
event.handleReceive() event.handleReceive()
if (!event.isCancelled) { if (!event.isCancelled) {
super.channelRead(ctx, msg) super.channelRead(ctx, event.packet.handle)
} }
} else { } else {
super.channelRead(ctx, msg) super.channelRead(ctx, msg)
@@ -39,7 +39,7 @@ class EcoChannelDuplexHandler(
event.handleSend() event.handleSend()
if (!event.isCancelled) { if (!event.isCancelled) {
super.write(ctx, msg, promise) super.write(ctx, event.packet.handle, promise)
} }
} else { } else {
super.write(ctx, msg, promise) super.write(ctx, msg, promise)

View File

@@ -3,12 +3,19 @@ package com.willfp.eco.internal.spigot.proxy.common.packet.display
import com.willfp.eco.core.EcoPlugin import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.packet.PacketEvent import com.willfp.eco.core.packet.PacketEvent
import com.willfp.eco.core.packet.PacketListener import com.willfp.eco.core.packet.PacketListener
import com.willfp.eco.internal.spigot.proxy.common.toResourceLocation
import com.willfp.eco.util.namespacedKeyOf
import net.minecraft.network.protocol.game.ClientboundPlaceGhostRecipePacket import net.minecraft.network.protocol.game.ClientboundPlaceGhostRecipePacket
import net.minecraft.resources.ResourceLocation import net.minecraft.resources.ResourceLocation
class PacketAutoRecipe( class PacketAutoRecipe(
private val plugin: EcoPlugin private val plugin: EcoPlugin
) : PacketListener { ) : PacketListener {
private val fKey = ClientboundPlaceGhostRecipePacket::class.java
.declaredFields
.first { it.type == ResourceLocation::class.java }
.apply { isAccessible = true }
override fun onSend(event: PacketEvent) { override fun onSend(event: PacketEvent) {
val packet = event.packet.handle as? ClientboundPlaceGhostRecipePacket ?: return val packet = event.packet.handle as? ClientboundPlaceGhostRecipePacket ?: return
@@ -24,9 +31,7 @@ class PacketAutoRecipe(
return return
} }
val fKey = packet.javaClass.getDeclaredField("b")
fKey.isAccessible = true
val key = fKey[packet] as ResourceLocation val key = fKey[packet] as ResourceLocation
fKey[packet] = ResourceLocation(key.namespace, key.path + "_displayed") fKey[packet] = namespacedKeyOf(key.namespace, key.path + "_displayed").toResourceLocation()
} }
} }

View File

@@ -15,7 +15,7 @@ import org.bukkit.inventory.ItemStack
import java.util.UUID import java.util.UUID
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
class PacketWindowItems( open class PacketWindowItems(
private val plugin: EcoPlugin private val plugin: EcoPlugin
) : PacketListener { ) : PacketListener {
private val lastKnownWindowIDs = ConcurrentHashMap<UUID, Int>() private val lastKnownWindowIDs = ConcurrentHashMap<UUID, Int>()
@@ -52,7 +52,7 @@ class PacketWindowItems(
} }
private fun modifyWindowItems( protected fun modifyWindowItems(
itemStacks: MutableList<ItemStack>, itemStacks: MutableList<ItemStack>,
windowId: Int, windowId: Int,
player: Player player: Player

View File

@@ -1,3 +1,5 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins { plugins {
id("io.papermc.paperweight.userdev") id("io.papermc.paperweight.userdev")
} }
@@ -7,17 +9,17 @@ version = rootProject.version
dependencies { dependencies {
compileOnly(project(":eco-core:core-nms:common")) compileOnly(project(":eco-core:core-nms:common"))
paperweight.paperDevBundle("1.21-R0.1-SNAPSHOT") paperweight.paperDevBundle("1.21.1-R0.1-SNAPSHOT")
} }
tasks { tasks {
compileJava { compileJava {
options.release = 21 options.release.set(21)
} }
compileKotlin { compileKotlin {
kotlinOptions { compilerOptions {
jvmTarget = "21" jvmTarget.set(JvmTarget.JVM_21)
} }
} }
} }

View File

@@ -0,0 +1,55 @@
package com.willfp.eco.internal.spigot.proxy.common.modern
import com.mojang.authlib.GameProfile
import com.mojang.authlib.properties.Property
import net.minecraft.world.item.component.ResolvableProfile
import org.bukkit.inventory.meta.SkullMeta
import java.lang.reflect.Field
import java.lang.reflect.Method
import java.util.UUID
private lateinit var setProfile: Method
private lateinit var profile: Field
private lateinit var value: Field
var SkullMeta.texture: String?
get() {
if (!::value.isInitialized) {
// Doing it this way because Property was changed to be a record and this is
// a quick hack to get around that
value = Property::class.java.getDeclaredField("value")
value.isAccessible = true
}
if (!::profile.isInitialized) {
// Assumes instance of CraftMetaSkull; package-private class so can't do manual type check
profile = this.javaClass.getDeclaredField("profile")
profile.isAccessible = true
}
val profile = profile[this] as ResolvableProfile? ?: return null
val properties = profile.properties ?: return null
val props = properties["textures"] ?: return null
val prop = props.toMutableList().firstOrNull() ?: return null
return value[prop] as String?
}
set(base64) {
if (!::setProfile.isInitialized) {
// Same here; that's why I can't delegate to a lazy initializer
setProfile = this.javaClass.getDeclaredMethod("setProfile", ResolvableProfile::class.java)
setProfile.isAccessible = true
}
if (base64 == null || base64.length < 20) {
setProfile.invoke(this, null)
} else {
val uuid = UUID(
base64.substring(base64.length - 20).hashCode().toLong(),
base64.substring(base64.length - 10).hashCode().toLong()
)
val profile = GameProfile(uuid, "eco")
profile.properties.put("textures", Property("textures", base64))
val resolvable = ResolvableProfile(profile)
setProfile.invoke(this, resolvable)
}
}

View File

@@ -14,8 +14,6 @@ import com.willfp.eco.util.StringUtils
import com.willfp.eco.util.toComponent import com.willfp.eco.util.toComponent
import com.willfp.eco.util.toLegacy import com.willfp.eco.util.toLegacy
import net.kyori.adventure.text.Component import net.kyori.adventure.text.Component
import net.kyori.adventure.text.format.Style
import net.kyori.adventure.text.format.TextColor
import net.kyori.adventure.text.format.TextDecoration import net.kyori.adventure.text.format.TextDecoration
import net.minecraft.core.component.DataComponentType import net.minecraft.core.component.DataComponentType
import net.minecraft.core.component.DataComponents import net.minecraft.core.component.DataComponents
@@ -23,11 +21,9 @@ import net.minecraft.core.registries.Registries
import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.CompoundTag
import net.minecraft.util.Unit import net.minecraft.util.Unit
import net.minecraft.world.item.component.CustomData import net.minecraft.world.item.component.CustomData
import net.minecraft.world.item.component.CustomModelData
import net.minecraft.world.item.component.ItemLore import net.minecraft.world.item.component.ItemLore
import org.bukkit.Bukkit import net.minecraft.world.item.enchantment.ItemEnchantments
import org.bukkit.craftbukkit.CraftRegistry import org.bukkit.craftbukkit.CraftRegistry
import org.bukkit.craftbukkit.CraftServer
import org.bukkit.craftbukkit.enchantments.CraftEnchantment import org.bukkit.craftbukkit.enchantments.CraftEnchantment
import org.bukkit.enchantments.Enchantment import org.bukkit.enchantments.Enchantment
import org.bukkit.inventory.ItemFlag import org.bukkit.inventory.ItemFlag
@@ -43,17 +39,17 @@ private fun Component.unstyled(): Component {
return unstyledComponent.append(this) return unstyledComponent.append(this)
} }
class NewEcoFastItemStack( open class NewEcoFastItemStack(
private val bukkit: ItemStack private val bukkit: ItemStack,
) : ImplementedFIS { ) : ImplementedFIS {
// Cast is there because, try as I might, I can't get IntellIJ to recognise half the classes in the dev bundle // Cast is there because, try as I might, I can't get IntellIJ to recognise half the classes in the dev bundle
@Suppress("USELESS_CAST") @Suppress("USELESS_CAST")
private val handle = bukkit.asNMSStack() as net.minecraft.world.item.ItemStack protected val handle = bukkit.asNMSStack() as net.minecraft.world.item.ItemStack
private val pdc = (handle.get(DataComponents.CUSTOM_DATA)?.copyTag() ?: CompoundTag()).makePdc() private val pdc = (handle.get(DataComponents.CUSTOM_DATA)?.copyTag() ?: CompoundTag()).makePdc()
override fun getEnchants(checkStored: Boolean): Map<Enchantment, Int> { override fun getEnchants(checkStored: Boolean): Map<Enchantment, Int> {
val enchantments = handle.get(DataComponents.ENCHANTMENTS) ?: return emptyMap() val enchantments = handle.get(DataComponents.ENCHANTMENTS) ?: ItemEnchantments.EMPTY
val map = mutableMapOf<Enchantment, Int>() val map = mutableMapOf<Enchantment, Int>()
@@ -80,22 +76,17 @@ class NewEcoFastItemStack(
enchantment: Enchantment, enchantment: Enchantment,
checkStored: Boolean checkStored: Boolean
): Int { ): Int {
val minecraft = val minecraft = CraftRegistry.bukkitToMinecraftHolder(
CraftRegistry.bukkitToMinecraft<Enchantment, net.minecraft.world.item.enchantment.Enchantment>( enchantment,
enchantment Registries.ENCHANTMENT
) )
val server = Bukkit.getServer() as CraftServer
val access = server.server.registryAccess()
val holder = access.registryOrThrow(Registries.ENCHANTMENT).wrapAsHolder(minecraft)
val enchantments = handle.get(DataComponents.ENCHANTMENTS) ?: return 0 val enchantments = handle.get(DataComponents.ENCHANTMENTS) ?: return 0
var level = enchantments.getLevel(holder) var level = enchantments.getLevel(minecraft)
if (checkStored) { if (checkStored) {
val storedEnchantments = handle.get(DataComponents.STORED_ENCHANTMENTS) ?: return 0 val storedEnchantments = handle.get(DataComponents.STORED_ENCHANTMENTS) ?: return 0
level = max(level, storedEnchantments.getLevel(holder)) level = max(level, storedEnchantments.getLevel(minecraft))
} }
return level return level
@@ -153,7 +144,7 @@ class NewEcoFastItemStack(
override fun getDisplayName(): String = displayNameComponent.toLegacy() override fun getDisplayName(): String = displayNameComponent.toLegacy()
private fun <T> net.minecraft.world.item.ItemStack.modifyComponent( protected fun <T> net.minecraft.world.item.ItemStack.modifyComponent(
component: DataComponentType<T>, component: DataComponentType<T>,
modifier: (T) -> T modifier: (T) -> T
) { ) {
@@ -368,19 +359,23 @@ class NewEcoFastItemStack(
override fun getType(): org.bukkit.Material = handle.getItem().toMaterial() override fun getType(): org.bukkit.Material = handle.getItem().toMaterial()
/*
Custom model data doesn't work based on an integer since 1.21.3, so these methods do nothing
*/
override fun getCustomModelData(): Int? = override fun getCustomModelData(): Int? =
handle.get(DataComponents.CUSTOM_MODEL_DATA)?.value null
override fun setCustomModelData(data: Int?) { override fun setCustomModelData(data: Int?) {
if (data == null) { if (data == null) {
handle.remove(DataComponents.CUSTOM_MODEL_DATA) handle.remove(DataComponents.CUSTOM_MODEL_DATA)
} else {
handle.set(DataComponents.CUSTOM_MODEL_DATA, CustomModelData(data))
} }
apply() apply()
} }
// END
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (other !is NewEcoFastItemStack) { if (other !is NewEcoFastItemStack) {
return false return false

View File

@@ -2,7 +2,6 @@ package com.willfp.eco.internal.spigot.proxy.v1_17_R1
import com.willfp.eco.core.data.ExtendedPersistentDataContainer import com.willfp.eco.core.data.ExtendedPersistentDataContainer
import com.willfp.eco.internal.spigot.proxy.ExtendedPersistentDataContainerFactoryProxy import com.willfp.eco.internal.spigot.proxy.ExtendedPersistentDataContainerFactoryProxy
import com.willfp.eco.internal.spigot.proxy.common.item.ContinuallyAppliedPersistentDataContainer
import net.minecraft.nbt.Tag import net.minecraft.nbt.Tag
import org.bukkit.Material import org.bukkit.Material
import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftItemStack import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftItemStack

View File

@@ -2,7 +2,6 @@ package com.willfp.eco.internal.spigot.proxy.v1_18_R1
import com.willfp.eco.core.data.ExtendedPersistentDataContainer import com.willfp.eco.core.data.ExtendedPersistentDataContainer
import com.willfp.eco.internal.spigot.proxy.ExtendedPersistentDataContainerFactoryProxy import com.willfp.eco.internal.spigot.proxy.ExtendedPersistentDataContainerFactoryProxy
import com.willfp.eco.internal.spigot.proxy.common.item.ContinuallyAppliedPersistentDataContainer
import net.minecraft.nbt.Tag import net.minecraft.nbt.Tag
import org.bukkit.Material import org.bukkit.Material
import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftItemStack import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftItemStack

View File

@@ -2,7 +2,6 @@ package com.willfp.eco.internal.spigot.proxy.v1_18_R2
import com.willfp.eco.core.data.ExtendedPersistentDataContainer import com.willfp.eco.core.data.ExtendedPersistentDataContainer
import com.willfp.eco.internal.spigot.proxy.ExtendedPersistentDataContainerFactoryProxy import com.willfp.eco.internal.spigot.proxy.ExtendedPersistentDataContainerFactoryProxy
import com.willfp.eco.internal.spigot.proxy.common.item.ContinuallyAppliedPersistentDataContainer
import net.minecraft.nbt.Tag import net.minecraft.nbt.Tag
import org.bukkit.Material import org.bukkit.Material
import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftItemStack import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftItemStack

View File

@@ -3,7 +3,6 @@ package com.willfp.eco.internal.spigot.proxy.v1_19_R1
import com.willfp.eco.core.items.TestableItem import com.willfp.eco.core.items.TestableItem
import com.willfp.eco.core.recipe.parts.EmptyTestableItem import com.willfp.eco.core.recipe.parts.EmptyTestableItem
import com.willfp.eco.internal.spigot.proxy.SNBTConverterProxy import com.willfp.eco.internal.spigot.proxy.SNBTConverterProxy
import com.willfp.eco.internal.spigot.proxy.common.toMaterial
import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.SnbtPrinterTagVisitor import net.minecraft.nbt.SnbtPrinterTagVisitor
import net.minecraft.nbt.TagParser import net.minecraft.nbt.TagParser

View File

@@ -5,7 +5,6 @@ import com.willfp.eco.internal.spigot.proxy.CommonsInitializerProxy
import com.willfp.eco.internal.spigot.proxy.common.CommonsProvider import com.willfp.eco.internal.spigot.proxy.common.CommonsProvider
import com.willfp.eco.internal.spigot.proxy.common.packet.PacketInjectorListener import com.willfp.eco.internal.spigot.proxy.common.packet.PacketInjectorListener
import com.willfp.eco.internal.spigot.proxy.common.toResourceLocation import com.willfp.eco.internal.spigot.proxy.common.toResourceLocation
import io.netty.channel.Channel
import net.kyori.adventure.text.Component import net.kyori.adventure.text.Component
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer
import net.minecraft.core.registries.BuiltInRegistries import net.minecraft.core.registries.BuiltInRegistries
@@ -13,7 +12,6 @@ import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.Tag import net.minecraft.nbt.Tag
import net.minecraft.resources.ResourceLocation import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.network.ServerGamePacketListenerImpl
import net.minecraft.world.entity.PathfinderMob import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.item.Item import net.minecraft.world.item.Item
import org.bukkit.Bukkit import org.bukkit.Bukkit

View File

@@ -2,7 +2,6 @@ package com.willfp.eco.internal.spigot.proxy.v1_19_R2
import com.willfp.eco.core.data.ExtendedPersistentDataContainer import com.willfp.eco.core.data.ExtendedPersistentDataContainer
import com.willfp.eco.internal.spigot.proxy.ExtendedPersistentDataContainerFactoryProxy import com.willfp.eco.internal.spigot.proxy.ExtendedPersistentDataContainerFactoryProxy
import com.willfp.eco.internal.spigot.proxy.common.item.ContinuallyAppliedPersistentDataContainer
import net.minecraft.nbt.Tag import net.minecraft.nbt.Tag
import org.bukkit.Material import org.bukkit.Material
import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftItemStack import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftItemStack

View File

@@ -4,7 +4,6 @@ import com.willfp.eco.core.packet.Packet
import com.willfp.eco.core.packet.sendPacket import com.willfp.eco.core.packet.sendPacket
import com.willfp.eco.internal.spigot.proxy.DisplayNameProxy import com.willfp.eco.internal.spigot.proxy.DisplayNameProxy
import com.willfp.eco.internal.spigot.proxy.common.toNMS import com.willfp.eco.internal.spigot.proxy.common.toNMS
import io.papermc.paper.adventure.PaperAdventure
import net.kyori.adventure.text.Component import net.kyori.adventure.text.Component
import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket
import net.minecraft.network.syncher.EntityDataAccessor import net.minecraft.network.syncher.EntityDataAccessor

View File

@@ -4,16 +4,13 @@ import com.willfp.eco.core.packet.Packet
import com.willfp.eco.core.packet.sendPacket import com.willfp.eco.core.packet.sendPacket
import com.willfp.eco.internal.spigot.proxy.DisplayNameProxy import com.willfp.eco.internal.spigot.proxy.DisplayNameProxy
import com.willfp.eco.internal.spigot.proxy.common.toNMS import com.willfp.eco.internal.spigot.proxy.common.toNMS
import io.papermc.paper.adventure.PaperAdventure
import net.kyori.adventure.text.Component import net.kyori.adventure.text.Component
import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket
import net.minecraft.network.syncher.EntityDataAccessor import net.minecraft.network.syncher.EntityDataAccessor
import net.minecraft.network.syncher.SynchedEntityData import net.minecraft.network.syncher.SynchedEntityData
import net.minecraft.world.entity.Entity import net.minecraft.world.entity.Entity
import org.bukkit.craftbukkit.v1_20_R2.entity.CraftLivingEntity import org.bukkit.craftbukkit.v1_20_R2.entity.CraftLivingEntity
import org.bukkit.craftbukkit.v1_20_R2.entity.CraftMob
import org.bukkit.entity.LivingEntity import org.bukkit.entity.LivingEntity
import org.bukkit.entity.Mob
import org.bukkit.entity.Player import org.bukkit.entity.Player
import java.util.Optional import java.util.Optional

View File

@@ -4,16 +4,13 @@ import com.willfp.eco.core.packet.Packet
import com.willfp.eco.core.packet.sendPacket import com.willfp.eco.core.packet.sendPacket
import com.willfp.eco.internal.spigot.proxy.DisplayNameProxy import com.willfp.eco.internal.spigot.proxy.DisplayNameProxy
import com.willfp.eco.internal.spigot.proxy.common.toNMS import com.willfp.eco.internal.spigot.proxy.common.toNMS
import io.papermc.paper.adventure.PaperAdventure
import net.kyori.adventure.text.Component import net.kyori.adventure.text.Component
import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket
import net.minecraft.network.syncher.EntityDataAccessor import net.minecraft.network.syncher.EntityDataAccessor
import net.minecraft.network.syncher.SynchedEntityData import net.minecraft.network.syncher.SynchedEntityData
import net.minecraft.world.entity.Entity import net.minecraft.world.entity.Entity
import org.bukkit.craftbukkit.v1_20_R3.entity.CraftLivingEntity import org.bukkit.craftbukkit.v1_20_R3.entity.CraftLivingEntity
import org.bukkit.craftbukkit.v1_20_R3.entity.CraftMob
import org.bukkit.entity.LivingEntity import org.bukkit.entity.LivingEntity
import org.bukkit.entity.Mob
import org.bukkit.entity.Player import org.bukkit.entity.Player
import java.util.Optional import java.util.Optional

View File

@@ -5,7 +5,6 @@ import com.willfp.eco.internal.spigot.proxy.DummyEntityFactoryProxy
import org.bukkit.Location import org.bukkit.Location
import org.bukkit.craftbukkit.v1_20_R3.CraftWorld import org.bukkit.craftbukkit.v1_20_R3.CraftWorld
import org.bukkit.entity.Entity import org.bukkit.entity.Entity
import org.bukkit.entity.EntityType
import org.bukkit.entity.Zombie import org.bukkit.entity.Zombie
class DummyEntityFactory : DummyEntityFactoryProxy { class DummyEntityFactory : DummyEntityFactoryProxy {

View File

@@ -2,7 +2,6 @@ package com.willfp.eco.internal.spigot.proxy.v1_20_R3
import com.willfp.eco.core.data.ExtendedPersistentDataContainer import com.willfp.eco.core.data.ExtendedPersistentDataContainer
import com.willfp.eco.internal.spigot.proxy.ExtendedPersistentDataContainerFactoryProxy import com.willfp.eco.internal.spigot.proxy.ExtendedPersistentDataContainerFactoryProxy
import com.willfp.eco.internal.spigot.proxy.common.item.ContinuallyAppliedPersistentDataContainer
import net.minecraft.nbt.Tag import net.minecraft.nbt.Tag
import org.bukkit.Material import org.bukkit.Material
import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftItemStack import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftItemStack

View File

@@ -1,3 +1,5 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins { plugins {
id("io.papermc.paperweight.userdev") id("io.papermc.paperweight.userdev")
} }
@@ -8,7 +10,7 @@ version = rootProject.version
dependencies { dependencies {
implementation(project(":eco-core:core-nms:modern")) implementation(project(":eco-core:core-nms:modern"))
implementation(project(":eco-core:core-nms:common")) implementation(project(":eco-core:core-nms:common"))
paperweight.paperDevBundle("1.21-R0.1-SNAPSHOT") paperweight.paperDevBundle("1.21.1-R0.1-SNAPSHOT")
implementation("net.kyori:adventure-text-minimessage:4.11.0") { implementation("net.kyori:adventure-text-minimessage:4.11.0") {
version { version {
@@ -39,12 +41,12 @@ tasks {
} }
compileJava { compileJava {
options.release = 21 options.release.set(21)
} }
compileKotlin { compileKotlin {
kotlinOptions { compilerOptions {
jvmTarget = "21" jvmTarget.set(JvmTarget.JVM_21)
} }
} }
} }

View File

@@ -3,10 +3,29 @@ package com.willfp.eco.internal.spigot.proxy.v1_21
import com.willfp.eco.core.fast.FastItemStack import com.willfp.eco.core.fast.FastItemStack
import com.willfp.eco.internal.spigot.proxy.FastItemStackFactoryProxy import com.willfp.eco.internal.spigot.proxy.FastItemStackFactoryProxy
import com.willfp.eco.internal.spigot.proxy.common.modern.NewEcoFastItemStack import com.willfp.eco.internal.spigot.proxy.common.modern.NewEcoFastItemStack
import net.minecraft.core.component.DataComponents
import net.minecraft.world.item.component.CustomModelData
import org.bukkit.inventory.ItemStack import org.bukkit.inventory.ItemStack
class FastItemStackFactory : FastItemStackFactoryProxy { class FastItemStackFactory : FastItemStackFactoryProxy {
override fun create(itemStack: ItemStack): FastItemStack { override fun create(itemStack: ItemStack): FastItemStack {
return NewEcoFastItemStack(itemStack) return LegacyNewEcoFastItemStack(itemStack)
}
private class LegacyNewEcoFastItemStack(itemStack: ItemStack) :
NewEcoFastItemStack(itemStack) {
override fun getCustomModelData(): Int? =
handle.get(DataComponents.CUSTOM_MODEL_DATA)?.value
override fun setCustomModelData(data: Int?) {
if (data == null) {
handle.remove(DataComponents.CUSTOM_MODEL_DATA)
} else {
handle.set(DataComponents.CUSTOM_MODEL_DATA, CustomModelData(data))
}
apply()
}
} }
} }

View File

@@ -1,7 +1,7 @@
package com.willfp.eco.internal.spigot.proxy.v1_21 package com.willfp.eco.internal.spigot.proxy.v1_21
import com.willfp.eco.internal.spigot.proxy.SkullProxy import com.willfp.eco.internal.spigot.proxy.SkullProxy
import com.willfp.eco.internal.spigot.proxy.common.texture import com.willfp.eco.internal.spigot.proxy.common.modern.texture
import org.bukkit.inventory.meta.SkullMeta import org.bukkit.inventory.meta.SkullMeta
class Skull : SkullProxy { class Skull : SkullProxy {

View File

@@ -0,0 +1,52 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
id("io.papermc.paperweight.userdev")
}
group = "com.willfp"
version = rootProject.version
dependencies {
implementation(project(":eco-core:core-nms:modern"))
implementation(project(":eco-core:core-nms:common"))
paperweight.paperDevBundle("1.21.3-R0.1-SNAPSHOT")
implementation("net.kyori:adventure-text-minimessage:4.11.0") {
version {
strictly("4.11.0")
}
exclude(group = "net.kyori", module = "adventure-api")
}
}
tasks {
build {
dependsOn(reobfJar)
}
reobfJar {
mustRunAfter(shadowJar)
}
shadowJar {
relocate(
"com.willfp.eco.internal.spigot.proxy.common",
"com.willfp.eco.internal.spigot.proxy.v1_21_3.common"
)
relocate(
"net.kyori.adventure.text.minimessage",
"com.willfp.eco.internal.spigot.proxy.v1_21_3.minimessage"
)
}
compileJava {
options.release.set(21)
}
compileKotlin {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_21)
}
}
}

View File

@@ -0,0 +1,35 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_3
import com.willfp.eco.core.command.PluginCommandBase
import com.willfp.eco.internal.spigot.proxy.BukkitCommandsProxy
import org.bukkit.Bukkit
import org.bukkit.command.Command
import org.bukkit.command.SimpleCommandMap
import org.bukkit.craftbukkit.CraftServer
import java.lang.reflect.Field
class BukkitCommands : BukkitCommandsProxy {
private val knownCommandsField: Field by lazy {
SimpleCommandMap::class.java.getDeclaredField("knownCommands")
.apply {
isAccessible = true
}
}
@Suppress("UNCHECKED_CAST")
private val knownCommands: MutableMap<String, Command>
get() = knownCommandsField.get(getCommandMap()) as MutableMap<String, Command>
override fun getCommandMap(): SimpleCommandMap {
return (Bukkit.getServer() as CraftServer).commandMap
}
override fun syncCommands() {
(Bukkit.getServer() as CraftServer).syncCommands()
}
override fun unregisterCommand(command: PluginCommandBase) {
knownCommands.remove(command.name)
knownCommands.remove("${command.plugin.name.lowercase()}:${command.name}")
}
}

View File

@@ -0,0 +1,171 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_3
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.internal.spigot.proxy.CommonsInitializerProxy
import com.willfp.eco.internal.spigot.proxy.common.CommonsProvider
import com.willfp.eco.internal.spigot.proxy.common.packet.PacketInjectorListener
import com.willfp.eco.internal.spigot.proxy.common.toResourceLocation
import net.kyori.adventure.text.Component
import net.kyori.adventure.text.serializer.json.JSONComponentSerializer
import net.minecraft.core.component.DataComponents
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.Tag
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.item.Item
import org.bukkit.Bukkit
import org.bukkit.Material
import org.bukkit.NamespacedKey
import org.bukkit.craftbukkit.CraftServer
import org.bukkit.craftbukkit.entity.CraftEntity
import org.bukkit.craftbukkit.entity.CraftMob
import org.bukkit.craftbukkit.entity.CraftPlayer
import org.bukkit.craftbukkit.inventory.CraftItemStack
import org.bukkit.craftbukkit.inventory.CraftMetaArmor
import org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer
import org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry
import org.bukkit.craftbukkit.util.CraftMagicNumbers
import org.bukkit.craftbukkit.util.CraftNamespacedKey
import org.bukkit.entity.LivingEntity
import org.bukkit.entity.Mob
import org.bukkit.entity.Player
import org.bukkit.inventory.ItemStack
import org.bukkit.persistence.PersistentDataContainer
import java.lang.reflect.Field
class CommonsInitializer : CommonsInitializerProxy {
override fun init(plugin: EcoPlugin) {
CommonsProvider.setIfNeeded(CommonsProviderImpl)
plugin.onEnable {
plugin.eventManager.registerListener(PacketInjectorListener)
}
}
object CommonsProviderImpl : CommonsProvider {
private val cisHandle: Field = CraftItemStack::class.java.getDeclaredField("handle").apply {
isAccessible = true
}
private val pdcRegsitry = CraftMetaArmor::class.java
.superclass // Access CraftMetaItem
.getDeclaredField("DATA_TYPE_REGISTRY")
.apply { isAccessible = true }
.get(null) as CraftPersistentDataTypeRegistry
override val nbtTagString = CraftMagicNumbers.NBT.TAG_STRING
override fun toPathfinderMob(mob: Mob): PathfinderMob? {
val craft = mob as? CraftMob ?: return null
return craft.handle as? PathfinderMob
}
override fun toResourceLocation(namespacedKey: NamespacedKey): ResourceLocation =
CraftNamespacedKey.toMinecraft(namespacedKey)
override fun asNMSStack(itemStack: ItemStack): net.minecraft.world.item.ItemStack {
return if (itemStack !is CraftItemStack) {
CraftItemStack.asNMSCopy(itemStack)
} else {
cisHandle[itemStack] as net.minecraft.world.item.ItemStack? ?: CraftItemStack.asNMSCopy(itemStack)
}
}
override fun asBukkitStack(itemStack: net.minecraft.world.item.ItemStack): ItemStack {
return CraftItemStack.asCraftMirror(itemStack)
}
override fun mergeIfNeeded(itemStack: ItemStack, nmsStack: net.minecraft.world.item.ItemStack) {
if (itemStack !is CraftItemStack) {
itemStack.itemMeta = CraftItemStack.asCraftMirror(nmsStack).itemMeta
}
}
override fun toBukkitEntity(entity: net.minecraft.world.entity.LivingEntity): LivingEntity? =
CraftEntity.getEntity(Bukkit.getServer() as CraftServer, entity) as? LivingEntity
override fun makePdc(tag: CompoundTag, base: Boolean): PersistentDataContainer {
fun emptyPdc(): CraftPersistentDataContainer = CraftPersistentDataContainer(pdcRegsitry)
fun CompoundTag?.toPdc(): PersistentDataContainer {
val pdc = emptyPdc()
this ?: return pdc
val keys = this.allKeys
for (key in keys) {
pdc.put(key, this[key])
}
return pdc
}
return if (base) {
tag.toPdc()
} else {
if (tag.contains("PublicBukkitValues")) {
tag.getCompound("PublicBukkitValues").toPdc()
} else {
emptyPdc()
}
}
}
override fun setPdc(
tag: CompoundTag,
pdc: PersistentDataContainer?,
item: net.minecraft.world.item.ItemStack?
) {
fun CraftPersistentDataContainer.toTag(): CompoundTag {
val compound = CompoundTag()
val rawPublicMap: Map<String, Tag> = this.raw
for ((key, value) in rawPublicMap) {
compound.put(key, value)
}
return compound
}
val container = when (pdc) {
is CraftPersistentDataContainer? -> pdc
else -> null
}
if (item != null) {
if (container != null && !container.isEmpty) {
for (key in tag.allKeys.toSet()) {
tag.remove(key)
}
tag.merge(container.toTag())
} else {
item.remove(DataComponents.CUSTOM_DATA)
}
} else {
if (container != null && !container.isEmpty) {
tag.put("PublicBukkitValues", container.toTag())
} else {
tag.remove("PublicBukkitValues")
}
}
}
override fun materialToItem(material: Material): Item =
BuiltInRegistries.ITEM.getOptional(material.key.toResourceLocation())
.orElseThrow { IllegalArgumentException("Material is not item!") }
override fun itemToMaterial(item: Item) =
Material.getMaterial(BuiltInRegistries.ITEM.getKey(item).path.uppercase())
?: throw IllegalArgumentException("Invalid material!")
override fun toNMS(player: Player): ServerPlayer {
return (player as CraftPlayer).handle
}
override fun toNMS(component: Component): net.minecraft.network.chat.Component {
val json = JSONComponentSerializer.json().serialize(component)
val holderLookupProvider = (Bukkit.getServer() as CraftServer).server.registryAccess()
return net.minecraft.network.chat.Component.Serializer.fromJson(json, holderLookupProvider)!!
}
}
}

View File

@@ -0,0 +1,57 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_3
import com.willfp.eco.core.packet.Packet
import com.willfp.eco.core.packet.sendPacket
import com.willfp.eco.internal.spigot.proxy.DisplayNameProxy
import com.willfp.eco.internal.spigot.proxy.common.toNMS
import net.kyori.adventure.text.Component
import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket
import net.minecraft.network.syncher.EntityDataAccessor
import net.minecraft.network.syncher.SynchedEntityData
import net.minecraft.world.entity.Entity
import org.bukkit.craftbukkit.entity.CraftLivingEntity
import org.bukkit.entity.LivingEntity
import org.bukkit.entity.Player
import java.util.Optional
@Suppress("UNCHECKED_CAST")
class DisplayName : DisplayNameProxy {
private val displayNameAccessor = Entity::class.java
.declaredFields
.filter { it.type == EntityDataAccessor::class.java }
.toList()[2]
.apply { isAccessible = true }
.get(null) as EntityDataAccessor<Optional<net.minecraft.network.chat.Component>>
private val customNameVisibleAccessor = Entity::class.java
.declaredFields
.filter { it.type == EntityDataAccessor::class.java }
.toList()[3]
.apply { isAccessible = true }
.get(null) as EntityDataAccessor<Boolean>
override fun setClientsideDisplayName(
entity: LivingEntity,
player: Player,
displayName: Component,
visible: Boolean
) {
if (entity !is CraftLivingEntity) {
return
}
val nmsComponent = displayName.toNMS()
val nmsEntity = entity.handle
val packet = ClientboundSetEntityDataPacket(
nmsEntity.id,
listOf(
SynchedEntityData.DataValue.create(displayNameAccessor, Optional.of(nmsComponent)),
SynchedEntityData.DataValue.create(customNameVisibleAccessor, visible)
)
)
player.sendPacket(Packet(packet))
}
}

View File

@@ -0,0 +1,15 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_3
import com.willfp.eco.internal.entities.EcoDummyEntity
import com.willfp.eco.internal.spigot.proxy.DummyEntityFactoryProxy
import org.bukkit.Location
import org.bukkit.craftbukkit.CraftWorld
import org.bukkit.entity.Entity
import org.bukkit.entity.Zombie
class DummyEntityFactory : DummyEntityFactoryProxy {
override fun createDummyEntity(location: Location): Entity {
val world = location.world as CraftWorld
return EcoDummyEntity(world.createEntity(location, Zombie::class.java))
}
}

View File

@@ -0,0 +1,12 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_3
import com.willfp.eco.core.entities.ai.EntityController
import com.willfp.eco.internal.spigot.proxy.EntityControllerFactoryProxy
import com.willfp.eco.internal.spigot.proxy.v1_21_3.entity.EcoEntityController
import org.bukkit.entity.Mob
class EntityControllerFactory : EntityControllerFactoryProxy {
override fun <T : Mob> createEntityController(entity: T): EntityController<T> {
return EcoEntityController(entity)
}
}

View File

@@ -0,0 +1,91 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_3
import com.willfp.eco.core.data.ExtendedPersistentDataContainer
import com.willfp.eco.internal.spigot.proxy.ExtendedPersistentDataContainerFactoryProxy
import net.minecraft.nbt.Tag
import org.bukkit.Material
import org.bukkit.craftbukkit.inventory.CraftItemStack
import org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer
import org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry
import org.bukkit.inventory.ItemStack
import org.bukkit.persistence.PersistentDataContainer
import org.bukkit.persistence.PersistentDataType
import java.lang.reflect.Field
class ExtendedPersistentDataContainerFactory : ExtendedPersistentDataContainerFactoryProxy {
private val registry: CraftPersistentDataTypeRegistry
init {
/*
Can't grab actual instance since it's in CraftMetaItem (which is package-private)
And getting it would mean more janky reflection
*/
val item = CraftItemStack.asCraftCopy(ItemStack(Material.STONE))
val pdc = item.itemMeta!!.persistentDataContainer
// Cross-version compatibility:
val registryField: Field = try {
CraftPersistentDataContainer::class.java.getDeclaredField("registry")
} catch (e: NoSuchFieldException) {
CraftPersistentDataContainer::class.java.superclass.getDeclaredField("registry")
}
this.registry = registryField
.apply { isAccessible = true }.get(pdc) as CraftPersistentDataTypeRegistry
}
override fun adapt(pdc: PersistentDataContainer): ExtendedPersistentDataContainer {
return when (pdc) {
is CraftPersistentDataContainer -> EcoPersistentDataContainer(pdc)
else -> throw IllegalArgumentException("Custom PDC instance ${pdc::class.java.name} is not supported!")
}
}
override fun newPdc(): PersistentDataContainer {
return CraftPersistentDataContainer(registry)
}
inner class EcoPersistentDataContainer(
private val handle: CraftPersistentDataContainer
) : ExtendedPersistentDataContainer {
@Suppress("UNCHECKED_CAST")
private val customDataTags: MutableMap<String, Tag> =
CraftPersistentDataContainer::class.java.getDeclaredField("customDataTags")
.apply { isAccessible = true }.get(handle) as MutableMap<String, Tag>
override fun <T : Any, Z : Any> set(key: String, dataType: PersistentDataType<T, Z>, value: Z) {
customDataTags[key] =
registry.wrap(dataType, dataType.toPrimitive(value, handle.adapterContext))
}
override fun <T : Any, Z : Any> has(key: String, dataType: PersistentDataType<T, Z>): Boolean {
val value = customDataTags[key] ?: return false
return registry.isInstanceOf(dataType, value)
}
override fun <T : Any, Z : Any> get(key: String, dataType: PersistentDataType<T, Z>): Z? {
val value = customDataTags[key] ?: return null
return dataType.fromPrimitive(registry.extract<T, Tag>(dataType, value), handle.adapterContext)
}
override fun <T : Any, Z : Any> getOrDefault(
key: String,
dataType: PersistentDataType<T, Z>,
defaultValue: Z
): Z {
return get(key, dataType) ?: defaultValue
}
override fun remove(key: String) {
customDataTags.remove(key)
}
override fun getAllKeys(): MutableSet<String> {
return customDataTags.keys
}
override fun getBase(): PersistentDataContainer {
return handle
}
}
}

View File

@@ -0,0 +1,12 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_3
import com.willfp.eco.core.fast.FastItemStack
import com.willfp.eco.internal.spigot.proxy.FastItemStackFactoryProxy
import com.willfp.eco.internal.spigot.proxy.common.modern.NewEcoFastItemStack
import org.bukkit.inventory.ItemStack
class FastItemStackFactory : FastItemStackFactoryProxy {
override fun create(itemStack: ItemStack): FastItemStack {
return NewEcoFastItemStack(itemStack)
}
}

View File

@@ -0,0 +1,33 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_3
import com.willfp.eco.core.display.Display
import com.willfp.eco.internal.spigot.proxy.MiniMessageTranslatorProxy
import com.willfp.eco.util.toLegacy
import net.kyori.adventure.text.minimessage.MiniMessage
class MiniMessageTranslator : MiniMessageTranslatorProxy {
override fun format(message: String): String {
var mut = message
val startsWithPrefix = mut.startsWith(Display.PREFIX)
if (startsWithPrefix) {
mut = mut.substring(2)
}
mut = mut.replace('§', '&')
val miniMessage = runCatching {
MiniMessage.miniMessage().deserialize(
mut
).toLegacy()
}.getOrNull() ?: mut
mut = if (startsWithPrefix) {
Display.PREFIX + miniMessage
} else {
miniMessage
}
return mut
}
}

View File

@@ -0,0 +1,46 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_3
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.packet.PacketListener
import com.willfp.eco.internal.spigot.proxy.PacketHandlerProxy
import com.willfp.eco.internal.spigot.proxy.common.packet.display.PacketHeldItemSlot
import com.willfp.eco.internal.spigot.proxy.common.packet.display.PacketSetSlot
import com.willfp.eco.internal.spigot.proxy.common.packet.display.PacketWindowItems
import com.willfp.eco.internal.spigot.proxy.common.packet.display.frame.clearFrames
import com.willfp.eco.internal.spigot.proxy.v1_21_3.packet.NewItemsPacketOpenWindowMerchant
import com.willfp.eco.internal.spigot.proxy.v1_21_3.packet.NewItemsPacketSetCreativeSlot
import net.minecraft.network.protocol.Packet
import org.bukkit.craftbukkit.entity.CraftPlayer
import org.bukkit.entity.Player
class PacketHandler : PacketHandlerProxy {
override fun sendPacket(player: Player, packet: com.willfp.eco.core.packet.Packet) {
if (player !is CraftPlayer) {
return
}
val handle = packet.handle
if (handle !is Packet<*>) {
return
}
player.handle.connection.send(handle)
}
override fun clearDisplayFrames() {
clearFrames()
}
override fun getPacketListeners(plugin: EcoPlugin): List<PacketListener> {
// No PacketAutoRecipe for 1.21.3+ because recipes have been changed internally
return listOf(
PacketHeldItemSlot,
NewItemsPacketOpenWindowMerchant,
NewItemsPacketSetCreativeSlot,
PacketSetSlot,
PacketWindowItems(plugin)
)
}
}

View File

@@ -0,0 +1,80 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_3
import com.mojang.serialization.Dynamic
import com.willfp.eco.core.items.TestableItem
import com.willfp.eco.core.recipe.parts.EmptyTestableItem
import com.willfp.eco.internal.spigot.proxy.SNBTConverterProxy
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.NbtOps
import net.minecraft.nbt.SnbtPrinterTagVisitor
import net.minecraft.nbt.TagParser
import net.minecraft.server.MinecraftServer
import net.minecraft.util.datafix.fixes.References
import org.bukkit.Bukkit
import org.bukkit.craftbukkit.CraftServer
import org.bukkit.craftbukkit.inventory.CraftItemStack
import org.bukkit.craftbukkit.util.CraftMagicNumbers
import org.bukkit.inventory.ItemStack
private val registryAccess = (Bukkit.getServer() as CraftServer).server.registryAccess()
class SNBTConverter : SNBTConverterProxy {
private fun parseItemSNBT(snbt: String): CompoundTag? {
val nbt = runCatching { TagParser.parseTag(snbt) }.getOrNull() ?: return null
val dataVersion = if (nbt.contains("DataVersion")) {
nbt.getInt("DataVersion")
} else null
// If the data version is the same as the server's data version, we don't need to fix it
if (dataVersion == CraftMagicNumbers.INSTANCE.dataVersion) {
return nbt
}
return MinecraftServer.getServer().fixerUpper.update(
References.ITEM_STACK,
Dynamic(NbtOps.INSTANCE, nbt),
dataVersion ?: 3700, // 3700 is the 1.20.4 data version
CraftMagicNumbers.INSTANCE.dataVersion
).value as CompoundTag
}
override fun fromSNBT(snbt: String): ItemStack? {
val tag = parseItemSNBT(snbt) ?: return null
val nms = net.minecraft.world.item.ItemStack.parse(registryAccess, tag).orElse(null) ?: return null
return CraftItemStack.asBukkitCopy(nms)
}
override fun toSNBT(itemStack: ItemStack): String {
val nms = CraftItemStack.asNMSCopy(itemStack)
val tag = nms.save(registryAccess) as CompoundTag
tag.putInt("DataVersion", CraftMagicNumbers.INSTANCE.dataVersion)
return SnbtPrinterTagVisitor().visit(tag)
}
override fun makeSNBTTestable(snbt: String): TestableItem {
val tag = parseItemSNBT(snbt) ?: return EmptyTestableItem()
val nms = net.minecraft.world.item.ItemStack.parse(registryAccess, tag).orElse(null)
?: return EmptyTestableItem()
tag.remove("Count")
return SNBTTestableItem(CraftItemStack.asBukkitCopy(nms), tag)
}
class SNBTTestableItem(
private val item: ItemStack,
private val tag: CompoundTag
) : TestableItem {
override fun matches(itemStack: ItemStack?): Boolean {
if (itemStack == null) {
return false
}
val nms = CraftItemStack.asNMSCopy(itemStack)
val nmsTag = nms.save(registryAccess) as CompoundTag
nmsTag.remove("Count")
return tag.copy().merge(nmsTag) == nmsTag && itemStack.type == item.type
}
override fun getItem(): ItemStack = item
}
}

View File

@@ -0,0 +1,18 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_3
import com.willfp.eco.internal.spigot.proxy.SkullProxy
import com.willfp.eco.internal.spigot.proxy.common.modern.texture
import org.bukkit.inventory.meta.SkullMeta
class Skull : SkullProxy {
override fun setSkullTexture(
meta: SkullMeta,
base64: String
) {
meta.texture = base64
}
override fun getSkullTexture(
meta: SkullMeta
): String? = meta.texture
}

View File

@@ -0,0 +1,11 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_3
import com.willfp.eco.internal.spigot.proxy.TPSProxy
import org.bukkit.Bukkit
import org.bukkit.craftbukkit.CraftServer
class TPS : TPSProxy {
override fun getTPS(): Double {
return (Bukkit.getServer() as CraftServer).handle.server.tps1.average
}
}

View File

@@ -0,0 +1,95 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_3.entity
import com.willfp.eco.core.entities.ai.CustomGoal
import com.willfp.eco.core.entities.ai.EntityController
import com.willfp.eco.core.entities.ai.EntityGoal
import com.willfp.eco.core.entities.ai.TargetGoal
import com.willfp.eco.internal.spigot.proxy.common.ai.CustomGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.getGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.toPathfinderMob
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.goal.Goal
import org.bukkit.entity.Mob
class EcoEntityController<T : Mob>(
private val handle: T
) : EntityController<T> {
override fun addEntityGoal(priority: Int, goal: EntityGoal<in T>): EntityController<T> {
val nms = getNms() ?: return this
nms.goalSelector.addGoal(
priority,
goal.getGoalFactory()?.create(goal, nms) ?: return this
)
return this
}
override fun removeEntityGoal(goal: EntityGoal<in T>): EntityController<T> {
val nms = getNms() ?: return this
val predicate: (Goal) -> Boolean = if (goal is CustomGoal<*>) {
{ CustomGoalFactory.isGoalOfType(it, goal) }
} else {
{ goal.getGoalFactory()?.isGoalOfType(it) == true }
}
for (wrapped in nms.goalSelector.availableGoals.toSet()) {
if (predicate(wrapped.goal)) {
nms.goalSelector.removeGoal(wrapped.goal)
}
}
return this
}
override fun clearEntityGoals(): EntityController<T> {
val nms = getNms() ?: return this
nms.goalSelector.availableGoals.clear()
return this
}
override fun addTargetGoal(priority: Int, goal: TargetGoal<in T>): EntityController<T> {
val nms = getNms() ?: return this
nms.targetSelector.addGoal(
priority, goal.getGoalFactory()?.create(goal, nms) ?: return this
)
nms.targetSelector
return this
}
override fun removeTargetGoal(goal: TargetGoal<in T>): EntityController<T> {
val nms = getNms() ?: return this
val predicate: (Goal) -> Boolean = if (goal is CustomGoal<*>) {
{ CustomGoalFactory.isGoalOfType(it, goal) }
} else {
{ goal.getGoalFactory()?.isGoalOfType(it) == true }
}
for (wrapped in nms.targetSelector.availableGoals.toSet()) {
if (predicate(wrapped.goal)) {
nms.targetSelector.removeGoal(wrapped.goal)
}
}
return this
}
override fun clearTargetGoals(): EntityController<T> {
val nms = getNms() ?: return this
nms.targetSelector.availableGoals.clear()
return this
}
private fun getNms(): PathfinderMob? {
return handle.toPathfinderMob()
}
override fun getEntity(): T {
return handle
}
}

View File

@@ -0,0 +1,35 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_3.packet
import com.willfp.eco.core.display.Display
import com.willfp.eco.core.packet.PacketEvent
import com.willfp.eco.core.packet.PacketListener
import com.willfp.eco.internal.spigot.proxy.common.asBukkitStack
import net.minecraft.network.protocol.game.ClientboundMerchantOffersPacket
import net.minecraft.world.item.trading.MerchantOffers
object NewItemsPacketOpenWindowMerchant : PacketListener {
private val field = ClientboundMerchantOffersPacket::class.java
.declaredFields
.first { it.type == MerchantOffers::class.java }
.apply { isAccessible = true }
override fun onSend(event: PacketEvent) {
val packet = event.packet.handle as? ClientboundMerchantOffersPacket ?: return
val offers = MerchantOffers()
for (offer in packet.offers) {
val new = offer.copy()
Display.display(new.baseCostA.itemStack.asBukkitStack(), event.player)
if (new.costB.isPresent) {
Display.display(new.costB.get().itemStack.asBukkitStack(), event.player)
}
Display.display(new.result.asBukkitStack(), event.player)
offers += new
}
field.set(packet, offers)
}
}

View File

@@ -0,0 +1,19 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_3.packet
import com.willfp.eco.core.display.Display
import com.willfp.eco.core.packet.PacketEvent
import com.willfp.eco.core.packet.PacketListener
import com.willfp.eco.internal.spigot.proxy.common.asBukkitStack
import com.willfp.eco.internal.spigot.proxy.common.packet.display.frame.DisplayFrame
import com.willfp.eco.internal.spigot.proxy.common.packet.display.frame.lastDisplayFrame
import net.minecraft.network.protocol.game.ServerboundSetCreativeModeSlotPacket
object NewItemsPacketSetCreativeSlot : PacketListener {
override fun onReceive(event: PacketEvent) {
val packet = event.packet.handle as? ServerboundSetCreativeModeSlotPacket ?: return
Display.revert(packet.itemStack.asBukkitStack())
event.player.lastDisplayFrame = DisplayFrame.EMPTY
}
}

View File

@@ -0,0 +1,52 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
id("io.papermc.paperweight.userdev")
}
group = "com.willfp"
version = rootProject.version
dependencies {
implementation(project(":eco-core:core-nms:modern"))
implementation(project(":eco-core:core-nms:common"))
paperweight.paperDevBundle("1.21.4-R0.1-SNAPSHOT")
implementation("net.kyori:adventure-text-minimessage:4.11.0") {
version {
strictly("4.11.0")
}
exclude(group = "net.kyori", module = "adventure-api")
}
}
tasks {
build {
dependsOn(reobfJar)
}
reobfJar {
mustRunAfter(shadowJar)
}
shadowJar {
relocate(
"com.willfp.eco.internal.spigot.proxy.common",
"com.willfp.eco.internal.spigot.proxy.v1_21_4.common"
)
relocate(
"net.kyori.adventure.text.minimessage",
"com.willfp.eco.internal.spigot.proxy.v1_21_4.minimessage"
)
}
compileJava {
options.release.set(21)
}
compileKotlin {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_21)
}
}
}

View File

@@ -0,0 +1,35 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_4
import com.willfp.eco.core.command.PluginCommandBase
import com.willfp.eco.internal.spigot.proxy.BukkitCommandsProxy
import org.bukkit.Bukkit
import org.bukkit.command.Command
import org.bukkit.command.SimpleCommandMap
import org.bukkit.craftbukkit.CraftServer
import java.lang.reflect.Field
class BukkitCommands : BukkitCommandsProxy {
private val knownCommandsField: Field by lazy {
SimpleCommandMap::class.java.getDeclaredField("knownCommands")
.apply {
isAccessible = true
}
}
@Suppress("UNCHECKED_CAST")
private val knownCommands: MutableMap<String, Command>
get() = knownCommandsField.get(getCommandMap()) as MutableMap<String, Command>
override fun getCommandMap(): SimpleCommandMap {
return (Bukkit.getServer() as CraftServer).commandMap
}
override fun syncCommands() {
(Bukkit.getServer() as CraftServer).syncCommands()
}
override fun unregisterCommand(command: PluginCommandBase) {
knownCommands.remove(command.name)
knownCommands.remove("${command.plugin.name.lowercase()}:${command.name}")
}
}

View File

@@ -0,0 +1,171 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_4
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.internal.spigot.proxy.CommonsInitializerProxy
import com.willfp.eco.internal.spigot.proxy.common.CommonsProvider
import com.willfp.eco.internal.spigot.proxy.common.packet.PacketInjectorListener
import com.willfp.eco.internal.spigot.proxy.common.toResourceLocation
import net.kyori.adventure.text.Component
import net.kyori.adventure.text.serializer.json.JSONComponentSerializer
import net.minecraft.core.component.DataComponents
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.Tag
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.item.Item
import org.bukkit.Bukkit
import org.bukkit.Material
import org.bukkit.NamespacedKey
import org.bukkit.craftbukkit.CraftServer
import org.bukkit.craftbukkit.entity.CraftEntity
import org.bukkit.craftbukkit.entity.CraftMob
import org.bukkit.craftbukkit.entity.CraftPlayer
import org.bukkit.craftbukkit.inventory.CraftItemStack
import org.bukkit.craftbukkit.inventory.CraftMetaArmor
import org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer
import org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry
import org.bukkit.craftbukkit.util.CraftMagicNumbers
import org.bukkit.craftbukkit.util.CraftNamespacedKey
import org.bukkit.entity.LivingEntity
import org.bukkit.entity.Mob
import org.bukkit.entity.Player
import org.bukkit.inventory.ItemStack
import org.bukkit.persistence.PersistentDataContainer
import java.lang.reflect.Field
class CommonsInitializer : CommonsInitializerProxy {
override fun init(plugin: EcoPlugin) {
CommonsProvider.setIfNeeded(CommonsProviderImpl)
plugin.onEnable {
plugin.eventManager.registerListener(PacketInjectorListener)
}
}
object CommonsProviderImpl : CommonsProvider {
private val cisHandle: Field = CraftItemStack::class.java.getDeclaredField("handle").apply {
isAccessible = true
}
private val pdcRegsitry = CraftMetaArmor::class.java
.superclass // Access CraftMetaItem
.getDeclaredField("DATA_TYPE_REGISTRY")
.apply { isAccessible = true }
.get(null) as CraftPersistentDataTypeRegistry
override val nbtTagString = CraftMagicNumbers.NBT.TAG_STRING
override fun toPathfinderMob(mob: Mob): PathfinderMob? {
val craft = mob as? CraftMob ?: return null
return craft.handle as? PathfinderMob
}
override fun toResourceLocation(namespacedKey: NamespacedKey): ResourceLocation =
CraftNamespacedKey.toMinecraft(namespacedKey)
override fun asNMSStack(itemStack: ItemStack): net.minecraft.world.item.ItemStack {
return if (itemStack !is CraftItemStack) {
CraftItemStack.asNMSCopy(itemStack)
} else {
cisHandle[itemStack] as net.minecraft.world.item.ItemStack? ?: CraftItemStack.asNMSCopy(itemStack)
}
}
override fun asBukkitStack(itemStack: net.minecraft.world.item.ItemStack): ItemStack {
return CraftItemStack.asCraftMirror(itemStack)
}
override fun mergeIfNeeded(itemStack: ItemStack, nmsStack: net.minecraft.world.item.ItemStack) {
if (itemStack !is CraftItemStack) {
itemStack.itemMeta = CraftItemStack.asCraftMirror(nmsStack).itemMeta
}
}
override fun toBukkitEntity(entity: net.minecraft.world.entity.LivingEntity): LivingEntity? =
CraftEntity.getEntity(Bukkit.getServer() as CraftServer, entity) as? LivingEntity
override fun makePdc(tag: CompoundTag, base: Boolean): PersistentDataContainer {
fun emptyPdc(): CraftPersistentDataContainer = CraftPersistentDataContainer(pdcRegsitry)
fun CompoundTag?.toPdc(): PersistentDataContainer {
val pdc = emptyPdc()
this ?: return pdc
val keys = this.allKeys
for (key in keys) {
pdc.put(key, this[key])
}
return pdc
}
return if (base) {
tag.toPdc()
} else {
if (tag.contains("PublicBukkitValues")) {
tag.getCompound("PublicBukkitValues").toPdc()
} else {
emptyPdc()
}
}
}
override fun setPdc(
tag: CompoundTag,
pdc: PersistentDataContainer?,
item: net.minecraft.world.item.ItemStack?
) {
fun CraftPersistentDataContainer.toTag(): CompoundTag {
val compound = CompoundTag()
val rawPublicMap: Map<String, Tag> = this.raw
for ((key, value) in rawPublicMap) {
compound.put(key, value)
}
return compound
}
val container = when (pdc) {
is CraftPersistentDataContainer? -> pdc
else -> null
}
if (item != null) {
if (container != null && !container.isEmpty) {
for (key in tag.allKeys.toSet()) {
tag.remove(key)
}
tag.merge(container.toTag())
} else {
item.remove(DataComponents.CUSTOM_DATA)
}
} else {
if (container != null && !container.isEmpty) {
tag.put("PublicBukkitValues", container.toTag())
} else {
tag.remove("PublicBukkitValues")
}
}
}
override fun materialToItem(material: Material): Item =
BuiltInRegistries.ITEM.getOptional(material.key.toResourceLocation())
.orElseThrow { IllegalArgumentException("Material is not item!") }
override fun itemToMaterial(item: Item) =
Material.getMaterial(BuiltInRegistries.ITEM.getKey(item).path.uppercase())
?: throw IllegalArgumentException("Invalid material!")
override fun toNMS(player: Player): ServerPlayer {
return (player as CraftPlayer).handle
}
override fun toNMS(component: Component): net.minecraft.network.chat.Component {
val json = JSONComponentSerializer.json().serialize(component)
val holderLookupProvider = (Bukkit.getServer() as CraftServer).server.registryAccess()
return net.minecraft.network.chat.Component.Serializer.fromJson(json, holderLookupProvider)!!
}
}
}

View File

@@ -0,0 +1,57 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_4
import com.willfp.eco.core.packet.Packet
import com.willfp.eco.core.packet.sendPacket
import com.willfp.eco.internal.spigot.proxy.DisplayNameProxy
import com.willfp.eco.internal.spigot.proxy.common.toNMS
import net.kyori.adventure.text.Component
import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket
import net.minecraft.network.syncher.EntityDataAccessor
import net.minecraft.network.syncher.SynchedEntityData
import net.minecraft.world.entity.Entity
import org.bukkit.craftbukkit.entity.CraftLivingEntity
import org.bukkit.entity.LivingEntity
import org.bukkit.entity.Player
import java.util.Optional
@Suppress("UNCHECKED_CAST")
class DisplayName : DisplayNameProxy {
private val displayNameAccessor = Entity::class.java
.declaredFields
.filter { it.type == EntityDataAccessor::class.java }
.toList()[2]
.apply { isAccessible = true }
.get(null) as EntityDataAccessor<Optional<net.minecraft.network.chat.Component>>
private val customNameVisibleAccessor = Entity::class.java
.declaredFields
.filter { it.type == EntityDataAccessor::class.java }
.toList()[3]
.apply { isAccessible = true }
.get(null) as EntityDataAccessor<Boolean>
override fun setClientsideDisplayName(
entity: LivingEntity,
player: Player,
displayName: Component,
visible: Boolean
) {
if (entity !is CraftLivingEntity) {
return
}
val nmsComponent = displayName.toNMS()
val nmsEntity = entity.handle
val packet = ClientboundSetEntityDataPacket(
nmsEntity.id,
listOf(
SynchedEntityData.DataValue.create(displayNameAccessor, Optional.of(nmsComponent)),
SynchedEntityData.DataValue.create(customNameVisibleAccessor, visible)
)
)
player.sendPacket(Packet(packet))
}
}

View File

@@ -0,0 +1,15 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_4
import com.willfp.eco.internal.entities.EcoDummyEntity
import com.willfp.eco.internal.spigot.proxy.DummyEntityFactoryProxy
import org.bukkit.Location
import org.bukkit.craftbukkit.CraftWorld
import org.bukkit.entity.Entity
import org.bukkit.entity.Zombie
class DummyEntityFactory : DummyEntityFactoryProxy {
override fun createDummyEntity(location: Location): Entity {
val world = location.world as CraftWorld
return EcoDummyEntity(world.createEntity(location, Zombie::class.java))
}
}

View File

@@ -0,0 +1,12 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_4
import com.willfp.eco.core.entities.ai.EntityController
import com.willfp.eco.internal.spigot.proxy.EntityControllerFactoryProxy
import com.willfp.eco.internal.spigot.proxy.v1_21_4.entity.EcoEntityController
import org.bukkit.entity.Mob
class EntityControllerFactory : EntityControllerFactoryProxy {
override fun <T : Mob> createEntityController(entity: T): EntityController<T> {
return EcoEntityController(entity)
}
}

View File

@@ -0,0 +1,91 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_4
import com.willfp.eco.core.data.ExtendedPersistentDataContainer
import com.willfp.eco.internal.spigot.proxy.ExtendedPersistentDataContainerFactoryProxy
import net.minecraft.nbt.Tag
import org.bukkit.Material
import org.bukkit.craftbukkit.inventory.CraftItemStack
import org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer
import org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry
import org.bukkit.inventory.ItemStack
import org.bukkit.persistence.PersistentDataContainer
import org.bukkit.persistence.PersistentDataType
import java.lang.reflect.Field
class ExtendedPersistentDataContainerFactory : ExtendedPersistentDataContainerFactoryProxy {
private val registry: CraftPersistentDataTypeRegistry
init {
/*
Can't grab actual instance since it's in CraftMetaItem (which is package-private)
And getting it would mean more janky reflection
*/
val item = CraftItemStack.asCraftCopy(ItemStack(Material.STONE))
val pdc = item.itemMeta!!.persistentDataContainer
// Cross-version compatibility:
val registryField: Field = try {
CraftPersistentDataContainer::class.java.getDeclaredField("registry")
} catch (e: NoSuchFieldException) {
CraftPersistentDataContainer::class.java.superclass.getDeclaredField("registry")
}
this.registry = registryField
.apply { isAccessible = true }.get(pdc) as CraftPersistentDataTypeRegistry
}
override fun adapt(pdc: PersistentDataContainer): ExtendedPersistentDataContainer {
return when (pdc) {
is CraftPersistentDataContainer -> EcoPersistentDataContainer(pdc)
else -> throw IllegalArgumentException("Custom PDC instance ${pdc::class.java.name} is not supported!")
}
}
override fun newPdc(): PersistentDataContainer {
return CraftPersistentDataContainer(registry)
}
inner class EcoPersistentDataContainer(
private val handle: CraftPersistentDataContainer
) : ExtendedPersistentDataContainer {
@Suppress("UNCHECKED_CAST")
private val customDataTags: MutableMap<String, Tag> =
CraftPersistentDataContainer::class.java.getDeclaredField("customDataTags")
.apply { isAccessible = true }.get(handle) as MutableMap<String, Tag>
override fun <T : Any, Z : Any> set(key: String, dataType: PersistentDataType<T, Z>, value: Z) {
customDataTags[key] =
registry.wrap(dataType, dataType.toPrimitive(value, handle.adapterContext))
}
override fun <T : Any, Z : Any> has(key: String, dataType: PersistentDataType<T, Z>): Boolean {
val value = customDataTags[key] ?: return false
return registry.isInstanceOf(dataType, value)
}
override fun <T : Any, Z : Any> get(key: String, dataType: PersistentDataType<T, Z>): Z? {
val value = customDataTags[key] ?: return null
return dataType.fromPrimitive(registry.extract<T, Tag>(dataType, value), handle.adapterContext)
}
override fun <T : Any, Z : Any> getOrDefault(
key: String,
dataType: PersistentDataType<T, Z>,
defaultValue: Z
): Z {
return get(key, dataType) ?: defaultValue
}
override fun remove(key: String) {
customDataTags.remove(key)
}
override fun getAllKeys(): MutableSet<String> {
return customDataTags.keys
}
override fun getBase(): PersistentDataContainer {
return handle
}
}
}

View File

@@ -0,0 +1,12 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_4
import com.willfp.eco.core.fast.FastItemStack
import com.willfp.eco.internal.spigot.proxy.FastItemStackFactoryProxy
import com.willfp.eco.internal.spigot.proxy.common.modern.NewEcoFastItemStack
import org.bukkit.inventory.ItemStack
class FastItemStackFactory : FastItemStackFactoryProxy {
override fun create(itemStack: ItemStack): FastItemStack {
return NewEcoFastItemStack(itemStack)
}
}

View File

@@ -0,0 +1,33 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_4
import com.willfp.eco.core.display.Display
import com.willfp.eco.internal.spigot.proxy.MiniMessageTranslatorProxy
import com.willfp.eco.util.toLegacy
import net.kyori.adventure.text.minimessage.MiniMessage
class MiniMessageTranslator : MiniMessageTranslatorProxy {
override fun format(message: String): String {
var mut = message
val startsWithPrefix = mut.startsWith(Display.PREFIX)
if (startsWithPrefix) {
mut = mut.substring(2)
}
mut = mut.replace('§', '&')
val miniMessage = runCatching {
MiniMessage.miniMessage().deserialize(
mut
).toLegacy()
}.getOrNull() ?: mut
mut = if (startsWithPrefix) {
Display.PREFIX + miniMessage
} else {
miniMessage
}
return mut
}
}

View File

@@ -0,0 +1,46 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_4
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.packet.PacketListener
import com.willfp.eco.internal.spigot.proxy.PacketHandlerProxy
import com.willfp.eco.internal.spigot.proxy.common.packet.display.PacketHeldItemSlot
import com.willfp.eco.internal.spigot.proxy.common.packet.display.PacketSetSlot
import com.willfp.eco.internal.spigot.proxy.common.packet.display.PacketWindowItems
import com.willfp.eco.internal.spigot.proxy.common.packet.display.frame.clearFrames
import com.willfp.eco.internal.spigot.proxy.v1_21_4.packet.NewItemsPacketOpenWindowMerchant
import com.willfp.eco.internal.spigot.proxy.v1_21_4.packet.NewItemsPacketSetCreativeSlot
import net.minecraft.network.protocol.Packet
import org.bukkit.craftbukkit.entity.CraftPlayer
import org.bukkit.entity.Player
class PacketHandler : PacketHandlerProxy {
override fun sendPacket(player: Player, packet: com.willfp.eco.core.packet.Packet) {
if (player !is CraftPlayer) {
return
}
val handle = packet.handle
if (handle !is Packet<*>) {
return
}
player.handle.connection.send(handle)
}
override fun clearDisplayFrames() {
clearFrames()
}
override fun getPacketListeners(plugin: EcoPlugin): List<PacketListener> {
// No PacketAutoRecipe for 1.21.3+ because recipes have been changed internally
return listOf(
PacketHeldItemSlot,
NewItemsPacketOpenWindowMerchant,
NewItemsPacketSetCreativeSlot,
PacketSetSlot,
PacketWindowItems(plugin)
)
}
}

View File

@@ -0,0 +1,80 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_4
import com.mojang.serialization.Dynamic
import com.willfp.eco.core.items.TestableItem
import com.willfp.eco.core.recipe.parts.EmptyTestableItem
import com.willfp.eco.internal.spigot.proxy.SNBTConverterProxy
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.NbtOps
import net.minecraft.nbt.SnbtPrinterTagVisitor
import net.minecraft.nbt.TagParser
import net.minecraft.server.MinecraftServer
import net.minecraft.util.datafix.fixes.References
import org.bukkit.Bukkit
import org.bukkit.craftbukkit.CraftServer
import org.bukkit.craftbukkit.inventory.CraftItemStack
import org.bukkit.craftbukkit.util.CraftMagicNumbers
import org.bukkit.inventory.ItemStack
private val registryAccess = (Bukkit.getServer() as CraftServer).server.registryAccess()
class SNBTConverter : SNBTConverterProxy {
private fun parseItemSNBT(snbt: String): CompoundTag? {
val nbt = runCatching { TagParser.parseTag(snbt) }.getOrNull() ?: return null
val dataVersion = if (nbt.contains("DataVersion")) {
nbt.getInt("DataVersion")
} else null
// If the data version is the same as the server's data version, we don't need to fix it
if (dataVersion == CraftMagicNumbers.INSTANCE.dataVersion) {
return nbt
}
return MinecraftServer.getServer().fixerUpper.update(
References.ITEM_STACK,
Dynamic(NbtOps.INSTANCE, nbt),
dataVersion ?: 3700, // 3700 is the 1.20.4 data version
CraftMagicNumbers.INSTANCE.dataVersion
).value as CompoundTag
}
override fun fromSNBT(snbt: String): ItemStack? {
val tag = parseItemSNBT(snbt) ?: return null
val nms = net.minecraft.world.item.ItemStack.parse(registryAccess, tag).orElse(null) ?: return null
return CraftItemStack.asBukkitCopy(nms)
}
override fun toSNBT(itemStack: ItemStack): String {
val nms = CraftItemStack.asNMSCopy(itemStack)
val tag = nms.save(registryAccess) as CompoundTag
tag.putInt("DataVersion", CraftMagicNumbers.INSTANCE.dataVersion)
return SnbtPrinterTagVisitor().visit(tag)
}
override fun makeSNBTTestable(snbt: String): TestableItem {
val tag = parseItemSNBT(snbt) ?: return EmptyTestableItem()
val nms = net.minecraft.world.item.ItemStack.parse(registryAccess, tag).orElse(null)
?: return EmptyTestableItem()
tag.remove("Count")
return SNBTTestableItem(CraftItemStack.asBukkitCopy(nms), tag)
}
class SNBTTestableItem(
private val item: ItemStack,
private val tag: CompoundTag
) : TestableItem {
override fun matches(itemStack: ItemStack?): Boolean {
if (itemStack == null) {
return false
}
val nms = CraftItemStack.asNMSCopy(itemStack)
val nmsTag = nms.save(registryAccess) as CompoundTag
nmsTag.remove("Count")
return tag.copy().merge(nmsTag) == nmsTag && itemStack.type == item.type
}
override fun getItem(): ItemStack = item
}
}

View File

@@ -0,0 +1,18 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_4
import com.willfp.eco.internal.spigot.proxy.SkullProxy
import com.willfp.eco.internal.spigot.proxy.common.modern.texture
import org.bukkit.inventory.meta.SkullMeta
class Skull : SkullProxy {
override fun setSkullTexture(
meta: SkullMeta,
base64: String
) {
meta.texture = base64
}
override fun getSkullTexture(
meta: SkullMeta
): String? = meta.texture
}

View File

@@ -0,0 +1,11 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_4
import com.willfp.eco.internal.spigot.proxy.TPSProxy
import org.bukkit.Bukkit
import org.bukkit.craftbukkit.CraftServer
class TPS : TPSProxy {
override fun getTPS(): Double {
return (Bukkit.getServer() as CraftServer).handle.server.tps1.average
}
}

View File

@@ -0,0 +1,95 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_4.entity
import com.willfp.eco.core.entities.ai.CustomGoal
import com.willfp.eco.core.entities.ai.EntityController
import com.willfp.eco.core.entities.ai.EntityGoal
import com.willfp.eco.core.entities.ai.TargetGoal
import com.willfp.eco.internal.spigot.proxy.common.ai.CustomGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.getGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.toPathfinderMob
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.goal.Goal
import org.bukkit.entity.Mob
class EcoEntityController<T : Mob>(
private val handle: T
) : EntityController<T> {
override fun addEntityGoal(priority: Int, goal: EntityGoal<in T>): EntityController<T> {
val nms = getNms() ?: return this
nms.goalSelector.addGoal(
priority,
goal.getGoalFactory()?.create(goal, nms) ?: return this
)
return this
}
override fun removeEntityGoal(goal: EntityGoal<in T>): EntityController<T> {
val nms = getNms() ?: return this
val predicate: (Goal) -> Boolean = if (goal is CustomGoal<*>) {
{ CustomGoalFactory.isGoalOfType(it, goal) }
} else {
{ goal.getGoalFactory()?.isGoalOfType(it) == true }
}
for (wrapped in nms.goalSelector.availableGoals.toSet()) {
if (predicate(wrapped.goal)) {
nms.goalSelector.removeGoal(wrapped.goal)
}
}
return this
}
override fun clearEntityGoals(): EntityController<T> {
val nms = getNms() ?: return this
nms.goalSelector.availableGoals.clear()
return this
}
override fun addTargetGoal(priority: Int, goal: TargetGoal<in T>): EntityController<T> {
val nms = getNms() ?: return this
nms.targetSelector.addGoal(
priority, goal.getGoalFactory()?.create(goal, nms) ?: return this
)
nms.targetSelector
return this
}
override fun removeTargetGoal(goal: TargetGoal<in T>): EntityController<T> {
val nms = getNms() ?: return this
val predicate: (Goal) -> Boolean = if (goal is CustomGoal<*>) {
{ CustomGoalFactory.isGoalOfType(it, goal) }
} else {
{ goal.getGoalFactory()?.isGoalOfType(it) == true }
}
for (wrapped in nms.targetSelector.availableGoals.toSet()) {
if (predicate(wrapped.goal)) {
nms.targetSelector.removeGoal(wrapped.goal)
}
}
return this
}
override fun clearTargetGoals(): EntityController<T> {
val nms = getNms() ?: return this
nms.targetSelector.availableGoals.clear()
return this
}
private fun getNms(): PathfinderMob? {
return handle.toPathfinderMob()
}
override fun getEntity(): T {
return handle
}
}

View File

@@ -0,0 +1,35 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_4.packet
import com.willfp.eco.core.display.Display
import com.willfp.eco.core.packet.PacketEvent
import com.willfp.eco.core.packet.PacketListener
import com.willfp.eco.internal.spigot.proxy.common.asBukkitStack
import net.minecraft.network.protocol.game.ClientboundMerchantOffersPacket
import net.minecraft.world.item.trading.MerchantOffers
object NewItemsPacketOpenWindowMerchant : PacketListener {
private val field = ClientboundMerchantOffersPacket::class.java
.declaredFields
.first { it.type == MerchantOffers::class.java }
.apply { isAccessible = true }
override fun onSend(event: PacketEvent) {
val packet = event.packet.handle as? ClientboundMerchantOffersPacket ?: return
val offers = MerchantOffers()
for (offer in packet.offers) {
val new = offer.copy()
Display.display(new.baseCostA.itemStack.asBukkitStack(), event.player)
if (new.costB.isPresent) {
Display.display(new.costB.get().itemStack.asBukkitStack(), event.player)
}
Display.display(new.result.asBukkitStack(), event.player)
offers += new
}
field.set(packet, offers)
}
}

View File

@@ -0,0 +1,20 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_4.packet
import com.willfp.eco.core.display.Display
import com.willfp.eco.core.packet.PacketEvent
import com.willfp.eco.core.packet.PacketListener
import com.willfp.eco.internal.spigot.proxy.common.asBukkitStack
import com.willfp.eco.internal.spigot.proxy.common.packet.display.frame.DisplayFrame
import com.willfp.eco.internal.spigot.proxy.common.packet.display.frame.lastDisplayFrame
import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket
import net.minecraft.network.protocol.game.ServerboundSetCreativeModeSlotPacket
object NewItemsPacketSetCreativeSlot : PacketListener {
override fun onReceive(event: PacketEvent) {
val packet = event.packet.handle as? ServerboundSetCreativeModeSlotPacket ?: return
Display.revert(packet.itemStack.asBukkitStack())
event.player.lastDisplayFrame = DisplayFrame.EMPTY
}
}

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