mirror of
https://github.com/Xiao-MoMi/craft-engine.git
synced 2025-12-27 10:59:07 +00:00
@@ -23,7 +23,7 @@ dependencies {
|
||||
// NMS
|
||||
compileOnly("net.momirealms:craft-engine-nms-helper:${rootProject.properties["nms_helper_version"]}")
|
||||
// Platform
|
||||
compileOnly("dev.folia:folia-api:${rootProject.properties["paper_version"]}-R0.1-SNAPSHOT")
|
||||
compileOnly("io.papermc.paper:paper-api:${rootProject.properties["paper_version"]}-R0.1-SNAPSHOT")
|
||||
// OpenGL Math
|
||||
compileOnly("org.joml:joml:${rootProject.properties["joml_version"]}")
|
||||
// Gson
|
||||
|
||||
@@ -15,7 +15,7 @@ dependencies {
|
||||
compileOnly(project(":core"))
|
||||
compileOnly("net.momirealms:sparrow-nbt:${rootProject.properties["sparrow_nbt_version"]}")
|
||||
// Platform
|
||||
compileOnly("dev.folia:folia-api:${rootProject.properties["paper_version"]}-R0.1-SNAPSHOT")
|
||||
compileOnly("io.papermc.paper:paper-api:${rootProject.properties["paper_version"]}-R0.1-SNAPSHOT")
|
||||
// NeigeItems
|
||||
compileOnly("pers.neige.neigeitems:NeigeItems:1.21.42")
|
||||
// Placeholder
|
||||
|
||||
@@ -9,7 +9,7 @@ repositories {
|
||||
|
||||
dependencies {
|
||||
// Platform
|
||||
compileOnly("dev.folia:folia-api:1.20.1-R0.1-SNAPSHOT")
|
||||
compileOnly("io.papermc.paper:paper-api:1.20.1-R0.1-SNAPSHOT")
|
||||
}
|
||||
|
||||
java {
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package net.momirealms.craftengine.bukkit.util;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.event.entity.CreatureSpawnEvent;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class LegacyEntityUtils {
|
||||
|
||||
public static Entity spawnEntity(World world, Location loc, EntityType type, Consumer<Entity> function) {
|
||||
return world.spawnEntity(loc, type, CreatureSpawnEvent.SpawnReason.CUSTOM, function::accept);
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@ repositories {
|
||||
|
||||
dependencies {
|
||||
// Platform
|
||||
compileOnly("dev.folia:folia-api:${rootProject.properties["paper_version"]}-R0.1-SNAPSHOT")
|
||||
compileOnly("io.papermc.paper:paper-api:${rootProject.properties["paper_version"]}-R0.1-SNAPSHOT")
|
||||
|
||||
implementation(project(":shared"))
|
||||
implementation(project(":core"))
|
||||
|
||||
@@ -76,6 +76,27 @@ totem_animation:
|
||||
- /craftengine feature totem-animation
|
||||
- /ce feature totem-animation
|
||||
|
||||
enable_resource:
|
||||
enable: true
|
||||
permission: ce.command.admin.resource
|
||||
usage:
|
||||
- /craftengine resource enable
|
||||
- /ce resource enable
|
||||
|
||||
disable_resource:
|
||||
enable: true
|
||||
permission: ce.command.admin.resource
|
||||
usage:
|
||||
- /craftengine resource disable
|
||||
- /ce resource disable
|
||||
|
||||
list_resource:
|
||||
enable: true
|
||||
permission: ce.command.admin.resource
|
||||
usage:
|
||||
- /craftengine resource list
|
||||
- /ce resource list
|
||||
|
||||
# Debug commands
|
||||
debug_set_block:
|
||||
enable: true
|
||||
|
||||
@@ -130,30 +130,61 @@ item:
|
||||
non-italic-tag: false
|
||||
|
||||
block:
|
||||
# Enables the sound system, which prevents the client from hearing some non-custom block sounds and improves the client experience.
|
||||
sound-system:
|
||||
enable: true
|
||||
# In Adventure Mode, players need the correct tool to break custom blocks.
|
||||
# Vanilla clients DO NOT recognize custom block IDs (e.g., craftengine:note_block_0).
|
||||
#
|
||||
# - When ENABLED:
|
||||
# - Players can break custom blocks if their tools can mine their VANILLA EQUIVALENTS.
|
||||
# Example: A tool for "note_block" can break "craftengine:note_block_0".
|
||||
#
|
||||
# - When DISABLED:
|
||||
# ⚠️ WARNING:
|
||||
# - Server MUST list ACTUAL CUSTOM BLOCK IDs in item's `can_break` component.
|
||||
# - Sending custom IDs (e.g., craftengine:note_block_0) to vanilla clients WILL CRASH THEM!
|
||||
# ✅ Solution:
|
||||
# - Use `client-bound-item-data` to safely sync custom block data to clients.
|
||||
# Documentation: https://mo-mi.gitbook.io/xiaomomi-plugins/craftengine/plugin-wiki/craftengine/add-new-contents/items/item-data/client-bound-item-data
|
||||
simplify-adventure-break-check: false
|
||||
# Similar to the option above, but designed for block placement
|
||||
simplify-adventure-place-check: false
|
||||
# Whether plugin should predict the next block to break
|
||||
# This can help improve mining experience to some extent at the cost of performance
|
||||
predict-breaking:
|
||||
enable: false
|
||||
interval: 10
|
||||
extended-interaction-range: 0.5
|
||||
|
||||
furniture:
|
||||
# Should the plugin remove invalid furniture on chunk load
|
||||
# Automatically remove outdated furniture entities when a chunk is loaded.
|
||||
remove-invalid-furniture-on-chunk-load:
|
||||
# Enable/disable the cleanup system
|
||||
enable: false
|
||||
# If you want to remove all invalid furniture, please set this list to empty, otherwise only furniture in the list will be removed.
|
||||
# - When EMPTY: Remove ALL invalid furniture entities
|
||||
# - When POPULATED: Only remove specified furniture types
|
||||
# Example for targeted removal:
|
||||
# list: [ "xxx:invalid_furniture", "yyy:broken_sofa" ]
|
||||
list:
|
||||
- "xxx:invalid_furniture"
|
||||
# Whether to hide the entity containing metadata
|
||||
# Hide technical entities used for storing furniture metadata.
|
||||
# NOTE:
|
||||
# - These are INVISIBLE entities used internally for tracking furniture states
|
||||
# - Recommended to keep enabled for better performance
|
||||
hide-base-entity: true
|
||||
|
||||
image:
|
||||
# Prevent players from using images set in minecraft:default font
|
||||
# Players with `craftengine.filter.bypass.xxx` would ignore the limitation
|
||||
# Block image tags using minecraft:default font in these interfaces
|
||||
# Permission bypass: craftengine.filter.bypass.xxx (replace xxx with context: anvil/book/chat/etc)
|
||||
illegal-characters-filter:
|
||||
anvil: true
|
||||
book: true
|
||||
chat: true
|
||||
command: true
|
||||
sign: true
|
||||
# By intercepting packets, you are allowed to use <image:...> <shift:...> in other plugins
|
||||
# Turning off some unused options would help reduce CPU usage on async threads
|
||||
# Allow <image:...> and <shift:...> tags in third-party plugins via packet manipulation
|
||||
# ⚠️ Disable unused handlers to reduce async thread workload
|
||||
intercept-packets:
|
||||
system-chat: true
|
||||
tab-list: true # Tab list header and footer
|
||||
@@ -168,17 +199,69 @@ image:
|
||||
entity-name: false
|
||||
armor-stand: true # Legacy Holograms
|
||||
text-display: true # Modern Holograms
|
||||
# Defines Unicode characters used for <shift:xxx> positioning
|
||||
# - Must match the font defined in resource packs
|
||||
# - Do NOT modify unless you understand text rendering mechanics
|
||||
offset-characters:
|
||||
font: minecraft:offset_chars
|
||||
-1: '\uf800'
|
||||
-2: '\uf801'
|
||||
-3: '\uf802'
|
||||
-4: '\uf803'
|
||||
-5: '\uf804'
|
||||
-6: '\uf805'
|
||||
-7: '\uf806'
|
||||
-8: '\uf807'
|
||||
-9: '\uf808'
|
||||
-10: '\uf809'
|
||||
-11: '\uf80a'
|
||||
-12: '\uf80b'
|
||||
-13: '\uf80c'
|
||||
-14: '\uf80d'
|
||||
-15: '\uf80e'
|
||||
-16: '\uf80f'
|
||||
-24: '\uf810'
|
||||
-32: '\uf811'
|
||||
-48: '\uf812'
|
||||
-64: '\uf813'
|
||||
-128: '\uf814'
|
||||
-256: '\uf815'
|
||||
1: '\uf830'
|
||||
2: '\uf831'
|
||||
3: '\uf832'
|
||||
4: '\uf833'
|
||||
5: '\uf834'
|
||||
6: '\uf835'
|
||||
7: '\uf836'
|
||||
8: '\uf837'
|
||||
9: '\uf838'
|
||||
10: '\uf839'
|
||||
11: '\uf83a'
|
||||
12: '\uf83b'
|
||||
13: '\uf83c'
|
||||
14: '\uf83d'
|
||||
15: '\uf83e'
|
||||
16: '\uf83f'
|
||||
24: '\uf840'
|
||||
32: '\uf841'
|
||||
48: '\uf842'
|
||||
64: '\uf843'
|
||||
128: '\uf844'
|
||||
256: '\uf845'
|
||||
|
||||
emoji: {}
|
||||
|
||||
recipe:
|
||||
# Enable the plugin's recipe system
|
||||
# Master switch for custom recipes
|
||||
# NOTE: When enabled, plugin recipes will OVERRIDE vanilla recipes
|
||||
enable: true
|
||||
# Disable vanilla recipes
|
||||
# Manage Minecraft's default recipe behavior
|
||||
disable-vanilla-recipes:
|
||||
# Disable all vanilla recipes
|
||||
# ⚠️ WARNING: When true, DISABLES ALL VANILLA RECIPES
|
||||
# - Conflicts with 'list' option (list will be ignored)
|
||||
all: false
|
||||
# Disable the recipes in list
|
||||
# Selective recipe disabling (safer alternative to 'all: true')
|
||||
# Example: ["minecraft:wooden_sword", "minecraft:stone_hoe"]
|
||||
list: []
|
||||
|
||||
gui:
|
||||
@@ -240,12 +323,13 @@ gui:
|
||||
performance:
|
||||
# Maximum chain update depth when fixing client visuals
|
||||
max-block-chain-update-limit: 64
|
||||
# Maximum number of emojis to parse per operation
|
||||
# Prevent lag or oversized packet when processing emoji-heavy content
|
||||
max-emojis-per-parse: 16
|
||||
|
||||
light-system:
|
||||
# Required for custom light-emitting blocks
|
||||
enable: true
|
||||
# Turning this option on will reduce lighting system issues to some extent, but will increase server bandwidth consumption
|
||||
# Turning this option on will reduce lighting system issues to some extent, but will increase server bandwidth consumption.
|
||||
force-update-light: false
|
||||
|
||||
chunk-system:
|
||||
@@ -255,59 +339,28 @@ chunk-system:
|
||||
# 4 = LZ4 | Blazing-Fast Blazing-Fast Low Low |
|
||||
# 5 = ZSTD | Medium-Fast Fast High Medium |
|
||||
compression-method: 4
|
||||
# Disabling this option prevents the plugin from converting custom blocks to vanilla states when chunks are unloaded.
|
||||
# While this can improve performance, custom blocks will turn into air if the plugin is uninstalled.
|
||||
# Auto-convert custom blocks -> vanilla blocks when unloading chunks
|
||||
#
|
||||
# - When ENABLED (true):
|
||||
# - Prevents custom blocks becoming AIR if plugin is uninstalled
|
||||
# - Ensures world portability for vanilla environments
|
||||
#
|
||||
# - When DISABLED (false):
|
||||
# ⚠️ IRREVERSIBLE DATA LOSS WARNING:
|
||||
# - Custom blocks permanently turn to AIR without plugin
|
||||
# - Recommended for temporary/test worlds only
|
||||
restore-vanilla-blocks-on-chunk-unload: true
|
||||
# Convert vanilla blocks -> custom blocks when loading chunks
|
||||
#
|
||||
# - Performance Mode (false):
|
||||
# ⚠️ REQUIRED CONDITIONS:
|
||||
# 1. Must disable restore-vanilla-blocks-on-chunk-unload
|
||||
# 2. Accept risk of custom block data loss on plugin removal
|
||||
#
|
||||
# - Compatibility Mode (true):
|
||||
# - Full state recovery with minor performance cost
|
||||
restore-custom-blocks-on-chunk-load: true
|
||||
# When you edit a map locally using CraftEngine fabric mod, the custom block data is not immediately synchronized with the
|
||||
# server's CraftEngine internal data. Enabling this option will synchronize the data when the chunk is loaded.
|
||||
# (This option only slightly impacts performance, which has been fully optimized, so you don't need to worry too much.)
|
||||
sync-custom-blocks-on-chunk-load: false
|
||||
# If you disable this, it's a must to disable the above option.
|
||||
restore-custom-blocks-on-chunk-load: true
|
||||
|
||||
offset-characters:
|
||||
font: minecraft:offset_chars
|
||||
-1: '\uf800'
|
||||
-2: '\uf801'
|
||||
-3: '\uf802'
|
||||
-4: '\uf803'
|
||||
-5: '\uf804'
|
||||
-6: '\uf805'
|
||||
-7: '\uf806'
|
||||
-8: '\uf807'
|
||||
-9: '\uf808'
|
||||
-10: '\uf809'
|
||||
-11: '\uf80a'
|
||||
-12: '\uf80b'
|
||||
-13: '\uf80c'
|
||||
-14: '\uf80d'
|
||||
-15: '\uf80e'
|
||||
-16: '\uf80f'
|
||||
-24: '\uf810'
|
||||
-32: '\uf811'
|
||||
-48: '\uf812'
|
||||
-64: '\uf813'
|
||||
-128: '\uf814'
|
||||
-256: '\uf815'
|
||||
1: '\uf830'
|
||||
2: '\uf831'
|
||||
3: '\uf832'
|
||||
4: '\uf833'
|
||||
5: '\uf834'
|
||||
6: '\uf835'
|
||||
7: '\uf836'
|
||||
8: '\uf837'
|
||||
9: '\uf838'
|
||||
10: '\uf839'
|
||||
11: '\uf83a'
|
||||
12: '\uf83b'
|
||||
13: '\uf83c'
|
||||
14: '\uf83d'
|
||||
15: '\uf83e'
|
||||
16: '\uf83f'
|
||||
24: '\uf840'
|
||||
32: '\uf841'
|
||||
48: '\uf842'
|
||||
64: '\uf843'
|
||||
128: '\uf844'
|
||||
256: '\uf845'
|
||||
sync-custom-blocks-on-chunk-load: false
|
||||
@@ -661,7 +661,7 @@ templates#settings#blocks:
|
||||
- "default:sound/stone"
|
||||
- "default:pickaxe_power/level_{break_power}"
|
||||
overrides:
|
||||
hardness: 4.5
|
||||
hardness: 3.0
|
||||
resistance: 3.0
|
||||
push-reaction: NORMAL
|
||||
is-redstone-conductor: true
|
||||
@@ -677,7 +677,7 @@ templates#settings#blocks:
|
||||
- "default:sound/deepslate"
|
||||
- "default:pickaxe_power/level_{break_power}"
|
||||
overrides:
|
||||
hardness: 6.0
|
||||
hardness: 4.5
|
||||
resistance: 3.0
|
||||
push-reaction: NORMAL
|
||||
is-redstone-conductor: true
|
||||
|
||||
@@ -54,6 +54,11 @@ command.search_usage.not_found: "<red>No usage found for this item</red>"
|
||||
command.search_recipe.no_item: "<red>Please hold an item before running this command</red>"
|
||||
command.search_usage.no_item: "<red>Please hold an item before running this command</red>"
|
||||
command.totem_animation.failure.not_totem: "<red>Item '<arg:0>' is not minecraft:totem_of_undying</red>"
|
||||
command.resource.enable.success: "<white>Enabled resource <arg:0>. Run <click:run_command:/ce reload all><u>/ce reload all</u></click> to apply changes</white>"
|
||||
command.resource.enable.failure.unknown: "<red>Unknown resource <arg:0></red>"
|
||||
command.resource.disable.success: "<white>Disabled resource <arg:0>. Run <click:run_command:/ce reload all><u>/ce reload all</u></click> to apply changes</white>"
|
||||
command.resource.disable.failure.unknown: "<red>Unknown resource <arg:0></red>"
|
||||
command.resource.list: "<white>Enabled resources(<arg:0>): <green><arg:1></green><newline>Disabled resources(<arg:2>): <red><arg:3></red></white>"
|
||||
warning.config.image.duplicated: "<yellow>Issue found in file <arg:0> - Duplicated image '<arg:1>'.</yellow>"
|
||||
warning.config.image.lack_height: "<yellow>Issue found in file <arg:0> - The image '<arg:1>' is missing the required 'height' argument.</yellow>"
|
||||
warning.config.image.height_smaller_than_ascent: "<yellow>Issue found in file <arg:0> - The image '<arg:1>' violates the bitmap image rule: 'height' should be no lower than 'ascent'.</yellow>"
|
||||
|
||||
@@ -54,6 +54,11 @@ command.search_usage.not_found: "<red>找不到此物品的用途</red>"
|
||||
command.search_recipe.no_item: "<red>请手持物品后再执行此命令</red>"
|
||||
command.search_usage.no_item: "<red>请手持物品后再执行此命令</red>"
|
||||
command.totem_animation.failure.not_totem: "<red>'<arg:0>' 不是 totem_of_undying 类型</red>"
|
||||
command.resource.enable.success: "<white>已启用 <arg:0>. 执行 <click:run_command:/ce reload all><u>/ce reload all</u></click> 以应用更改</white>"
|
||||
command.resource.enable.failure.unknown: "<red>未知资源 <arg:0></red>"
|
||||
command.resource.disable.success: "<white>已禁用 <arg:0>. 执行 <click:run_command:/ce reload all><u>/ce reload all</u></click> 以应用更改</white>"
|
||||
command.resource.disable.failure.unknown: "<red>未知资源 <arg:0></red>"
|
||||
command.resource.list: "<white>启用的资源(<arg:0>): <green><arg:1></green><newline>禁用的资源(<arg:2>): <red><arg:3></red></white>"
|
||||
warning.config.image.duplicated: "<yellow>在文件 <arg:0> 中发现问题 - 图片 '<arg:1>' 重复定义</yellow>"
|
||||
warning.config.image.lack_height: "<yellow>在文件 <arg:0> 中发现问题 - 图片 '<arg:1>' 缺少必要的 'height' 高度参数</yellow>"
|
||||
warning.config.image.height_smaller_than_ascent: "<yellow>在文件 <arg:0> 中发现问题 - 图片 '<arg:1>' 违反位图规则:'height' 高度值不应小于 'ascent' 基准线高度</yellow>"
|
||||
|
||||
@@ -17,6 +17,7 @@ import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.context.ContextHolder;
|
||||
import net.momirealms.craftengine.core.world.Vec3d;
|
||||
import net.momirealms.craftengine.core.world.World;
|
||||
import net.momirealms.craftengine.core.world.WorldEvents;
|
||||
import net.momirealms.sparrow.nbt.CompoundTag;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
@@ -183,7 +184,7 @@ public final class CraftEngineBlocks {
|
||||
world.playBlockSound(vec3d, state.sounds().breakSound());
|
||||
}
|
||||
if (sendParticles) {
|
||||
FastNMS.INSTANCE.method$Level$levelEvent(world.serverWorld(), 2001, LocationUtils.toBlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()), state.customBlockState().registryId());
|
||||
FastNMS.INSTANCE.method$Level$levelEvent(world.serverWorld(), WorldEvents.BLOCK_BREAK_EFFECT, LocationUtils.toBlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()), state.customBlockState().registryId());
|
||||
}
|
||||
block.setType(Material.AIR, applyPhysics);
|
||||
return true;
|
||||
|
||||
@@ -7,15 +7,14 @@ import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
|
||||
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
|
||||
import net.momirealms.craftengine.bukkit.util.*;
|
||||
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
|
||||
import net.momirealms.craftengine.core.block.BlockSettings;
|
||||
import net.momirealms.craftengine.core.block.ImmutableBlockState;
|
||||
import net.momirealms.craftengine.core.block.properties.Property;
|
||||
import net.momirealms.craftengine.core.entity.player.InteractionHand;
|
||||
import net.momirealms.craftengine.core.item.Item;
|
||||
import net.momirealms.craftengine.core.item.ItemKeys;
|
||||
import net.momirealms.craftengine.core.loot.LootTable;
|
||||
import net.momirealms.craftengine.core.loot.parameter.LootParameters;
|
||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
import net.momirealms.craftengine.core.util.context.ContextHolder;
|
||||
import net.momirealms.craftengine.core.world.BlockPos;
|
||||
@@ -38,7 +37,6 @@ import org.bukkit.event.world.GenericGameEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class BlockEventListener implements Listener {
|
||||
private final BukkitCraftEngine plugin;
|
||||
@@ -95,7 +93,7 @@ public class BlockEventListener implements Listener {
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) // I forget why it's LOW before
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
|
||||
public void onPlayerBreak(BlockBreakEvent event) {
|
||||
org.bukkit.block.Block block = event.getBlock();
|
||||
Object blockState = BlockStateUtils.blockDataToBlockState(block.getBlockData());
|
||||
@@ -105,6 +103,11 @@ public class BlockEventListener implements Listener {
|
||||
ImmutableBlockState state = manager.getImmutableBlockStateUnsafe(stateId);
|
||||
if (!state.isEmpty()) {
|
||||
Location location = block.getLocation();
|
||||
BukkitServerPlayer serverPlayer = this.plugin.adapt(player);
|
||||
// double check adventure mode to prevent dupe
|
||||
if (!FastNMS.INSTANCE.mayBuild(serverPlayer.serverPlayer()) && !serverPlayer.canBreak(LocationUtils.toBlockPos(location), null)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// trigger event
|
||||
CustomBlockBreakEvent customBreakEvent = new CustomBlockBreakEvent(event.getPlayer(), location, block, state);
|
||||
@@ -127,16 +130,19 @@ public class BlockEventListener implements Listener {
|
||||
// play sound
|
||||
Vec3d vec3d = new Vec3d(location.getBlockX() + 0.5, location.getBlockY() + 0.5, location.getBlockZ() + 0.5);
|
||||
world.playBlockSound(vec3d, state.sounds().breakSound());
|
||||
if (player.getGameMode() == GameMode.CREATIVE) {
|
||||
if (player.getGameMode() == GameMode.CREATIVE || !customBreakEvent.dropItems()) {
|
||||
return;
|
||||
}
|
||||
|
||||
BukkitServerPlayer serverPlayer = this.plugin.adapt(player);
|
||||
Item<ItemStack> itemInHand = serverPlayer.getItemInHand(InteractionHand.MAIN_HAND);
|
||||
Key itemId = Optional.ofNullable(itemInHand).map(Item::id).orElse(ItemKeys.AIR);
|
||||
// do not drop if it's not the correct tool
|
||||
if (!state.settings().isCorrectTool(itemId) || !customBreakEvent.dropItems()) {
|
||||
return;
|
||||
BlockSettings settings = state.settings();
|
||||
if (settings.requireCorrectTool()) {
|
||||
if (itemInHand == null) return;
|
||||
if (!settings.isCorrectTool(itemInHand.id()) &&
|
||||
(!settings.respectToolComponent() || !FastNMS.INSTANCE.method$ItemStack$isCorrectToolForDrops(itemInHand.getLiteralObject(), state.customBlockState().handle()))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// drop items
|
||||
ContextHolder.Builder builder = ContextHolder.builder();
|
||||
|
||||
@@ -133,6 +133,7 @@ public class BukkitCustomBlock extends CustomBlock {
|
||||
if (settings.burnable()) {
|
||||
Reflections.method$FireBlock$setFlammable.invoke(Reflections.instance$Blocks$FIRE, mcBlock, settings.burnChance(), settings.fireSpreadChance());
|
||||
}
|
||||
Reflections.field$BlockStateBase$requiresCorrectToolForDrops.set(mcBlockState, settings.requireCorrectTool());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
CraftEngine.instance().logger().warn("Failed to init block settings", e);
|
||||
|
||||
@@ -19,6 +19,7 @@ import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
import net.momirealms.craftengine.core.util.context.ContextHolder;
|
||||
import net.momirealms.craftengine.core.world.BlockPos;
|
||||
import net.momirealms.craftengine.core.world.Vec3d;
|
||||
import net.momirealms.craftengine.core.world.WorldEvents;
|
||||
import net.momirealms.craftengine.shared.block.BlockBehavior;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
@@ -76,7 +77,7 @@ public class BushBlockBehavior extends BukkitBlockBehavior {
|
||||
world.dropItemNaturally(vec3d, item);
|
||||
}
|
||||
world.playBlockSound(vec3d, previousState.sounds().breakSound());
|
||||
FastNMS.INSTANCE.method$Level$levelEvent(level, 2001, blockPos, stateId);
|
||||
FastNMS.INSTANCE.method$Level$levelEvent(level, WorldEvents.BLOCK_BREAK_EFFECT, blockPos, stateId);
|
||||
}
|
||||
return Reflections.method$Block$defaultBlockState.invoke(Reflections.instance$Blocks$AIR);
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
import net.momirealms.craftengine.core.util.context.ContextHolder;
|
||||
import net.momirealms.craftengine.core.world.BlockPos;
|
||||
import net.momirealms.craftengine.core.world.Vec3d;
|
||||
import net.momirealms.craftengine.core.world.WorldEvents;
|
||||
import net.momirealms.craftengine.shared.block.BlockBehavior;
|
||||
|
||||
import java.util.List;
|
||||
@@ -69,7 +70,7 @@ public class SugarCaneBlockBehavior extends BushBlockBehavior {
|
||||
world.dropItemNaturally(vec3d, item);
|
||||
}
|
||||
world.playBlockSound(vec3d, currentState.sounds().breakSound());
|
||||
FastNMS.INSTANCE.method$Level$levelEvent(level, 2001, blockPos, stateId);
|
||||
FastNMS.INSTANCE.method$Level$levelEvent(level, WorldEvents.BLOCK_BREAK_EFFECT, blockPos, stateId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,63 +1,116 @@
|
||||
package net.momirealms.craftengine.bukkit.item;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
|
||||
import net.momirealms.craftengine.bukkit.util.MaterialUtils;
|
||||
import net.momirealms.craftengine.core.item.CustomItem;
|
||||
import net.momirealms.craftengine.core.item.Item;
|
||||
import net.momirealms.craftengine.core.item.ItemBuildContext;
|
||||
import net.momirealms.craftengine.core.item.ItemSettings;
|
||||
import net.momirealms.craftengine.core.item.*;
|
||||
import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
|
||||
import net.momirealms.craftengine.core.item.modifier.ItemModifier;
|
||||
import net.momirealms.craftengine.core.item.modifier.ItemDataModifier;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class BukkitCustomItem implements CustomItem<ItemStack> {
|
||||
private final Key id;
|
||||
private final Key materialKey;
|
||||
private final Material material;
|
||||
private final List<ItemModifier<ItemStack>> modifiers;
|
||||
private final List<ItemBehavior> behavior;
|
||||
private final ItemDataModifier<ItemStack>[] modifiers;
|
||||
private final Map<String, ItemDataModifier<ItemStack>> modifierMap;
|
||||
private final ItemDataModifier<ItemStack>[] clientBoundModifiers;
|
||||
private final Map<String, ItemDataModifier<ItemStack>> clientBoundModifierMap;
|
||||
private final NetworkItemDataProcessor<ItemStack>[] networkItemDataProcessors;
|
||||
private final List<ItemBehavior> behaviors;
|
||||
private final ItemSettings settings;
|
||||
|
||||
public BukkitCustomItem(Key id, Key materialKey, Material material, List<ItemModifier<ItemStack>> modifiers, List<ItemBehavior> behavior, ItemSettings settings) {
|
||||
@SuppressWarnings("unchecked")
|
||||
public BukkitCustomItem(Key id,
|
||||
Key materialKey,
|
||||
Material material,
|
||||
List<ItemDataModifier<ItemStack>> modifiers,
|
||||
List<ItemDataModifier<ItemStack>> clientBoundModifiers,
|
||||
List<ItemBehavior> behaviors,
|
||||
ItemSettings settings) {
|
||||
this.id = id;
|
||||
this.material = material;
|
||||
this.modifiers = modifiers;
|
||||
this.behavior = behavior;
|
||||
this.materialKey = materialKey;
|
||||
// unchecked cast
|
||||
this.modifiers = modifiers.toArray(new ItemDataModifier[0]);
|
||||
// unchecked cast
|
||||
this.clientBoundModifiers = clientBoundModifiers.toArray(new ItemDataModifier[0]);
|
||||
this.behaviors = List.copyOf(behaviors);
|
||||
this.settings = settings;
|
||||
ImmutableMap.Builder<String, ItemDataModifier<ItemStack>> modifierMapBuilder = ImmutableMap.builder();
|
||||
for (ItemDataModifier<ItemStack> modifier : modifiers) {
|
||||
modifierMapBuilder.put(modifier.name(), modifier);
|
||||
}
|
||||
this.modifierMap = modifierMapBuilder.build();
|
||||
ImmutableMap.Builder<String, ItemDataModifier<ItemStack>> clientSideModifierMapBuilder = ImmutableMap.builder();
|
||||
List<NetworkItemDataProcessor<ItemStack>> networkItemDataProcessors = new ArrayList<>();
|
||||
for (ItemDataModifier<ItemStack> modifier : clientBoundModifiers) {
|
||||
String name = modifier.name();
|
||||
clientSideModifierMapBuilder.put(name, modifier);
|
||||
if (this.modifierMap.containsKey(name)) {
|
||||
networkItemDataProcessors.add(NetworkItemDataProcessor.both(this.modifierMap.get(name), modifier));
|
||||
} else {
|
||||
networkItemDataProcessors.add(NetworkItemDataProcessor.clientOnly(modifier));
|
||||
}
|
||||
}
|
||||
this.clientBoundModifierMap = clientSideModifierMapBuilder.build();
|
||||
// unchecked cast
|
||||
this.networkItemDataProcessors = networkItemDataProcessors.toArray(new NetworkItemDataProcessor[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key id() {
|
||||
return id;
|
||||
return this.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key material() {
|
||||
return materialKey;
|
||||
return this.materialKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ItemModifier<ItemStack>> modifiers() {
|
||||
return modifiers;
|
||||
public NetworkItemDataProcessor<ItemStack>[] networkItemDataProcessors() {
|
||||
return this.networkItemDataProcessors;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemDataModifier<ItemStack>[] dataModifiers() {
|
||||
return this.modifiers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, ItemDataModifier<ItemStack>> dataModifierMap() {
|
||||
return this.modifierMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasClientBoundDataModifier() {
|
||||
return this.clientBoundModifiers.length != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemDataModifier<ItemStack>[] clientBoundDataModifiers() {
|
||||
return this.clientBoundModifiers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, ItemDataModifier<ItemStack>> clientBoundDataModifierMap() {
|
||||
return this.clientBoundModifierMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack buildItemStack(ItemBuildContext context, int count) {
|
||||
ItemStack item = new ItemStack(material);
|
||||
if (this.modifiers.isEmpty()) {
|
||||
return item;
|
||||
}
|
||||
ItemStack item = new ItemStack(this.material);
|
||||
Item<ItemStack> wrapped = BukkitCraftEngine.instance().itemManager().wrap(item);
|
||||
wrapped.count(count);
|
||||
for (ItemModifier<ItemStack> modifier : this.modifiers) {
|
||||
for (ItemDataModifier<ItemStack> modifier : this.modifiers) {
|
||||
modifier.apply(wrapped, context);
|
||||
}
|
||||
return wrapped.load();
|
||||
@@ -65,14 +118,14 @@ public class BukkitCustomItem implements CustomItem<ItemStack> {
|
||||
|
||||
@Override
|
||||
public ItemSettings settings() {
|
||||
return settings;
|
||||
return this.settings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Item<ItemStack> buildItem(ItemBuildContext context) {
|
||||
ItemStack item = new ItemStack(material);
|
||||
ItemStack item = new ItemStack(this.material);
|
||||
Item<ItemStack> wrapped = BukkitCraftEngine.instance().itemManager().wrap(item);
|
||||
for (ItemModifier<ItemStack> modifier : modifiers()) {
|
||||
for (ItemDataModifier<ItemStack> modifier : dataModifiers()) {
|
||||
modifier.apply(wrapped, context);
|
||||
}
|
||||
wrapped.load();
|
||||
@@ -81,7 +134,7 @@ public class BukkitCustomItem implements CustomItem<ItemStack> {
|
||||
|
||||
@Override
|
||||
public @NotNull List<ItemBehavior> behaviors() {
|
||||
return this.behavior;
|
||||
return this.behaviors;
|
||||
}
|
||||
|
||||
public static Builder<ItemStack> builder() {
|
||||
@@ -92,9 +145,10 @@ public class BukkitCustomItem implements CustomItem<ItemStack> {
|
||||
private Key id;
|
||||
private Material material;
|
||||
private Key materialKey;
|
||||
private List<ItemBehavior> behavior = List.of();
|
||||
private ItemSettings settings = ItemSettings.of();
|
||||
private final List<ItemModifier<ItemStack>> modifiers = new ArrayList<>();
|
||||
private ItemSettings settings;
|
||||
private final List<ItemBehavior> behaviors = new ArrayList<>();
|
||||
private final List<ItemDataModifier<ItemStack>> modifiers = new ArrayList<>();
|
||||
private final List<ItemDataModifier<ItemStack>> clientBoundModifiers = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public Builder<ItemStack> id(Key id) {
|
||||
@@ -110,26 +164,38 @@ public class BukkitCustomItem implements CustomItem<ItemStack> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder<ItemStack> modifier(ItemModifier<ItemStack> modifier) {
|
||||
public Builder<ItemStack> dataModifier(ItemDataModifier<ItemStack> modifier) {
|
||||
this.modifiers.add(modifier);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder<ItemStack> modifiers(List<ItemModifier<ItemStack>> list) {
|
||||
this.modifiers.addAll(list);
|
||||
public Builder<ItemStack> dataModifiers(List<ItemDataModifier<ItemStack>> modifiers) {
|
||||
this.modifiers.addAll(modifiers);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder<ItemStack> clientBoundDataModifier(ItemDataModifier<ItemStack> modifier) {
|
||||
this.clientBoundModifiers.add(modifier);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder<ItemStack> clientBoundDataModifiers(List<ItemDataModifier<ItemStack>> modifiers) {
|
||||
this.clientBoundModifiers.addAll(modifiers);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder<ItemStack> behavior(ItemBehavior behavior) {
|
||||
this.behavior= List.of(behavior);
|
||||
this.behaviors.add(behavior);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder<ItemStack> behavior(List<ItemBehavior> behaviors) {
|
||||
this.behavior = behaviors;
|
||||
public Builder<ItemStack> behaviors(List<ItemBehavior> behaviors) {
|
||||
this.behaviors.addAll(behaviors);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -142,7 +208,7 @@ public class BukkitCustomItem implements CustomItem<ItemStack> {
|
||||
@Override
|
||||
public CustomItem<ItemStack> build() {
|
||||
this.modifiers.addAll(this.settings.modifiers());
|
||||
return new BukkitCustomItem(id, materialKey, material, Collections.unmodifiableList(modifiers), behavior, settings);
|
||||
return new BukkitCustomItem(this.id, this.materialKey, this.material, this.modifiers, this.clientBoundModifiers, this.behaviors, this.settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,10 +35,7 @@ import net.momirealms.craftengine.core.plugin.locale.TranslationManager;
|
||||
import net.momirealms.craftengine.core.registry.BuiltInRegistries;
|
||||
import net.momirealms.craftengine.core.registry.Holder;
|
||||
import net.momirealms.craftengine.core.registry.WritableRegistry;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.MiscUtils;
|
||||
import net.momirealms.craftengine.core.util.ResourceKey;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
import net.momirealms.craftengine.core.util.*;
|
||||
import net.momirealms.craftengine.core.util.context.ContextHolder;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
@@ -50,8 +47,10 @@ import org.incendo.cloud.suggestion.Suggestion;
|
||||
import org.incendo.cloud.type.Either;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class BukkitItemManager extends AbstractItemManager<ItemStack> {
|
||||
static {
|
||||
@@ -70,13 +69,63 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
|
||||
|
||||
public BukkitItemManager(BukkitCraftEngine plugin) {
|
||||
super(plugin);
|
||||
instance = this;
|
||||
this.plugin = plugin;
|
||||
this.factory = BukkitItemFactory.create(plugin);
|
||||
this.itemEventListener = new ItemEventListener(plugin);
|
||||
this.debugStickListener = new DebugStickListener(plugin);
|
||||
this.itemParser = new ItemParser();
|
||||
this.registerAllVanillaItems();
|
||||
instance = this;
|
||||
if (plugin.hasMod() && VersionHelper.isVersionNewerThan1_20_5()) {
|
||||
Class<?> clazz$CustomStreamCodec = ReflectionUtils.getClazz("net.momirealms.craftengine.mod.item.CustomStreamCodec");
|
||||
if (clazz$CustomStreamCodec != null) {
|
||||
Field s2cProcessor = ReflectionUtils.getDeclaredField(clazz$CustomStreamCodec, Function.class, 0);
|
||||
Field c2sProcessor = ReflectionUtils.getDeclaredField(clazz$CustomStreamCodec, Function.class, 1);
|
||||
Function<Object, Object> s2c = (raw) -> {
|
||||
ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(raw);
|
||||
Item<ItemStack> wrapped = this.wrap(itemStack.clone());
|
||||
Optional<CustomItem<ItemStack>> customItem = wrapped.getCustomItem();
|
||||
if (customItem.isEmpty()) {
|
||||
return raw;
|
||||
}
|
||||
CustomItem<ItemStack> custom = customItem.get();
|
||||
if (!custom.hasClientBoundDataModifier()) {
|
||||
return raw;
|
||||
}
|
||||
for (NetworkItemDataProcessor<ItemStack> processor : custom.networkItemDataProcessors()) {
|
||||
processor.toClient(wrapped, ItemBuildContext.EMPTY);
|
||||
}
|
||||
wrapped.load();
|
||||
return wrapped.getLiteralObject();
|
||||
};
|
||||
|
||||
Function<Object, Object> c2s = (raw) -> {
|
||||
ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(raw);
|
||||
Item<ItemStack> wrapped = this.wrap(itemStack);
|
||||
Optional<CustomItem<ItemStack>> customItem = wrapped.getCustomItem();
|
||||
if (customItem.isEmpty()) {
|
||||
return raw;
|
||||
}
|
||||
CustomItem<ItemStack> custom = customItem.get();
|
||||
if (!custom.hasClientBoundDataModifier()) {
|
||||
return raw;
|
||||
}
|
||||
for (NetworkItemDataProcessor<ItemStack> processor : custom.networkItemDataProcessors()) {
|
||||
processor.toServer(wrapped, ItemBuildContext.EMPTY);
|
||||
}
|
||||
wrapped.load();
|
||||
return wrapped.getLiteralObject();
|
||||
};
|
||||
try {
|
||||
assert s2cProcessor != null;
|
||||
s2cProcessor.set(null, s2c);
|
||||
assert c2sProcessor != null;
|
||||
c2sProcessor.set(null, c2s);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
plugin.logger().warn("Failed to load custom stream codec", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -218,7 +267,10 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
|
||||
.orElseGet(() -> ((WritableRegistry<Key>) BuiltInRegistries.OPTIMIZED_ITEM_ID)
|
||||
.register(new ResourceKey<>(BuiltInRegistries.OPTIMIZED_ITEM_ID.key().location(), id), id));
|
||||
|
||||
boolean isVanillaItem = id.namespace().equals("minecraft") && Registry.MATERIAL.get(new NamespacedKey(id.namespace(), id.value())) != null;
|
||||
String materialStringId = (String) section.get("material");
|
||||
if (isVanillaItem)
|
||||
materialStringId = id.value();
|
||||
if (materialStringId == null) {
|
||||
TranslationManager.instance().log("warning.config.item.lack_material", path.toString(), id.toString());
|
||||
return;
|
||||
@@ -235,14 +287,12 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
|
||||
Key itemModelKey = null;
|
||||
|
||||
CustomItem.Builder<ItemStack> itemBuilder = BukkitCustomItem.builder().id(id).material(materialId);
|
||||
itemBuilder.modifier(new IdModifier<>(id));
|
||||
|
||||
boolean hasItemModelSection = section.containsKey("item-model");
|
||||
|
||||
// To get at least one model provider
|
||||
// Sets some basic model info
|
||||
if (customModelData != 0) {
|
||||
itemBuilder.modifier(new CustomModelDataModifier<>(customModelData));
|
||||
itemBuilder.dataModifier(new CustomModelDataModifier<>(customModelData));
|
||||
}
|
||||
// Requires the item to have model before apply item-model
|
||||
else if (!hasItemModelSection && section.containsKey("model") && VersionHelper.isVersionNewerThan1_21_2()) {
|
||||
@@ -250,7 +300,7 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
|
||||
// customize or use the id
|
||||
itemModelKey = Key.from(section.getOrDefault("item-model", id.toString()).toString());
|
||||
if (ResourceLocation.isValid(itemModelKey.toString())) {
|
||||
itemBuilder.modifier(new ItemModelModifier<>(itemModelKey));
|
||||
itemBuilder.dataModifier(new ItemModelModifier<>(itemModelKey));
|
||||
} else {
|
||||
itemModelKey = null;
|
||||
}
|
||||
@@ -258,7 +308,7 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
|
||||
|
||||
if (hasItemModelSection) {
|
||||
itemModelKey = Key.from(section.get("item-model").toString());
|
||||
itemBuilder.modifier(new ItemModelModifier<>(itemModelKey));
|
||||
itemBuilder.dataModifier(new ItemModelModifier<>(itemModelKey));
|
||||
}
|
||||
|
||||
// Get item behaviors
|
||||
@@ -270,7 +320,7 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
|
||||
for (Map<String, Object> behaviorMap : behavior) {
|
||||
behaviors.add(ItemBehaviors.fromMap(pack, path, id, behaviorMap));
|
||||
}
|
||||
itemBuilder.behavior(behaviors);
|
||||
itemBuilder.behaviors(behaviors);
|
||||
} else if (behaviorConfig instanceof Map<?, ?>) {
|
||||
Map<String, Object> behaviorSection = MiscUtils.castToMap(section.get("behavior"), true);
|
||||
if (behaviorSection != null) {
|
||||
@@ -284,7 +334,7 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
|
||||
for (Map.Entry<String, Object> dataEntry : dataSection.entrySet()) {
|
||||
Optional.ofNullable(dataFunctions.get(dataEntry.getKey())).ifPresent(function -> {
|
||||
try {
|
||||
itemBuilder.modifier(function.apply(dataEntry.getValue()));
|
||||
itemBuilder.dataModifier(function.apply(dataEntry.getValue()));
|
||||
} catch (IllegalArgumentException e) {
|
||||
plugin.logger().warn("Invalid data format", e);
|
||||
}
|
||||
@@ -292,10 +342,35 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
|
||||
}
|
||||
}
|
||||
|
||||
// Add it here to make sure that ce id is always applied
|
||||
if (!isVanillaItem)
|
||||
itemBuilder.dataModifier(new IdModifier<>(id));
|
||||
|
||||
// Get item data
|
||||
Map<String, Object> clientSideDataSection = MiscUtils.castToMap(section.get("client-bound-data"), true);
|
||||
if (clientSideDataSection != null) {
|
||||
for (Map.Entry<String, Object> dataEntry : clientSideDataSection.entrySet()) {
|
||||
Optional.ofNullable(dataFunctions.get(dataEntry.getKey())).ifPresent(function -> {
|
||||
try {
|
||||
itemBuilder.clientBoundDataModifier(function.apply(dataEntry.getValue()));
|
||||
} catch (IllegalArgumentException e) {
|
||||
plugin.logger().warn("Invalid client bound data format", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ItemSettings itemSettings;
|
||||
if (section.containsKey("settings")) {
|
||||
Map<String, Object> settings = MiscUtils.castToMap(section.get("settings"), false);
|
||||
itemBuilder.settings(ItemSettings.fromMap(settings));
|
||||
itemSettings = ItemSettings.fromMap(settings);
|
||||
} else {
|
||||
itemSettings = ItemSettings.of();
|
||||
}
|
||||
if (isVanillaItem) {
|
||||
itemSettings.canPlaceRelatedVanillaBlock(true);
|
||||
}
|
||||
itemBuilder.settings(itemSettings);
|
||||
|
||||
CustomItem<ItemStack> customItem = itemBuilder.build();
|
||||
customItems.put(id, customItem);
|
||||
|
||||
@@ -11,6 +11,7 @@ import net.momirealms.craftengine.bukkit.util.*;
|
||||
import net.momirealms.craftengine.core.block.ImmutableBlockState;
|
||||
import net.momirealms.craftengine.core.entity.player.InteractionHand;
|
||||
import net.momirealms.craftengine.core.entity.player.InteractionResult;
|
||||
import net.momirealms.craftengine.core.item.CustomItem;
|
||||
import net.momirealms.craftengine.core.item.Item;
|
||||
import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
|
||||
import net.momirealms.craftengine.core.item.context.UseOnContext;
|
||||
@@ -78,15 +79,15 @@ public class ItemEventListener implements Listener {
|
||||
|
||||
@EventHandler
|
||||
public void onInteractAir(PlayerInteractEvent event) {
|
||||
if (event.getAction() != Action.RIGHT_CLICK_AIR) return;
|
||||
if (event.getAction() != Action.RIGHT_CLICK_AIR)
|
||||
return;
|
||||
Player bukkitPlayer = event.getPlayer();
|
||||
BukkitServerPlayer player = this.plugin.adapt(bukkitPlayer);
|
||||
InteractionHand hand = event.getHand() == EquipmentSlot.HAND ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND;
|
||||
if (cancelEventIfHasInteraction(event, player, hand)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (player.isSpectatorMode() || player.isAdventureMode()) {
|
||||
if (player.isSpectatorMode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -138,7 +139,12 @@ public class ItemEventListener implements Listener {
|
||||
boolean interactable = InteractUtils.isInteractable(BlockStateUtils.getBlockOwnerId(clickedBlock), bukkitPlayer, clickedBlock.getBlockData(), hitResult, itemInHand);
|
||||
|
||||
// do not allow to place block if it's a vanilla block
|
||||
if (itemInHand.isBlockItem() && itemInHand.isCustomItem()) {
|
||||
Optional<CustomItem<ItemStack>> optionalCustomItem = itemInHand.getCustomItem();
|
||||
if (itemInHand.isBlockItem() && optionalCustomItem.isPresent()) {
|
||||
// it's a custom item, but now it's ignored
|
||||
if (optionalCustomItem.get().settings().canPlaceRelatedVanillaBlock()) {
|
||||
return;
|
||||
}
|
||||
if (!interactable || player.isSecondaryUseActive()) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
@@ -149,9 +155,8 @@ public class ItemEventListener implements Listener {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO We need to further investigate how to handle adventure mode
|
||||
// no spectator interactions
|
||||
if (player.isSpectatorMode() || player.isAdventureMode()) {
|
||||
if (player.isSpectatorMode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -214,7 +219,7 @@ public class ItemEventListener implements Listener {
|
||||
int currentTicks = player.gameTicks();
|
||||
// The client will send multiple packets to the server if the client thinks it should
|
||||
// However, if the main hand item interaction is successful, the off-hand item should be blocked.
|
||||
if (!player.updateLastSuccessfulInteractionTick(currentTicks)) {
|
||||
if (player.lastSuccessfulInteractionTick() == currentTicks) {
|
||||
event.setCancelled(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -53,6 +53,10 @@ public class AxeItemBehavior extends ItemBehavior {
|
||||
}
|
||||
|
||||
Player player = context.getPlayer();
|
||||
// no adventure mode
|
||||
if (player.isAdventureMode()) {
|
||||
return InteractionResult.PASS;
|
||||
}
|
||||
|
||||
Item<ItemStack> offHandItem = (Item<ItemStack>) player.getItemInHand(InteractionHand.OFF_HAND);
|
||||
// is using a shield
|
||||
|
||||
@@ -5,10 +5,7 @@ import net.momirealms.craftengine.bukkit.api.event.CustomBlockAttemptPlaceEvent;
|
||||
import net.momirealms.craftengine.bukkit.api.event.CustomBlockPlaceEvent;
|
||||
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.util.DirectionUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.EventUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.LocationUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.Reflections;
|
||||
import net.momirealms.craftengine.bukkit.util.*;
|
||||
import net.momirealms.craftengine.core.block.CustomBlock;
|
||||
import net.momirealms.craftengine.core.block.ImmutableBlockState;
|
||||
import net.momirealms.craftengine.core.block.UpdateOption;
|
||||
@@ -22,6 +19,7 @@ import net.momirealms.craftengine.core.item.context.BlockPlaceContext;
|
||||
import net.momirealms.craftengine.core.item.context.UseOnContext;
|
||||
import net.momirealms.craftengine.core.pack.Pack;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.MiscUtils;
|
||||
import net.momirealms.craftengine.core.world.BlockPos;
|
||||
@@ -76,20 +74,36 @@ public class BlockItemBehavior extends ItemBehavior {
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
Player player = placeContext.getPlayer();
|
||||
int gameTicks = player.gameTicks();
|
||||
if (!player.updateLastSuccessfulInteractionTick(gameTicks)) {
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
|
||||
BlockPos pos = placeContext.getClickedPos();
|
||||
BlockPos againstPos = placeContext.getAgainstPos();
|
||||
World world = (World) placeContext.getLevel().platformWorld();
|
||||
Location placeLocation = new Location(world, pos.x(), pos.y(), pos.z());
|
||||
|
||||
int gameTicks = player.gameTicks();
|
||||
if (!player.updateLastSuccessfulInteractionTick(gameTicks)) {
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
|
||||
Block bukkitBlock = world.getBlockAt(placeLocation);
|
||||
Block againstBlock = world.getBlockAt(againstPos.x(), againstPos.y(), againstPos.z());
|
||||
org.bukkit.entity.Player bukkitPlayer = (org.bukkit.entity.Player) player.platformPlayer();
|
||||
|
||||
if (player.isAdventureMode()) {
|
||||
Object againstBlockState = BlockStateUtils.blockDataToBlockState(againstBlock.getBlockData());
|
||||
int stateId = BlockStateUtils.blockStateToId(againstBlockState);
|
||||
if (BlockStateUtils.isVanillaBlock(stateId)) {
|
||||
if (!AdventureModeUtils.canPlace(context.getItem(), context.getLevel(), againstPos, againstBlockState)) {
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
} else {
|
||||
ImmutableBlockState customState = BukkitBlockManager.instance().getImmutableBlockStateUnsafe(stateId);
|
||||
// custom block
|
||||
if (!AdventureModeUtils.canPlace(context.getItem(), context.getLevel(), againstPos, Config.simplifyAdventurePlaceCheck() ? customState.vanillaBlockState().handle() : againstBlockState)) {
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// trigger event
|
||||
CustomBlockAttemptPlaceEvent attemptPlaceEvent = new CustomBlockAttemptPlaceEvent(bukkitPlayer, placeLocation.clone(), blockStateToPlace,
|
||||
DirectionUtils.toBlockFace(context.getClickedFace()), bukkitBlock, context.getHand());
|
||||
|
||||
@@ -26,6 +26,10 @@ public class BoneMealItemBehavior extends ItemBehavior {
|
||||
|
||||
@Override
|
||||
public InteractionResult useOnBlock(UseOnContext context) {
|
||||
if (context.getPlayer().isAdventureMode()) {
|
||||
return InteractionResult.PASS;
|
||||
}
|
||||
|
||||
BukkitWorldBlock clicked = (BukkitWorldBlock) context.getLevel().getBlockAt(context.getClickedPos());
|
||||
Block block = clicked.block();
|
||||
ImmutableBlockState state = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockDataToId(block.getBlockData()));
|
||||
|
||||
@@ -34,6 +34,7 @@ public class BucketItemBehavior extends ItemBehavior {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public InteractionResult useOnBlock(UseOnContext context) {
|
||||
if (context.getPlayer().isAdventureMode()) return InteractionResult.PASS;
|
||||
BukkitWorldBlock clicked = (BukkitWorldBlock) context.getLevel().getBlockAt(context.getClickedPos());
|
||||
Block block = clicked.block();
|
||||
ImmutableBlockState state = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockDataToId(block.getBlockData()));
|
||||
|
||||
@@ -67,6 +67,11 @@ public class FurnitureItemBehavior extends ItemBehavior {
|
||||
}
|
||||
|
||||
Player player = context.getPlayer();
|
||||
// todo adventure check
|
||||
if (player.isAdventureMode()) {
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
|
||||
int gameTicks = player.gameTicks();
|
||||
if (!player.updateLastSuccessfulInteractionTick(gameTicks)) {
|
||||
return InteractionResult.FAIL;
|
||||
|
||||
@@ -31,6 +31,7 @@ public class WaterBucketItemBehavior extends ItemBehavior {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public InteractionResult useOnBlock(UseOnContext context) {
|
||||
if (context.getPlayer().isAdventureMode()) return InteractionResult.PASS;
|
||||
BlockPos pos = context.getClickedPos();
|
||||
BukkitWorldBlock clicked = (BukkitWorldBlock) context.getLevel().getBlockAt(pos);
|
||||
Block block = clicked.block();
|
||||
|
||||
@@ -9,6 +9,7 @@ import net.momirealms.craftengine.core.item.ItemWrapper;
|
||||
import net.momirealms.craftengine.core.item.modifier.IdModifier;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.Objects;
|
||||
@@ -43,7 +44,10 @@ public abstract class BukkitItemFactory extends ItemFactory<CraftEngine, RTagIte
|
||||
@Override
|
||||
protected Key id(ItemWrapper<ItemStack> item) {
|
||||
Object id = item.get(IdModifier.CRAFT_ENGINE_ID);
|
||||
if (id == null) return Key.of(item.getItem().getType().getKey().asString());
|
||||
if (id == null) {
|
||||
NamespacedKey key = item.getItem().getType().getKey();
|
||||
return Key.of(key.getNamespace(), key.getKey());
|
||||
}
|
||||
return Key.of(id.toString());
|
||||
}
|
||||
|
||||
@@ -89,26 +93,6 @@ public abstract class BukkitItemFactory extends ItemFactory<CraftEngine, RTagIte
|
||||
return item.remove(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setComponent(ItemWrapper<ItemStack> item, String type, Object value) {
|
||||
item.setComponent(type, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object getComponent(ItemWrapper<ItemStack> item, String type) {
|
||||
return item.getComponent(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean hasComponent(ItemWrapper<ItemStack> item, String type) {
|
||||
return item.hasComponent(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void removeComponent(ItemWrapper<ItemStack> item, String type) {
|
||||
item.removeComponent(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void update(ItemWrapper<ItemStack> item) {
|
||||
item.update();
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
package net.momirealms.craftengine.bukkit.item.factory;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.saicone.rtag.RtagItem;
|
||||
import com.saicone.rtag.data.ComponentType;
|
||||
import com.saicone.rtag.item.ItemObject;
|
||||
import net.momirealms.craftengine.bukkit.item.RTagItemWrapper;
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.util.EnchantmentUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.KeyUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.Reflections;
|
||||
import net.momirealms.craftengine.core.item.ComponentKeys;
|
||||
import net.momirealms.craftengine.core.item.Enchantment;
|
||||
import net.momirealms.craftengine.core.item.ItemWrapper;
|
||||
import net.momirealms.craftengine.core.item.Trim;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
@@ -26,17 +30,63 @@ public class ComponentItemFactory extends BukkitItemFactory {
|
||||
super(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setComponent(ItemWrapper<ItemStack> item, Key type, Object value) {
|
||||
if (value instanceof JsonElement jsonElement) {
|
||||
setJsonComponentDirectly(item, type, jsonElement);
|
||||
} else {
|
||||
setJavaComponentDirectly(item, type, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void resetComponent(ItemWrapper<ItemStack> item, Key type) {
|
||||
FastNMS.INSTANCE.resetComponent(item.getLiteralObject(), KeyUtils.toResourceLocation(type));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object getComponent(ItemWrapper<ItemStack> item, Key type) {
|
||||
return item.getComponent(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean hasComponent(ItemWrapper<ItemStack> item, Key type) {
|
||||
return item.hasComponent(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void removeComponent(ItemWrapper<ItemStack> item, Key type) {
|
||||
FastNMS.INSTANCE.removeComponent(item.getLiteralObject(), KeyUtils.toResourceLocation(type));
|
||||
}
|
||||
|
||||
protected void setJavaComponentDirectly(ItemWrapper<ItemStack> item, Key type, Object value) {
|
||||
ComponentType.parseJava(type, value).ifPresent(it -> FastNMS.INSTANCE.setComponent(item.getLiteralObject(), KeyUtils.toResourceLocation(type), it));
|
||||
}
|
||||
|
||||
protected void setJsonComponentDirectly(ItemWrapper<ItemStack> item, Key type, JsonElement value) {
|
||||
ComponentType.parseJson(type, value).ifPresent(it -> FastNMS.INSTANCE.setComponent(item.getLiteralObject(), KeyUtils.toResourceLocation(type), it));
|
||||
}
|
||||
|
||||
protected void setNBTComponentDirectly(ItemWrapper<ItemStack> item, Key type, Object value) {
|
||||
ComponentType.parseNbt(type, value).ifPresent(it -> FastNMS.INSTANCE.setComponent(item.getLiteralObject(), KeyUtils.toResourceLocation(type), it));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object encodeJava(Key componentType, @Nullable Object component) {
|
||||
return ComponentType.encodeJava(componentType, component).orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsonElement encodeJson(Key type, Object component) {
|
||||
return ComponentType.encodeJson(type, component).orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void customModelData(ItemWrapper<ItemStack> item, Integer data) {
|
||||
if (data == null) {
|
||||
item.removeComponent(ComponentKeys.CUSTOM_MODEL_DATA);
|
||||
resetComponent(item, ComponentKeys.CUSTOM_MODEL_DATA);
|
||||
} else {
|
||||
item.setComponent(ComponentKeys.CUSTOM_MODEL_DATA, data);
|
||||
setJavaComponentDirectly(item, ComponentKeys.CUSTOM_MODEL_DATA, data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,9 +103,9 @@ public class ComponentItemFactory extends BukkitItemFactory {
|
||||
@Override
|
||||
protected void customName(ItemWrapper<ItemStack> item, String json) {
|
||||
if (json == null) {
|
||||
item.removeComponent(ComponentKeys.CUSTOM_NAME);
|
||||
resetComponent(item, ComponentKeys.CUSTOM_NAME);
|
||||
} else {
|
||||
item.setComponent(ComponentKeys.CUSTOM_NAME, json);
|
||||
setJavaComponentDirectly(item, ComponentKeys.CUSTOM_NAME, json);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,9 +123,9 @@ public class ComponentItemFactory extends BukkitItemFactory {
|
||||
@Override
|
||||
protected void itemName(ItemWrapper<ItemStack> item, String json) {
|
||||
if (json == null) {
|
||||
item.removeComponent(ComponentKeys.ITEM_NAME);
|
||||
resetComponent(item, ComponentKeys.ITEM_NAME);
|
||||
} else {
|
||||
item.setComponent(ComponentKeys.ITEM_NAME, json);
|
||||
setJavaComponentDirectly(item, ComponentKeys.ITEM_NAME, json);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,15 +142,12 @@ public class ComponentItemFactory extends BukkitItemFactory {
|
||||
|
||||
@Override
|
||||
protected void skull(ItemWrapper<ItemStack> item, String skullData) {
|
||||
final Map<String, Object> profile = Map.of(
|
||||
"properties", List.of(
|
||||
Map.of(
|
||||
"name", "textures",
|
||||
"value", skullData
|
||||
)
|
||||
)
|
||||
);
|
||||
item.setComponent("minecraft:profile", profile);
|
||||
if (skullData == null) {
|
||||
resetComponent(item, ComponentKeys.PROFILE);
|
||||
} else {
|
||||
Map<String, Object> profile = Map.of("properties", List.of(Map.of("name", "textures", "value", skullData)));
|
||||
setJavaComponentDirectly(item, ComponentKeys.PROFILE, profile);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -118,9 +165,9 @@ public class ComponentItemFactory extends BukkitItemFactory {
|
||||
@Override
|
||||
protected void lore(ItemWrapper<ItemStack> item, List<String> lore) {
|
||||
if (lore == null || lore.isEmpty()) {
|
||||
item.removeComponent(ComponentKeys.LORE);
|
||||
resetComponent(item, ComponentKeys.LORE);
|
||||
} else {
|
||||
item.setComponent(ComponentKeys.LORE, lore);
|
||||
setJavaComponentDirectly(item, ComponentKeys.LORE, lore);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,9 +179,9 @@ public class ComponentItemFactory extends BukkitItemFactory {
|
||||
@Override
|
||||
protected void unbreakable(ItemWrapper<ItemStack> item, boolean unbreakable) {
|
||||
if (unbreakable) {
|
||||
item.removeComponent(ComponentKeys.UNBREAKABLE);
|
||||
resetComponent(item, ComponentKeys.UNBREAKABLE);
|
||||
} else {
|
||||
item.setComponent(ComponentKeys.UNBREAKABLE, true);
|
||||
setJavaComponentDirectly(item, ComponentKeys.UNBREAKABLE, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,7 +192,11 @@ public class ComponentItemFactory extends BukkitItemFactory {
|
||||
|
||||
@Override
|
||||
protected void glint(ItemWrapper<ItemStack> item, Boolean glint) {
|
||||
item.setComponent(ComponentKeys.ENCHANTMENT_GLINT_OVERRIDE, glint);
|
||||
if (glint == null) {
|
||||
resetComponent(item, ComponentKeys.ENCHANTMENT_GLINT_OVERRIDE);
|
||||
} else {
|
||||
setJavaComponentDirectly(item, ComponentKeys.ENCHANTMENT_GLINT_OVERRIDE, glint);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -161,8 +212,11 @@ public class ComponentItemFactory extends BukkitItemFactory {
|
||||
|
||||
@Override
|
||||
protected void damage(ItemWrapper<ItemStack> item, Integer damage) {
|
||||
if (damage == null) damage = 0;
|
||||
item.setComponent(ComponentKeys.DAMAGE, damage);
|
||||
if (damage == null) {
|
||||
resetComponent(item, ComponentKeys.DAMAGE);
|
||||
} else {
|
||||
setJavaComponentDirectly(item, ComponentKeys.DAMAGE, damage);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -179,9 +233,9 @@ public class ComponentItemFactory extends BukkitItemFactory {
|
||||
@Override
|
||||
protected void maxDamage(ItemWrapper<ItemStack> item, Integer damage) {
|
||||
if (damage == null) {
|
||||
item.removeComponent(ComponentKeys.MAX_DAMAGE);
|
||||
resetComponent(item, ComponentKeys.MAX_DAMAGE);
|
||||
} else {
|
||||
item.setComponent(ComponentKeys.MAX_DAMAGE, damage);
|
||||
setJavaComponentDirectly(item, ComponentKeys.MAX_DAMAGE, damage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,20 +255,28 @@ public class ComponentItemFactory extends BukkitItemFactory {
|
||||
|
||||
@Override
|
||||
protected void enchantments(ItemWrapper<ItemStack> item, List<Enchantment> enchantments) {
|
||||
Map<String, Integer> enchants = new HashMap<>();
|
||||
for (Enchantment enchantment : enchantments) {
|
||||
enchants.put(enchantment.id().toString(), enchantment.level());
|
||||
if (enchantments == null || enchantments.isEmpty()) {
|
||||
resetComponent(item, ComponentKeys.ENCHANTMENTS);
|
||||
} else {
|
||||
Map<String, Integer> enchants = new HashMap<>();
|
||||
for (Enchantment enchantment : enchantments) {
|
||||
enchants.put(enchantment.id().toString(), enchantment.level());
|
||||
}
|
||||
setJavaComponentDirectly(item, ComponentKeys.ENCHANTMENTS, enchants);
|
||||
}
|
||||
item.setComponent(ComponentKeys.ENCHANTMENTS, enchants);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void storedEnchantments(ItemWrapper<ItemStack> item, List<Enchantment> enchantments) {
|
||||
Map<String, Integer> enchants = new HashMap<>();
|
||||
for (Enchantment enchantment : enchantments) {
|
||||
enchants.put(enchantment.id().toString(), enchantment.level());
|
||||
if (enchantments == null || enchantments.isEmpty()) {
|
||||
resetComponent(item, ComponentKeys.STORED_ENCHANTMENTS);
|
||||
} else {
|
||||
Map<String, Integer> enchants = new HashMap<>();
|
||||
for (Enchantment enchantment : enchantments) {
|
||||
enchants.put(enchantment.id().toString(), enchantment.level());
|
||||
}
|
||||
setJavaComponentDirectly(item, ComponentKeys.STORED_ENCHANTMENTS, enchants);
|
||||
}
|
||||
item.setComponent(ComponentKeys.STORED_ENCHANTMENTS, enchants);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -223,7 +285,7 @@ public class ComponentItemFactory extends BukkitItemFactory {
|
||||
try {
|
||||
Map<String, Integer> map = EnchantmentUtils.toMap(enchant);
|
||||
map.put(enchantment.toString(), enchantment.level());
|
||||
item.setComponent(ComponentKeys.ENCHANTMENTS, map);
|
||||
setJavaComponentDirectly(item, ComponentKeys.ENCHANTMENTS, map);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
plugin.logger().warn("Failed to add enchantment", e);
|
||||
}
|
||||
@@ -235,7 +297,7 @@ public class ComponentItemFactory extends BukkitItemFactory {
|
||||
try {
|
||||
Map<String, Integer> map = EnchantmentUtils.toMap(enchant);
|
||||
map.put(enchantment.toString(), enchantment.level());
|
||||
item.setComponent(ComponentKeys.STORED_ENCHANTMENTS, map);
|
||||
setJavaComponentDirectly(item, ComponentKeys.STORED_ENCHANTMENTS, map);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
plugin.logger().warn("Failed to add stored enchantment", e);
|
||||
}
|
||||
@@ -256,18 +318,18 @@ public class ComponentItemFactory extends BukkitItemFactory {
|
||||
@Override
|
||||
protected void maxStackSize(ItemWrapper<ItemStack> item, Integer maxStackSize) {
|
||||
if (maxStackSize == null) {
|
||||
item.removeComponent(ComponentKeys.MAX_STACK_SIZE);
|
||||
resetComponent(item, ComponentKeys.MAX_STACK_SIZE);
|
||||
} else {
|
||||
item.setComponent(ComponentKeys.MAX_STACK_SIZE, maxStackSize);
|
||||
setJavaComponentDirectly(item, ComponentKeys.MAX_STACK_SIZE, maxStackSize);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void repairCost(ItemWrapper<ItemStack> item, Integer data) {
|
||||
if (data == null) {
|
||||
item.removeComponent(ComponentKeys.REPAIR_COST);
|
||||
resetComponent(item, ComponentKeys.REPAIR_COST);
|
||||
} else {
|
||||
item.setComponent(ComponentKeys.REPAIR_COST, data);
|
||||
setJavaComponentDirectly(item, ComponentKeys.REPAIR_COST, data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,7 +356,6 @@ public class ComponentItemFactory extends BukkitItemFactory {
|
||||
@Override
|
||||
protected void merge(ItemWrapper<ItemStack> item1, ItemWrapper<ItemStack> item2) {
|
||||
// load previous changes on nms items
|
||||
item1.load();
|
||||
Object itemStack1 = item1.getLiteralObject();
|
||||
Object itemStack2 = item2.getLiteralObject();
|
||||
try {
|
||||
@@ -303,4 +364,28 @@ public class ComponentItemFactory extends BukkitItemFactory {
|
||||
plugin.logger().warn("Failed to merge item", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void trim(ItemWrapper<ItemStack> item, Trim trim) {
|
||||
if (trim == null) {
|
||||
resetComponent(item, ComponentKeys.TRIM);
|
||||
} else {
|
||||
setJavaComponentDirectly(item, ComponentKeys.TRIM, Map.of(
|
||||
"pattern", trim.pattern(),
|
||||
"material", trim.material()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Optional<Trim> trim(ItemWrapper<ItemStack> item) {
|
||||
if (!item.hasComponent(ComponentKeys.TRIM)) return Optional.empty();
|
||||
Optional<Object> trim = ComponentType.encodeJava(ComponentKeys.TRIM, item.getComponent(ComponentKeys.TRIM));
|
||||
if (trim.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, String> trimMap = (Map<String, String>) trim.get();
|
||||
return Optional.of(new Trim(trimMap.get("pattern"), trimMap.get("material")));
|
||||
}
|
||||
}
|
||||
@@ -33,9 +33,9 @@ public class ComponentItemFactory1_21_4 extends ComponentItemFactory {
|
||||
@Override
|
||||
protected void customModelData(ItemWrapper<ItemStack> item, Integer data) {
|
||||
if (data == null) {
|
||||
item.removeComponent(ComponentKeys.CUSTOM_MODEL_DATA);
|
||||
resetComponent(item, ComponentKeys.CUSTOM_MODEL_DATA);
|
||||
} else {
|
||||
item.setComponent(ComponentKeys.CUSTOM_MODEL_DATA, Map.of("floats", List.of(data.floatValue())));
|
||||
setComponent(item, ComponentKeys.CUSTOM_MODEL_DATA, Map.of("floats", List.of(data.floatValue())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,9 +26,9 @@ public class ComponentItemFactory1_21_5 extends ComponentItemFactory1_21_4 {
|
||||
@Override
|
||||
protected void customName(ItemWrapper<ItemStack> item, String json) {
|
||||
if (json == null) {
|
||||
item.removeComponent(ComponentKeys.CUSTOM_NAME);
|
||||
resetComponent(item, ComponentKeys.CUSTOM_NAME);
|
||||
} else {
|
||||
item.setComponent(ComponentKeys.CUSTOM_NAME, ChatComponent.toTag(ComponentUtils.jsonToMinecraft(json)));
|
||||
setNBTComponentDirectly(item, ComponentKeys.CUSTOM_NAME, ChatComponent.toTag(ComponentUtils.jsonToMinecraft(json)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,9 +41,9 @@ public class ComponentItemFactory1_21_5 extends ComponentItemFactory1_21_4 {
|
||||
@Override
|
||||
protected void itemName(ItemWrapper<ItemStack> item, String json) {
|
||||
if (json == null) {
|
||||
item.removeComponent(ComponentKeys.ITEM_NAME);
|
||||
resetComponent(item, ComponentKeys.ITEM_NAME);
|
||||
} else {
|
||||
item.setComponent(ComponentKeys.ITEM_NAME, ChatComponent.toTag(ComponentUtils.jsonToMinecraft(json)));
|
||||
setNBTComponentDirectly(item, ComponentKeys.ITEM_NAME, ChatComponent.toTag(ComponentUtils.jsonToMinecraft(json)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,13 +71,13 @@ public class ComponentItemFactory1_21_5 extends ComponentItemFactory1_21_4 {
|
||||
@Override
|
||||
protected void lore(ItemWrapper<ItemStack> item, List<String> lore) {
|
||||
if (lore == null || lore.isEmpty()) {
|
||||
item.removeComponent(ComponentKeys.LORE);
|
||||
resetComponent(item, ComponentKeys.LORE);
|
||||
} else {
|
||||
List<Object> loreTags = new ArrayList<>();
|
||||
for (String json : lore) {
|
||||
loreTags.add(ChatComponent.toTag(ComponentUtils.jsonToMinecraft(json)));
|
||||
}
|
||||
item.setComponent(ComponentKeys.LORE, TagList.newTag(loreTags));
|
||||
setNBTComponentDirectly(item, ComponentKeys.LORE, TagList.newTag(loreTags));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package net.momirealms.craftengine.bukkit.item.factory;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.saicone.rtag.RtagItem;
|
||||
import com.saicone.rtag.item.ItemObject;
|
||||
import com.saicone.rtag.tag.TagBase;
|
||||
@@ -8,6 +9,7 @@ import com.saicone.rtag.tag.TagList;
|
||||
import net.momirealms.craftengine.bukkit.item.RTagItemWrapper;
|
||||
import net.momirealms.craftengine.core.item.Enchantment;
|
||||
import net.momirealms.craftengine.core.item.ItemWrapper;
|
||||
import net.momirealms.craftengine.core.item.Trim;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.SkullUtils;
|
||||
@@ -26,6 +28,36 @@ public class UniversalItemFactory extends BukkitItemFactory {
|
||||
super(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void resetComponent(ItemWrapper<ItemStack> item, Key type) {
|
||||
throw new UnsupportedOperationException("This feature is only available on 1.20.5+");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsonElement encodeJson(Key type, Object component) {
|
||||
throw new UnsupportedOperationException("This feature is only available on 1.20.5+");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setComponent(ItemWrapper<ItemStack> item, Key type, Object value) {
|
||||
throw new UnsupportedOperationException("This feature is only available on 1.20.5+");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object getComponent(ItemWrapper<ItemStack> item, Key type) {
|
||||
throw new UnsupportedOperationException("This feature is only available on 1.20.5+");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean hasComponent(ItemWrapper<ItemStack> item, Key type) {
|
||||
throw new UnsupportedOperationException("This feature is only available on 1.20.5+");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void removeComponent(ItemWrapper<ItemStack> item, Key type) {
|
||||
throw new UnsupportedOperationException("This feature is only available on 1.20.5+");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object encodeJava(Key componentType, @Nullable Object component) {
|
||||
throw new UnsupportedOperationException("This feature is only available on 1.20.5+");
|
||||
@@ -142,6 +174,10 @@ public class UniversalItemFactory extends BukkitItemFactory {
|
||||
|
||||
@Override
|
||||
protected void enchantments(ItemWrapper<ItemStack> item, List<Enchantment> enchantments) {
|
||||
if (enchantments == null || enchantments.isEmpty()) {
|
||||
item.remove("Enchantments");
|
||||
return;
|
||||
}
|
||||
ArrayList<Object> tags = new ArrayList<>();
|
||||
for (Enchantment enchantment : enchantments) {
|
||||
tags.add((Map.of("id", enchantment.id().toString(), "lvl", (short) enchantment.level())));
|
||||
@@ -151,6 +187,10 @@ public class UniversalItemFactory extends BukkitItemFactory {
|
||||
|
||||
@Override
|
||||
protected void storedEnchantments(ItemWrapper<ItemStack> item, List<Enchantment> enchantments) {
|
||||
if (enchantments == null || enchantments.isEmpty()) {
|
||||
item.remove("StoredEnchantments");
|
||||
return;
|
||||
}
|
||||
ArrayList<Object> tags = new ArrayList<>();
|
||||
for (Enchantment enchantment : enchantments) {
|
||||
tags.add((Map.of("id", enchantment.id().toString(), "lvl", (short) enchantment.level())));
|
||||
@@ -248,4 +288,22 @@ public class UniversalItemFactory extends BukkitItemFactory {
|
||||
item1.load();
|
||||
TagCompound.merge(ItemObject.getCustomDataTag(item1.getLiteralObject()), ItemObject.getCustomDataTag(item2.getLiteralObject()), true, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void trim(ItemWrapper<ItemStack> item, Trim trim) {
|
||||
if (trim == null) {
|
||||
item.remove("Trim");
|
||||
return;
|
||||
}
|
||||
item.set(trim.material(), "Trim", "material");
|
||||
item.set(trim.pattern(), "Trim", "pattern");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Optional<Trim> trim(ItemWrapper<ItemStack> item) {
|
||||
String material = item.get("Trim", "material");
|
||||
String pattern = item.get("Trim", "pattern");
|
||||
if (material == null || pattern == null) return Optional.empty();
|
||||
return Optional.of(new Trim(material, pattern));
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,10 @@ public class BukkitCommandManager extends AbstractCommandManager<CommandSender>
|
||||
new DebugSetBlockCommand(this, plugin),
|
||||
new DebugSpawnFurnitureCommand(this, plugin),
|
||||
new DebugTargetBlockCommand(this, plugin),
|
||||
new TotemAnimationCommand(this, plugin)
|
||||
new TotemAnimationCommand(this, plugin),
|
||||
new EnableResourceCommand(this, plugin),
|
||||
new DisableResourceCommand(this, plugin),
|
||||
new ListResourceCommand(this, plugin)
|
||||
));
|
||||
final LegacyPaperCommandManager<CommandSender> manager = (LegacyPaperCommandManager<CommandSender>) getCommandManager();
|
||||
manager.settings().set(ManagerSetting.ALLOW_UNSAFE_REGISTRATION, true);
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
package net.momirealms.craftengine.bukkit.plugin.command.feature;
|
||||
|
||||
import dev.dejvokep.boostedyaml.YamlDocument;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature;
|
||||
import net.momirealms.craftengine.core.pack.Pack;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager;
|
||||
import net.momirealms.craftengine.core.plugin.locale.MessageConstants;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.incendo.cloud.Command;
|
||||
import org.incendo.cloud.context.CommandContext;
|
||||
import org.incendo.cloud.context.CommandInput;
|
||||
import org.incendo.cloud.parser.standard.StringParser;
|
||||
import org.incendo.cloud.suggestion.Suggestion;
|
||||
import org.incendo.cloud.suggestion.SuggestionProvider;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class DisableResourceCommand extends BukkitCommandFeature<CommandSender> {
|
||||
|
||||
public DisableResourceCommand(CraftEngineCommandManager<CommandSender> commandManager, CraftEngine plugin) {
|
||||
super(commandManager, plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Command.Builder<? extends CommandSender> assembleCommand(org.incendo.cloud.CommandManager<CommandSender> manager, Command.Builder<CommandSender> builder) {
|
||||
return builder
|
||||
.flag(manager.flagBuilder("silent").withAliases("s"))
|
||||
.required("pack", StringParser.stringComponent(StringParser.StringMode.GREEDY).suggestionProvider(new SuggestionProvider<>() {
|
||||
@Override
|
||||
public @NonNull CompletableFuture<? extends @NonNull Iterable<? extends @NonNull Suggestion>> suggestionsFuture(@NonNull CommandContext<Object> context, @NonNull CommandInput input) {
|
||||
return CompletableFuture.completedFuture(plugin().packManager().loadedPacks().stream().filter(Pack::enabled).map(pack -> Suggestion.suggestion(pack.name())).toList());
|
||||
}
|
||||
}))
|
||||
.handler(context -> {
|
||||
String packFolder = context.get("pack");
|
||||
Path path = plugin().dataFolderPath().resolve("resources").resolve(packFolder);
|
||||
if (!Files.exists(path)) {
|
||||
handleFeedback(context, MessageConstants.COMMAND_RESOURCE_DISABLE_FAILURE, Component.text(packFolder));
|
||||
return;
|
||||
}
|
||||
Path packMetaPath = path.resolve("pack.yml");
|
||||
if (!Files.exists(packMetaPath)) {
|
||||
try {
|
||||
Files.createFile(packMetaPath);
|
||||
} catch (IOException e) {
|
||||
plugin().logger().warn("Could not create pack.yml file: " + packMetaPath);
|
||||
return;
|
||||
}
|
||||
}
|
||||
YamlDocument document = plugin().config().loadYamlData(packMetaPath.toFile());
|
||||
document.set("enable", false);
|
||||
try {
|
||||
document.save(packMetaPath.toFile());
|
||||
} catch (IOException e) {
|
||||
plugin().logger().warn("Could not save pack.yml file: " + packMetaPath);
|
||||
return;
|
||||
}
|
||||
handleFeedback(context, MessageConstants.COMMAND_RESOURCE_DISABLE_SUCCESS, Component.text(packFolder));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFeatureID() {
|
||||
return "disable_resource";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package net.momirealms.craftengine.bukkit.plugin.command.feature;
|
||||
|
||||
import dev.dejvokep.boostedyaml.YamlDocument;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager;
|
||||
import net.momirealms.craftengine.core.plugin.locale.MessageConstants;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.incendo.cloud.Command;
|
||||
import org.incendo.cloud.context.CommandContext;
|
||||
import org.incendo.cloud.context.CommandInput;
|
||||
import org.incendo.cloud.parser.standard.StringParser;
|
||||
import org.incendo.cloud.suggestion.Suggestion;
|
||||
import org.incendo.cloud.suggestion.SuggestionProvider;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class EnableResourceCommand extends BukkitCommandFeature<CommandSender> {
|
||||
|
||||
public EnableResourceCommand(CraftEngineCommandManager<CommandSender> commandManager, CraftEngine plugin) {
|
||||
super(commandManager, plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Command.Builder<? extends CommandSender> assembleCommand(org.incendo.cloud.CommandManager<CommandSender> manager, Command.Builder<CommandSender> builder) {
|
||||
return builder
|
||||
.flag(manager.flagBuilder("silent").withAliases("s"))
|
||||
.required("pack", StringParser.stringComponent(StringParser.StringMode.GREEDY).suggestionProvider(new SuggestionProvider<>() {
|
||||
@Override
|
||||
public @NonNull CompletableFuture<? extends @NonNull Iterable<? extends @NonNull Suggestion>> suggestionsFuture(@NonNull CommandContext<Object> context, @NonNull CommandInput input) {
|
||||
return CompletableFuture.completedFuture(plugin().packManager().loadedPacks().stream().filter(pack -> !pack.enabled()).map(pack -> Suggestion.suggestion(pack.name())).toList());
|
||||
}
|
||||
}))
|
||||
.handler(context -> {
|
||||
String packFolder = context.get("pack");
|
||||
Path path = plugin().dataFolderPath().resolve("resources").resolve(packFolder);
|
||||
if (!Files.exists(path)) {
|
||||
handleFeedback(context, MessageConstants.COMMAND_RESOURCE_ENABLE_FAILURE, Component.text(packFolder));
|
||||
return;
|
||||
}
|
||||
Path packMetaPath = path.resolve("pack.yml");
|
||||
if (!Files.exists(packMetaPath)) {
|
||||
try {
|
||||
Files.createFile(packMetaPath);
|
||||
} catch (IOException e) {
|
||||
plugin().logger().warn("Could not create pack.yml file: " + packMetaPath);
|
||||
return;
|
||||
}
|
||||
}
|
||||
YamlDocument document = plugin().config().loadYamlData(packMetaPath.toFile());
|
||||
document.set("enable", true);
|
||||
try {
|
||||
document.save(packMetaPath.toFile());
|
||||
} catch (IOException e) {
|
||||
plugin().logger().warn("Could not save pack.yml file: " + packMetaPath);
|
||||
return;
|
||||
}
|
||||
handleFeedback(context, MessageConstants.COMMAND_RESOURCE_ENABLE_SUCCESS, Component.text(packFolder));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFeatureID() {
|
||||
return "enable_resource";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package net.momirealms.craftengine.bukkit.plugin.command.feature;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.HoverEvent;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature;
|
||||
import net.momirealms.craftengine.core.pack.Pack;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager;
|
||||
import net.momirealms.craftengine.core.plugin.locale.MessageConstants;
|
||||
import net.momirealms.craftengine.core.util.AdventureHelper;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.incendo.cloud.Command;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public class ListResourceCommand extends BukkitCommandFeature<CommandSender> {
|
||||
|
||||
public ListResourceCommand(CraftEngineCommandManager<CommandSender> commandManager, CraftEngine plugin) {
|
||||
super(commandManager, plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Command.Builder<? extends CommandSender> assembleCommand(org.incendo.cloud.CommandManager<CommandSender> manager, Command.Builder<CommandSender> builder) {
|
||||
return builder
|
||||
.handler(context -> {
|
||||
Collection<Pack> packs = plugin().packManager().loadedPacks();
|
||||
List<Pack> enabled = new ArrayList<>();
|
||||
List<Pack> disabled = new ArrayList<>();
|
||||
for (Pack pack : packs) {
|
||||
if (pack.enabled()) {
|
||||
enabled.add(pack);
|
||||
} else {
|
||||
disabled.add(pack);
|
||||
}
|
||||
}
|
||||
handleFeedback(context, MessageConstants.COMMAND_RESOURCE_LIST, Component.text(enabled.size()), Component.empty().children(getChildComponents(enabled)), Component.text(disabled.size()), Component.empty().children(getChildComponents(disabled)));
|
||||
});
|
||||
}
|
||||
|
||||
private List<Component> getChildComponents(List<Pack> disabled) {
|
||||
List<Component> components = new ArrayList<>();
|
||||
for (int i = 0; i < disabled.size(); i++) {
|
||||
Pack pack = disabled.get(i);
|
||||
components.add(getPackComponent(pack));
|
||||
if (i != disabled.size() - 1) {
|
||||
components.add(Component.text(", "));
|
||||
}
|
||||
}
|
||||
if (components.isEmpty()) {
|
||||
return List.of(Component.text("[]"));
|
||||
}
|
||||
return components;
|
||||
}
|
||||
|
||||
private Component getPackComponent(Pack pack) {
|
||||
String description = pack.meta().description();
|
||||
String version = pack.meta().version();
|
||||
String author = pack.meta().author();
|
||||
String text = version == null ? pack.name() : pack.name() + " v" + version;
|
||||
Component base = Component.text("[" + text + "]");
|
||||
if (author != null || description != null) {
|
||||
if (author != null && description != null) {
|
||||
base = base.hoverEvent(HoverEvent.showText(Component.empty().children(List.of(Component.text("by: " + author).color(NamedTextColor.YELLOW), Component.newline(), AdventureHelper.miniMessage().deserialize(description)))));
|
||||
} else if (author != null) {
|
||||
base = base.hoverEvent(HoverEvent.showText(Component.text("by: " + author)));
|
||||
} else {
|
||||
base = base.hoverEvent(HoverEvent.showText(AdventureHelper.miniMessage().deserialize(description)));
|
||||
}
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFeatureID() {
|
||||
return "list_resource";
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,16 @@
|
||||
package net.momirealms.craftengine.bukkit.plugin.command.feature;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
|
||||
import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature;
|
||||
import net.momirealms.craftengine.core.item.Item;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.entity.Shulker;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.incendo.cloud.Command;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public class TestCommand extends BukkitCommandFeature<CommandSender> {
|
||||
|
||||
public TestCommand(CraftEngineCommandManager<CommandSender> commandManager, CraftEngine plugin) {
|
||||
@@ -24,16 +23,10 @@ public class TestCommand extends BukkitCommandFeature<CommandSender> {
|
||||
.senderType(Player.class)
|
||||
.handler(context -> {
|
||||
Player player = context.sender();
|
||||
Location location = player.getLocation();
|
||||
try {
|
||||
Collection<Entity> entities = player.getLocation().getNearbyEntities(2,2,2);
|
||||
for (Entity entity : entities) {
|
||||
if (entity instanceof Shulker) {
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
ItemStack itemStack = new ItemStack(Material.STONE);
|
||||
Item<ItemStack> wrapped = BukkitItemManager.instance().wrap(itemStack);
|
||||
wrapped.lore(null);
|
||||
player.getInventory().addItem(wrapped.load());
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ import net.momirealms.craftengine.core.plugin.network.NetWorkUser;
|
||||
import net.momirealms.craftengine.core.plugin.network.NetworkManager;
|
||||
import net.momirealms.craftengine.core.util.*;
|
||||
import net.momirealms.craftengine.core.world.BlockPos;
|
||||
import net.momirealms.craftengine.core.world.WorldEvents;
|
||||
import net.momirealms.craftengine.core.world.chunk.Palette;
|
||||
import net.momirealms.craftengine.core.world.chunk.PalettedContainer;
|
||||
import net.momirealms.craftengine.core.world.chunk.packet.MCSection;
|
||||
@@ -221,7 +222,7 @@ public class PacketConsumers {
|
||||
try {
|
||||
FriendlyByteBuf buf = event.getBuffer();
|
||||
int eventId = buf.readInt();
|
||||
if (eventId != 2001) return;
|
||||
if (eventId != WorldEvents.BLOCK_BREAK_EFFECT) return;
|
||||
BlockPos blockPos = buf.readBlockPos(buf);
|
||||
int state = buf.readInt();
|
||||
boolean global = buf.readBoolean();
|
||||
@@ -1118,38 +1119,32 @@ public class PacketConsumers {
|
||||
if (Config.enableSoundSystem()) {
|
||||
Object blockOwner = Reflections.field$StateHolder$owner.get(blockState);
|
||||
if (BukkitBlockManager.instance().isBlockSoundRemoved(blockOwner)) {
|
||||
player.startMiningBlock(world, pos, blockState, false, null);
|
||||
player.startMiningBlock(pos, blockState, null);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (player.isMiningBlock() || player.shouldSyncAttribute()) {
|
||||
if (player.isMiningBlock()) {
|
||||
player.stopMiningBlock();
|
||||
} else {
|
||||
player.setClientSideCanBreakBlock(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (player.isAdventureMode()) {
|
||||
Object itemStack = FastNMS.INSTANCE.method$CraftItemStack$asNMSCopy(player.platformPlayer().getInventory().getItemInMainHand());
|
||||
Object blockPos = LocationUtils.toBlockPos(pos);
|
||||
Object blockInWorld = Reflections.constructor$BlockInWorld.newInstance(serverLevel, blockPos, false);
|
||||
if (VersionHelper.isVersionNewerThan1_20_5()) {
|
||||
if (Reflections.method$ItemStack$canBreakBlockInAdventureMode != null
|
||||
&& !(boolean) Reflections.method$ItemStack$canBreakBlockInAdventureMode.invoke(
|
||||
itemStack, blockInWorld
|
||||
)) {
|
||||
if (Config.simplifyAdventureBreakCheck()) {
|
||||
ImmutableBlockState state = BukkitBlockManager.instance().getImmutableBlockStateUnsafe(stateId);
|
||||
if (!player.canBreak(pos, state.vanillaBlockState().handle())) {
|
||||
player.preventMiningBlock();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (Reflections.method$ItemStack$canDestroy != null
|
||||
&& !(boolean) Reflections.method$ItemStack$canDestroy.invoke(
|
||||
itemStack, Reflections.instance$BuiltInRegistries$BLOCK, blockInWorld
|
||||
)) {
|
||||
if (!player.canBreak(pos, null)) {
|
||||
player.preventMiningBlock();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
player.startMiningBlock(world, pos, blockState, true, BukkitBlockManager.instance().getImmutableBlockStateUnsafe(stateId));
|
||||
player.startMiningBlock(pos, blockState, BukkitBlockManager.instance().getImmutableBlockStateUnsafe(stateId));
|
||||
} else if (action == Reflections.instance$ServerboundPlayerActionPacket$Action$ABORT_DESTROY_BLOCK) {
|
||||
if (player.isMiningBlock()) {
|
||||
player.abortMiningBlock();
|
||||
@@ -1272,7 +1267,7 @@ public class PacketConsumers {
|
||||
if (slot - 36 != bukkitPlayer.getInventory().getHeldItemSlot()) {
|
||||
return;
|
||||
}
|
||||
double interactionRange = player.getInteractionRange();
|
||||
double interactionRange = player.getCachedInteractionRange();
|
||||
// do ray trace to get current block
|
||||
RayTraceResult result = bukkitPlayer.rayTraceBlocks(interactionRange, FluidCollisionMode.NEVER);
|
||||
if (result == null) return;
|
||||
@@ -1566,9 +1561,11 @@ public class PacketConsumers {
|
||||
if (actionType == null) return;
|
||||
Location location = furniture.baseEntity().getLocation();
|
||||
BukkitServerPlayer serverPlayer = (BukkitServerPlayer) user;
|
||||
if (serverPlayer.isSpectatorMode() || serverPlayer.isAdventureMode()) return;
|
||||
if (serverPlayer.isSpectatorMode()) return;
|
||||
BukkitCraftEngine.instance().scheduler().sync().run(() -> {
|
||||
if (actionType == Reflections.instance$ServerboundInteractPacket$ActionType$ATTACK) {
|
||||
// todo 冒险模式破坏工具白名单
|
||||
if (serverPlayer.isAdventureMode()) return;
|
||||
if (furniture.isValid()) {
|
||||
if (!BukkitCraftEngine.instance().antiGrief().canBreak(player, location)) {
|
||||
return;
|
||||
|
||||
@@ -9,26 +9,27 @@ import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
|
||||
import net.momirealms.craftengine.bukkit.util.*;
|
||||
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
|
||||
import net.momirealms.craftengine.core.block.BlockSettings;
|
||||
import net.momirealms.craftengine.core.block.ImmutableBlockState;
|
||||
import net.momirealms.craftengine.core.block.PackedBlockState;
|
||||
import net.momirealms.craftengine.core.entity.player.InteractionHand;
|
||||
import net.momirealms.craftengine.core.entity.player.Player;
|
||||
import net.momirealms.craftengine.core.item.Item;
|
||||
import net.momirealms.craftengine.core.item.ItemKeys;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
import net.momirealms.craftengine.core.plugin.network.ConnectionState;
|
||||
import net.momirealms.craftengine.core.util.Direction;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
import net.momirealms.craftengine.core.world.BlockPos;
|
||||
import net.momirealms.craftengine.core.world.World;
|
||||
import net.momirealms.craftengine.core.world.*;
|
||||
import net.momirealms.craftengine.core.world.WorldEvents;
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.inventory.EquipmentSlot;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.PlayerInventory;
|
||||
import org.bukkit.util.RayTraceResult;
|
||||
import org.bukkit.util.Vector;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.ref.Reference;
|
||||
@@ -39,20 +40,22 @@ import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class BukkitServerPlayer extends Player {
|
||||
private final Channel channel;
|
||||
private final BukkitCraftEngine plugin;
|
||||
|
||||
// connection state
|
||||
private final Channel channel;
|
||||
private ConnectionState decoderState;
|
||||
private ConnectionState encoderState;
|
||||
|
||||
// some references
|
||||
private Reference<org.bukkit.entity.Player> playerRef;
|
||||
private Reference<Object> serverPlayerRef;
|
||||
|
||||
// client side dimension info
|
||||
private int sectionCount;
|
||||
private int lastSuccessfulInteraction;
|
||||
private long lastAttributeSyncTime;
|
||||
private Key clientSideDimension;
|
||||
|
||||
// check main hand/offhand interaction
|
||||
private int lastSuccessfulInteraction;
|
||||
// re-sync attribute timely to prevent some bugs
|
||||
private long lastAttributeSyncTime;
|
||||
// for breaking blocks
|
||||
private int lastSentState = -1;
|
||||
private int lastHitBlockTime;
|
||||
private BlockPos destroyPos;
|
||||
@@ -61,15 +64,25 @@ public class BukkitServerPlayer extends Player {
|
||||
private boolean isDestroyingCustomBlock;
|
||||
private boolean swingHandAck;
|
||||
private float miningProgress;
|
||||
|
||||
// for client visual sync
|
||||
private int resentSoundTick;
|
||||
private int resentSwingTick;
|
||||
|
||||
// cache used recipe
|
||||
private Key lastUsedRecipe = null;
|
||||
|
||||
// has fabric client mod or not
|
||||
private boolean hasClientMod = false;
|
||||
// cache if player can break blocks
|
||||
private boolean clientSideCanBreak = true;
|
||||
// prevent AFK players from consuming too much CPU resource on predicting
|
||||
private Location previousEyeLocation;
|
||||
// a cooldown for better breaking experience
|
||||
private int lastSuccessfulBreak;
|
||||
// player's game tick
|
||||
private int gameTicks;
|
||||
// cache interaction range here
|
||||
private int lastUpdateInteractionRangeTick;
|
||||
private double cachedInteractionRange;
|
||||
// for better fake furniture visual sync
|
||||
// TODO CLEAR ENTITY VIEW
|
||||
private final Map<Integer, List<Integer>> furnitureView = new ConcurrentHashMap<>();
|
||||
private final Map<Integer, Object> entityTypeView = new ConcurrentHashMap<>();
|
||||
|
||||
@@ -79,8 +92,15 @@ public class BukkitServerPlayer extends Player {
|
||||
}
|
||||
|
||||
public void setPlayer(org.bukkit.entity.Player player) {
|
||||
playerRef = new WeakReference<>(player);
|
||||
serverPlayerRef = new WeakReference<>(FastNMS.INSTANCE.method$CraftPlayer$getHandle(player));
|
||||
this.playerRef = new WeakReference<>(player);
|
||||
this.serverPlayerRef = new WeakReference<>(FastNMS.INSTANCE.method$CraftPlayer$getHandle(player));
|
||||
if (Reflections.method$CraftPlayer$setSimplifyContainerDesyncCheck != null) {
|
||||
try {
|
||||
Reflections.method$CraftPlayer$setSimplifyContainerDesyncCheck.invoke(player, true);
|
||||
} catch (Exception e) {
|
||||
this.plugin.logger().warn("Failed to setSimplifyContainerDesyncCheck", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -108,8 +128,8 @@ public class BukkitServerPlayer extends Player {
|
||||
|
||||
@Override
|
||||
public boolean shouldSyncAttribute() {
|
||||
long current = System.currentTimeMillis();
|
||||
if (current - this.lastAttributeSyncTime > 10000) {
|
||||
long current = gameTicks();
|
||||
if (current - this.lastAttributeSyncTime > 100) {
|
||||
this.lastAttributeSyncTime = current;
|
||||
return true;
|
||||
}
|
||||
@@ -136,6 +156,16 @@ public class BukkitServerPlayer extends Player {
|
||||
return platformPlayer().getGameMode() == GameMode.ADVENTURE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBreak(BlockPos pos, @Nullable Object state) {
|
||||
return AdventureModeUtils.canBreak(platformPlayer().getInventory().getItemInMainHand(), new Location(platformPlayer().getWorld(), pos.x(), pos.y(), pos.z()), state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPlace(BlockPos pos, @Nullable Object state) {
|
||||
return AdventureModeUtils.canPlace(platformPlayer().getInventory().getItemInMainHand(), new Location(platformPlayer().getWorld(), pos.x(), pos.y(), pos.z()), state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendActionBar(Component text) {
|
||||
try {
|
||||
@@ -156,15 +186,14 @@ public class BukkitServerPlayer extends Player {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int lastSuccessfulInteractionTick() {
|
||||
return lastSuccessfulInteraction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int gameTicks() {
|
||||
try {
|
||||
Object serverPlayer = serverPlayer();
|
||||
Object gameMode = Reflections.field$ServerPlayer$gameMode.get(serverPlayer);
|
||||
return (int) Reflections.field$ServerPlayerGameMode$gameTicks.get(gameMode);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException("Failed to get current tick", e);
|
||||
}
|
||||
return this.gameTicks;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -210,36 +239,16 @@ public class BukkitServerPlayer extends Player {
|
||||
platformPlayer().closeInventory();
|
||||
}
|
||||
|
||||
// TODO DO NOT USE BUKKIT API
|
||||
@Override
|
||||
public BlockHitResult rayTrace(double distance, FluidCollisionRule collisionRule) {
|
||||
RayTraceResult result = platformPlayer().rayTraceBlocks(distance, FluidUtils.toCollisionRule(collisionRule));
|
||||
if (result == null) {
|
||||
Location eyeLocation = platformPlayer().getEyeLocation();
|
||||
Location targetLocation = eyeLocation.clone();
|
||||
targetLocation.add(eyeLocation.getDirection().multiply(distance));
|
||||
return BlockHitResult.miss(new Vec3d(eyeLocation.getX(), eyeLocation.getY(), eyeLocation.getZ()),
|
||||
Direction.getApproximateNearest(eyeLocation.getX() - targetLocation.getX(), eyeLocation.getY() - targetLocation.getY(), eyeLocation.getZ() - targetLocation.getZ()),
|
||||
new BlockPos(targetLocation.getBlockX(), targetLocation.getBlockY(), targetLocation.getBlockZ())
|
||||
);
|
||||
} else {
|
||||
Vector hitPos = result.getHitPosition();
|
||||
Block hitBlock = result.getHitBlock();
|
||||
Location hitBlockLocation = hitBlock.getLocation();
|
||||
return new BlockHitResult(
|
||||
new Vec3d(hitPos.getX(), hitPos.getY(), hitPos.getZ()),
|
||||
DirectionUtils.toDirection(result.getHitBlockFace()),
|
||||
new BlockPos(hitBlockLocation.getBlockX(), hitBlockLocation.getBlockY(), hitBlockLocation.getBlockZ()),
|
||||
false
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendPacket(Object packet, boolean immediately) {
|
||||
this.plugin.networkManager().sendPacket(this, packet, immediately);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendPackets(List<Object> packet, boolean immediately) {
|
||||
this.plugin.networkManager().sendPackets(this, packet, immediately);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receivePacket(Object packet) {
|
||||
this.plugin.networkManager().receivePacket(this, packet);
|
||||
@@ -290,48 +299,89 @@ public class BukkitServerPlayer extends Player {
|
||||
public void tick() {
|
||||
// not fully online
|
||||
if (serverPlayer() == null) return;
|
||||
this.gameTicks = FastNMS.INSTANCE.field$MinecraftServer$currentTick();
|
||||
if (this.isDestroyingBlock) {
|
||||
this.tickBlockDestroy();
|
||||
}
|
||||
if (Config.predictBreaking() && !this.isDestroyingCustomBlock) {
|
||||
// if it's not destroying blocks, we do predict
|
||||
if ((gameTicks() + entityID()) % Config.predictBreakingInterval() == 0) {
|
||||
Location eyeLocation = platformPlayer().getEyeLocation();
|
||||
if (eyeLocation.equals(this.previousEyeLocation)) {
|
||||
return;
|
||||
}
|
||||
this.previousEyeLocation = eyeLocation;
|
||||
this.predictNextBlockToMine();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getDestroyProgress(Object blockState, BlockPos pos) {
|
||||
try {
|
||||
Object serverPlayer = serverPlayer();
|
||||
Object blockPos = LocationUtils.toBlockPos(pos.x(), pos.y(), pos.z());
|
||||
return (float) Reflections.method$BlockStateBase$getDestroyProgress.invoke(blockState, serverPlayer, Reflections.method$Entity$level.invoke(serverPlayer), blockPos);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
this.plugin.logger().warn("Failed to get destroy progress for player " + platformPlayer().getName());
|
||||
return 0f;
|
||||
return FastNMS.INSTANCE.method$BlockStateBase$getDestroyProgress(blockState, serverPlayer(), FastNMS.INSTANCE.field$CraftWorld$ServerLevel(platformPlayer().getWorld()), LocationUtils.toBlockPos(pos));
|
||||
}
|
||||
|
||||
private void predictNextBlockToMine() {
|
||||
double range = getCachedInteractionRange() + Config.extendedInteractionRange();
|
||||
RayTraceResult result = platformPlayer().rayTraceBlocks(range, FluidCollisionMode.NEVER);
|
||||
if (result == null) {
|
||||
if (!this.clientSideCanBreak) {
|
||||
setClientSideCanBreakBlock(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
Block hitBlock = result.getHitBlock();
|
||||
if (hitBlock == null) {
|
||||
if (!this.clientSideCanBreak) {
|
||||
setClientSideCanBreakBlock(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
int stateId = BlockStateUtils.blockDataToId(hitBlock.getBlockData());
|
||||
if (BlockStateUtils.isVanillaBlock(stateId)) {
|
||||
if (!this.clientSideCanBreak) {
|
||||
setClientSideCanBreakBlock(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (this.clientSideCanBreak) {
|
||||
setClientSideCanBreakBlock(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void startMiningBlock(org.bukkit.World world, BlockPos pos, Object state, boolean custom, @Nullable ImmutableBlockState immutableBlockState) {
|
||||
public void startMiningBlock(BlockPos pos, Object state, @Nullable ImmutableBlockState immutableBlockState) {
|
||||
// instant break
|
||||
boolean custom = immutableBlockState != null;
|
||||
if (custom && getDestroyProgress(state, pos) >= 1f) {
|
||||
assert immutableBlockState != null;
|
||||
// not an instant break on client side
|
||||
PackedBlockState vanillaBlockState = immutableBlockState.vanillaBlockState();
|
||||
// if it's not an instant break on client side, we should resend level event
|
||||
if (vanillaBlockState != null && getDestroyProgress(vanillaBlockState.handle(), pos) < 1f) {
|
||||
try {
|
||||
Object levelEventPacket = Reflections.constructor$ClientboundLevelEventPacket.newInstance(2001, LocationUtils.toBlockPos(pos), BlockStateUtils.blockStateToId(this.destroyedState), false);
|
||||
sendPacket(levelEventPacket, false);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
this.plugin.logger().warn("Failed to send level event packet", e);
|
||||
}
|
||||
Object levelEventPacket = FastNMS.INSTANCE.constructor$ClientboundLevelEventPacket(
|
||||
WorldEvents.BLOCK_BREAK_EFFECT, LocationUtils.toBlockPos(pos), BlockStateUtils.blockStateToId(state), false);
|
||||
sendPacket(levelEventPacket, false);
|
||||
}
|
||||
//ParticleUtils.addBlockBreakParticles(world, LocationUtils.toBlockPos(pos), state);
|
||||
return;
|
||||
}
|
||||
setCanBreakBlock(!custom);
|
||||
if (!custom && !this.clientSideCanBreak && getDestroyProgress(state, pos) >= 1f) {
|
||||
Object levelEventPacket = FastNMS.INSTANCE.constructor$ClientboundLevelEventPacket(
|
||||
WorldEvents.BLOCK_BREAK_EFFECT, LocationUtils.toBlockPos(pos), BlockStateUtils.blockStateToId(state), false);
|
||||
sendPacket(levelEventPacket, false);
|
||||
}
|
||||
// if it's a custom one, we prevent it, otherwise we allow it
|
||||
setClientSideCanBreakBlock(!custom);
|
||||
// set some base info
|
||||
setDestroyPos(pos);
|
||||
setDestroyedState(state);
|
||||
setIsDestroyingBlock(true, custom);
|
||||
}
|
||||
|
||||
private void setCanBreakBlock(boolean canBreak) {
|
||||
@Override
|
||||
public void setClientSideCanBreakBlock(boolean canBreak) {
|
||||
try {
|
||||
if (this.clientSideCanBreak == canBreak && !shouldSyncAttribute()) {
|
||||
return;
|
||||
}
|
||||
this.clientSideCanBreak = canBreak;
|
||||
if (canBreak) {
|
||||
if (VersionHelper.isVersionNewerThan1_20_5()) {
|
||||
Object serverPlayer = serverPlayer();
|
||||
@@ -353,8 +403,7 @@ public class BukkitServerPlayer extends Player {
|
||||
} else {
|
||||
Object fatiguePacket = MobEffectUtils.createPacket(Reflections.instance$MobEffecr$mining_fatigue, entityID(), (byte) 9, -1, false, false, false);
|
||||
Object hastePacket = MobEffectUtils.createPacket(Reflections.instance$MobEffecr$haste, entityID(), (byte) 0, -1, false, false, false);
|
||||
sendPacket(fatiguePacket, true);
|
||||
sendPacket(hastePacket, true);
|
||||
sendPackets(List.of(fatiguePacket, hastePacket), true);
|
||||
}
|
||||
}
|
||||
} catch (ReflectiveOperationException e) {
|
||||
@@ -364,17 +413,26 @@ public class BukkitServerPlayer extends Player {
|
||||
|
||||
@Override
|
||||
public void stopMiningBlock() {
|
||||
setCanBreakBlock(true);
|
||||
setClientSideCanBreakBlock(true);
|
||||
setIsDestroyingBlock(false, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preventMiningBlock() {
|
||||
setCanBreakBlock(false);
|
||||
setClientSideCanBreakBlock(false);
|
||||
setIsDestroyingBlock(false, false);
|
||||
abortMiningBlock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void abortMiningBlock() {
|
||||
this.swingHandAck = false;
|
||||
this.miningProgress = 0;
|
||||
if (this.destroyPos != null) {
|
||||
this.broadcastDestroyProgress(platformPlayer(), this.destroyPos, LocationUtils.toBlockPos(this.destroyPos), -1);
|
||||
}
|
||||
}
|
||||
|
||||
private void resetEffect(Object mobEffect) throws ReflectiveOperationException {
|
||||
Object effectInstance = Reflections.method$ServerPlayer$getEffect.invoke(serverPlayer(), mobEffect);
|
||||
Object packet;
|
||||
@@ -386,27 +444,16 @@ public class BukkitServerPlayer extends Player {
|
||||
sendPacket(packet, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void abortMiningBlock() {
|
||||
abortDestroyProgress();
|
||||
}
|
||||
|
||||
private void tickBlockDestroy() {
|
||||
// prevent server from taking over breaking blocks
|
||||
if (this.isDestroyingCustomBlock) {
|
||||
try {
|
||||
Object serverPlayer = serverPlayer();
|
||||
Object gameMode = Reflections.field$ServerPlayer$gameMode.get(serverPlayer);
|
||||
Reflections.field$ServerPlayerGameMode$isDestroyingBlock.set(gameMode, false);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
// if player swings hand is this tick
|
||||
if (!this.swingHandAck) return;
|
||||
this.swingHandAck = false;
|
||||
int currentTick = gameTicks();
|
||||
// optimize break speed, otherwise it would be too fast
|
||||
if (currentTick - this.lastSuccessfulBreak <= 5) return;
|
||||
try {
|
||||
org.bukkit.entity.Player player = platformPlayer();
|
||||
double range = getInteractionRange();
|
||||
double range = getCachedInteractionRange();
|
||||
RayTraceResult result = player.rayTraceBlocks(range, FluidCollisionMode.NEVER);
|
||||
if (result == null) return;
|
||||
Block hitBlock = result.getHitBlock();
|
||||
@@ -418,8 +465,8 @@ public class BukkitServerPlayer extends Player {
|
||||
}
|
||||
Object blockPos = LocationUtils.toBlockPos(hitPos);
|
||||
Object serverPlayer = serverPlayer();
|
||||
Object gameMode = Reflections.field$ServerPlayer$gameMode.get(serverPlayer);
|
||||
int currentTick = (int) Reflections.field$ServerPlayerGameMode$gameTicks.get(gameMode);
|
||||
|
||||
// send hit sound if the sound is removed
|
||||
if (currentTick - this.lastHitBlockTime > 3) {
|
||||
Object blockOwner = Reflections.field$StateHolder$owner.get(this.destroyedState);
|
||||
Object soundType = Reflections.field$BlockBehaviour$soundType.get(blockOwner);
|
||||
@@ -431,9 +478,14 @@ public class BukkitServerPlayer extends Player {
|
||||
|
||||
// accumulate progress (custom blocks only)
|
||||
if (this.isDestroyingCustomBlock) {
|
||||
// prevent server from taking over breaking custom blocks
|
||||
Object gameMode = FastNMS.INSTANCE.field$ServerPlayer$gameMode(serverPlayer);
|
||||
Reflections.field$ServerPlayerGameMode$isDestroyingBlock.set(gameMode, false);
|
||||
// check item in hand
|
||||
Item<ItemStack> item = this.getItemInHand(InteractionHand.MAIN_HAND);
|
||||
if (item != null) {
|
||||
Material itemMaterial = item.getItem().getType();
|
||||
// creative mode + invalid item in hand
|
||||
if (canInstabuild() && (itemMaterial == Material.DEBUG_STICK
|
||||
|| itemMaterial == Material.TRIDENT
|
||||
|| (VersionHelper.isVersionNewerThan1_20_5() && itemMaterial == MaterialUtils.MACE)
|
||||
@@ -442,27 +494,66 @@ public class BukkitServerPlayer extends Player {
|
||||
}
|
||||
}
|
||||
|
||||
float progressToAdd = (float) Reflections.method$BlockStateBase$getDestroyProgress.invoke(this.destroyedState, serverPlayer, Reflections.method$Entity$level.invoke(serverPlayer), blockPos);
|
||||
float progressToAdd = getDestroyProgress(this.destroyedState, hitPos);
|
||||
int id = BlockStateUtils.blockStateToId(this.destroyedState);
|
||||
ImmutableBlockState customState = BukkitBlockManager.instance().getImmutableBlockState(id);
|
||||
if (customState != null && !customState.isEmpty()
|
||||
&& !customState.settings().isCorrectTool(item == null ? ItemKeys.AIR : item.id())) {
|
||||
progressToAdd *= customState.settings().incorrectToolSpeed();
|
||||
}
|
||||
// double check custom block
|
||||
if (customState != null && !customState.isEmpty()) {
|
||||
BlockSettings blockSettings = customState.settings();
|
||||
if (blockSettings.requireCorrectTool()) {
|
||||
if (item != null) {
|
||||
// it's correct on plugin side
|
||||
if (blockSettings.isCorrectTool(item.id())) {
|
||||
// but not on serverside
|
||||
if (!FastNMS.INSTANCE.method$ItemStack$isCorrectToolForDrops(item.getLiteralObject(), this.destroyedState)) {
|
||||
// we fix the speed
|
||||
progressToAdd = progressToAdd * (10f / 3f);
|
||||
}
|
||||
} else {
|
||||
// not a correct tool on plugin side and not a correct tool on serverside
|
||||
if (!blockSettings.respectToolComponent() || !FastNMS.INSTANCE.method$ItemStack$isCorrectToolForDrops(item.getLiteralObject(), this.destroyedState)) {
|
||||
progressToAdd = progressToAdd * (10f / 3f) * blockSettings.incorrectToolSpeed();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// item is null, but it requires correct tool, then we reset the speed
|
||||
progressToAdd = progressToAdd * (10f / 3f) * blockSettings.incorrectToolSpeed();
|
||||
}
|
||||
}
|
||||
|
||||
this.miningProgress = progressToAdd + miningProgress;
|
||||
int packetStage = (int) (this.miningProgress * 10.0F);
|
||||
if (packetStage != this.lastSentState) {
|
||||
this.lastSentState = packetStage;
|
||||
broadcastDestroyProgress(player, hitPos, blockPos, packetStage);
|
||||
}
|
||||
// accumulate progress
|
||||
this.miningProgress = progressToAdd + miningProgress;
|
||||
int packetStage = (int) (this.miningProgress * 10.0F);
|
||||
if (packetStage != this.lastSentState) {
|
||||
this.lastSentState = packetStage;
|
||||
// broadcast changes
|
||||
broadcastDestroyProgress(player, hitPos, blockPos, packetStage);
|
||||
}
|
||||
|
||||
if (this.miningProgress >= 1f) {
|
||||
//Reflections.method$ServerLevel$levelEvent.invoke(Reflections.field$CraftWorld$ServerLevel.get(player.getWorld()), null, 2001, blockPos, BlockStateUtils.blockStateToId(this.destroyedState));
|
||||
Reflections.method$ServerPlayerGameMode$destroyBlock.invoke(gameMode, blockPos);
|
||||
Object levelEventPacket = Reflections.constructor$ClientboundLevelEventPacket.newInstance(2001, blockPos, id, false);
|
||||
sendPacket(levelEventPacket, false);
|
||||
this.stopMiningBlock();
|
||||
// can break now
|
||||
if (this.miningProgress >= 1f) {
|
||||
// for simplified adventure break, switch mayBuild temporarily
|
||||
if (isAdventureMode() && Config.simplifyAdventureBreakCheck()) {
|
||||
// check the appearance state
|
||||
if (canBreak(hitPos, customState.vanillaBlockState().handle())) {
|
||||
// Error might occur so we use try here
|
||||
try {
|
||||
FastNMS.INSTANCE.setMayBuild(serverPlayer, true);
|
||||
Reflections.method$ServerPlayerGameMode$destroyBlock.invoke(gameMode, blockPos);
|
||||
} finally {
|
||||
FastNMS.INSTANCE.setMayBuild(serverPlayer, false);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// normal break check
|
||||
Reflections.method$ServerPlayerGameMode$destroyBlock.invoke(gameMode, blockPos);
|
||||
}
|
||||
// send break particle + (removed sounds)
|
||||
sendPacket(FastNMS.INSTANCE.constructor$ClientboundLevelEventPacket(WorldEvents.BLOCK_BREAK_EFFECT, blockPos, id, false), false);
|
||||
this.lastSuccessfulBreak = currentTick;
|
||||
this.destroyPos = null;
|
||||
this.setIsDestroyingBlock(false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
@@ -470,8 +561,8 @@ public class BukkitServerPlayer extends Player {
|
||||
}
|
||||
}
|
||||
|
||||
private void broadcastDestroyProgress(org.bukkit.entity.Player player, BlockPos hitPos, Object blockPos, int stage) throws ReflectiveOperationException {
|
||||
Object packet = Reflections.constructor$ClientboundBlockDestructionPacket.newInstance(Integer.MAX_VALUE - entityID(), blockPos, stage);
|
||||
private void broadcastDestroyProgress(org.bukkit.entity.Player player, BlockPos hitPos, Object blockPos, int stage) {
|
||||
Object packet = FastNMS.INSTANCE.constructor$ClientboundBlockDestructionPacket(Integer.MAX_VALUE - entityID(), blockPos, stage);
|
||||
for (org.bukkit.entity.Player other : player.getWorld().getPlayers()) {
|
||||
Location otherLocation = other.getLocation();
|
||||
double d0 = (double) hitPos.x() - otherLocation.getX();
|
||||
@@ -484,53 +575,28 @@ public class BukkitServerPlayer extends Player {
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getInteractionRange() {
|
||||
try {
|
||||
if (VersionHelper.isVersionNewerThan1_20_5()) {
|
||||
Object attributeInstance = Reflections.method$ServerPlayer$getAttribute.invoke(serverPlayer(), Reflections.instance$Holder$Attribute$block_interaction_range);
|
||||
if (attributeInstance == null) return 4.5d;
|
||||
return (double) Reflections.method$AttributeInstance$getValue.invoke(attributeInstance);
|
||||
} else {
|
||||
return 4.5d;
|
||||
}
|
||||
} catch (ReflectiveOperationException e) {
|
||||
plugin.logger().warn("Failed to get interaction range for player " + platformPlayer().getName(), e);
|
||||
return 4.5d;
|
||||
public double getCachedInteractionRange() {
|
||||
if (this.lastUpdateInteractionRangeTick + 20 > gameTicks()) {
|
||||
return this.cachedInteractionRange;
|
||||
}
|
||||
this.cachedInteractionRange = FastNMS.INSTANCE.getInteractionRange(serverPlayer());
|
||||
this.lastUpdateInteractionRangeTick = gameTicks();
|
||||
return this.cachedInteractionRange;
|
||||
}
|
||||
|
||||
public void setIsDestroyingBlock(boolean value, boolean custom) {
|
||||
if (value) {
|
||||
this.isDestroyingBlock = true;
|
||||
this.isDestroyingCustomBlock = custom;
|
||||
this.swingHandAck = true;
|
||||
this.miningProgress = 0;
|
||||
} else {
|
||||
this.isDestroyingBlock = false;
|
||||
this.swingHandAck = false;
|
||||
if (this.destroyPos != null) {
|
||||
try {
|
||||
this.broadcastDestroyProgress(platformPlayer(), this.destroyPos, LocationUtils.toBlockPos(this.destroyPos), -1);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
plugin.logger().warn("Failed to set isDestroyingCustomBlock", e);
|
||||
}
|
||||
}
|
||||
this.destroyPos = null;
|
||||
this.miningProgress = 0;
|
||||
this.destroyedState = null;
|
||||
this.isDestroyingCustomBlock = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void abortDestroyProgress() {
|
||||
this.swingHandAck = false;
|
||||
public void setIsDestroyingBlock(boolean is, boolean custom) {
|
||||
this.miningProgress = 0;
|
||||
if (this.destroyPos == null) return;
|
||||
try {
|
||||
this.broadcastDestroyProgress(platformPlayer(), this.destroyPos, LocationUtils.toBlockPos(this.destroyPos), -1);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
plugin.logger().warn("Failed to abort destroyProgress", e);
|
||||
this.isDestroyingBlock = is;
|
||||
this.isDestroyingCustomBlock = custom && is;
|
||||
if (is) {
|
||||
this.swingHandAck = true;
|
||||
} else {
|
||||
this.swingHandAck = false;
|
||||
this.destroyedState = null;
|
||||
if (this.destroyPos != null) {
|
||||
this.broadcastDestroyProgress(platformPlayer(), this.destroyPos, LocationUtils.toBlockPos(this.destroyPos), -1);
|
||||
this.destroyPos = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
package net.momirealms.craftengine.bukkit.util;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.core.item.Item;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.world.BlockPos;
|
||||
import net.momirealms.craftengine.core.world.World;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
@SuppressWarnings("DuplicatedCode")
|
||||
public class AdventureModeUtils {
|
||||
|
||||
private AdventureModeUtils() {}
|
||||
|
||||
public static boolean canBreak(ItemStack itemStack, Location pos) {
|
||||
return canPlace(itemStack, pos, null);
|
||||
}
|
||||
|
||||
public static boolean canBreak(ItemStack itemStack, Location pos, Object state) {
|
||||
Object blockPos = LocationUtils.toBlockPos(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ());
|
||||
Object blockInWorld = FastNMS.INSTANCE.constructor$BlockInWorld(FastNMS.INSTANCE.field$CraftWorld$ServerLevel(pos.getWorld()), blockPos, false);
|
||||
if (state != null) {
|
||||
try {
|
||||
Reflections.field$BlockInWorld$state.set(blockInWorld, state);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
CraftEngine.instance().logger().warn("Failed to set field$BlockInWorld$state", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return FastNMS.INSTANCE.canBreakInAdventureMode(FastNMS.INSTANCE.field$CraftItemStack$handle(itemStack), blockInWorld);
|
||||
}
|
||||
|
||||
public static boolean canPlace(Item<?> itemStack, World world, BlockPos pos, Object state) {
|
||||
Object blockPos = LocationUtils.toBlockPos(pos);
|
||||
Object item = itemStack == null ? Reflections.instance$ItemStack$Air : itemStack.getLiteralObject();
|
||||
Object blockInWorld = FastNMS.INSTANCE.constructor$BlockInWorld(FastNMS.INSTANCE.field$CraftWorld$ServerLevel((org.bukkit.World) world.platformWorld()), blockPos, false);
|
||||
if (state != null) {
|
||||
try {
|
||||
Reflections.field$BlockInWorld$state.set(blockInWorld, state);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
CraftEngine.instance().logger().warn("Failed to set field$BlockInWorld$state", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return FastNMS.INSTANCE.canPlaceInAdventureMode(item, blockInWorld);
|
||||
}
|
||||
|
||||
public static boolean canPlace(ItemStack itemStack, Location pos, Object state) {
|
||||
Object blockPos = LocationUtils.toBlockPos(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ());
|
||||
Object blockInWorld = FastNMS.INSTANCE.constructor$BlockInWorld(FastNMS.INSTANCE.field$CraftWorld$ServerLevel(pos.getWorld()), blockPos, false);
|
||||
if (state != null) {
|
||||
try {
|
||||
Reflections.field$BlockInWorld$state.set(blockInWorld, state);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
CraftEngine.instance().logger().warn("Failed to set field$BlockInWorld$state", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return FastNMS.INSTANCE.canPlaceInAdventureMode(FastNMS.INSTANCE.field$CraftItemStack$handle(itemStack), blockInWorld);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package net.momirealms.craftengine.bukkit.util;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
import net.momirealms.craftengine.core.world.BlockPos;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
@@ -9,6 +10,8 @@ import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.entity.CreatureSpawnEvent;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class EntityUtils {
|
||||
|
||||
private EntityUtils() {}
|
||||
@@ -23,12 +26,11 @@ public class EntityUtils {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public static Entity spawnEntity(World world, Location loc, EntityType type, org.bukkit.util.Consumer<Entity> function) {
|
||||
try {
|
||||
return (Entity) Reflections.method$World$spawnEntity.invoke(world, loc, type, CreatureSpawnEvent.SpawnReason.CUSTOM, function);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException("Failed to spawn entity", e);
|
||||
public static Entity spawnEntity(World world, Location loc, EntityType type, Consumer<Entity> function) {
|
||||
if (VersionHelper.isVersionNewerThan1_20_2()) {
|
||||
return world.spawnEntity(loc, type, CreatureSpawnEvent.SpawnReason.CUSTOM, function);
|
||||
} else {
|
||||
return LegacyEntityUtils.spawnEntity(world, loc, type, function);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,17 +14,12 @@ import net.kyori.adventure.text.Component;
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.core.util.ReflectionUtils;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.event.block.BlockPhysicsEvent;
|
||||
import org.bukkit.event.block.BlockPlaceEvent;
|
||||
import org.bukkit.event.entity.CreatureSpawnEvent;
|
||||
import org.bukkit.inventory.*;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import sun.misc.Unsafe;
|
||||
@@ -2418,6 +2413,12 @@ public class Reflections {
|
||||
)
|
||||
);
|
||||
|
||||
public static final Field field$BlockStateBase$requiresCorrectToolForDrops = requireNonNull(
|
||||
ReflectionUtils.getDeclaredField(
|
||||
clazz$BlockStateBase, boolean.class, 5
|
||||
)
|
||||
);
|
||||
|
||||
public static final Field field$BlockStateBase$canOcclude = requireNonNull(
|
||||
ReflectionUtils.getDeclaredField(
|
||||
clazz$BlockStateBase, boolean.class, 6
|
||||
@@ -2925,11 +2926,11 @@ public class Reflections {
|
||||
)
|
||||
);
|
||||
|
||||
public static final Field field$ServerPlayer$gameMode = requireNonNull(
|
||||
ReflectionUtils.getDeclaredField(
|
||||
clazz$ServerPlayer, clazz$ServerPlayerGameMode, 0
|
||||
)
|
||||
);
|
||||
// public static final Field field$ServerPlayer$gameMode = requireNonNull(
|
||||
// ReflectionUtils.getDeclaredField(
|
||||
// clazz$ServerPlayer, clazz$ServerPlayerGameMode, 0
|
||||
// )
|
||||
// );
|
||||
|
||||
public static final Field field$ServerPlayerGameMode$destroyProgressStart = requireNonNull(
|
||||
ReflectionUtils.getDeclaredField(
|
||||
@@ -2937,11 +2938,11 @@ public class Reflections {
|
||||
)
|
||||
);
|
||||
|
||||
public static final Field field$ServerPlayerGameMode$gameTicks = requireNonNull(
|
||||
ReflectionUtils.getDeclaredField(
|
||||
clazz$ServerPlayerGameMode, int.class, 1
|
||||
)
|
||||
);
|
||||
// public static final Field field$ServerPlayerGameMode$gameTicks = requireNonNull(
|
||||
// ReflectionUtils.getDeclaredField(
|
||||
// clazz$ServerPlayerGameMode, int.class, 1
|
||||
// )
|
||||
// );
|
||||
|
||||
public static final Field field$ServerPlayerGameMode$delayedTickStart = requireNonNull(
|
||||
ReflectionUtils.getDeclaredField(
|
||||
@@ -3009,11 +3010,11 @@ public class Reflections {
|
||||
)
|
||||
);
|
||||
|
||||
public static final Method method$BlockStateBase$getDestroyProgress = requireNonNull(
|
||||
ReflectionUtils.getDeclaredMethod(
|
||||
clazz$BlockStateBase, float.class, clazz$Player, clazz$BlockGetter, clazz$BlockPos
|
||||
)
|
||||
);
|
||||
// public static final Method method$BlockStateBase$getDestroyProgress = requireNonNull(
|
||||
// ReflectionUtils.getDeclaredMethod(
|
||||
// clazz$BlockStateBase, float.class, clazz$Player, clazz$BlockGetter, clazz$BlockPos
|
||||
// )
|
||||
// );
|
||||
|
||||
public static final Class<?> clazz$ClientboundBlockDestructionPacket = requireNonNull(
|
||||
ReflectionUtils.getClazz(
|
||||
@@ -3022,11 +3023,11 @@ public class Reflections {
|
||||
)
|
||||
);
|
||||
|
||||
public static final Constructor<?> constructor$ClientboundBlockDestructionPacket = requireNonNull(
|
||||
ReflectionUtils.getConstructor(
|
||||
clazz$ClientboundBlockDestructionPacket, int.class, clazz$BlockPos, int.class
|
||||
)
|
||||
);
|
||||
// public static final Constructor<?> constructor$ClientboundBlockDestructionPacket = requireNonNull(
|
||||
// ReflectionUtils.getConstructor(
|
||||
// clazz$ClientboundBlockDestructionPacket, int.class, clazz$BlockPos, int.class
|
||||
// )
|
||||
// );
|
||||
|
||||
public static final Class<?> clazz$ServerboundSwingPacket = requireNonNull(
|
||||
ReflectionUtils.getClazz(
|
||||
@@ -3994,12 +3995,12 @@ public class Reflections {
|
||||
)
|
||||
);
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public static final Method method$World$spawnEntity = requireNonNull(
|
||||
VersionHelper.isVersionNewerThan1_20_2() ?
|
||||
ReflectionUtils.getMethod(World.class, Entity.class, Location.class, EntityType.class, CreatureSpawnEvent.SpawnReason.class, Consumer.class) :
|
||||
ReflectionUtils.getMethod(World.class, Entity.class, Location.class, EntityType.class, CreatureSpawnEvent.SpawnReason.class, org.bukkit.util.Consumer.class)
|
||||
);
|
||||
// @SuppressWarnings("deprecation")
|
||||
// public static final Method method$World$spawnEntity = requireNonNull(
|
||||
// VersionHelper.isVersionNewerThan1_20_2() ?
|
||||
// ReflectionUtils.getMethod(World.class, Entity.class, Location.class, EntityType.class, CreatureSpawnEvent.SpawnReason.class, Consumer.class) :
|
||||
// ReflectionUtils.getMethod(World.class, Entity.class, Location.class, EntityType.class, CreatureSpawnEvent.SpawnReason.class, org.bukkit.util.Consumer.class)
|
||||
// );
|
||||
|
||||
// 1.21.4+
|
||||
public static final Class<?> clazz$ServerboundPickItemFromEntityPacket =
|
||||
@@ -4955,11 +4956,11 @@ public class Reflections {
|
||||
)
|
||||
);
|
||||
|
||||
public static final Constructor<?> constructor$ClientboundLevelEventPacket = requireNonNull(
|
||||
ReflectionUtils.getConstructor(
|
||||
clazz$ClientboundLevelEventPacket, int.class, clazz$BlockPos, int.class, boolean.class
|
||||
)
|
||||
);
|
||||
// public static final Constructor<?> constructor$ClientboundLevelEventPacket = requireNonNull(
|
||||
// ReflectionUtils.getConstructor(
|
||||
// clazz$ClientboundLevelEventPacket, int.class, clazz$BlockPos, int.class, boolean.class
|
||||
// )
|
||||
// );
|
||||
|
||||
public static final Field field$ClientboundLevelEventPacket$eventId = requireNonNull(
|
||||
ReflectionUtils.getDeclaredField(
|
||||
@@ -5768,24 +5769,30 @@ public class Reflections {
|
||||
)
|
||||
);
|
||||
|
||||
public static final Constructor<?> constructor$BlockInWorld = requireNonNull(
|
||||
ReflectionUtils.getConstructor(
|
||||
clazz$BlockInWorld, 0
|
||||
public static final Field field$BlockInWorld$state = requireNonNull(
|
||||
ReflectionUtils.getDeclaredField(
|
||||
clazz$BlockInWorld, clazz$BlockState, 0
|
||||
)
|
||||
);
|
||||
|
||||
// 1.20.5+
|
||||
public static final Method method$ItemStack$canBreakBlockInAdventureMode =
|
||||
ReflectionUtils.getMethod(
|
||||
clazz$ItemStack, new String[]{"canBreakBlockInAdventureMode"}, clazz$BlockInWorld
|
||||
);
|
||||
// public static final Constructor<?> constructor$BlockInWorld = requireNonNull(
|
||||
// ReflectionUtils.getConstructor(
|
||||
// clazz$BlockInWorld, 0
|
||||
// )
|
||||
// );
|
||||
|
||||
// 1.20 ~ 1.20.4
|
||||
// instance$BuiltInRegistries$BLOCK
|
||||
public static final Method method$ItemStack$canDestroy =
|
||||
ReflectionUtils.getMethod(
|
||||
clazz$ItemStack,new String[]{"b"}, clazz$Registry, clazz$BlockInWorld
|
||||
);
|
||||
// // 1.20.5+
|
||||
// public static final Method method$ItemStack$canBreakBlockInAdventureMode =
|
||||
// ReflectionUtils.getMethod(
|
||||
// clazz$ItemStack, new String[]{"canBreakBlockInAdventureMode"}, clazz$BlockInWorld
|
||||
// );
|
||||
|
||||
// // 1.20 ~ 1.20.4
|
||||
// // instance$BuiltInRegistries$BLOCK
|
||||
// public static final Method method$ItemStack$canDestroy =
|
||||
// ReflectionUtils.getMethod(
|
||||
// clazz$ItemStack,new String[]{"b"}, clazz$Registry, clazz$BlockInWorld
|
||||
// );
|
||||
|
||||
public static final Method method$BlockStateBase$getBlock = requireNonNull(
|
||||
ReflectionUtils.getMethod(
|
||||
@@ -6432,4 +6439,9 @@ public class Reflections {
|
||||
BukkitReflectionUtils.assembleMCClass("network.protocol.game.PacketPlayOutScoreboardScore")
|
||||
)
|
||||
);
|
||||
|
||||
public static final Method method$CraftPlayer$setSimplifyContainerDesyncCheck =
|
||||
ReflectionUtils.getMethod(
|
||||
clazz$CraftPlayer, new String[]{"setSimplifyContainerDesyncCheck"}, boolean.class
|
||||
);
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ public class BukkitWorldBlock implements WorldBlock {
|
||||
Object serverLevel = FastNMS.INSTANCE.field$CraftWorld$ServerLevel(block.getWorld());
|
||||
Object fluidData = Reflections.method$Level$getFluidState.invoke(serverLevel, LocationUtils.toBlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()));
|
||||
if (fluidData == null) return false;
|
||||
return (boolean) Reflections.method$FluidState$isSource.invoke(fluidData);
|
||||
return Reflections.method$FluidState$getType.invoke(fluidData) == Reflections.instance$Fluids$WATER;
|
||||
} catch (ReflectiveOperationException e) {
|
||||
CraftEngine.instance().logger().warn("Failed to check if water source is available", e);
|
||||
return false;
|
||||
|
||||
@@ -17,6 +17,8 @@ public class BlockSettings {
|
||||
float resistance = 2f;
|
||||
boolean canOcclude;
|
||||
boolean fluidState;
|
||||
boolean requireCorrectTools;
|
||||
boolean respectToolComponent;
|
||||
Tristate isRedstoneConductor = Tristate.UNDEFINED;
|
||||
Tristate isSuffocating = Tristate.UNDEFINED;
|
||||
Tristate isViewBlocking = Tristate.UNDEFINED;
|
||||
@@ -74,6 +76,8 @@ public class BlockSettings {
|
||||
newSettings.itemId = settings.itemId;
|
||||
newSettings.tags = settings.tags;
|
||||
newSettings.burnChance = settings.burnChance;
|
||||
newSettings.requireCorrectTools = settings.requireCorrectTools;
|
||||
newSettings.respectToolComponent = settings.respectToolComponent;
|
||||
newSettings.fireSpreadChance = settings.fireSpreadChance;
|
||||
newSettings.isRedstoneConductor = settings.isRedstoneConductor;
|
||||
newSettings.isSuffocating = settings.isSuffocating;
|
||||
@@ -130,6 +134,10 @@ public class BlockSettings {
|
||||
return incorrectToolSpeed;
|
||||
}
|
||||
|
||||
public boolean requireCorrectTool() {
|
||||
return requireCorrectTools || !correctTools.isEmpty();
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
@@ -175,10 +183,13 @@ public class BlockSettings {
|
||||
}
|
||||
|
||||
public boolean isCorrectTool(Key key) {
|
||||
if (this.correctTools.isEmpty()) return true;
|
||||
return this.correctTools.contains(key);
|
||||
}
|
||||
|
||||
public boolean respectToolComponent() {
|
||||
return respectToolComponent;
|
||||
}
|
||||
|
||||
public BlockSettings correctTools(Set<Key> correctTools) {
|
||||
this.correctTools = correctTools;
|
||||
return this;
|
||||
@@ -249,6 +260,16 @@ public class BlockSettings {
|
||||
return this;
|
||||
}
|
||||
|
||||
public BlockSettings requireCorrectTool(boolean requireCorrectTool) {
|
||||
this.requireCorrectTools = requireCorrectTool;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BlockSettings respectToolComponent(boolean respectToolComponent) {
|
||||
this.respectToolComponent = respectToolComponent;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BlockSettings incorrectToolSpeed(float incorrectToolSpeed) {
|
||||
this.incorrectToolSpeed = incorrectToolSpeed;
|
||||
return this;
|
||||
@@ -392,6 +413,14 @@ public class BlockSettings {
|
||||
List<String> tools = MiscUtils.getAsStringList(value);
|
||||
return settings -> settings.correctTools(tools.stream().map(Key::of).collect(Collectors.toSet()));
|
||||
}));
|
||||
registerFactory("require-correct-tools", (value -> {
|
||||
boolean booleanValue = (boolean) value;
|
||||
return settings -> settings.requireCorrectTool(booleanValue);
|
||||
}));
|
||||
registerFactory("respect-tool-component", (value -> {
|
||||
boolean booleanValue = (boolean) value;
|
||||
return settings -> settings.respectToolComponent(booleanValue);
|
||||
}));
|
||||
registerFactory("incorrect-tool-dig-speed", (value -> {
|
||||
float floatValue = MiscUtils.getAsFloat(value);
|
||||
return settings -> settings.incorrectToolSpeed(floatValue);
|
||||
|
||||
@@ -5,11 +5,11 @@ import net.momirealms.craftengine.core.entity.Entity;
|
||||
import net.momirealms.craftengine.core.item.Item;
|
||||
import net.momirealms.craftengine.core.plugin.network.NetWorkUser;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.world.BlockHitResult;
|
||||
import net.momirealms.craftengine.core.world.BlockPos;
|
||||
import net.momirealms.craftengine.core.world.FluidCollisionRule;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public abstract class Player extends Entity implements NetWorkUser {
|
||||
|
||||
public abstract boolean isSecondaryUseActive();
|
||||
@@ -23,17 +23,19 @@ public abstract class Player extends Entity implements NetWorkUser {
|
||||
@Override
|
||||
public abstract Object serverPlayer();
|
||||
|
||||
public abstract void sendPackets(List<Object> packet, boolean immediately);
|
||||
|
||||
public abstract float getDestroyProgress(Object blockState, BlockPos pos);
|
||||
|
||||
public abstract void setClientSideCanBreakBlock(boolean canBreak);
|
||||
|
||||
public abstract void stopMiningBlock();
|
||||
|
||||
public abstract void preventMiningBlock();
|
||||
|
||||
public abstract void abortMiningBlock();
|
||||
|
||||
public abstract double getInteractionRange();
|
||||
|
||||
public abstract void abortDestroyProgress();
|
||||
public abstract double getCachedInteractionRange();
|
||||
|
||||
public abstract void onSwingHand();
|
||||
|
||||
@@ -49,10 +51,16 @@ public abstract class Player extends Entity implements NetWorkUser {
|
||||
|
||||
public abstract boolean isAdventureMode();
|
||||
|
||||
public abstract boolean canBreak(BlockPos pos, Object state);
|
||||
|
||||
public abstract boolean canPlace(BlockPos pos, Object state);
|
||||
|
||||
public abstract void sendActionBar(Component text);
|
||||
|
||||
public abstract boolean updateLastSuccessfulInteractionTick(int tick);
|
||||
|
||||
public abstract int lastSuccessfulInteractionTick();
|
||||
|
||||
public abstract int gameTicks();
|
||||
|
||||
public abstract void swingHand(InteractionHand hand);
|
||||
@@ -73,7 +81,5 @@ public abstract class Player extends Entity implements NetWorkUser {
|
||||
|
||||
public abstract void closeInventory();
|
||||
|
||||
public abstract BlockHitResult rayTrace(double distance, FluidCollisionRule collisionRule);
|
||||
|
||||
public abstract void clearView();
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ public abstract class AbstractFontManager implements FontManager {
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
this.offsetFont = Optional.ofNullable(plugin.config().settings().getSection("offset-characters"))
|
||||
this.offsetFont = Optional.ofNullable(plugin.config().settings().getSection("image.offset-characters"))
|
||||
.map(OffsetFont::new)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package net.momirealms.craftengine.core.item;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
|
||||
@@ -95,6 +96,17 @@ public class AbstractItem<W extends ItemWrapper<I>, I> implements Item<I> {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Item<I> trim(Trim trim) {
|
||||
this.factory.trim(this.item, trim);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Trim> trim() {
|
||||
return this.factory.trim(this.item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Item<I> customModelData(Integer data) {
|
||||
this.factory.customModelData(this.item, data);
|
||||
@@ -224,25 +236,40 @@ public class AbstractItem<W extends ItemWrapper<I>, I> implements Item<I> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasComponent(String type) {
|
||||
public boolean hasComponent(Key type) {
|
||||
return this.factory.hasComponent(this.item, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeComponent(String type) {
|
||||
public void removeComponent(Key type) {
|
||||
this.factory.removeComponent(this.item, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getComponent(String type) {
|
||||
public Object getComponent(Key type) {
|
||||
return this.factory.getComponent(this.item, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setComponent(String type, Object value) {
|
||||
public Object getJavaTypeComponent(Key type) {
|
||||
return this.factory.encodeJava(type, getComponent(type));
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonElement getJsonTypeComponent(Key type) {
|
||||
return this.factory.encodeJson(type, getComponent(type));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setComponent(Key type, Object value) {
|
||||
this.factory.setComponent(this.item, type, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetComponent(Key type) {
|
||||
this.factory.resetComponent(this.item, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public I getItem() {
|
||||
return this.factory.getItem(this.item);
|
||||
|
||||
@@ -24,7 +24,7 @@ public abstract class AbstractItemManager<I> extends AbstractModelGenerator impl
|
||||
protected static final Map<Key, List<Holder<Key>>> VANILLA_ITEM_TAGS = new HashMap<>();
|
||||
|
||||
protected final Map<String, ExternalItemProvider<I>> externalItemProviders = new HashMap<>();
|
||||
protected final Map<String, Function<Object, ItemModifier<I>>> dataFunctions = new HashMap<>();
|
||||
protected final Map<String, Function<Object, ItemDataModifier<I>>> dataFunctions = new HashMap<>();
|
||||
protected final Map<Key, CustomItem<I>> customItems = new HashMap<>();
|
||||
protected final Map<Key, List<Holder<Key>>> customItemTags;
|
||||
protected final Map<Key, Map<Integer, Key>> cmdConflictChecker;
|
||||
@@ -37,7 +37,7 @@ public abstract class AbstractItemManager<I> extends AbstractModelGenerator impl
|
||||
protected final List<Suggestion> cachedSuggestions = new ArrayList<>();
|
||||
protected final List<Suggestion> cachedTotemSuggestions = new ArrayList<>();
|
||||
|
||||
protected void registerDataFunction(Function<Object, ItemModifier<I>> function, String... alias) {
|
||||
protected void registerDataFunction(Function<Object, ItemDataModifier<I>> function, String... alias) {
|
||||
for (String a : alias) {
|
||||
dataFunctions.put(a, function);
|
||||
}
|
||||
|
||||
@@ -1,23 +1,25 @@
|
||||
package net.momirealms.craftengine.core.item;
|
||||
|
||||
import net.kyori.adventure.key.Key;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
|
||||
public class ComponentKeys {
|
||||
public static final String CUSTOM_MODEL_DATA = Key.key("minecraft", "custom_model_data").asString();
|
||||
public static final String CUSTOM_NAME = Key.key("minecraft", "custom_name").asString();
|
||||
public static final String ITEM_NAME = Key.key("minecraft", "item_name").asString();
|
||||
public static final String LORE = Key.key("minecraft", "lore").asString();
|
||||
public static final String DAMAGE = Key.key("minecraft", "damage").asString();
|
||||
public static final String MAX_DAMAGE = Key.key("minecraft", "max_damage").asString();
|
||||
public static final String ENCHANTMENT_GLINT_OVERRIDE = Key.key("minecraft", "enchantment_glint_override").asString();
|
||||
public static final String ENCHANTMENTS = Key.key("minecraft", "enchantments").asString();
|
||||
public static final String STORED_ENCHANTMENTS = Key.key("minecraft", "stored_enchantments").asString();
|
||||
public static final String UNBREAKABLE = Key.key("minecraft", "unbreakable").asString();
|
||||
public static final String MAX_STACK_SIZE = Key.key("minecraft", "max_stack_size").asString();
|
||||
public static final String EQUIPPABLE = Key.key("minecraft", "equippable").asString();
|
||||
public static final String ITEM_MODEL = Key.key("minecraft", "item_model").asString();
|
||||
public static final String TOOLTIP_STYLE = Key.key("minecraft", "tooltip_style").asString();
|
||||
public static final String JUKEBOX_PLAYABLE = Key.key("minecraft", "jukebox_playable").asString();
|
||||
public static final String TRIM = Key.key("minecraft", "trim").asString();
|
||||
public static final String REPAIR_COST = Key.key("minecraft", "repair_cost").asString();
|
||||
public static final Key CUSTOM_MODEL_DATA = Key.of("minecraft", "custom_model_data");
|
||||
public static final Key CUSTOM_NAME = Key.of("minecraft", "custom_name");
|
||||
public static final Key ITEM_NAME = Key.of("minecraft", "item_name");
|
||||
public static final Key LORE = Key.of("minecraft", "lore");
|
||||
public static final Key DAMAGE = Key.of("minecraft", "damage");
|
||||
public static final Key MAX_DAMAGE = Key.of("minecraft", "max_damage");
|
||||
public static final Key ENCHANTMENT_GLINT_OVERRIDE = Key.of("minecraft", "enchantment_glint_override");
|
||||
public static final Key ENCHANTMENTS = Key.of("minecraft", "enchantments");
|
||||
public static final Key STORED_ENCHANTMENTS = Key.of("minecraft", "stored_enchantments");
|
||||
public static final Key UNBREAKABLE = Key.of("minecraft", "unbreakable");
|
||||
public static final Key MAX_STACK_SIZE = Key.of("minecraft", "max_stack_size");
|
||||
public static final Key EQUIPPABLE = Key.of("minecraft", "equippable");
|
||||
public static final Key ITEM_MODEL = Key.of("minecraft", "item_model");
|
||||
public static final Key TOOLTIP_STYLE = Key.of("minecraft", "tooltip_style");
|
||||
public static final Key JUKEBOX_PLAYABLE = Key.of("minecraft", "jukebox_playable");
|
||||
public static final Key TRIM = Key.of("minecraft", "trim");
|
||||
public static final Key REPAIR_COST = Key.of("minecraft", "repair_cost");
|
||||
public static final Key CUSTOM_DATA = Key.of("minecraft", "custom_data");
|
||||
public static final Key PROFILE = Key.of("minecraft", "profile");
|
||||
}
|
||||
|
||||
@@ -2,12 +2,13 @@ package net.momirealms.craftengine.core.item;
|
||||
|
||||
import net.momirealms.craftengine.core.entity.player.Player;
|
||||
import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
|
||||
import net.momirealms.craftengine.core.item.modifier.ItemModifier;
|
||||
import net.momirealms.craftengine.core.item.modifier.ItemDataModifier;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.context.ContextHolder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public interface CustomItem<I> extends BuildableItem<I> {
|
||||
|
||||
@@ -15,7 +16,17 @@ public interface CustomItem<I> extends BuildableItem<I> {
|
||||
|
||||
Key material();
|
||||
|
||||
List<ItemModifier<I>> modifiers();
|
||||
NetworkItemDataProcessor<I>[] networkItemDataProcessors();
|
||||
|
||||
ItemDataModifier<I>[] dataModifiers();
|
||||
|
||||
Map<String, ItemDataModifier<I>> dataModifierMap();
|
||||
|
||||
boolean hasClientBoundDataModifier();
|
||||
|
||||
ItemDataModifier<I>[] clientBoundDataModifiers();
|
||||
|
||||
Map<String, ItemDataModifier<I>> clientBoundDataModifierMap();
|
||||
|
||||
ItemSettings settings();
|
||||
|
||||
@@ -24,7 +35,7 @@ public interface CustomItem<I> extends BuildableItem<I> {
|
||||
}
|
||||
|
||||
default Item<I> buildItem(Player player) {
|
||||
return buildItem(new ItemBuildContext(player, ContextHolder.EMPTY));
|
||||
return buildItem(ItemBuildContext.of(player, ContextHolder.EMPTY));
|
||||
}
|
||||
|
||||
Item<I> buildItem(ItemBuildContext context);
|
||||
@@ -37,13 +48,17 @@ public interface CustomItem<I> extends BuildableItem<I> {
|
||||
|
||||
Builder<I> material(Key material);
|
||||
|
||||
Builder<I> modifiers(List<ItemModifier<I>> modifiers);
|
||||
Builder<I> dataModifier(ItemDataModifier<I> modifier);
|
||||
|
||||
Builder<I> modifier(ItemModifier<I> modifier);
|
||||
Builder<I> dataModifiers(List<ItemDataModifier<I>> modifiers);
|
||||
|
||||
Builder<I> clientBoundDataModifier(ItemDataModifier<I> modifier);
|
||||
|
||||
Builder<I> clientBoundDataModifiers(List<ItemDataModifier<I>> modifiers);
|
||||
|
||||
Builder<I> behavior(ItemBehavior behavior);
|
||||
|
||||
Builder<I> behavior(List<ItemBehavior> behaviors);
|
||||
Builder<I> behaviors(List<ItemBehavior> behaviors);
|
||||
|
||||
Builder<I> settings(ItemSettings settings);
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package net.momirealms.craftengine.core.item;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
|
||||
@@ -33,6 +34,10 @@ public interface Item<I> {
|
||||
|
||||
Item<I> count(int amount);
|
||||
|
||||
Item<I> trim(Trim trim);
|
||||
|
||||
Optional<Trim> trim();
|
||||
|
||||
Item<I> customModelData(Integer data);
|
||||
|
||||
Optional<Integer> customModelData();
|
||||
@@ -91,13 +96,19 @@ public interface Item<I> {
|
||||
|
||||
boolean removeTag(Object... path);
|
||||
|
||||
boolean hasComponent(String type);
|
||||
boolean hasComponent(Key type);
|
||||
|
||||
void removeComponent(String type);
|
||||
void removeComponent(Key type);
|
||||
|
||||
Object getComponent(String type);
|
||||
Object getComponent(Key type);
|
||||
|
||||
void setComponent(String type, Object value);
|
||||
Object getJavaTypeComponent(Key type);
|
||||
|
||||
JsonElement getJsonTypeComponent(Key type);
|
||||
|
||||
void setComponent(Key type, Object value);
|
||||
|
||||
void resetComponent(Key type);
|
||||
|
||||
I getItem();
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package net.momirealms.craftengine.core.item;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import net.momirealms.craftengine.core.plugin.Plugin;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -22,6 +23,8 @@ public abstract class ItemFactory<P extends Plugin, W extends ItemWrapper<I>, I>
|
||||
|
||||
public abstract Object encodeJava(Key componentType, @Nullable Object component);
|
||||
|
||||
protected abstract JsonElement encodeJson(Key type, Object component);
|
||||
|
||||
protected abstract ItemWrapper<I> wrapInternal(I item);
|
||||
|
||||
protected abstract Object getTag(ItemWrapper<I> item, Object... path);
|
||||
@@ -32,13 +35,15 @@ public abstract class ItemFactory<P extends Plugin, W extends ItemWrapper<I>, I>
|
||||
|
||||
protected abstract boolean removeTag(ItemWrapper<I> item, Object... path);
|
||||
|
||||
protected abstract void setComponent(ItemWrapper<I> item, String type, Object value);
|
||||
protected abstract void setComponent(ItemWrapper<I> item, Key type, Object value);
|
||||
|
||||
protected abstract Object getComponent(ItemWrapper<I> item, String type);
|
||||
protected abstract Object getComponent(ItemWrapper<I> item, Key type);
|
||||
|
||||
protected abstract boolean hasComponent(ItemWrapper<I> item, String type);
|
||||
protected abstract boolean hasComponent(ItemWrapper<I> item, Key type);
|
||||
|
||||
protected abstract void removeComponent(ItemWrapper<I> item, String type);
|
||||
protected abstract void removeComponent(ItemWrapper<I> item, Key type);
|
||||
|
||||
protected abstract void resetComponent(ItemWrapper<I> item, Key type);
|
||||
|
||||
protected abstract void update(ItemWrapper<I> item);
|
||||
|
||||
@@ -112,6 +117,10 @@ public abstract class ItemFactory<P extends Plugin, W extends ItemWrapper<I>, I>
|
||||
|
||||
protected abstract Optional<Integer> repairCost(ItemWrapper<I> item);
|
||||
|
||||
protected abstract void trim(ItemWrapper<I> item, Trim trim);
|
||||
|
||||
protected abstract Optional<Trim> trim(ItemWrapper<I> item);
|
||||
|
||||
protected abstract ItemWrapper<I> mergeCopy(ItemWrapper<I> item1, ItemWrapper<I> item2);
|
||||
|
||||
protected abstract void merge(ItemWrapper<I> item1, ItemWrapper<I> item2);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package net.momirealms.craftengine.core.item;
|
||||
|
||||
import net.momirealms.craftengine.core.item.modifier.EquippableModifier;
|
||||
import net.momirealms.craftengine.core.item.modifier.ItemModifier;
|
||||
import net.momirealms.craftengine.core.item.modifier.ItemDataModifier;
|
||||
import net.momirealms.craftengine.core.pack.misc.EquipmentGeneration;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.MiscUtils;
|
||||
@@ -19,13 +19,14 @@ public class ItemSettings {
|
||||
boolean canRepair = true;
|
||||
List<AnvilRepairItem> anvilRepairItems = List.of();
|
||||
boolean renameable = true;
|
||||
boolean canPlaceRelatedVanillaBlock = false;
|
||||
|
||||
private ItemSettings() {}
|
||||
|
||||
public <I> List<ItemModifier<I>> modifiers() {
|
||||
ArrayList<ItemModifier<I>> modifiers = new ArrayList<>();
|
||||
public <I> List<ItemDataModifier<I>> modifiers() {
|
||||
ArrayList<ItemDataModifier<I>> modifiers = new ArrayList<>();
|
||||
if (VersionHelper.isVersionNewerThan1_21_2() && this.equipment != null && this.equipment.modernData() != null) modifiers.add(new EquippableModifier<>(this.equipment.modernData()));
|
||||
// TODO 1.20
|
||||
// TODO 1.20 leather armor
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
@@ -45,6 +46,7 @@ public class ItemSettings {
|
||||
newSettings.canRepair = settings.canRepair;
|
||||
newSettings.anvilRepairItems = settings.anvilRepairItems;
|
||||
newSettings.renameable = settings.renameable;
|
||||
newSettings.canPlaceRelatedVanillaBlock = settings.canPlaceRelatedVanillaBlock;
|
||||
return newSettings;
|
||||
}
|
||||
|
||||
@@ -60,6 +62,10 @@ public class ItemSettings {
|
||||
return settings;
|
||||
}
|
||||
|
||||
public boolean canPlaceRelatedVanillaBlock() {
|
||||
return canPlaceRelatedVanillaBlock;
|
||||
}
|
||||
|
||||
public boolean canRepair() {
|
||||
return canRepair;
|
||||
}
|
||||
@@ -100,6 +106,11 @@ public class ItemSettings {
|
||||
return this;
|
||||
}
|
||||
|
||||
public ItemSettings canPlaceRelatedVanillaBlock(boolean canPlaceRelatedVanillaBlock) {
|
||||
this.canPlaceRelatedVanillaBlock = canPlaceRelatedVanillaBlock;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ItemSettings fuelTime(int fuelTime) {
|
||||
this.fuelTime = fuelTime;
|
||||
return this;
|
||||
@@ -175,6 +186,10 @@ public class ItemSettings {
|
||||
);
|
||||
return settings -> settings.equipment(equipment);
|
||||
}));
|
||||
registerFactory("can-place", (value -> {
|
||||
boolean bool = (boolean) value;
|
||||
return settings -> settings.canPlaceRelatedVanillaBlock(bool);
|
||||
}));
|
||||
}
|
||||
|
||||
private static void registerFactory(String id, ItemSettings.Modifier.Factory factory) {
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package net.momirealms.craftengine.core.item;
|
||||
|
||||
import net.momirealms.craftengine.core.item.modifier.ItemDataModifier;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public record NetworkItemDataProcessor<I>(@Nullable ItemDataModifier<I> server, @NotNull ItemDataModifier<I> client) {
|
||||
|
||||
public static <I> NetworkItemDataProcessor<I> clientOnly(ItemDataModifier<I> client) {
|
||||
return new NetworkItemDataProcessor<>(null, client);
|
||||
}
|
||||
|
||||
public static <I> NetworkItemDataProcessor<I> both(ItemDataModifier<I> server, ItemDataModifier<I> client) {
|
||||
return new NetworkItemDataProcessor<>(server, client);
|
||||
}
|
||||
|
||||
public void toClient(Item<I> item, ItemBuildContext context) {
|
||||
this.client.apply(item, context);
|
||||
}
|
||||
|
||||
public void toServer(Item<I> item, ItemBuildContext context) {
|
||||
this.client.remove(item);
|
||||
if (this.server != null) {
|
||||
this.server.apply(item, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package net.momirealms.craftengine.core.item;
|
||||
|
||||
public record Trim(String pattern, String material) {
|
||||
}
|
||||
@@ -1,15 +1,57 @@
|
||||
package net.momirealms.craftengine.core.item.modifier;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import net.momirealms.craftengine.core.item.ComponentKeys;
|
||||
import net.momirealms.craftengine.core.item.Item;
|
||||
import net.momirealms.craftengine.core.item.ItemBuildContext;
|
||||
import net.momirealms.craftengine.core.util.GsonHelper;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.Pair;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class ComponentModifier<I> implements ItemModifier<I> {
|
||||
private final Map<String, Object> arguments;
|
||||
public class ComponentModifier<I> implements ItemDataModifier<I> {
|
||||
private final List<Pair<Key, Object>> arguments;
|
||||
private JsonObject customData = null;
|
||||
|
||||
public ComponentModifier(Map<String, Object> arguments) {
|
||||
this.arguments = arguments;
|
||||
List<Pair<Key, Object>> pairs = new ArrayList<>(arguments.size());
|
||||
for (Map.Entry<String, Object> entry : arguments.entrySet()) {
|
||||
Key key = Key.of(entry.getKey());
|
||||
if (key.equals(ComponentKeys.CUSTOM_DATA)) {
|
||||
this.customData = parseJsonObjectValue(entry.getValue());
|
||||
} else {
|
||||
pairs.add(new Pair<>(key, parseValue(entry.getValue())));
|
||||
}
|
||||
}
|
||||
this.arguments = pairs;
|
||||
}
|
||||
|
||||
public List<Pair<Key, Object>> arguments() {
|
||||
return arguments;
|
||||
}
|
||||
|
||||
private Object parseValue(Object value) {
|
||||
if (value instanceof String string) {
|
||||
if (string.startsWith("(json) ")) {
|
||||
return GsonHelper.get().fromJson(string.substring("(json) ".length()), JsonElement.class);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private JsonObject parseJsonObjectValue(Object value) {
|
||||
if (value instanceof String string) {
|
||||
if (string.startsWith("(json) ")) {
|
||||
return GsonHelper.get().fromJson(string.substring("(json) ".length()), JsonObject.class);
|
||||
}
|
||||
} else if (value instanceof Map<?,?> map) {
|
||||
return (JsonObject) GsonHelper.get().toJsonTree(map, Map.class);
|
||||
}
|
||||
throw new UnsupportedOperationException("Invalid minecraft:custom_data value: " + value.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -19,10 +61,33 @@ public class ComponentModifier<I> implements ItemModifier<I> {
|
||||
|
||||
@Override
|
||||
public void apply(Item<I> item, ItemBuildContext context) {
|
||||
for (Map.Entry<String, Object> entry : arguments.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
item.setComponent(key, value);
|
||||
for (Pair<Key, Object> entry : this.arguments) {
|
||||
item.setComponent(entry.left(), entry.right());
|
||||
}
|
||||
if (this.customData != null) {
|
||||
JsonObject tag = (JsonObject) item.getJsonTypeComponent(ComponentKeys.CUSTOM_DATA);
|
||||
if (tag != null) {
|
||||
item.setComponent(ComponentKeys.CUSTOM_DATA, GsonHelper.shallowMerge(this.customData, tag));
|
||||
} else {
|
||||
item.setComponent(ComponentKeys.CUSTOM_DATA, this.customData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(Item<I> item) {
|
||||
for (Pair<Key, Object> entry : this.arguments) {
|
||||
item.resetComponent(entry.left());
|
||||
}
|
||||
if (this.customData != null) {
|
||||
JsonObject tag = (JsonObject) item.getJsonTypeComponent(ComponentKeys.CUSTOM_DATA);
|
||||
if (tag != null) {
|
||||
// crude method
|
||||
for (String key : this.customData.keySet()) {
|
||||
tag.remove(key);
|
||||
}
|
||||
item.setComponent(ComponentKeys.CUSTOM_DATA, tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package net.momirealms.craftengine.core.item.modifier;
|
||||
import net.momirealms.craftengine.core.item.Item;
|
||||
import net.momirealms.craftengine.core.item.ItemBuildContext;
|
||||
|
||||
public class CustomModelDataModifier<I> implements ItemModifier<I> {
|
||||
public class CustomModelDataModifier<I> implements ItemDataModifier<I> {
|
||||
private final int argument;
|
||||
|
||||
public CustomModelDataModifier(int argument) {
|
||||
@@ -19,4 +19,9 @@ public class CustomModelDataModifier<I> implements ItemModifier<I> {
|
||||
public void apply(Item<I> item, ItemBuildContext context) {
|
||||
item.customModelData(argument);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(Item<I> item) {
|
||||
item.customModelData(null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import net.momirealms.craftengine.core.item.ItemBuildContext;
|
||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
import net.momirealms.craftengine.core.util.AdventureHelper;
|
||||
|
||||
public class DisplayNameModifier<I> implements ItemModifier<I> {
|
||||
public class DisplayNameModifier<I> implements ItemDataModifier<I> {
|
||||
private final String argument;
|
||||
|
||||
public DisplayNameModifier(String argument) {
|
||||
@@ -21,4 +21,9 @@ public class DisplayNameModifier<I> implements ItemModifier<I> {
|
||||
public void apply(Item<I> item, ItemBuildContext context) {
|
||||
item.customName(AdventureHelper.componentToJson(AdventureHelper.miniMessage().deserialize(this.argument, context.tagResolvers())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(Item<I> item) {
|
||||
item.customName(null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import net.momirealms.craftengine.core.item.ItemKeys;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class EnchantmentModifier<I> implements ItemModifier<I> {
|
||||
public class EnchantmentModifier<I> implements ItemDataModifier<I> {
|
||||
private final List<Enchantment> enchantments;
|
||||
|
||||
public EnchantmentModifier(List<Enchantment> enchantments) {
|
||||
@@ -24,4 +24,10 @@ public class EnchantmentModifier<I> implements ItemModifier<I> {
|
||||
if (item.vanillaId().equals(ItemKeys.ENCHANTED_BOOK)) item.setStoredEnchantments(enchantments);
|
||||
else item.setEnchantments(enchantments);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(Item<I> item) {
|
||||
if (item.vanillaId().equals(ItemKeys.ENCHANTED_BOOK)) item.setStoredEnchantments(null);
|
||||
else item.setEnchantments(null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import net.momirealms.craftengine.core.item.EquipmentData;
|
||||
import net.momirealms.craftengine.core.item.Item;
|
||||
import net.momirealms.craftengine.core.item.ItemBuildContext;
|
||||
|
||||
public class EquippableModifier<I> implements ItemModifier<I> {
|
||||
public class EquippableModifier<I> implements ItemDataModifier<I> {
|
||||
private final EquipmentData data;
|
||||
|
||||
public EquippableModifier(EquipmentData data) {
|
||||
@@ -21,4 +21,9 @@ public class EquippableModifier<I> implements ItemModifier<I> {
|
||||
public void apply(Item<I> item, ItemBuildContext context) {
|
||||
item.setComponent(ComponentKeys.EQUIPPABLE, this.data.toMap());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(Item<I> item) {
|
||||
item.removeComponent(ComponentKeys.EQUIPPABLE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import net.momirealms.craftengine.core.item.Item;
|
||||
import net.momirealms.craftengine.core.item.ItemBuildContext;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
|
||||
public class ExternalModifier<I> implements ItemModifier<I> {
|
||||
public class ExternalModifier<I> implements ItemDataModifier<I> {
|
||||
private final String id;
|
||||
private final ExternalItemProvider<I> provider;
|
||||
|
||||
@@ -30,4 +30,9 @@ public class ExternalModifier<I> implements ItemModifier<I> {
|
||||
Item<I> anotherWrapped = (Item<I>) CraftEngine.instance().itemManager().wrap(another);
|
||||
item.merge(anotherWrapped);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(Item<I> item) {
|
||||
// cannot remove
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,29 @@
|
||||
package net.momirealms.craftengine.core.item.modifier;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import net.momirealms.craftengine.core.item.ComponentKeys;
|
||||
import net.momirealms.craftengine.core.item.Item;
|
||||
import net.momirealms.craftengine.core.item.ItemBuildContext;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
|
||||
public class IdModifier<I> implements ItemModifier<I> {
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
public class IdModifier<I> implements ItemDataModifier<I> {
|
||||
public static final String CRAFT_ENGINE_ID = "craftengine:id";
|
||||
private static final BiConsumer<Item<?>, String> ID_SETTER = VersionHelper.isVersionNewerThan1_20_5() ?
|
||||
((item, id) -> {
|
||||
JsonElement element = item.getJsonTypeComponent(ComponentKeys.CUSTOM_DATA);
|
||||
if (element instanceof JsonObject jo) {
|
||||
jo.add(CRAFT_ENGINE_ID, new JsonPrimitive(id));
|
||||
item.setComponent(ComponentKeys.CUSTOM_DATA, jo);
|
||||
} else {
|
||||
item.setComponent(ComponentKeys.CUSTOM_DATA, Map.of(CRAFT_ENGINE_ID, id));
|
||||
}
|
||||
}) : ((item, id) -> item.setTag(id, CRAFT_ENGINE_ID));
|
||||
private final Key argument;
|
||||
|
||||
public IdModifier(Key argument) {
|
||||
@@ -19,6 +37,11 @@ public class IdModifier<I> implements ItemModifier<I> {
|
||||
|
||||
@Override
|
||||
public void apply(Item<I> item, ItemBuildContext context) {
|
||||
item.setTag(argument.toString(), CRAFT_ENGINE_ID);
|
||||
ID_SETTER.accept(item, argument.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(Item<I> item) {
|
||||
item.removeTag(CRAFT_ENGINE_ID);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,11 @@ package net.momirealms.craftengine.core.item.modifier;
|
||||
import net.momirealms.craftengine.core.item.Item;
|
||||
import net.momirealms.craftengine.core.item.ItemBuildContext;
|
||||
|
||||
public interface ItemModifier<I> {
|
||||
public interface ItemDataModifier<I> {
|
||||
|
||||
String name();
|
||||
|
||||
void apply(Item<I> item, ItemBuildContext context);
|
||||
|
||||
void remove(Item<I> item);
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import net.momirealms.craftengine.core.item.Item;
|
||||
import net.momirealms.craftengine.core.item.ItemBuildContext;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
|
||||
public class ItemModelModifier<I> implements ItemModifier<I> {
|
||||
public class ItemModelModifier<I> implements ItemDataModifier<I> {
|
||||
private final Key data;
|
||||
|
||||
public ItemModelModifier(Key data) {
|
||||
@@ -19,6 +19,11 @@ public class ItemModelModifier<I> implements ItemModifier<I> {
|
||||
|
||||
@Override
|
||||
public void apply(Item<I> item, ItemBuildContext context) {
|
||||
item.setComponent(ComponentKeys.ITEM_MODEL, this.data.toString());
|
||||
item.setComponent(ComponentKeys.ITEM_MODEL, this.data.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(Item<I> item) {
|
||||
item.removeComponent(ComponentKeys.ITEM_MODEL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import net.momirealms.craftengine.core.item.ItemBuildContext;
|
||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
import net.momirealms.craftengine.core.util.AdventureHelper;
|
||||
|
||||
public class ItemNameModifier<I> implements ItemModifier<I> {
|
||||
public class ItemNameModifier<I> implements ItemDataModifier<I> {
|
||||
private final String argument;
|
||||
|
||||
public ItemNameModifier(String argument) {
|
||||
@@ -21,4 +21,9 @@ public class ItemNameModifier<I> implements ItemModifier<I> {
|
||||
public void apply(Item<I> item, ItemBuildContext context) {
|
||||
item.itemName(AdventureHelper.componentToJson(AdventureHelper.miniMessage().deserialize(this.argument, context.tagResolvers())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(Item<I> item) {
|
||||
item.itemName(null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class JukeboxSongModifier<I> implements ItemModifier<I> {
|
||||
public class JukeboxSongModifier<I> implements ItemDataModifier<I> {
|
||||
private final Key song;
|
||||
|
||||
public JukeboxSongModifier(Key song) {
|
||||
@@ -31,4 +31,9 @@ public class JukeboxSongModifier<I> implements ItemModifier<I> {
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(Item<I> item) {
|
||||
item.removeComponent(ComponentKeys.JUKEBOX_PLAYABLE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import net.momirealms.craftengine.core.util.AdventureHelper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class LoreModifier<I> implements ItemModifier<I> {
|
||||
public class LoreModifier<I> implements ItemDataModifier<I> {
|
||||
private final List<String> argument;
|
||||
|
||||
public LoreModifier(List<String> argument) {
|
||||
@@ -24,4 +24,9 @@ public class LoreModifier<I> implements ItemModifier<I> {
|
||||
item.lore(argument.stream().map(it -> AdventureHelper.componentToJson(AdventureHelper.miniMessage().deserialize(
|
||||
it, context.tagResolvers()))).toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(Item<I> item) {
|
||||
item.lore(null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,13 +8,17 @@ import net.momirealms.craftengine.core.util.TypeUtils;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class TagsModifier<I> implements ItemModifier<I> {
|
||||
public class TagsModifier<I> implements ItemDataModifier<I> {
|
||||
private final Map<String, Object> arguments;
|
||||
|
||||
public TagsModifier(Map<String, Object> arguments) {
|
||||
this.arguments = mapToMap(arguments);
|
||||
}
|
||||
|
||||
public Map<String, Object> arguments() {
|
||||
return arguments;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "tags";
|
||||
@@ -29,6 +33,13 @@ public class TagsModifier<I> implements ItemModifier<I> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(Item<I> item) {
|
||||
for (Map.Entry<String, Object> entry : arguments.entrySet()) {
|
||||
item.removeTag(entry.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
private static Map<String, Object> mapToMap(final Map<String, Object> source) {
|
||||
Map<String, Object> resultMap = new LinkedHashMap<>();
|
||||
recursiveMapProcessing(source, resultMap);
|
||||
|
||||
@@ -5,7 +5,7 @@ import net.momirealms.craftengine.core.item.Item;
|
||||
import net.momirealms.craftengine.core.item.ItemBuildContext;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
|
||||
public class TooltipStyleModifier<I> implements ItemModifier<I> {
|
||||
public class TooltipStyleModifier<I> implements ItemDataModifier<I> {
|
||||
private final Key argument;
|
||||
|
||||
public TooltipStyleModifier(Key argument) {
|
||||
@@ -21,4 +21,9 @@ public class TooltipStyleModifier<I> implements ItemModifier<I> {
|
||||
public void apply(Item<I> item, ItemBuildContext context) {
|
||||
item.setComponent(ComponentKeys.TOOLTIP_STYLE, argument.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(Item<I> item) {
|
||||
item.removeComponent(ComponentKeys.TOOLTIP_STYLE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,11 +3,10 @@ package net.momirealms.craftengine.core.item.modifier;
|
||||
import net.momirealms.craftengine.core.item.ComponentKeys;
|
||||
import net.momirealms.craftengine.core.item.Item;
|
||||
import net.momirealms.craftengine.core.item.ItemBuildContext;
|
||||
import net.momirealms.craftengine.core.item.Trim;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class TrimModifier<I> implements ItemModifier<I> {
|
||||
public class TrimModifier<I> implements ItemDataModifier<I> {
|
||||
private final String material;
|
||||
private final String pattern;
|
||||
|
||||
@@ -23,14 +22,22 @@ public class TrimModifier<I> implements ItemModifier<I> {
|
||||
|
||||
@Override
|
||||
public void apply(Item<I> item, ItemBuildContext context) {
|
||||
item.trim(new Trim(this.material, this.pattern));
|
||||
|
||||
if (VersionHelper.isVersionNewerThan1_20_5()) {
|
||||
item.setComponent(ComponentKeys.TRIM, Map.of(
|
||||
"pattern", this.pattern,
|
||||
"material", this.material
|
||||
));
|
||||
|
||||
} else {
|
||||
item.setTag(this.material, "Trim", "material");
|
||||
item.setTag(this.pattern, "Trim", "pattern");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(Item<I> item) {
|
||||
item.trim(null);
|
||||
if (VersionHelper.isVersionNewerThan1_20_5()) {
|
||||
item.removeComponent(ComponentKeys.TRIM);
|
||||
} else {
|
||||
item.removeTag("Trim");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package net.momirealms.craftengine.core.item.modifier;
|
||||
import net.momirealms.craftengine.core.item.Item;
|
||||
import net.momirealms.craftengine.core.item.ItemBuildContext;
|
||||
|
||||
public class UnbreakableModifier<I> implements ItemModifier<I> {
|
||||
public class UnbreakableModifier<I> implements ItemDataModifier<I> {
|
||||
private final boolean argument;
|
||||
|
||||
public UnbreakableModifier(boolean argument) {
|
||||
@@ -17,6 +17,13 @@ public class UnbreakableModifier<I> implements ItemModifier<I> {
|
||||
|
||||
@Override
|
||||
public void apply(Item<I> item, ItemBuildContext context) {
|
||||
item.unbreakable(argument);
|
||||
item.unbreakable(this.argument);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(Item<I> item) {
|
||||
if (this.argument) {
|
||||
item.unbreakable(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,9 +217,9 @@ public class CustomSmithingTransformRecipe<T> implements Recipe<T> {
|
||||
@Override
|
||||
public void accept(Item<?> item1, Item<?> item2, Item<?> item3) {
|
||||
for (Key component : this.components) {
|
||||
Object componentObj = item1.getComponent(component.toString());
|
||||
Object componentObj = item1.getComponent(component);
|
||||
if (componentObj != null) {
|
||||
item3.setComponent(component.toString(), CraftEngine.instance().itemManager().encodeJava(component, componentObj));
|
||||
item3.setComponent(component, componentObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,17 +52,7 @@ public abstract class AbstractPackManager implements PackManager {
|
||||
public static final Set<Key> VANILLA_ITEM_TEXTURES = new HashSet<>();
|
||||
public static final Set<Key> VANILLA_BLOCK_TEXTURES = new HashSet<>();
|
||||
public static final Set<Key> VANILLA_FONT_TEXTURES = new HashSet<>();
|
||||
|
||||
private final CraftEngine plugin;
|
||||
private final BiConsumer<Path, Path> eventDispatcher;
|
||||
private final Map<String, Pack> loadedPacks = new HashMap<>();
|
||||
private final Map<String, ConfigSectionParser> sectionParsers = new HashMap<>();
|
||||
private final TreeMap<ConfigSectionParser, List<CachedConfig>> cachedConfigs = new TreeMap<>();
|
||||
private static final byte[] emptyImage;
|
||||
protected BiConsumer<Path, Path> zipGenerator;
|
||||
protected String packHash;
|
||||
protected UUID packUUID;
|
||||
|
||||
private static final byte[] EMPTY_IMAGE;
|
||||
static {
|
||||
var stream = new ByteArrayOutputStream();
|
||||
try {
|
||||
@@ -70,9 +60,19 @@ public abstract class AbstractPackManager implements PackManager {
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
emptyImage = stream.toByteArray();
|
||||
EMPTY_IMAGE = stream.toByteArray();
|
||||
}
|
||||
|
||||
private final CraftEngine plugin;
|
||||
private final BiConsumer<Path, Path> eventDispatcher;
|
||||
private final Map<String, Pack> loadedPacks = new HashMap<>();
|
||||
private final Map<String, ConfigSectionParser> sectionParsers = new HashMap<>();
|
||||
private final TreeMap<ConfigSectionParser, List<CachedConfig>> cachedConfigs = new TreeMap<>();
|
||||
|
||||
protected BiConsumer<Path, Path> zipGenerator;
|
||||
protected String packHash;
|
||||
protected UUID packUUID;
|
||||
|
||||
public AbstractPackManager(CraftEngine plugin, BiConsumer<Path, Path> eventDispatcher) {
|
||||
this.plugin = plugin;
|
||||
this.eventDispatcher = eventDispatcher;
|
||||
@@ -233,7 +233,6 @@ public abstract class AbstractPackManager implements PackManager {
|
||||
Files.createDirectories(resourcesFolder);
|
||||
this.saveDefaultConfigs();
|
||||
}
|
||||
|
||||
try (DirectoryStream<Path> paths = Files.newDirectoryStream(resourcesFolder)) {
|
||||
for (Path path : paths) {
|
||||
if (!Files.isDirectory(path)) {
|
||||
@@ -245,14 +244,16 @@ public abstract class AbstractPackManager implements PackManager {
|
||||
String description = null;
|
||||
String version = null;
|
||||
String author = null;
|
||||
boolean enable = true;
|
||||
if (Files.exists(metaFile) && Files.isRegularFile(metaFile)) {
|
||||
YamlDocument metaYML = Config.instance().loadYamlData(metaFile.toFile());
|
||||
enable = metaYML.getBoolean("enable", true);
|
||||
namespace = metaYML.getString("namespace", namespace);
|
||||
description = metaYML.getString("description");
|
||||
version = metaYML.getString("version");
|
||||
author = metaYML.getString("author");
|
||||
}
|
||||
Pack pack = new Pack(path, new PackMeta(author, description, version, namespace));
|
||||
Pack pack = new Pack(path, new PackMeta(author, description, version, namespace), enable);
|
||||
this.loadedPacks.put(path.getFileName().toString(), pack);
|
||||
this.plugin.logger().info("Loaded pack: " + pack.folder().getFileName() + ". Default namespace: " + namespace);
|
||||
}
|
||||
@@ -396,6 +397,7 @@ public abstract class AbstractPackManager implements PackManager {
|
||||
private void loadResourceConfigs(Predicate<ConfigSectionParser> predicate) {
|
||||
long o1 = System.nanoTime();
|
||||
for (Pack pack : loadedPacks()) {
|
||||
if (!pack.enabled()) continue;
|
||||
Pair<List<Path>, List<Path>> files = FileUtils.getConfigsDeeply(pack.configurationFolder());
|
||||
for (Path path : files.left()) {
|
||||
try (InputStreamReader inputStream = new InputStreamReader(new FileInputStream(path.toFile()), StandardCharsets.UTF_8)) {
|
||||
@@ -571,7 +573,7 @@ public abstract class AbstractPackManager implements PackManager {
|
||||
}
|
||||
try {
|
||||
GsonHelper.writeJsonFile(json, jsonPath);
|
||||
Files.write(pngPath, emptyImage);
|
||||
Files.write(pngPath, EMPTY_IMAGE);
|
||||
} catch (IOException e) {
|
||||
this.plugin.logger().severe("Error writing particles file", e);
|
||||
}
|
||||
|
||||
@@ -15,16 +15,26 @@ import java.nio.file.Path;
|
||||
public class Pack {
|
||||
private final Path folder;
|
||||
private final PackMeta meta;
|
||||
private final boolean enabled;
|
||||
|
||||
public Pack(Path folder, PackMeta meta) {
|
||||
public Pack(Path folder, PackMeta meta, boolean enabled) {
|
||||
this.folder = folder;
|
||||
this.meta = meta;
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return folder.getFileName().toString();
|
||||
}
|
||||
|
||||
public String namespace() {
|
||||
return meta.namespace();
|
||||
}
|
||||
|
||||
public boolean enabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public PackMeta meta() {
|
||||
return meta;
|
||||
}
|
||||
|
||||
@@ -53,6 +53,9 @@ public class CompositeItemModel implements ItemModel {
|
||||
@Override
|
||||
public ItemModel create(Map<String, Object> arguments) {
|
||||
List<Map<String, Object>> models = (List<Map<String, Object>>) arguments.get("models");
|
||||
if (models == null || models.isEmpty()) {
|
||||
throw new IllegalArgumentException("No 'models' specified for 'minecraft:composite'");
|
||||
}
|
||||
List<ItemModel> modelList = new ArrayList<>();
|
||||
for (Map<String, Object> model : models) {
|
||||
modelList.add(ItemModels.fromMap(model));
|
||||
|
||||
@@ -64,8 +64,8 @@ public class ConditionItemModel implements ItemModel {
|
||||
@Override
|
||||
public ItemModel create(Map<String, Object> arguments) {
|
||||
ConditionProperty property = ConditionProperties.fromMap(arguments);
|
||||
Map<String, Object> onTrue = Objects.requireNonNull((Map<String, Object>) arguments.get("on-true"));
|
||||
Map<String, Object> onFalse = Objects.requireNonNull((Map<String, Object>) arguments.get("on-false"));
|
||||
Map<String, Object> onTrue = Objects.requireNonNull((Map<String, Object>) arguments.get("on-true"), "No 'on-true' set for 'minecraft:condition'");
|
||||
Map<String, Object> onFalse = Objects.requireNonNull((Map<String, Object>) arguments.get("on-false"), "No 'on-false' set for 'minecraft:condition'");
|
||||
return new ConditionItemModel(property, ItemModels.fromMap(onTrue), ItemModels.fromMap(onFalse));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ public class RangeDispatchItemModel implements ItemModel {
|
||||
}
|
||||
return new RangeDispatchItemModel(property, scale, fallback == null ? null : ItemModels.fromMap(fallback), entryMap);
|
||||
}
|
||||
throw new IllegalArgumentException("No entries set for range dispatch");
|
||||
throw new IllegalArgumentException("No 'entries' set for 'minecraft:range_dispatch'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ public class SelectItemModel implements ItemModel {
|
||||
}
|
||||
return new SelectItemModel(property, whenMap, fallback == null ? null : ItemModels.fromMap(fallback));
|
||||
}
|
||||
throw new IllegalArgumentException("No cases set for select");
|
||||
throw new IllegalArgumentException("No 'cases' set for 'minecraft:select'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import net.momirealms.craftengine.core.sound.SoundManager;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
import net.momirealms.craftengine.core.world.WorldManager;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.core.Logger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -90,8 +91,8 @@ public abstract class CraftEngine implements Plugin {
|
||||
}
|
||||
|
||||
public void onPluginLoad() {
|
||||
((org.apache.logging.log4j.core.Logger) LogManager.getRootLogger()).addFilter(new LogFilter());
|
||||
((org.apache.logging.log4j.core.Logger) LogManager.getRootLogger()).addFilter(new DisconnectLogFilter());
|
||||
((Logger) LogManager.getRootLogger()).addFilter(new LogFilter());
|
||||
((Logger) LogManager.getRootLogger()).addFilter(new DisconnectLogFilter());
|
||||
this.dependencyManager = new DependencyManagerImpl(this);
|
||||
ArrayList<Dependency> dependenciesToLoad = new ArrayList<>();
|
||||
dependenciesToLoad.addAll(commonDependencies());
|
||||
@@ -115,72 +116,77 @@ public abstract class CraftEngine implements Plugin {
|
||||
public CompletableFuture<ReloadResult> reloadPlugin(Executor asyncExecutor, Executor syncExecutor, boolean reloadRecipe) {
|
||||
CompletableFuture<ReloadResult> future = new CompletableFuture<>();
|
||||
asyncExecutor.execute(() -> {
|
||||
if (this.isReloading) {
|
||||
future.complete(ReloadResult.failure());
|
||||
return;
|
||||
}
|
||||
this.isReloading = true;
|
||||
long time1 = System.currentTimeMillis();
|
||||
// firstly reload main config
|
||||
this.config.load();
|
||||
// reset debugger
|
||||
this.debugger = Config.debug() ? (s) -> logger.info("[Debug] " + s.get()) : (s) -> {};
|
||||
// now we reload the translations
|
||||
this.translationManager.reload();
|
||||
// clear the outdated cache by reloading the managers
|
||||
this.templateManager.reload();
|
||||
this.furnitureManager.reload();
|
||||
this.fontManager.reload();
|
||||
this.itemManager.reload();
|
||||
this.soundManager.reload();
|
||||
this.itemBrowserManager.reload();
|
||||
this.blockManager.reload();
|
||||
this.worldManager.reload();
|
||||
this.vanillaLootManager.reload();
|
||||
this.guiManager.reload();
|
||||
this.packManager.reload();
|
||||
if (reloadRecipe) {
|
||||
this.recipeManager.reload();
|
||||
}
|
||||
long asyncTime = -1;
|
||||
try {
|
||||
// now we load resources
|
||||
this.packManager.loadResources(reloadRecipe);
|
||||
} catch (Exception e) {
|
||||
this.logger().warn("Failed to load resources folder", e);
|
||||
}
|
||||
// init suggestions and packet mapper
|
||||
this.blockManager.delayedLoad();
|
||||
// handle some special client lang for instance block_name
|
||||
this.translationManager.delayedLoad();
|
||||
// init suggestions
|
||||
this.furnitureManager.delayedLoad();
|
||||
// sort the categories
|
||||
this.itemBrowserManager.delayedLoad();
|
||||
// collect illegal characters from minecraft:default font
|
||||
this.fontManager.delayedLoad();
|
||||
if (reloadRecipe) {
|
||||
// convert data pack recipes
|
||||
this.recipeManager.delayedLoad();
|
||||
}
|
||||
long time2 = System.currentTimeMillis();
|
||||
long asyncTime = time2 - time1;
|
||||
syncExecutor.execute(() -> {
|
||||
try {
|
||||
long time3 = System.currentTimeMillis();
|
||||
// register songs
|
||||
this.soundManager.runDelayedSyncTasks();
|
||||
// register recipes
|
||||
if (reloadRecipe) {
|
||||
this.recipeManager.runDelayedSyncTasks();
|
||||
}
|
||||
long time4 = System.currentTimeMillis();
|
||||
long syncTime = time4 - time3;
|
||||
this.reloadEventDispatcher.accept(this);
|
||||
future.complete(ReloadResult.success(asyncTime, syncTime));
|
||||
} finally {
|
||||
this.isReloading = false;
|
||||
if (this.isReloading) {
|
||||
future.complete(ReloadResult.failure());
|
||||
return;
|
||||
}
|
||||
});
|
||||
this.isReloading = true;
|
||||
long time1 = System.currentTimeMillis();
|
||||
// firstly reload main config
|
||||
this.config.load();
|
||||
// reset debugger
|
||||
this.debugger = Config.debug() ? (s) -> logger.info("[Debug] " + s.get()) : (s) -> {};
|
||||
// now we reload the translations
|
||||
this.translationManager.reload();
|
||||
// clear the outdated cache by reloading the managers
|
||||
this.templateManager.reload();
|
||||
this.furnitureManager.reload();
|
||||
this.fontManager.reload();
|
||||
this.itemManager.reload();
|
||||
this.soundManager.reload();
|
||||
this.itemBrowserManager.reload();
|
||||
this.blockManager.reload();
|
||||
this.worldManager.reload();
|
||||
this.vanillaLootManager.reload();
|
||||
this.guiManager.reload();
|
||||
this.packManager.reload();
|
||||
if (reloadRecipe) {
|
||||
this.recipeManager.reload();
|
||||
}
|
||||
try {
|
||||
// now we load resources
|
||||
this.packManager.loadResources(reloadRecipe);
|
||||
} catch (Exception e) {
|
||||
this.logger().warn("Failed to load resources folder", e);
|
||||
}
|
||||
// init suggestions and packet mapper
|
||||
this.blockManager.delayedLoad();
|
||||
// handle some special client lang for instance block_name
|
||||
this.translationManager.delayedLoad();
|
||||
// init suggestions
|
||||
this.furnitureManager.delayedLoad();
|
||||
// sort the categories
|
||||
this.itemBrowserManager.delayedLoad();
|
||||
// collect illegal characters from minecraft:default font
|
||||
this.fontManager.delayedLoad();
|
||||
if (reloadRecipe) {
|
||||
// convert data pack recipes
|
||||
this.recipeManager.delayedLoad();
|
||||
}
|
||||
long time2 = System.currentTimeMillis();
|
||||
asyncTime = time2 - time1;
|
||||
} finally {
|
||||
long finalAsyncTime = asyncTime;
|
||||
syncExecutor.execute(() -> {
|
||||
try {
|
||||
long time3 = System.currentTimeMillis();
|
||||
// register songs
|
||||
this.soundManager.runDelayedSyncTasks();
|
||||
// register recipes
|
||||
if (reloadRecipe) {
|
||||
this.recipeManager.runDelayedSyncTasks();
|
||||
}
|
||||
long time4 = System.currentTimeMillis();
|
||||
long syncTime = time4 - time3;
|
||||
this.reloadEventDispatcher.accept(this);
|
||||
future.complete(ReloadResult.success(finalAsyncTime, syncTime));
|
||||
} finally {
|
||||
this.isReloading = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
@@ -111,6 +111,11 @@ public class Config {
|
||||
protected boolean furniture$hide_base_entity;
|
||||
|
||||
protected boolean block$sound_system$enable;
|
||||
protected boolean block$simplify_adventure_break_check;
|
||||
protected boolean block$simplify_adventure_place_check;
|
||||
protected boolean block$predict_breaking;
|
||||
protected int block$predict_breaking_interval;
|
||||
protected double block$extended_interaction_range;
|
||||
|
||||
protected boolean recipe$enable;
|
||||
protected boolean recipe$disable_vanilla_recipes$all;
|
||||
@@ -281,6 +286,11 @@ public class Config {
|
||||
|
||||
// block
|
||||
block$sound_system$enable = config.getBoolean("block.sound-system.enable", true);
|
||||
block$simplify_adventure_break_check = config.getBoolean("block.simplify-adventure-break-check", false);
|
||||
block$simplify_adventure_place_check = config.getBoolean("block.simplify-adventure-place-check", false);
|
||||
block$predict_breaking = config.getBoolean("block.predict-breaking.enable", true);
|
||||
block$predict_breaking_interval = Math.max(config.getInt("block.predict-breaking.interval", 10), 1);
|
||||
block$extended_interaction_range = Math.max(config.getDouble("block.predict-breaking.extended-interaction-range", 0.5), 0.0);
|
||||
|
||||
// recipe
|
||||
recipe$enable = config.getBoolean("recipe.enable", true);
|
||||
@@ -390,6 +400,14 @@ public class Config {
|
||||
return instance.block$sound_system$enable;
|
||||
}
|
||||
|
||||
public static boolean simplifyAdventureBreakCheck() {
|
||||
return instance.block$simplify_adventure_break_check;
|
||||
}
|
||||
|
||||
public static boolean simplifyAdventurePlaceCheck() {
|
||||
return instance.block$simplify_adventure_place_check;
|
||||
}
|
||||
|
||||
public static boolean enableRecipeSystem() {
|
||||
return instance.recipe$enable;
|
||||
}
|
||||
@@ -662,6 +680,18 @@ public class Config {
|
||||
return instance.image$intercept_packets$set_score;
|
||||
}
|
||||
|
||||
public static boolean predictBreaking() {
|
||||
return instance.block$predict_breaking;
|
||||
}
|
||||
|
||||
public static int predictBreakingInterval() {
|
||||
return instance.block$predict_breaking_interval;
|
||||
}
|
||||
|
||||
public static double extendedInteractionRange() {
|
||||
return instance.block$extended_interaction_range;
|
||||
}
|
||||
|
||||
public YamlDocument loadOrCreateYamlData(String fileName) {
|
||||
File file = new File(this.plugin.dataFolderFile(), fileName);
|
||||
if (!file.exists()) {
|
||||
|
||||
@@ -21,4 +21,9 @@ public interface MessageConstants {
|
||||
TranslatableComponent.Builder COMMAND_SEARCH_USAGE_NOT_FOUND = Component.translatable().key("command.search_usage.not_found");
|
||||
TranslatableComponent.Builder COMMAND_SEARCH_USAGE_NO_ITEM = Component.translatable().key("command.search_usage.no_item");
|
||||
TranslatableComponent.Builder COMMAND_TOTEM_NOT_TOTEM = Component.translatable().key("command.totem_animation.failure.not_totem");
|
||||
TranslatableComponent.Builder COMMAND_RESOURCE_ENABLE_SUCCESS = Component.translatable().key("command.resource.enable.success");
|
||||
TranslatableComponent.Builder COMMAND_RESOURCE_ENABLE_FAILURE = Component.translatable().key("command.resource.enable.failure.unknown");
|
||||
TranslatableComponent.Builder COMMAND_RESOURCE_DISABLE_SUCCESS = Component.translatable().key("command.resource.disable.success");
|
||||
TranslatableComponent.Builder COMMAND_RESOURCE_DISABLE_FAILURE = Component.translatable().key("command.resource.disable.failure.unknown");
|
||||
TranslatableComponent.Builder COMMAND_RESOURCE_LIST = Component.translatable().key("command.resource.list");
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ public class IndexedArgumentTag implements TagResolver {
|
||||
throw ctx.newException("Invalid argument number", arguments);
|
||||
}
|
||||
|
||||
return Tag.inserting(argumentComponents.get(index));
|
||||
return Tag.selfClosingInserting(argumentComponents.get(index));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -3,6 +3,7 @@ package net.momirealms.craftengine.core.util;
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public class ArrayUtils {
|
||||
@@ -59,4 +60,14 @@ public class ArrayUtils {
|
||||
public static boolean isEmpty(Object[] array) {
|
||||
return array == null || array.length == 0;
|
||||
}
|
||||
|
||||
public static <T> T[] collectionToArray(Collection<T> array, Class<T> clazz) {
|
||||
@SuppressWarnings("unchecked")
|
||||
T[] res = (T[]) Array.newInstance(clazz, array.size());
|
||||
int i = 0;
|
||||
for (T item : array) {
|
||||
res[i++] = item;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ public class MiscUtils {
|
||||
if (obj instanceof Map<?, ?> map) {
|
||||
return (Map<String, Object>) map;
|
||||
}
|
||||
throw new IllegalArgumentException("Expected Map, got: " + obj.getClass().getSimpleName());
|
||||
throw new IllegalArgumentException("Expected Map, got: " + (obj == null ? null : obj.getClass().getSimpleName()));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
package net.momirealms.craftengine.core.world;
|
||||
|
||||
public class WorldEvents {
|
||||
public static final int DISPENSER_DISPENSES = 1000;
|
||||
public static final int DISPENSER_FAILS_TO_DISPENSE = 1001;
|
||||
public static final int DISPENSER_SHOOTS = 1002;
|
||||
public static final int FIREWORK_SHOT = 1004;
|
||||
public static final int FIRE_EXTINGUISHED = 1009;
|
||||
public static final int PLAY_RECORD = 1010;
|
||||
public static final int STOP_RECORD = 1011;
|
||||
public static final int GHAST_WARNS = 1015;
|
||||
public static final int GHAST_SHOOTS = 1016;
|
||||
public static final int ENDER_DRAGON_SHOOTS = 1017;
|
||||
public static final int BLAZE_SHOOTS = 1018;
|
||||
public static final int ZOMBIE_ATTACKS_WOODEN_DOOR = 1019;
|
||||
public static final int ZOMBIE_ATTACKS_IRON_DOOR = 1020;
|
||||
public static final int ZOMBIE_BREAKS_WOODEN_DOOR = 1021;
|
||||
public static final int WITHER_BREAKS_BLOCK = 1022;
|
||||
public static final int WITHER_SPAWNED = 1023;
|
||||
public static final int WITHER_SHOOTS = 1024;
|
||||
public static final int BAT_TAKES_OFF = 1025;
|
||||
public static final int ZOMBIE_INFECTS = 1026;
|
||||
public static final int ZOMBIE_VILLAGER_CONVERTED = 1027;
|
||||
public static final int ENDER_DRAGON_DIES = 1028;
|
||||
public static final int ANVIL_DESTROYED = 1029;
|
||||
public static final int ANVIL_USED = 1030;
|
||||
public static final int ANVIL_LANDS = 1031;
|
||||
public static final int PORTAL_TRAVEL = 1032;
|
||||
public static final int CHORUS_FLOWER_GROWS = 1033;
|
||||
public static final int CHORUS_FLOWER_DIES = 1034;
|
||||
public static final int BREWING_STAND_BREWS = 1035;
|
||||
public static final int END_PORTAL_CREATED = 1038;
|
||||
public static final int PHANTOM_BITES = 1039;
|
||||
public static final int ZOMBIE_CONVERTS_TO_DROWNED = 1040;
|
||||
public static final int HUSK_CONVERTS_TO_ZOMBIE_BY_DROWNING = 1041;
|
||||
public static final int GRINDSTONE_USED = 1042;
|
||||
public static final int BOOK_PAGE_TURNED = 1043;
|
||||
public static final int SMITHING_TABLE_USED = 1044;
|
||||
public static final int POINTED_DRIPSTONE_LANDING = 1045;
|
||||
public static final int LAVA_DRIPPING_ON_CAULDRON_FROM_DRIPSTONE = 1046;
|
||||
public static final int WATER_DRIPPING_ON_CAULDRON_FROM_DRIPSTONE = 1047;
|
||||
public static final int SKELETON_CONVERTS_TO_STRAY = 1048;
|
||||
public static final int CRAFTER_SUCCESSFULLY_CRAFTS_ITEM = 1049;
|
||||
public static final int CRAFTER_FAILS_TO_CRAFT_ITEM = 1050;
|
||||
public static final int COMPOSTER_COMPOSTS = 1500;
|
||||
public static final int LAVA_CONVERTS_BLOCK = 1501;
|
||||
public static final int REDSTONE_TORCH_BURNS_OUT = 1502;
|
||||
public static final int ENDER_EYE_PLACED_IN_END_PORTAL_FRAME = 1503;
|
||||
public static final int FLUID_DRIPS_FROM_DRIPSTONE = 1504;
|
||||
public static final int BONE_MEAL_PARTICLES_AND_SOUND = 1505;
|
||||
public static final int DISPENSER_ACTIVATION_SMOKE = 2000;
|
||||
public static final int BLOCK_BREAK_EFFECT = 2001;
|
||||
public static final int SPLASH_POTION_EFFECT = 2002;
|
||||
public static final int ENDER_EYE_ENTITY_BREAK_ANIMATION = 2003;
|
||||
public static final int SPAWNER_SPAWNS_MOB = 2004;
|
||||
public static final int DRAGON_BREATH = 2006;
|
||||
public static final int INSTANT_SPLASH_POTION = 2007;
|
||||
public static final int ENDER_DRAGON_DESTROYS_BLOCK = 2008;
|
||||
public static final int WET_SPONGE_VAPORIZES = 2009;
|
||||
public static final int CRAFTER_ACTIVATION_SMOKE = 2010;
|
||||
public static final int BEE_FERTILIZES_PLANT = 2011;
|
||||
public static final int TURTLE_EGG_PLACED = 2012;
|
||||
public static final int SMASH_ATTACK = 2013;
|
||||
public static final int END_GATEWAY_SPAWNS = 3000;
|
||||
public static final int ENDER_DRAGON_RESURRECTED = 3001;
|
||||
public static final int ELECTRIC_SPARK = 3002;
|
||||
public static final int COPPER_APPLY_WAX = 3003;
|
||||
public static final int COPPER_REMOVE_WAX = 3004;
|
||||
public static final int COPPER_SCRAPE_OXIDATION = 3005;
|
||||
public static final int SCULK_CHARGE = 3006;
|
||||
public static final int SCULK_SHRIEKER_SHRIEK = 3007;
|
||||
public static final int BLOCK_FINISHED_BRUSHING = 3008;
|
||||
public static final int SNIFFER_EGG_CRACKS = 3009;
|
||||
public static final int TRIAL_SPAWNER_SPAWNS_MOB_AT_SPAWNER = 3011;
|
||||
public static final int TRIAL_SPAWNER_SPAWNS_MOB_AT_LOCATION = 3012;
|
||||
public static final int TRIAL_SPAWNER_DETECTS_PLAYER = 3013;
|
||||
public static final int TRIAL_SPAWNER_EJECTS_ITEM = 3014;
|
||||
public static final int VAULT_ACTIVATES = 3015;
|
||||
public static final int VAULT_DEACTIVATES = 3016;
|
||||
public static final int VAULT_EJECTS_ITEM = 3017;
|
||||
public static final int COBWEB_WEAVED = 3018;
|
||||
public static final int OMINOUS_TRIAL_SPAWNER_DETECTS_PLAYER = 3019;
|
||||
public static final int TRIAL_SPAWNER_TURNS_OMINOUS = 3020;
|
||||
public static final int OMINOUS_ITEM_SPAWNER_SPAWNS_ITEM = 3021;
|
||||
|
||||
private WorldEvents() {}
|
||||
}
|
||||
@@ -2,18 +2,18 @@ org.gradle.jvmargs=-Xmx1G
|
||||
|
||||
# Project settings
|
||||
# Rule: [major update].[feature update].[bug fix]
|
||||
project_version=0.0.47.1
|
||||
config_version=25
|
||||
project_version=0.0.48
|
||||
config_version=26
|
||||
lang_version=4
|
||||
project_group=net.momirealms
|
||||
latest_supported_version=1.21.5
|
||||
latest_minecraft_version=1.21.4
|
||||
latest_minecraft_version=1.21.5
|
||||
|
||||
# Supported languages
|
||||
supported_languages=en,zh_cn,zh_tw,es
|
||||
|
||||
# Dependency settings
|
||||
paper_version=1.21.4
|
||||
paper_version=1.21.5
|
||||
jetbrains_annotations_version=26.0.2
|
||||
slf4j_version=2.0.17
|
||||
log4j_version=2.24.3
|
||||
@@ -51,7 +51,7 @@ byte_buddy_version=1.17.5
|
||||
ahocorasick_version=0.6.3
|
||||
snake_yaml_version=2.4
|
||||
anti_grief_version=0.13
|
||||
nms_helper_version=0.56.2
|
||||
nms_helper_version=0.58.8
|
||||
# Ignite Dependencies
|
||||
mixinextras_version=0.4.1
|
||||
mixin_version=0.15.2+mixin.0.8.7
|
||||
|
||||
@@ -15,7 +15,7 @@ repositories {
|
||||
dependencies {
|
||||
implementation(project(":shared"))
|
||||
remapper("net.fabricmc:tiny-remapper:${rootProject.properties["tiny_remapper_version"]}:fat")
|
||||
paperweightDevelopmentBundle("io.papermc.paper:dev-bundle:${rootProject.properties["latest_minecraft_version"]}-R0.1-SNAPSHOT")
|
||||
paperweightDevelopmentBundle("io.papermc.paper:dev-bundle:1.21.4-R0.1-SNAPSHOT")
|
||||
compileOnly("space.vectrix.ignite:ignite-api:${rootProject.properties["ignite_version"]}")
|
||||
compileOnly("net.fabricmc:sponge-mixin:${rootProject.properties["mixin_version"]}")
|
||||
compileOnly("io.github.llamalad7:mixinextras-common:${rootProject.properties["mixinextras_version"]}")
|
||||
@@ -44,7 +44,7 @@ artifacts {
|
||||
tasks {
|
||||
shadowJar {
|
||||
archiveClassifier = ""
|
||||
archiveFileName = "${rootProject.name}-ignite-mod-${rootProject.properties["project_version"]}+${rootProject.properties["latest_minecraft_version"]}.jar"
|
||||
archiveFileName = "${rootProject.name}-ignite-mod-${rootProject.properties["project_version"]}+mc1.20.5-1.21.4-mojmap.jar"
|
||||
destinationDirectory.set(file("$rootDir/target"))
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package net.momirealms.craftengine.mod;
|
||||
package net.momirealms.craftengine.mod.block;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
@@ -14,6 +14,7 @@ import net.minecraft.world.level.block.*;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.shapes.CollisionContext;
|
||||
import net.minecraft.world.phys.shapes.VoxelShape;
|
||||
import net.momirealms.craftengine.mod.CraftEnginePlugin;
|
||||
import net.momirealms.craftengine.mod.util.NoteBlockUtils;
|
||||
import net.momirealms.craftengine.shared.ObjectHolder;
|
||||
import net.momirealms.craftengine.shared.block.*;
|
||||
@@ -1,6 +1,5 @@
|
||||
package net.momirealms.craftengine.mod.mixin;
|
||||
package net.momirealms.craftengine.mod.block;
|
||||
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import net.minecraft.commands.arguments.blocks.BlockStateParser;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
@@ -8,54 +7,54 @@ import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.momirealms.craftengine.mod.CraftEngineBlock;
|
||||
import net.momirealms.craftengine.mod.CraftEnginePlugin;
|
||||
import net.momirealms.craftengine.mod.util.NoteBlockUtils;
|
||||
import net.momirealms.craftengine.mod.util.Reflections;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Mixin(value = Blocks.class)
|
||||
public abstract class MixinBlocks {
|
||||
public class CustomBlocks {
|
||||
|
||||
@Inject(method = "<clinit>", at = @At("RETURN"))
|
||||
private static void onBlocksInit(CallbackInfo ci) {
|
||||
public static void register() {
|
||||
CraftEnginePlugin.setVanillaRegistrySize(Block.BLOCK_STATE_REGISTRY.size());
|
||||
ResourceLocation noteBlock = ResourceLocation.fromNamespaceAndPath("minecraft", "note_block");
|
||||
Map<ResourceLocation, Integer> map = loadMappingsAndAdditionalBlocks();
|
||||
for (Map.Entry<ResourceLocation, Integer> entry : map.entrySet()) {
|
||||
ResourceLocation replacedBlockId = entry.getKey();
|
||||
boolean isNoteBlock = replacedBlockId.equals(noteBlock);
|
||||
Block replacedBlock = BuiltInRegistries.BLOCK.getValue(replacedBlockId);
|
||||
for (int i = 0; i < entry.getValue(); i++) {
|
||||
ResourceLocation location = ResourceLocation.fromNamespaceAndPath("craftengine", replacedBlockId.getPath() + "_" + i);
|
||||
ResourceKey<Block> resourceKey = ResourceKey.create(Registries.BLOCK, location);
|
||||
BlockBehaviour.Properties properties = BlockBehaviour.Properties.of()
|
||||
.setId(resourceKey);
|
||||
if (!replacedBlock.hasCollision) {
|
||||
properties.noCollission();
|
||||
try {
|
||||
Block replacedBlock = (Block) Reflections.method$DefaultedRegistry$get.invoke(BuiltInRegistries.BLOCK, replacedBlockId);
|
||||
for (int i = 0; i < entry.getValue(); i++) {
|
||||
ResourceLocation location = ResourceLocation.fromNamespaceAndPath("craftengine", replacedBlockId.getPath() + "_" + i);
|
||||
ResourceKey<Block> resourceKey = ResourceKey.create(Registries.BLOCK, location);
|
||||
BlockBehaviour.Properties properties = BlockBehaviour.Properties.of();
|
||||
if (Reflections.field$BlockBehaviour$Properties$id != null) {
|
||||
Reflections.field$BlockBehaviour$Properties$id.set(properties, resourceKey);
|
||||
}
|
||||
if (!replacedBlock.hasCollision) {
|
||||
properties.noCollission();
|
||||
}
|
||||
CraftEngineBlock block = new CraftEngineBlock(properties);
|
||||
if (isNoteBlock) {
|
||||
block.setNoteBlock(true);
|
||||
NoteBlockUtils.CLIENT_SIDE_NOTE_BLOCKS.add(block.defaultBlockState());
|
||||
}
|
||||
Registry.register(BuiltInRegistries.BLOCK, location, block);
|
||||
Block.BLOCK_STATE_REGISTRY.add(block.defaultBlockState());
|
||||
}
|
||||
CraftEngineBlock block = new CraftEngineBlock(properties);
|
||||
if (isNoteBlock) {
|
||||
block.setNoteBlock(true);
|
||||
NoteBlockUtils.CLIENT_SIDE_NOTE_BLOCKS.add(block.defaultBlockState());
|
||||
}
|
||||
Registry.register(BuiltInRegistries.BLOCK, location, block);
|
||||
Block.BLOCK_STATE_REGISTRY.add(block.defaultBlockState());
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
NoteBlockUtils.CLIENT_SIDE_NOTE_BLOCKS.addAll(Blocks.NOTE_BLOCK.getStateDefinition().getPossibleStates());
|
||||
NoteBlockUtils.CLIENT_SIDE_NOTE_BLOCKS.addAll(net.minecraft.world.level.block.Blocks.NOTE_BLOCK.getStateDefinition().getPossibleStates());
|
||||
if (!map.isEmpty()) {
|
||||
CraftEnginePlugin.setIsSuccessfullyRegistered(true);
|
||||
}
|
||||
@@ -121,10 +120,14 @@ public abstract class MixinBlocks {
|
||||
|
||||
private static BlockState createBlockData(String blockState) {
|
||||
try {
|
||||
StringReader reader = new StringReader(blockState);
|
||||
BlockStateParser.BlockResult arg = BlockStateParser.parseForBlock(BuiltInRegistries.BLOCK, reader, false);
|
||||
return arg.blockState();
|
||||
Object holderLookUp = BuiltInRegistries.BLOCK;
|
||||
if (Reflections.method$Registry$asLookup != null) {
|
||||
holderLookUp = Reflections.method$Registry$asLookup.invoke(holderLookUp);
|
||||
}
|
||||
BlockStateParser.BlockResult result = (BlockStateParser.BlockResult) Reflections.method$BlockStateParser$parseForBlock.invoke(null, holderLookUp, blockState, false);
|
||||
return result.blockState();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package net.momirealms.craftengine.mod;
|
||||
package net.momirealms.craftengine.mod.block;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.BlockGetter;
|
||||
@@ -0,0 +1,41 @@
|
||||
package net.momirealms.craftengine.mod.item;
|
||||
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class CustomStreamCodec implements StreamCodec<RegistryFriendlyByteBuf, ItemStack> {
|
||||
public static Function<ItemStack, ItemStack> clientBoundDataProcessor;
|
||||
public static Function<ItemStack, ItemStack> serverBoundDataProcessor;
|
||||
|
||||
private final StreamCodec<RegistryFriendlyByteBuf, ItemStack> original;
|
||||
|
||||
public CustomStreamCodec(StreamCodec<RegistryFriendlyByteBuf, ItemStack> original) {
|
||||
this.original = Objects.requireNonNull(original);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack decode(@NotNull RegistryFriendlyByteBuf buffer) {
|
||||
ItemStack itemStack = this.original.decode(buffer);
|
||||
if (!itemStack.isEmpty()) {
|
||||
if (serverBoundDataProcessor != null) {
|
||||
itemStack = serverBoundDataProcessor.apply(itemStack);
|
||||
}
|
||||
}
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(@NotNull RegistryFriendlyByteBuf buffer, @NotNull ItemStack value) {
|
||||
if (!value.isEmpty()) {
|
||||
if (clientBoundDataProcessor != null) {
|
||||
value = clientBoundDataProcessor.apply(value);
|
||||
}
|
||||
}
|
||||
this.original.encode(buffer, value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package net.momirealms.craftengine.mod.mixin;
|
||||
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.momirealms.craftengine.mod.block.CustomBlocks;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(value = Blocks.class)
|
||||
public abstract class BlocksMixin {
|
||||
|
||||
@Inject(method = "<clinit>", at = @At("RETURN"))
|
||||
private static void onBlocksInit(CallbackInfo ci) {
|
||||
CustomBlocks.register();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package net.momirealms.craftengine.mod.mixin;
|
||||
|
||||
import net.minecraft.core.NonNullList;
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.codec.ByteBufCodecs;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.momirealms.craftengine.mod.item.CustomStreamCodec;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Mutable;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mixin(ItemStack.class)
|
||||
public abstract class ItemStackMixin {
|
||||
@Shadow(remap = false)
|
||||
@Mutable
|
||||
public static StreamCodec<RegistryFriendlyByteBuf, ItemStack> OPTIONAL_STREAM_CODEC;
|
||||
@Shadow(remap = false)
|
||||
@Mutable
|
||||
public static StreamCodec<RegistryFriendlyByteBuf, List<ItemStack>> OPTIONAL_LIST_STREAM_CODEC;
|
||||
|
||||
private static StreamCodec<RegistryFriendlyByteBuf, ItemStack> ORIGINAL_OPTIONAL_STREAM_CODEC;
|
||||
|
||||
@Inject(
|
||||
method = "<clinit>",
|
||||
at = @At(
|
||||
value = "FIELD",
|
||||
target = "Lnet/minecraft/world/item/ItemStack;OPTIONAL_STREAM_CODEC:Lnet/minecraft/network/codec/StreamCodec;",
|
||||
opcode = Opcodes.PUTSTATIC,
|
||||
shift = At.Shift.AFTER
|
||||
)
|
||||
)
|
||||
private static void captureOriginalAfterAssignment(CallbackInfo ci) {
|
||||
ORIGINAL_OPTIONAL_STREAM_CODEC = OPTIONAL_STREAM_CODEC;
|
||||
}
|
||||
|
||||
@Inject(method = "<clinit>", at = @At("RETURN"))
|
||||
private static void replaceStreamCodec(CallbackInfo ci) {
|
||||
OPTIONAL_STREAM_CODEC = new CustomStreamCodec(ORIGINAL_OPTIONAL_STREAM_CODEC);
|
||||
OPTIONAL_LIST_STREAM_CODEC = OPTIONAL_STREAM_CODEC.apply(ByteBufCodecs.collection(NonNullList::createWithCapacity));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package net.momirealms.craftengine.mod.util;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Objects;
|
||||
|
||||
public final class BukkitReflectionUtils {
|
||||
private static final String PREFIX_MC = "net.minecraft.";
|
||||
private static final String PREFIX_CRAFTBUKKIT = "org.bukkit.craftbukkit";
|
||||
private static final String CRAFT_SERVER = "CraftServer";
|
||||
private static final String CB_PKG_VERSION;
|
||||
public static final int MAJOR_REVISION;
|
||||
|
||||
private BukkitReflectionUtils() {}
|
||||
|
||||
static {
|
||||
final Class<?> serverClass;
|
||||
if (Bukkit.getServer() == null) {
|
||||
// Paper plugin Bootstrapper 1.20.6+
|
||||
serverClass = Objects.requireNonNull(ReflectionUtils.getClazz("org.bukkit.craftbukkit.CraftServer"));
|
||||
} else {
|
||||
serverClass = Bukkit.getServer().getClass();
|
||||
}
|
||||
final String pkg = serverClass.getPackage().getName();
|
||||
final String nmsVersion = pkg.substring(pkg.lastIndexOf(".") + 1);
|
||||
if (!nmsVersion.contains("_")) {
|
||||
int fallbackVersion = -1;
|
||||
if (Bukkit.getServer() != null) {
|
||||
try {
|
||||
final Method getMinecraftVersion = serverClass.getDeclaredMethod("getMinecraftVersion");
|
||||
fallbackVersion = Integer.parseInt(getMinecraftVersion.invoke(Bukkit.getServer()).toString().split("\\.")[1]);
|
||||
} catch (final Exception ignored) {
|
||||
}
|
||||
} else {
|
||||
// Paper plugin bootstrapper 1.20.6+
|
||||
try {
|
||||
final Class<?> sharedConstants = Objects.requireNonNull(ReflectionUtils.getClazz("net.minecraft.SharedConstants"));
|
||||
final Method getCurrentVersion = sharedConstants.getDeclaredMethod("getCurrentVersion");
|
||||
final Object currentVersion = getCurrentVersion.invoke(null);
|
||||
final Method getName = currentVersion.getClass().getDeclaredMethod("getName");
|
||||
final String versionName = (String) getName.invoke(currentVersion);
|
||||
try {
|
||||
fallbackVersion = Integer.parseInt(versionName.split("\\.")[1]);
|
||||
} catch (final Exception ignored) {
|
||||
}
|
||||
} catch (final ReflectiveOperationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
MAJOR_REVISION = fallbackVersion;
|
||||
} else {
|
||||
MAJOR_REVISION = Integer.parseInt(nmsVersion.split("_")[1]);
|
||||
}
|
||||
String name = serverClass.getName();
|
||||
name = name.substring(PREFIX_CRAFTBUKKIT.length());
|
||||
name = name.substring(0, name.length() - CRAFT_SERVER.length());
|
||||
CB_PKG_VERSION = name;
|
||||
}
|
||||
|
||||
public static String assembleCBClass(String className) {
|
||||
return PREFIX_CRAFTBUKKIT + CB_PKG_VERSION + className;
|
||||
}
|
||||
|
||||
public static String assembleMCClass(String className) {
|
||||
return PREFIX_MC + className;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,489 @@
|
||||
package net.momirealms.craftengine.mod.util;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.lang.reflect.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class ReflectionUtils {
|
||||
private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
|
||||
|
||||
private ReflectionUtils() {}
|
||||
|
||||
public static Class<?> getClazz(String... classes) {
|
||||
for (String className : classes) {
|
||||
Class<?> clazz = getClazz(className);
|
||||
if (clazz != null) {
|
||||
return clazz;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Class<?> getClazz(String clazz) {
|
||||
try {
|
||||
return Class.forName(clazz);
|
||||
} catch (Throwable e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean classExists(@NotNull final String clazz) {
|
||||
try {
|
||||
Class.forName(clazz);
|
||||
return true;
|
||||
} catch (Throwable e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean methodExists(@NotNull final Class<?> clazz, @NotNull final String method, @NotNull final Class<?>... parameterTypes) {
|
||||
try {
|
||||
clazz.getMethod(method, parameterTypes);
|
||||
return true;
|
||||
} catch (NoSuchMethodException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Field getDeclaredField(final Class<?> clazz, final String field) {
|
||||
try {
|
||||
return setAccessible(clazz.getDeclaredField(field));
|
||||
} catch (NoSuchFieldException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Field getDeclaredField(@NotNull Class<?> clazz, @NotNull String... possibleNames) {
|
||||
List<String> possibleNameList = Arrays.asList(possibleNames);
|
||||
for (Field field : clazz.getDeclaredFields()) {
|
||||
if (possibleNameList.contains(field.getName())) {
|
||||
return field;
|
||||
}
|
||||
}
|
||||
throw new RuntimeException("Class " + clazz.getName() + " does not contain a field with possible names " + Arrays.toString(possibleNames));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Field getDeclaredField(final Class<?> clazz, final int index) {
|
||||
int i = 0;
|
||||
for (final Field field : clazz.getDeclaredFields()) {
|
||||
if (index == i) {
|
||||
return setAccessible(field);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Field getInstanceDeclaredField(final Class<?> clazz, final int index) {
|
||||
int i = 0;
|
||||
for (final Field field : clazz.getDeclaredFields()) {
|
||||
if (!Modifier.isStatic(field.getModifiers())) {
|
||||
if (index == i) {
|
||||
return setAccessible(field);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Field getDeclaredField(final Class<?> clazz, final Class<?> type, int index) {
|
||||
int i = 0;
|
||||
for (final Field field : clazz.getDeclaredFields()) {
|
||||
if (field.getType() == type) {
|
||||
if (index == i) {
|
||||
return setAccessible(field);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Field getDeclaredFieldBackwards(final Class<?> clazz, final Class<?> type, int index) {
|
||||
int i = 0;
|
||||
Field[] fields = clazz.getDeclaredFields();
|
||||
for (int j = fields.length - 1; j >= 0; j--) {
|
||||
Field field = fields[j];
|
||||
if (field.getType() == type) {
|
||||
if (index == i) {
|
||||
return setAccessible(field);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Field getInstanceDeclaredField(@NotNull Class<?> clazz, final Class<?> type, int index) {
|
||||
int i = 0;
|
||||
for (final Field field : clazz.getDeclaredFields()) {
|
||||
if (field.getType() == type && !Modifier.isStatic(field.getModifiers())) {
|
||||
if (index == i) {
|
||||
return setAccessible(field);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static List<Field> getDeclaredFields(final Class<?> clazz) {
|
||||
List<Field> fields = new ArrayList<>();
|
||||
for (Field field : clazz.getDeclaredFields()) {
|
||||
fields.add(setAccessible(field));
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static List<Field> getInstanceDeclaredFields(@NotNull Class<?> clazz) {
|
||||
List<Field> list = new ArrayList<>();
|
||||
for (Field field : clazz.getDeclaredFields()) {
|
||||
if (!Modifier.isStatic(field.getModifiers())) {
|
||||
list.add(setAccessible(field));
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static List<Field> getDeclaredFields(@NotNull final Class<?> clazz, @NotNull final Class<?> type) {
|
||||
List<Field> fields = new ArrayList<>();
|
||||
for (Field field : clazz.getDeclaredFields()) {
|
||||
if (field.getType() == type) {
|
||||
fields.add(setAccessible(field));
|
||||
}
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static List<Field> getInstanceDeclaredFields(@NotNull Class<?> clazz, @NotNull Class<?> type) {
|
||||
List<Field> list = new ArrayList<>();
|
||||
for (Field field : clazz.getDeclaredFields()) {
|
||||
if (field.getType() == type && !Modifier.isStatic(field.getModifiers())) {
|
||||
list.add(setAccessible(field));
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Method getMethod(final Class<?> clazz, Class<?> returnType, final String[] possibleMethodNames, final Class<?>... parameterTypes) {
|
||||
outer:
|
||||
for (Method method : clazz.getMethods()) {
|
||||
if (method.getParameterCount() != parameterTypes.length) {
|
||||
continue;
|
||||
}
|
||||
Class<?>[] types = method.getParameterTypes();
|
||||
for (int i = 0; i < types.length; i++) {
|
||||
if (types[i] != parameterTypes[i]) {
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
for (String name : possibleMethodNames) {
|
||||
if (name.equals(method.getName())) {
|
||||
if (returnType.isAssignableFrom(method.getReturnType())) {
|
||||
return method;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Method getMethod(final Class<?> clazz, final String[] possibleMethodNames, final Class<?>... parameterTypes) {
|
||||
outer:
|
||||
for (Method method : clazz.getMethods()) {
|
||||
if (method.getParameterCount() != parameterTypes.length) {
|
||||
continue;
|
||||
}
|
||||
Class<?>[] types = method.getParameterTypes();
|
||||
for (int i = 0; i < types.length; i++) {
|
||||
if (types[i] != parameterTypes[i]) {
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
for (String name : possibleMethodNames) {
|
||||
if (name.equals(method.getName())) return method;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Method getMethod(final Class<?> clazz, Class<?> returnType, final Class<?>... parameterTypes) {
|
||||
outer:
|
||||
for (Method method : clazz.getMethods()) {
|
||||
if (method.getParameterCount() != parameterTypes.length) {
|
||||
continue;
|
||||
}
|
||||
Class<?>[] types = method.getParameterTypes();
|
||||
for (int i = 0; i < types.length; i++) {
|
||||
if (types[i] != parameterTypes[i]) {
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
if (returnType.isAssignableFrom(method.getReturnType())) return method;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Method getDeclaredMethod(final Class<?> clazz, Class<?> returnType, final String[] possibleMethodNames, final Class<?>... parameterTypes) {
|
||||
outer:
|
||||
for (Method method : clazz.getDeclaredMethods()) {
|
||||
if (method.getParameterCount() != parameterTypes.length) {
|
||||
continue;
|
||||
}
|
||||
Class<?>[] types = method.getParameterTypes();
|
||||
for (int i = 0; i < types.length; i++) {
|
||||
if (types[i] != parameterTypes[i]) {
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
for (String name : possibleMethodNames) {
|
||||
if (name.equals(method.getName())) {
|
||||
if (returnType.isAssignableFrom(method.getReturnType())) {
|
||||
return setAccessible(method);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Method getDeclaredMethod(final Class<?> clazz, Class<?> returnType, final Class<?>... parameterTypes) {
|
||||
outer:
|
||||
for (Method method : clazz.getDeclaredMethods()) {
|
||||
if (method.getParameterCount() != parameterTypes.length) {
|
||||
continue;
|
||||
}
|
||||
Class<?>[] types = method.getParameterTypes();
|
||||
for (int i = 0; i < types.length; i++) {
|
||||
if (types[i] != parameterTypes[i]) {
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
if (returnType.isAssignableFrom(method.getReturnType())) return setAccessible(method);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Method getMethod(final Class<?> clazz, Class<?> returnType, int index) {
|
||||
int i = 0;
|
||||
for (Method method : clazz.getMethods()) {
|
||||
if (returnType.isAssignableFrom(method.getReturnType())) {
|
||||
if (i == index) {
|
||||
return setAccessible(method);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Method getStaticMethod(final Class<?> clazz, Class<?> returnType, final Class<?>... parameterTypes) {
|
||||
outer:
|
||||
for (Method method : clazz.getMethods()) {
|
||||
if (method.getParameterCount() != parameterTypes.length) {
|
||||
continue;
|
||||
}
|
||||
if (!Modifier.isStatic(method.getModifiers())) {
|
||||
continue;
|
||||
}
|
||||
Class<?>[] types = method.getParameterTypes();
|
||||
for (int i = 0; i < types.length; i++) {
|
||||
if (types[i] != parameterTypes[i]) {
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
if (returnType.isAssignableFrom(method.getReturnType()))
|
||||
return setAccessible(method);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Method getStaticMethod(final Class<?> clazz, Class<?> returnType, String[] possibleNames, final Class<?>... parameterTypes) {
|
||||
outer:
|
||||
for (Method method : clazz.getMethods()) {
|
||||
if (method.getParameterCount() != parameterTypes.length) {
|
||||
continue;
|
||||
}
|
||||
if (!Modifier.isStatic(method.getModifiers())) {
|
||||
continue;
|
||||
}
|
||||
Class<?>[] types = method.getParameterTypes();
|
||||
for (int i = 0; i < types.length; i++) {
|
||||
if (types[i] != parameterTypes[i]) {
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
if (returnType.isAssignableFrom(method.getReturnType())) {
|
||||
for (String name : possibleNames) {
|
||||
if (name.equals(method.getName())) {
|
||||
return setAccessible(method);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Method getStaticMethod(final Class<?> clazz, int index) {
|
||||
int i = 0;
|
||||
for (Method method : clazz.getMethods()) {
|
||||
if (Modifier.isStatic(method.getModifiers())) {
|
||||
if (i == index) {
|
||||
return setAccessible(method);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Method getMethod(final Class<?> clazz, int index) {
|
||||
int i = 0;
|
||||
for (Method method : clazz.getMethods()) {
|
||||
if (i == index) {
|
||||
return setAccessible(method);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Method getMethodOrElseThrow(final Class<?> clazz, final String[] possibleMethodNames, final Class<?>[] parameterTypes) throws NoSuchMethodException {
|
||||
Method method = getMethod(clazz, possibleMethodNames, parameterTypes);
|
||||
if (method == null) {
|
||||
throw new NoSuchMethodException("No method found with possible names " + Arrays.toString(possibleMethodNames) + " with parameters " +
|
||||
Arrays.toString(parameterTypes) + " in class " + clazz.getName());
|
||||
}
|
||||
return method;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static List<Method> getMethods(@NotNull Class<?> clazz, @NotNull Class<?> returnType, @NotNull Class<?>... parameterTypes) {
|
||||
List<Method> list = new ArrayList<>();
|
||||
for (Method method : clazz.getMethods()) {
|
||||
if (!returnType.isAssignableFrom(method.getReturnType()) // check type
|
||||
|| method.getParameterCount() != parameterTypes.length // check length
|
||||
) continue;
|
||||
Class<?>[] types = method.getParameterTypes();
|
||||
outer: {
|
||||
for (int i = 0; i < types.length; i++) {
|
||||
if (types[i] != parameterTypes[i]) {
|
||||
break outer;
|
||||
}
|
||||
}
|
||||
list.add(method);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static <T extends AccessibleObject> T setAccessible(@NotNull final T o) {
|
||||
o.setAccessible(true);
|
||||
return o;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Constructor<?> getConstructor(Class<?> clazz, Class<?>... parameterTypes) {
|
||||
try {
|
||||
return clazz.getConstructor(parameterTypes);
|
||||
} catch (NoSuchMethodException | SecurityException ignore) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Constructor<?> getDeclaredConstructor(Class<?> clazz, Class<?>... parameterTypes) {
|
||||
try {
|
||||
return setAccessible(clazz.getDeclaredConstructor(parameterTypes));
|
||||
} catch (NoSuchMethodException | SecurityException ignore) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Constructor<?> getConstructor(Class<?> clazz, int index) {
|
||||
try {
|
||||
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
|
||||
if (index < 0 || index >= constructors.length) {
|
||||
throw new IndexOutOfBoundsException("Invalid constructor index: " + index);
|
||||
}
|
||||
return setAccessible(constructors[index]);
|
||||
} catch (SecurityException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Constructor<?> getTheOnlyConstructor(Class<?> clazz) {
|
||||
Constructor<?>[] constructors = clazz.getConstructors();
|
||||
if (constructors.length != 1) {
|
||||
throw new RuntimeException("This class is expected to have only one constructor but it has " + constructors.length);
|
||||
}
|
||||
return constructors[0];
|
||||
}
|
||||
|
||||
public static MethodHandle unreflectGetter(Field field) throws IllegalAccessException {
|
||||
try {
|
||||
return LOOKUP.unreflectGetter(field);
|
||||
} catch (IllegalAccessException e) {
|
||||
field.setAccessible(true);
|
||||
return LOOKUP.unreflectGetter(field);
|
||||
}
|
||||
}
|
||||
|
||||
public static MethodHandle unreflectMethod(Method method) throws IllegalAccessException {
|
||||
try {
|
||||
return LOOKUP.unreflect(method);
|
||||
} catch (IllegalAccessException e) {
|
||||
method.setAccessible(true);
|
||||
return LOOKUP.unreflect(method);
|
||||
}
|
||||
}
|
||||
|
||||
public static VarHandle findVarHandle(Class<?> clazz, String name, Class<?> type) {
|
||||
try {
|
||||
return MethodHandles.privateLookupIn(clazz, LOOKUP)
|
||||
.findVarHandle(clazz, name, type);
|
||||
} catch (NoSuchFieldException | SecurityException | IllegalAccessException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static VarHandle findVarHandle(Field field) {
|
||||
try {
|
||||
return MethodHandles.privateLookupIn(field.getDeclaringClass(), LOOKUP)
|
||||
.findVarHandle(field.getDeclaringClass(), field.getName(), field.getType());
|
||||
} catch (IllegalAccessException | NoSuchFieldException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package net.momirealms.craftengine.mod.util;
|
||||
|
||||
import net.minecraft.commands.arguments.blocks.BlockStateParser;
|
||||
import net.minecraft.core.DefaultedRegistry;
|
||||
import net.minecraft.core.HolderLookup;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
public class Reflections {
|
||||
|
||||
public static final Method method$DefaultedRegistry$get = requireNonNull(
|
||||
ReflectionUtils.getMethod(
|
||||
DefaultedRegistry.class, Object.class, ResourceLocation.class
|
||||
)
|
||||
);
|
||||
|
||||
public static final Field field$BlockBehaviour$Properties$id = ReflectionUtils.getDeclaredField(
|
||||
BlockBehaviour.Properties.class, ResourceKey.class, 0
|
||||
);
|
||||
|
||||
public static final Method method$BlockStateParser$parseForBlock = requireNonNull(
|
||||
ReflectionUtils.getStaticMethod(
|
||||
BlockStateParser.class, BlockStateParser.BlockResult.class, new String[]{"parseForBlock"}, HolderLookup.class, String.class, boolean.class
|
||||
)
|
||||
);
|
||||
|
||||
public static final Method method$Registry$asLookup = ReflectionUtils.getMethod(
|
||||
Registry.class, new String[]{"asLookup"}
|
||||
);
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
"target": "@env(DEFAULT)",
|
||||
"compatibilityLevel": "JAVA_21",
|
||||
"server": [
|
||||
"MixinBlocks"
|
||||
"BlocksMixin",
|
||||
"ItemStackMixin"
|
||||
]
|
||||
}
|
||||
50
server-mod/v1_21_5/build.gradle.kts
Normal file
50
server-mod/v1_21_5/build.gradle.kts
Normal file
@@ -0,0 +1,50 @@
|
||||
plugins {
|
||||
id("java-library")
|
||||
id("com.gradleup.shadow") version "9.0.0-beta11"
|
||||
id("io.papermc.paperweight.userdev") version "2.0.0-beta.16"
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven("https://maven.fabricmc.net/")
|
||||
maven("https://oss.sonatype.org/content/groups/public/")
|
||||
maven("https://repo.papermc.io/repository/maven-public/")
|
||||
maven("https://repo.spongepowered.org/maven/")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":shared"))
|
||||
remapper("net.fabricmc:tiny-remapper:${rootProject.properties["tiny_remapper_version"]}:fat")
|
||||
paperweightDevelopmentBundle("io.papermc.paper:dev-bundle:1.21.5-R0.1-SNAPSHOT")
|
||||
compileOnly("space.vectrix.ignite:ignite-api:${rootProject.properties["ignite_version"]}")
|
||||
compileOnly("net.fabricmc:sponge-mixin:${rootProject.properties["mixin_version"]}")
|
||||
compileOnly("io.github.llamalad7:mixinextras-common:${rootProject.properties["mixinextras_version"]}")
|
||||
}
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_21
|
||||
targetCompatibility = JavaVersion.VERSION_21
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(21)
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<JavaCompile> {
|
||||
options.encoding = "UTF-8"
|
||||
options.release.set(21)
|
||||
dependsOn(tasks.clean)
|
||||
}
|
||||
|
||||
paperweight.reobfArtifactConfiguration = io.papermc.paperweight.userdev.ReobfArtifactConfiguration.MOJANG_PRODUCTION
|
||||
|
||||
artifacts {
|
||||
archives(tasks.shadowJar)
|
||||
}
|
||||
|
||||
tasks {
|
||||
shadowJar {
|
||||
archiveClassifier = ""
|
||||
archiveFileName = "${rootProject.name}-ignite-mod-${rootProject.properties["project_version"]}+mc1.21.5-mojmap.jar"
|
||||
destinationDirectory.set(file("$rootDir/target"))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package net.momirealms.craftengine.mod;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin;
|
||||
import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
|
||||
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.CodeSource;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public final class CraftEnginePlugin implements IMixinConfigPlugin {
|
||||
public static final Logger LOGGER = Logger.getLogger(CraftEnginePlugin.class.getName());
|
||||
private static int vanillaRegistrySize;
|
||||
private static boolean isSuccessfullyRegistered = false;
|
||||
private static int maxChainUpdate = 32;
|
||||
|
||||
public static void setVanillaRegistrySize(int vanillaRegistrySize) {
|
||||
CraftEnginePlugin.vanillaRegistrySize = vanillaRegistrySize;
|
||||
}
|
||||
|
||||
public static void setIsSuccessfullyRegistered(boolean isSuccessfullyRegistered) {
|
||||
CraftEnginePlugin.isSuccessfullyRegistered = isSuccessfullyRegistered;
|
||||
}
|
||||
|
||||
public static int maxChainUpdate() {
|
||||
return maxChainUpdate;
|
||||
}
|
||||
|
||||
public static void setMaxChainUpdate(int maxChainUpdate) {
|
||||
CraftEnginePlugin.maxChainUpdate = maxChainUpdate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad(final @NotNull String mixinPackage) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String getRefMapperConfig() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldApplyMixin(@NotNull String targetClassName,
|
||||
@NotNull String mixinClassName) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void acceptTargets(@NotNull Set<String> myTargets,
|
||||
@NotNull Set<String> otherTargets) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable List<String> getMixins() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preApply(@NotNull String targetClassName,
|
||||
@NotNull ClassNode targetClass,
|
||||
@NotNull String mixinClassName,
|
||||
@NotNull IMixinInfo mixinInfo) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postApply(@NotNull String targetClassName,
|
||||
@NotNull ClassNode targetClass,
|
||||
@NotNull String mixinClassName,
|
||||
@NotNull IMixinInfo mixinInfo) {
|
||||
}
|
||||
|
||||
public static Path getPluginFolderPath() {
|
||||
ProtectionDomain protectionDomain = CraftEnginePlugin.class.getProtectionDomain();
|
||||
CodeSource codeSource = protectionDomain.getCodeSource();
|
||||
URL jarUrl = codeSource.getLocation();
|
||||
try {
|
||||
return Paths.get(jarUrl.toURI()).getParent().getParent().resolve("plugins");
|
||||
} catch (URISyntaxException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Path getCraftEngineMappingsPath() {
|
||||
return getPluginFolderPath()
|
||||
.resolve("CraftEngine")
|
||||
.resolve("mappings.yml");
|
||||
}
|
||||
|
||||
public static Path getCraftEngineAdditionalBlocksPath() {
|
||||
return getPluginFolderPath()
|
||||
.resolve("CraftEngine")
|
||||
.resolve("additional-real-blocks.yml");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,270 @@
|
||||
package net.momirealms.craftengine.mod.block;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.entity.item.FallingBlockEntity;
|
||||
import net.minecraft.world.level.BlockGetter;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.LevelReader;
|
||||
import net.minecraft.world.level.ScheduledTickAccess;
|
||||
import net.minecraft.world.level.block.*;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.shapes.CollisionContext;
|
||||
import net.minecraft.world.phys.shapes.VoxelShape;
|
||||
import net.momirealms.craftengine.mod.CraftEnginePlugin;
|
||||
import net.momirealms.craftengine.mod.util.NoteBlockUtils;
|
||||
import net.momirealms.craftengine.shared.ObjectHolder;
|
||||
import net.momirealms.craftengine.shared.block.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class CraftEngineBlock
|
||||
extends Block
|
||||
implements BehaviorHolder, ShapeHolder, NoteBlockIndicator, Fallable, BonemealableBlock
|
||||
//TODO , SimpleWaterloggedBlock
|
||||
{
|
||||
private static final StoneBlockShape STONE = new StoneBlockShape(Blocks.STONE.defaultBlockState());
|
||||
private boolean isNoteBlock;
|
||||
public ObjectHolder<BlockBehavior> behaviorHolder;
|
||||
public ObjectHolder<BlockShape> shapeHolder;
|
||||
public boolean isClientSideNoteBlock;
|
||||
|
||||
public CraftEngineBlock(Properties properties) {
|
||||
super(properties);
|
||||
this.behaviorHolder = new ObjectHolder<>(EmptyBlockBehavior.INSTANCE);
|
||||
this.shapeHolder = new ObjectHolder<>(STONE);
|
||||
}
|
||||
|
||||
public void setNoteBlock(boolean noteBlock) {
|
||||
isNoteBlock = noteBlock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectHolder<BlockBehavior> getBehaviorHolder() {
|
||||
return behaviorHolder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectHolder<BlockShape> getShapeHolder() {
|
||||
return shapeHolder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNoteBlock() {
|
||||
return isClientSideNoteBlock;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull VoxelShape getShape(@NotNull BlockState state, @NotNull BlockGetter level, @NotNull BlockPos pos, @NotNull CollisionContext context) {
|
||||
try {
|
||||
return (VoxelShape) shapeHolder.value().getShape(this, new Object[]{state, level, pos, context});
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return super.getShape(state, level, pos, context);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull BlockState rotate(@NotNull BlockState state, @NotNull Rotation rotation) {
|
||||
try {
|
||||
return (BlockState) this.behaviorHolder.value().rotate(this, new Object[]{state, rotation}, () -> super.rotate(state, rotation));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return super.rotate(state, rotation);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull BlockState mirror(@NotNull BlockState state, @NotNull Mirror mirror) {
|
||||
try {
|
||||
return (BlockState) this.behaviorHolder.value().mirror(this, new Object[]{state, mirror}, () -> super.mirror(state, mirror));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return super.mirror(state, mirror);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tick(@NotNull BlockState state, @NotNull ServerLevel level, @NotNull BlockPos pos, @NotNull RandomSource random) {
|
||||
try {
|
||||
this.behaviorHolder.value().tick(this, new Object[]{state, level, pos, random}, () -> {
|
||||
super.tick(state, level, pos, random);
|
||||
return null;
|
||||
});
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
super.tick(state, level, pos, random);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void randomTick(@NotNull BlockState state, @NotNull ServerLevel level, @NotNull BlockPos pos, @NotNull RandomSource random) {
|
||||
try {
|
||||
behaviorHolder.value().randomTick(this, new Object[]{state, level, pos, random}, () -> {
|
||||
super.randomTick(state, level, pos, random);
|
||||
return null;
|
||||
});
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
super.randomTick(state, level, pos, random);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPlace(@NotNull BlockState state, @NotNull Level level, @NotNull BlockPos pos, @NotNull BlockState oldState, boolean movedByPiston) {
|
||||
try {
|
||||
behaviorHolder.value().onPlace(this, new Object[]{state, level, pos, oldState, movedByPiston}, () -> {
|
||||
super.onPlace(state, level, pos, oldState, movedByPiston);
|
||||
return null;
|
||||
});
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
super.onPlace(state, level, pos, oldState, movedByPiston);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBrokenAfterFall(@NotNull Level level, @NotNull BlockPos pos, @NotNull FallingBlockEntity fallingBlock) {
|
||||
try {
|
||||
behaviorHolder.value().onBrokenAfterFall(this, new Object[]{level, pos, fallingBlock});
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Fallable.super.onBrokenAfterFall(level, pos, fallingBlock);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canSurvive(@NotNull BlockState state, @NotNull LevelReader level, @NotNull BlockPos pos) {
|
||||
try {
|
||||
return behaviorHolder.value().canSurvive(this, new Object[]{state, level, pos}, () -> super.canSurvive(state, level, pos));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return super.canSurvive(state, level, pos);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BlockState updateShape(@NotNull BlockState state,
|
||||
@NotNull LevelReader level,
|
||||
@NotNull ScheduledTickAccess scheduledTickAccess,
|
||||
@NotNull BlockPos pos,
|
||||
@NotNull Direction direction,
|
||||
@NotNull BlockPos neighborPos,
|
||||
@NotNull BlockState neighborState,
|
||||
@NotNull RandomSource random) {
|
||||
try {
|
||||
if (isNoteBlock && level instanceof ServerLevel serverLevel) {
|
||||
startNoteBlockChain(direction, serverLevel, pos);
|
||||
}
|
||||
return (BlockState) behaviorHolder.value().updateShape(this, new Object[]{state, level, scheduledTickAccess, pos, direction, neighborPos, neighborState, random}, () -> super.updateShape(state, level, scheduledTickAccess, pos, direction, neighborPos, neighborState, random));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return super.updateShape(state, level, scheduledTickAccess, pos, direction, neighborPos, neighborState, random);
|
||||
}
|
||||
}
|
||||
|
||||
private static void startNoteBlockChain(Direction direction, ServerLevel serverLevel, BlockPos blockPos) {
|
||||
int id = direction.get3DDataValue();
|
||||
// Y axis
|
||||
if (id == 0 || id == 1) {
|
||||
ServerChunkCache chunkSource = serverLevel.chunkSource;
|
||||
chunkSource.blockChanged(blockPos);
|
||||
if (id == 1) {
|
||||
noteBlockChainUpdate(serverLevel, chunkSource, Direction.DOWN, blockPos, 0);
|
||||
} else {
|
||||
noteBlockChainUpdate(serverLevel, chunkSource, Direction.UP, blockPos, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void noteBlockChainUpdate(ServerLevel level, ServerChunkCache chunkSource, Direction direction, BlockPos blockPos, int times) {
|
||||
if (times >= CraftEnginePlugin.maxChainUpdate()) return;
|
||||
BlockPos relativePos = blockPos.relative(direction);
|
||||
BlockState state = level.getBlockState(relativePos);
|
||||
if (NoteBlockUtils.CLIENT_SIDE_NOTE_BLOCKS.contains(state)) {
|
||||
chunkSource.blockChanged(relativePos);
|
||||
noteBlockChainUpdate(level, chunkSource, direction, relativePos, times+1);
|
||||
}
|
||||
}
|
||||
|
||||
// @Override
|
||||
// protected @NotNull FluidState getFluidState(@NotNull BlockState state) {
|
||||
// try {
|
||||
// return (FluidState) behaviorHolder.value().getFluidState(this, new Object[]{state}, () -> super.getFluidState(state));
|
||||
// } catch (Exception e) {
|
||||
// e.printStackTrace();
|
||||
// return super.getFluidState(state);
|
||||
// }
|
||||
// }
|
||||
|
||||
@Override
|
||||
public boolean isValidBonemealTarget(@NotNull LevelReader levelReader, @NotNull BlockPos blockPos, @NotNull BlockState blockState) {
|
||||
try {
|
||||
return behaviorHolder.value().isValidBoneMealTarget(this, new Object[]{levelReader, blockPos, blockState});
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBonemealSuccess(@NotNull Level level, @NotNull RandomSource randomSource, @NotNull BlockPos blockPos, @NotNull BlockState blockState) {
|
||||
try {
|
||||
return behaviorHolder.value().isBoneMealSuccess(this, new Object[]{level, randomSource, blockPos, blockState});
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performBonemeal(@NotNull ServerLevel serverLevel, @NotNull RandomSource randomSource, @NotNull BlockPos blockPos, @NotNull BlockState blockState) {
|
||||
try {
|
||||
behaviorHolder.value().performBoneMeal(this, new Object[]{serverLevel, randomSource, blockPos, blockState});
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLand(@NotNull Level level, @NotNull BlockPos pos, @NotNull BlockState state, @NotNull BlockState replaceableState, @NotNull FallingBlockEntity fallingBlock) {
|
||||
try {
|
||||
behaviorHolder.value().onLand(this, new Object[]{level, pos, state, replaceableState, fallingBlock});
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public boolean canPlaceLiquid(@Nullable Player player, @NotNull BlockGetter level, @NotNull BlockPos pos, @NotNull BlockState state, @NotNull Fluid fluid) {
|
||||
// try {
|
||||
// return behaviorHolder.value().canPlaceLiquid(this, new Object[]{player, level, pos, state, fluid}, () -> SimpleWaterloggedBlock.super.canPlaceLiquid(player, level, pos, state, fluid));
|
||||
// } catch (Exception e) {
|
||||
// e.printStackTrace();
|
||||
// return SimpleWaterloggedBlock.super.canPlaceLiquid(player, level, pos, state, fluid);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean placeLiquid(@NotNull LevelAccessor level, @NotNull BlockPos pos, @NotNull BlockState state, @NotNull FluidState fluidState) {
|
||||
// try {
|
||||
// return behaviorHolder.value().placeLiquid(this, new Object[]{level, pos, state, fluidState}, () -> SimpleWaterloggedBlock.super.placeLiquid(level, pos, state, fluidState));
|
||||
// } catch (Exception e) {
|
||||
// e.printStackTrace();
|
||||
// return SimpleWaterloggedBlock.super.placeLiquid(level, pos, state, fluidState);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @NotNull
|
||||
// @Override
|
||||
// public ItemStack pickupBlock(@Nullable Player player, @NotNull LevelAccessor level, @NotNull BlockPos pos, @NotNull BlockState state) {
|
||||
// try {
|
||||
// return (ItemStack) behaviorHolder.value().pickupBlock(this, new Object[]{player, level, pos, state}, () -> SimpleWaterloggedBlock.super.pickupBlock(player, level, pos, state));
|
||||
// } catch (Exception e) {
|
||||
// e.printStackTrace();
|
||||
// return SimpleWaterloggedBlock.super.pickupBlock(player, level, pos, state);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user