9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-26 10:29:20 +00:00

Merge pull request #178 from Xiao-MoMi/dev

0.0.54
This commit is contained in:
XiaoMoMi
2025-05-17 02:27:47 +08:00
committed by GitHub
297 changed files with 6765 additions and 2341 deletions

View File

@@ -6,11 +6,14 @@
</h1>
<p align="center">
<a href="https://mo-mi.gitbook.io/xiaomomi-plugins/plugin-wiki/craftengine" alt="GitBook">
<img src="https://img.shields.io/badge/Docs-User Manual-D2691E" alt="Gitbook"/>
</a>
<a href="https://github.com/Xiao-MoMi/craft-engine/">
<img src="https://sloc.xyz/github/Xiao-MoMi/craft-engine/?category=codes" alt="Scc Count Badge"/>
<img src="https://sloc.xyz/github/Xiao-MoMi/craft-engine/?category=code" alt="Scc Count Badge"/>
</a>
<a href="https://deepwiki.com/Xiao-MoMi/craft-engine">
<img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki">
</a>
<a href="https://mo-mi.gitbook.io/xiaomomi-plugins/craftengine" alt="GitBook">
<img src="https://img.shields.io/badge/Docs-User Manual-D2691E" alt="Gitbook"/>
</a>
</p>

View File

@@ -1,5 +1,5 @@
plugins {
id("com.gradleup.shadow") version "9.0.0-beta11"
id("com.gradleup.shadow") version "9.0.0-beta13"
id("maven-publish")
}
@@ -95,6 +95,8 @@ tasks {
relocate("org.yaml.snakeyaml", "net.momirealms.craftengine.libraries.snakeyaml")
relocate("org.ahocorasick", "net.momirealms.craftengine.libraries.ahocorasick")
relocate("com.ezylang.evalex", "net.momirealms.craftengine.libraries.evalex")
relocate("com.google.common.jimfs", "net.momirealms.craftengine.libraries.jimfs")
relocate("org.apache.commons.imaging", "net.momirealms.craftengine.libraries.imaging")
}
}

View File

@@ -192,6 +192,11 @@ public class BukkitCompatibilityManager implements CompatibilityManager {
return PlaceholderAPIUtils.parse((org.bukkit.entity.Player) player.platformPlayer(), text);
}
@Override
public String parse(Player player1, Player player2, String text) {
return PlaceholderAPIUtils.parse((org.bukkit.entity.Player) player1.platformPlayer(), (org.bukkit.entity.Player) player2.platformPlayer(), text);
}
@Override
public int getPlayerProtocolVersion(UUID uuid) {
return ViaVersionUtils.getPlayerProtocolVersion(uuid);

View File

@@ -1,6 +1,6 @@
package net.momirealms.craftengine.bukkit.compatibility.bettermodel;
import net.momirealms.craftengine.core.entity.Entity;
import net.momirealms.craftengine.core.entity.AbstractEntity;
import net.momirealms.craftengine.core.entity.furniture.AbstractExternalModel;
public class BetterModelModel extends AbstractExternalModel {
@@ -15,7 +15,7 @@ public class BetterModelModel extends AbstractExternalModel {
}
@Override
public void bindModel(Entity entity) {
public void bindModel(AbstractEntity entity) {
org.bukkit.entity.Entity bukkitEntity = (org.bukkit.entity.Entity) entity.literalObject();
BetterModelUtils.bindModel(bukkitEntity, id());
}

View File

@@ -1,6 +1,6 @@
package net.momirealms.craftengine.bukkit.compatibility.modelengine;
import net.momirealms.craftengine.core.entity.Entity;
import net.momirealms.craftengine.core.entity.AbstractEntity;
import net.momirealms.craftengine.core.entity.furniture.AbstractExternalModel;
public class ModelEngineModel extends AbstractExternalModel {
@@ -15,7 +15,7 @@ public class ModelEngineModel extends AbstractExternalModel {
}
@Override
public void bindModel(Entity entity) {
public void bindModel(AbstractEntity entity) {
org.bukkit.entity.Entity bukkitEntity = (org.bukkit.entity.Entity) entity.literalObject();
ModelEngineUtils.bindModel(bukkitEntity, id());
}

View File

@@ -3,6 +3,7 @@ package net.momirealms.craftengine.bukkit.compatibility.papi;
import me.clip.placeholderapi.PlaceholderAPI;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
public class PlaceholderAPIUtils {
@@ -12,6 +13,10 @@ public class PlaceholderAPIUtils {
return PlaceholderAPI.setPlaceholders(player, text);
}
public static String parse(Player player1, Player player2, String text) {
return PlaceholderAPI.setRelationalPlaceholders(player1, player2, text);
}
public static void registerExpansions(CraftEngine plugin) {
new ImageExpansion(plugin).register();
new ShiftExpansion(plugin).register();

View File

@@ -1,5 +1,5 @@
plugins {
id("com.gradleup.shadow") version "9.0.0-beta11"
id("com.gradleup.shadow") version "9.0.0-beta13"
}
repositories {

View File

@@ -1,6 +1,6 @@
plugins {
id("com.gradleup.shadow") version "9.0.0-beta11"
id("net.minecrell.plugin-yml.bukkit") version "0.6.0"
id("com.gradleup.shadow") version "9.0.0-beta13"
id("de.eldoria.plugin-yml.bukkit") version "0.7.1"
}
repositories {
@@ -60,7 +60,7 @@ artifacts {
tasks {
shadowJar {
archiveFileName = "${rootProject.name}-plugin-${rootProject.properties["project_version"]}.jar"
archiveFileName = "${rootProject.name}-bukkit-plugin-${rootProject.properties["project_version"]}.jar"
destinationDirectory.set(file("$rootDir/target"))
relocate("net.kyori", "net.momirealms.craftengine.libraries")
relocate("net.momirealms.sparrow.nbt", "net.momirealms.craftengine.libraries.nbt")
@@ -78,5 +78,7 @@ tasks {
relocate("net.jpountz", "net.momirealms.craftengine.libraries.jpountz")
relocate("software.amazon.awssdk", "net.momirealms.craftengine.libraries.awssdk")
relocate("software.amazon.eventstream", "net.momirealms.craftengine.libraries.eventstream")
relocate("com.google.common.jimfs", "net.momirealms.craftengine.libraries.jimfs")
relocate("org.apache.commons.imaging", "net.momirealms.craftengine.libraries.imaging")
}
}

View File

@@ -65,6 +65,8 @@ resource-pack:
- CustomNameplates/ResourcePack
- BetterModel/build
- BetterHud/build
merge-external-zip-files:
- CraftEngine/external_packs/example.zip
delivery:
# Send the resource pack on joining the server
send-on-join: true

View File

@@ -20,6 +20,7 @@ caffeine=${caffeine_version}
slf4j-api=${slf4j_version}
zstd-jni=${zstd_version}
commons-io=${commons_io_version}
commons-imaging=${commons_imaging_version}
byte-buddy=${byte_buddy_version}
snake-yaml=${snake_yaml_version}
adventure-text-minimessage=${adventure_bundle_version}
@@ -33,4 +34,5 @@ netty-codec-http2=${netty_version}
reactive-streams=${reactive_streams_version}
amazon-sdk-s3=${amazon_awssdk_version}
amazon-sdk-eventstream=${amazon_awssdk_eventstream_version}
evalex=${evalex_version}
evalex=${evalex_version}
jimfs=${jimfs_version}

View File

@@ -38,6 +38,7 @@ categories:
- default:topaz_bow
- default:topaz_crossbow
- default:topaz_rod
- default:topaz_trident
- default:topaz_helmet
- default:topaz_chestplate
- default:topaz_leggings
@@ -62,4 +63,6 @@ categories:
- default:flame_cane
- default:gunpowder_block
- default:solid_gunpowder_block
- default:ender_pearl_flower_seeds
- default:ender_pearl_flower_seeds
- default:gui_head_size_1
- default:gui_head_size_4

View File

@@ -1,5 +1,5 @@
# client-bound-data requires CraftEngine mod to apply
items:
# client-bound-data requires CraftEngine mod to apply
minecraft:string:
client-bound-data:
components:
@@ -17,4 +17,11 @@ items:
minecraft:block_state:
instrument: "harp"
powered: "false"
note: "0"
note: "0"
blocks:
minecraft:note_block:
settings:
client-bound-tags:
- minecraft:beacon_base_blocks
- minecraft:mineable/axe

View File

@@ -17,6 +17,7 @@ items:
place: minecraft:block.bamboo_wood.place
placement:
ground:
loot-spawn-offset: 0.5,0.5,0
rules:
# ANY / FOUR / EIGHT / SIXTEEN / NORTH / EAST / WEST / SOUTH
rotation: FOUR
@@ -40,7 +41,7 @@ items:
- 0,0,-0.1 0
- 1,0,-0.1 0
loot:
template: "default:loot_table/basic"
template: "default:loot_table/furniture"
arguments:
item: default:bench
default:table_lamp:
@@ -61,6 +62,7 @@ items:
place: minecraft:block.lantern.place
placement:
ground:
loot-spawn-offset: 0,0.2,0
rules:
rotation: ANY
alignment: QUARTER
@@ -90,7 +92,7 @@ items:
height: 0.4
interactive: true
loot:
template: "default:loot_table/basic"
template: "default:loot_table/furniture"
arguments:
item: default:table_lamp
default:wooden_chair:
@@ -111,6 +113,7 @@ items:
place: minecraft:block.bamboo_wood.place
placement:
ground:
loot-spawn-offset: 0,0.4,0
rules:
rotation: ANY
alignment: ANY
@@ -129,6 +132,6 @@ items:
seats:
- 0,0,-0.1 0
loot:
template: "default:loot_table/basic"
template: "default:loot_table/furniture"
arguments:
item: default:wooden_chair

View File

@@ -20,6 +20,7 @@ i18n:
item.topaz_chestplate: "Topaz Chestplate"
item.topaz_leggings: "Topaz Leggings"
item.topaz_boots: "Topaz Boots"
item.topaz_trident: "Topaz Trident"
item.topaz_ore: "Topaz Ore"
item.deepslate_topaz_ore: "Deepslate Topaz Ore"
item.topaz: "Topaz"
@@ -63,6 +64,7 @@ i18n:
item.topaz_chestplate: "黄玉胸甲"
item.topaz_leggings: "黄玉护腿"
item.topaz_boots: "黄玉靴子"
item.topaz_trident: "黄玉三叉戟"
item.topaz_ore: "黄玉矿石"
item.deepslate_topaz_ore: "深层黄玉矿石"
item.topaz: "黄玉"

View File

@@ -1,4 +1,37 @@
items:
items#gui_head:
default:gui_head_size_1:
material: player_head
custom-model-data: 1000
model:
type: minecraft:special
path: minecraft:item/custom/gui_head_size_1
generation:
parent: minecraft:item/template_skull
gui-light: front
display:
gui:
translation: 0,8,0
scale: 2,2,2
model:
type: minecraft:head
kind: player
default:gui_head_size_4:
material: player_head
custom-model-data: 1001
model:
type: minecraft:special
path: minecraft:item/custom/gui_head_size_4
generation:
parent: minecraft:item/template_skull
gui-light: front
display:
gui:
translation: 9,7,0
scale: 4,4,4
model:
type: minecraft:head
kind: player
items#topaz_gears:
default:topaz_rod:
material: fishing_rod
custom-model-data: 1000
@@ -142,7 +175,42 @@ items:
arguments:
part: boots
slot: feet
default:topaz_trident:
material: trident
custom-model-data: 1000
settings:
projectile:
item: default:topaz_trident
translation: 0,0,0
rotation: 1,1,1,1
display-transform: NONE
scale: 0.5
tags:
- "default:topaz_tools"
data:
item-name: "<!i><#FF8C00><i18n:item.topaz_trident>"
tooltip-style: minecraft:topaz
model:
type: minecraft:select
property: minecraft:display_context
cases:
- when: ["gui", "ground", "fixed"]
model:
type: minecraft:model
path: minecraft:item/custom/topaz_trident
generation:
parent: minecraft:item/generated
textures:
layer0: minecraft:item/custom/topaz_trident
fallback:
type: minecraft:condition
property: minecraft:using_item
on-true:
type: minecraft:model
path: minecraft:item/custom/topaz_trident_throwing
on-false:
type: minecraft:model
path: minecraft:item/custom/topaz_trident_in_hand
templates:
default:armor/topaz:
material: "chainmail_{part}"
@@ -395,4 +463,12 @@ recipes#11:
template-type: default:topaz
result:
id: default:topaz_rod
count: 1
default:topaz_trident:
type: smithing_transform
base: minecraft:trident
addition: default:topaz
template-type: default:topaz
result:
id: default:topaz_trident
count: 1

View File

@@ -968,6 +968,18 @@ templates#loot_tables:
- type: item
item: "{item}"
# drop the original furniture item or a fallback item
# template: default:loot_table/furniture
# arguments:
# item: the fallback item
default:loot_table/furniture:
pools:
- rolls: 1
entries:
- type: furniture_item
item: "{item}"
# drop with silk touch
# template: default:loot_table/silk_touch

View File

@@ -1,43 +0,0 @@
{
"ambientocclusion": false,
"textures": {
"1": "item/ender_pearl",
"particle": "block/custom/ender_pearl_flower_stage_3",
"cross": "block/custom/ender_pearl_flower_stage_3"
},
"elements": [
{
"from": [0.8, 0, 8],
"to": [15.2, 16, 8],
"shade": false,
"rotation": {"angle": 45, "axis": "y", "origin": [8, 8, 8], "rescale": true},
"faces": {
"north": {"uv": [0, 0, 16, 16], "texture": "#cross"},
"south": {"uv": [0, 0, 16, 16], "texture": "#cross"}
}
},
{
"from": [8, 0, 0.8],
"to": [8, 16, 15.2],
"shade": false,
"rotation": {"angle": 45, "axis": "y", "origin": [8, 8, 8], "rescale": true},
"faces": {
"east": {"uv": [0, 0, 16, 16], "texture": "#cross"},
"west": {"uv": [0, 0, 16, 16], "texture": "#cross"}
}
},
{
"from": [8, 5, -1],
"to": [8, 21, 15],
"rotation": {"angle": 0, "axis": "y", "origin": [7, 5, 7]},
"faces": {
"north": {"uv": [0, 0, 0, 16], "texture": "#1"},
"east": {"uv": [0, 0, 16, 16], "texture": "#1"},
"south": {"uv": [0, 0, 0, 16], "texture": "#1"},
"west": {"uv": [0, 0, 16, 16], "texture": "#1"},
"up": {"uv": [0, 0, 0, 16], "texture": "#1"},
"down": {"uv": [0, 0, 0, 16], "texture": "#1"}
}
}
]
}

View File

@@ -0,0 +1,108 @@
{
"credit": "Made with Blockbench",
"textures": {
"0": "item/custom/topaz_trident_3d",
"particle": "item/custom/topaz_trident_3d"
},
"elements": [
{
"from": [7.75, -5.5, 7.75],
"to": [8.25, 10, 8.25],
"rotation": {"angle": 0, "axis": "y", "origin": [4, -5.5, 4]},
"faces": {
"north": {"uv": [10.5, 0.5, 11, 16], "texture": "#0"},
"east": {"uv": [10.5, 0.5, 11, 16], "texture": "#0"},
"south": {"uv": [10.5, 0.5, 11, 16], "texture": "#0"},
"west": {"uv": [10.5, 0.5, 11, 16], "texture": "#0"},
"up": {"uv": [3, 0, 3.5, 0.5], "texture": "#0"},
"down": {"uv": [0.5, 15.5, 1, 16], "texture": "#0"}
}
},
{
"from": [7.25, 7, 7.75],
"to": [7.75, 8, 8.25],
"rotation": {"angle": 0, "axis": "y", "origin": [4, -5.5, 4]},
"faces": {
"north": {"uv": [11, 2.5, 11.5, 3.5], "texture": "#0"},
"east": {"uv": [11, 2.5, 11.5, 3.5], "texture": "#0"},
"south": {"uv": [11, 2.5, 11.5, 3.5], "texture": "#0"},
"west": {"uv": [11, 2.5, 11.5, 3.5], "texture": "#0"},
"up": {"uv": [11, 2.5, 11.5, 3], "texture": "#0"},
"down": {"uv": [11, 3, 11.5, 3.5], "texture": "#0"}
}
},
{
"from": [6.75, 7.5, 7.75],
"to": [7.25, 9.5, 8.25],
"rotation": {"angle": 0, "axis": "y", "origin": [4, -5.5, 4]},
"faces": {
"north": {"uv": [11.5, 1, 12, 3], "texture": "#0"},
"east": {"uv": [11.5, 1, 12, 3], "texture": "#0"},
"south": {"uv": [11.5, 1, 12, 3], "texture": "#0"},
"west": {"uv": [11.5, 1, 12, 3], "texture": "#0"},
"up": {"uv": [11.5, 1, 12, 1.5], "texture": "#0"},
"down": {"uv": [11.5, 2.5, 12, 3], "texture": "#0"}
}
},
{
"from": [8.75, 7.5, 7.75],
"to": [9.25, 9.5, 8.25],
"rotation": {"angle": 0, "axis": "y", "origin": [4, -5.5, 4]},
"faces": {
"north": {"uv": [11.5, 1, 12, 3], "texture": "#0"},
"east": {"uv": [11.5, 1, 12, 3], "texture": "#0"},
"south": {"uv": [11.5, 1, 12, 3], "texture": "#0"},
"west": {"uv": [11.5, 1, 12, 3], "texture": "#0"},
"up": {"uv": [11.5, 1, 12, 1.5], "texture": "#0"},
"down": {"uv": [11.5, 2.5, 12, 3], "texture": "#0"}
}
},
{
"from": [8.25, 7, 7.75],
"to": [8.75, 8, 8.25],
"rotation": {"angle": 0, "axis": "y", "origin": [4, -5.5, 4]},
"faces": {
"north": {"uv": [11, 2.5, 11.5, 3.5], "texture": "#0"},
"east": {"uv": [11, 2.5, 11.5, 3.5], "texture": "#0"},
"south": {"uv": [11, 2.5, 11.5, 3.5], "texture": "#0"},
"west": {"uv": [11, 2.5, 11.5, 3.5], "texture": "#0"},
"up": {"uv": [11, 2.5, 11.5, 3], "texture": "#0"},
"down": {"uv": [11, 3, 11.5, 3.5], "texture": "#0"}
}
}
],
"gui_light": "front",
"display": {
"thirdperson_righthand": {
"rotation": [0, 60, 0],
"translation": [0.25, 9, 1.5],
"scale": [2, 2, 2]
},
"thirdperson_lefthand": {
"rotation": [0, 60, 0],
"translation": [0.25, 9, 1.5],
"scale": [2, 2, 2]
},
"firstperson_righthand": {
"rotation": [0, -90, 25],
"translation": [-2.5, 7.5, 4.75]
},
"firstperson_lefthand": {
"rotation": [0, 90, -25],
"translation": [-2.5, 7.5, 4.75]
},
"ground": {
"rotation": [0, 0, 45],
"translation": [-2, 2, 0],
"scale": [0.5, 0.5, 0.5]
},
"gui": {
"rotation": [0, 0, -45],
"translation": [5, 5, 0],
"scale": [1.25, 1.25, 1.25]
},
"fixed": {
"translation": [0, 6, 0]
}
}
}

View File

@@ -0,0 +1,110 @@
{
"credit": "Made with Blockbench",
"textures": {
"0": "item/custom/topaz_trident_3d",
"particle": "item/custom/topaz_trident_3d"
},
"elements": [
{
"from": [7.75, -5.5, 7.75],
"to": [8.25, 10, 8.25],
"rotation": {"angle": 0, "axis": "y", "origin": [4, -5.5, 4]},
"faces": {
"north": {"uv": [10.5, 0.5, 11, 16], "texture": "#0"},
"east": {"uv": [10.5, 0.5, 11, 16], "texture": "#0"},
"south": {"uv": [10.5, 0.5, 11, 16], "texture": "#0"},
"west": {"uv": [10.5, 0.5, 11, 16], "texture": "#0"},
"up": {"uv": [3, 0, 3.5, 0.5], "texture": "#0"},
"down": {"uv": [0.5, 15.5, 1, 16], "texture": "#0"}
}
},
{
"from": [7.25, 7, 7.75],
"to": [7.75, 8, 8.25],
"rotation": {"angle": 0, "axis": "y", "origin": [4, -5.5, 4]},
"faces": {
"north": {"uv": [11, 2.5, 11.5, 3.5], "texture": "#0"},
"east": {"uv": [11, 2.5, 11.5, 3.5], "texture": "#0"},
"south": {"uv": [11, 2.5, 11.5, 3.5], "texture": "#0"},
"west": {"uv": [11, 2.5, 11.5, 3.5], "texture": "#0"},
"up": {"uv": [11, 2.5, 11.5, 3], "texture": "#0"},
"down": {"uv": [11, 3, 11.5, 3.5], "texture": "#0"}
}
},
{
"from": [6.75, 7.5, 7.75],
"to": [7.25, 9.5, 8.25],
"rotation": {"angle": 0, "axis": "y", "origin": [4, -5.5, 4]},
"faces": {
"north": {"uv": [11.5, 1, 12, 3], "texture": "#0"},
"east": {"uv": [11.5, 1, 12, 3], "texture": "#0"},
"south": {"uv": [11.5, 1, 12, 3], "texture": "#0"},
"west": {"uv": [11.5, 1, 12, 3], "texture": "#0"},
"up": {"uv": [11.5, 1, 12, 1.5], "texture": "#0"},
"down": {"uv": [11.5, 2.5, 12, 3], "texture": "#0"}
}
},
{
"from": [8.75, 7.5, 7.75],
"to": [9.25, 9.5, 8.25],
"rotation": {"angle": 0, "axis": "y", "origin": [4, -5.5, 4]},
"faces": {
"north": {"uv": [11.5, 1, 12, 3], "texture": "#0"},
"east": {"uv": [11.5, 1, 12, 3], "texture": "#0"},
"south": {"uv": [11.5, 1, 12, 3], "texture": "#0"},
"west": {"uv": [11.5, 1, 12, 3], "texture": "#0"},
"up": {"uv": [11.5, 1, 12, 1.5], "texture": "#0"},
"down": {"uv": [11.5, 2.5, 12, 3], "texture": "#0"}
}
},
{
"from": [8.25, 7, 7.75],
"to": [8.75, 8, 8.25],
"rotation": {"angle": 0, "axis": "y", "origin": [4, -5.5, 4]},
"faces": {
"north": {"uv": [11, 2.5, 11.5, 3.5], "texture": "#0"},
"east": {"uv": [11, 2.5, 11.5, 3.5], "texture": "#0"},
"south": {"uv": [11, 2.5, 11.5, 3.5], "texture": "#0"},
"west": {"uv": [11, 2.5, 11.5, 3.5], "texture": "#0"},
"up": {"uv": [11, 2.5, 11.5, 3], "texture": "#0"},
"down": {"uv": [11, 3, 11.5, 3.5], "texture": "#0"}
}
}
],
"gui_light": "front",
"display": {
"thirdperson_righthand": {
"rotation": [0, 90, 180],
"translation": [0.25, -9, 1.5],
"scale": [2, 2, 2]
},
"thirdperson_lefthand": {
"rotation": [0, 90, 180],
"translation": [0.25, -9, 1.5],
"scale": [2, 2, 2]
},
"firstperson_righthand": {
"rotation": [0, -90, 25],
"translation": [0.75, -5, -0.75],
"scale": [0.5, 0.5, 0.5]
},
"firstperson_lefthand": {
"rotation": [0, 90, -25],
"translation": [0.75, -5, -0.75],
"scale": [0.5, 0.5, 0.5]
},
"ground": {
"rotation": [0, 0, 45],
"translation": [-2, 2, 0],
"scale": [0.5, 0.5, 0.5]
},
"gui": {
"rotation": [0, 0, -45],
"translation": [5, 5, 0],
"scale": [1.25, 1.25, 1.25]
},
"fixed": {
"translation": [0, 6, 0]
}
}
}

View File

@@ -90,6 +90,10 @@ warning.config.condition.match_block_property.missing_properties: "<yellow>Issue
warning.config.condition.match_item.missing_id: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'id' argument for 'match_item' condition.</yellow>"
warning.config.condition.table_bonus.missing_enchantment: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'enchantment' argument for 'table_bonus' condition.</yellow>"
warning.config.condition.table_bonus.missing_chances: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'chances' argument for 'table_bonus' condition.</yellow>"
warning.config.condition.permission.missing_permission: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'permission' argument for 'permission' condition.</yellow>"
warning.config.condition.equals.missing_value1: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'value1' argument for 'equals' condition.</yellow>"
warning.config.condition.equals.missing_value2: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'value2' argument for 'equals' condition.</yellow>"
warning.config.condition.expression.missing_expression: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'expression' argument for 'expression' condition.</yellow>"
warning.config.structure.not_section: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is expected to be a config section while it's actually a(n) '<arg:2>'.</yellow>"
warning.config.image.duplicate: "<yellow>Issue found in file <arg:0> - Duplicated image '<arg:1>'. Please check if there is the same configuration in other files.</yellow>"
warning.config.image.missing_height: "<yellow>Issue found in file <arg:0> - The image '<arg:1>' is missing the required 'height' argument.</yellow>"
@@ -179,6 +183,8 @@ warning.config.item.model.select.case.missing_model: "<yellow>Issue found in fil
warning.config.item.model.select.block_state.missing_property: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'block-state-property' argument for property 'minecraft:block_state'.</yellow>"
warning.config.item.model.select.local_time.missing_pattern: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'pattern' argument for property 'minecraft:local_time'.</yellow>"
warning.config.item.model.special.missing_type: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'type' argument for model 'minecraft:special'.</yellow>"
warning.config.item.model.special.missing_path: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'path' argument for model 'minecraft:special'.</yellow>"
warning.config.item.model.special.invalid_path: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' has an invalid 'path' argument '<arg:2>' for model 'minecraft:special' which contains illegal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters.</yellow>"
warning.config.item.model.special.invalid_type: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is using an invalid type '<arg:2>' for model 'minecraft:special'.</yellow>"
warning.config.item.model.special.banner.missing_color: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'color' argument for special model 'minecraft:banner'.</yellow>"
warning.config.item.model.special.bed.missing_texture: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'texture' argument for special model 'minecraft:bed'.</yellow>"
@@ -189,7 +195,6 @@ warning.config.item.model.special.chest.invalid_openness: "<yellow>Issue found i
warning.config.item.model.special.shulker_box.missing_texture: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'texture' argument for special model 'minecraft:shulker_box'.</yellow>"
warning.config.item.model.special.shulker_box.invalid_openness: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is using an invalid 'openness' value '<arg:2>' for special model 'minecraft:shulker_box'. Valid range '0~1.'</yellow>"
warning.config.item.model.special.head.missing_kind: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'kind' argument for special model 'minecraft:head'.</yellow>"
warning.config.item.model.special.head.missing_texture: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'texture' argument for special model 'minecraft:head'.</yellow>"
warning.config.block.duplicate: "<yellow>Issue found in file <arg:0> - Duplicated block '<arg:1>'. Please check if there is the same configuration in other files.</yellow>"
warning.config.block.missing_state: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'state' argument.</yellow>"
warning.config.block.state.property.missing_type: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'type' argument for property '<arg:2>'.</yellow>"
@@ -223,9 +228,9 @@ warning.config.block.behavior.leaves.missing_distance: "<yellow>Issue found in f
warning.config.block.behavior.sapling.missing_stage: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'stage' property for 'sapling_block' behavior.</yellow>"
warning.config.block.behavior.sapling.missing_feature: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'feature' argument for 'sapling_block' behavior.</yellow>"
warning.config.block.behavior.strippable.missing_stripped: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'stripped' argument for 'strippable_block' behavior.</yellow>"
warning.config.block.event.condition.missing_type: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'type' argument for event condition.</yellow>"
warning.config.block.event.condition.invalid_type: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is using an invalid 'type' argument '<arg:2>' for event condition.</yellow>"
warning.config.model.generation.missing_parent: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'parent' argument in 'generation' section.</yellow>"
warning.config.model.generation.invalid_display_position: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is using an invalid display position '<arg:2>' in 'generation.display' section. Allowed display positions: [<arg:3>]</yellow>"
warning.config.model.generation.invalid_gui_light: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is using an invalid gui-light option '<arg:2>' in 'generation' section. Allowed gui light options: [<arg:3>]</yellow>"
warning.config.model.generation.conflict: "<yellow>Issue found in file <arg:0> - Failed to generate model for '<arg:1>' as two or more configurations attempt to generate different json models with the same path: '<arg:2>'.</yellow>"
warning.config.model.generation.texture.invalid: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' has a texture '<arg:2>' with path '<arg:3>' that contains illegal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters.</yellow>"
warning.config.model.generation.parent.invalid: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' has a parent argument '<arg:2>' that contains illegal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters.</yellow>"
@@ -294,4 +299,18 @@ warning.config.conflict_matcher.all_of.missing_terms: "<yellow>Issue found in co
warning.config.conflict_matcher.any_of.missing_terms: "<yellow>Issue found in config.yml at 'resource-pack.duplicated-files-handler' - Missing required 'terms' argument for 'any_of' matcher.</yellow>"
warning.config.conflict_resolution.missing_type: "<yellow>Issue found in config.yml at 'resource-pack.duplicated-files-handler' - Missing required 'type' argument for one of the resolutions.</yellow>"
warning.config.conflict_resolution.invalid_type: "<yellow>Issue found in config.yml at 'resource-pack.duplicated-files-handler' - One of the resolutions is using the invalid type '<arg:0>'.</yellow>"
warning.config.function.command.missing_command: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'command' argument for 'command' function.</yellow>"
warning.config.event.missing_trigger: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'on' argument for event triggers.</yellow>"
warning.config.event.invalid_trigger: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is using an invalid event trigger '<arg:2>'.</yellow>"
warning.config.event.condition.missing_type: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'type' argument for event condition.</yellow>"
warning.config.event.condition.invalid_type: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is using an invalid 'type' argument '<arg:2>' for event condition.</yellow>"
warning.config.function.missing_type: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'type' argument for function.</yellow>"
warning.config.function.invalid_type: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is using an invalid function type '<arg:2>'.</yellow>"
warning.config.function.command.missing_command: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'command' argument for 'command' function.</yellow>"
warning.config.function.actionbar.missing_actionbar: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'actionbar' argument for 'actionbar' function.</yellow>"
warning.config.function.message.missing_message: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'message' argument for 'message' function.</yellow>"
warning.config.selector.missing_type: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'type' argument for selector.</yellow>"
warning.config.selector.invalid_type: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is using an invalid selector type '<arg:2>'.</yellow>"
warning.config.selector.invalid_target: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is using an invalid selector target '<arg:2>'.</yellow>"
warning.config.resource_pack.item_model.conflict.vanilla: "<yellow>Failed to generate item model for '<arg:0>' because this item model has been occupied by a vanilla item.</yellow>"
warning.config.resource_pack.item_model.already_exist: "<yellow>Failed to generate item model for '<arg:0>' because the file '<arg:1>' already exists.</yellow>"
warning.config.resource_pack.model.generation.already_exist: "<yellow>Failed to generate model because the model file '<arg:0>' already exists.</yellow>"

View File

@@ -167,7 +167,6 @@ warning.config.item.model.special.chest.invalid_openness: "<yellow><arg:0> dosya
warning.config.item.model.special.shulker_box.missing_texture: "<yellow><arg:0> dosyasında sorun bulundu - '<arg:1>' eşyası, 'minecraft:shulker_box' özel modeli için gerekli 'texture' argümanı eksik.</yellow>"
warning.config.item.model.special.shulker_box.invalid_openness: "<yellow><arg:0> dosyasında sorun bulundu - '<arg:1>' eşyası, 'minecraft:shulker_box' özel modeli için geçersiz bir 'openness' değeri '<arg:2>' kullanıyor. Geçerli aralık '0~1.'</yellow>"
warning.config.item.model.special.head.missing_kind: "<yellow><arg:0> dosyasında sorun bulundu - '<arg:1>' eşyası, 'minecraft:head' özel modeli için gerekli 'kind' argümanı eksik.</yellow>"
warning.config.item.model.special.head.missing_texture: "<yellow><arg:0> dosyasında sorun bulundu - '<arg:1>' eşyası, 'minecraft:head' özel modeli için gerekli 'texture' argümanı eksik.</yellow>"
warning.config.block.duplicate: "<yellow><arg:0> dosyasında sorun bulundu - Yinelenen blok '<arg:1>'. Diğer dosyalarda aynı yapılandırmanın olup olmadığını kontrol edin.</yellow>"
warning.config.block.missing_state: "<yellow><arg:0> dosyasında sorun bulundu - '<arg:1>' bloğu gerekli 'state' argümanı eksik.</yellow>"
warning.config.block.state.property.missing_type: "<yellow><arg:0> dosyasında sorun bulundu - '<arg:1>' bloğu, '<arg:2>' özelliği için gerekli 'type' argümanı eksik.</yellow>"

View File

@@ -90,6 +90,8 @@ warning.config.condition.match_block_property.missing_properties: "<yellow>在
warning.config.condition.match_item.missing_id: "<yellow>在文件 <arg:0> 发现问题 - 配置项 '<arg:1>' 缺少 'match_item' 条件所需的 'id' 参数</yellow>"
warning.config.condition.table_bonus.missing_enchantment: "<yellow>在文件 <arg:0> 发现问题 - 配置项 '<arg:1>' 缺少 'table_bonus' 条件所需的 'enchantment' 参数</yellow>"
warning.config.condition.table_bonus.missing_chances: "<yellow>在文件 <arg:0> 发现问题 - 配置项 '<arg:1>' 缺少 'table_bonus' 条件所需的 'chances' 参数</yellow>"
warning.config.structure.not_section: "<yellow>在文件 <arg:0> 发现问题 - 配置项 '<arg:1>' 应为配置段落 但实际类型为 '<arg:2>'</yellow>"
warning.config.image.duplicate: "<yellow>在文件 <arg:0> 发现问题 - 重复的图片配置 '<arg:1>' 请检查其他文件中是否存在相同配置</yellow>"
warning.config.image.missing_height: "<yellow>在文件 <arg:0> 发现问题 - 图片 '<arg:1>' 缺少必需的 'height' 参数</yellow>"
@@ -179,6 +181,8 @@ warning.config.item.model.select.case.missing_model: "<yellow>在文件 <arg:0>
warning.config.item.model.select.block_state.missing_property: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:block_state' 属性缺少必需的 'block-state-property' 参数</yellow>"
warning.config.item.model.select.local_time.missing_pattern: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:local_time' 属性缺少必需的 'pattern' 参数</yellow>"
warning.config.item.model.special.missing_type: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:special' 模型缺少必需的 'type' 参数</yellow>"
warning.config.item.model.special.missing_path: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:special' 模型缺少必需的模型 'path' 参数</yellow>"
warning.config.item.model.special.invalid_path: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:special' 模型路径 '<arg:2>' 包含非法字符 请参考 https://zh.minecraft.wiki/w/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4ID#%E5%90%88%E6%B3%95%E5%AD%97%E7%AC%A6</yellow>"
warning.config.item.model.special.invalid_type: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:special' 模型使用了无效类型 '<arg:2>'</yellow>"
warning.config.item.model.special.banner.missing_color: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:banner' 特殊模型缺少必需的 'color' 参数</yellow>"
warning.config.item.model.special.bed.missing_texture: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:bed' 特殊模型缺少必需的 'texture' 参数</yellow>"
@@ -189,7 +193,6 @@ warning.config.item.model.special.chest.invalid_openness: "<yellow>在文件 <ar
warning.config.item.model.special.shulker_box.missing_texture: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:shulker_box' 特殊模型缺少必需的 'texture' 参数</yellow>"
warning.config.item.model.special.shulker_box.invalid_openness: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:shulker_box' 特殊模型使用了无效的 'openness' 值 '<arg:2>' 有效范围应为 0~1</yellow>"
warning.config.item.model.special.head.missing_kind: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:head' 特殊模型缺少必需的 'kind' 参数</yellow>"
warning.config.item.model.special.head.missing_texture: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'minecraft:head' 特殊模型缺少必需的 'texture' 参数</yellow>"
warning.config.block.duplicate: "<yellow>在文件 <arg:0> 发现问题 - 重复的方块 '<arg:1>' 请检查其他文件中是否存在相同配置</yellow>"
warning.config.block.missing_state: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 缺少必需的 'state' 参数</yellow>"
warning.config.block.state.property.missing_type: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的属性 '<arg:2>' 缺少必需的 'type' 参数</yellow>"
@@ -223,10 +226,10 @@ warning.config.block.behavior.leaves.missing_distance: "<yellow>在文件 <arg:0
warning.config.block.behavior.sapling.missing_stage: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的 'sapling_block' 行为缺少必需的 'stage' 属性</yellow>"
warning.config.block.behavior.sapling.missing_feature: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的 'sapling_block' 行为缺少必需的 'feature' 参数</yellow>"
warning.config.block.behavior.strippable.missing_stripped: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的 'strippable_block' 行为缺少必需的 'stripped' 参数</yellow>"
warning.config.block.event.condition.missing_type: "<yellow>在文件 <arg:0> - 方块 '<arg:1>' 的事件条件缺少 'type' 参数</yellow>"
warning.config.block.event.condition.invalid_type: "<yellow>在文件 <arg:0> - 方块 '<arg:1>' 使用了无效的事件条件类型 '<arg:2>'</yellow>"
warning.config.model.generation.missing_parent: "<yellow>在文件 <arg:0> 发现问题 - 配置项 '<arg:1>' 的 'generation' 段落缺少必需的 'parent' 参数</yellow>"
warning.config.model.generation.conflict: "<yellow>在文件 <arg:0> 发现问题 - 无法为 '<arg:1>' 生成模型 存在多个配置尝试使用相同路径 '<arg:2>' 生成不同的 JSON 模型</yellow>"
warning.config.model.generation.invalid_display_position: "<yellow>在文件 <arg:0> 发现问题 - 配置项 '<arg:1>' 在 'generation.display' 区域使用了无效的 display 位置类型 '<arg:2>'. 可用展示类型: [<arg:3>]</yellow>"
warning.config.model.generation.invalid_gui_light: "<yellow>在文件 <arg:0> 发现问题 - 配置项 '<arg:1>' 在 'generation' 区域使用了无效的GUI光照 '<arg:2>'. 可用GUI光照: [<arg:3>]</yellow>"
warning.config.model.generation.texture.invalid: "<yellow>在文件 <arg:0> 发现问题 - 配置项 '<arg:1>' 的纹理 '<arg:2>' 路径 '<arg:3>' 包含非法字符 请参考 https://zh.minecraft.wiki/w/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4ID#%E5%90%88%E6%B3%95%E5%AD%97%E7%AC%A6</yellow>"
warning.config.model.generation.parent.invalid: "<yellow>在文件 <arg:0> 发现问题 - 配置项 '<arg:1>' 的父级参数 '<arg:2>' 包含非法字符 请参考 https://zh.minecraft.wiki/w/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4ID#%E5%90%88%E6%B3%95%E5%AD%97%E7%AC%A6</yellow>"
warning.config.emoji.missing_keywords: "<yellow>在文件 <arg:0> 发现问题 - 表情 '<arg:1>' 缺少必需的 'keywords' 参数</yellow>"
@@ -293,4 +296,11 @@ warning.config.conflict_matcher.inverted.missing_term: "<yellow>在 config.yml
warning.config.conflict_matcher.all_of.missing_terms: "<yellow>在 config.yml 的 'resource-pack.duplicated-files-handler' 处发现问题 - 全匹配器缺少必需的 'terms' 参数</yellow>"
warning.config.conflict_matcher.any_of.missing_terms: "<yellow>在 config.yml 的 'resource-pack.duplicated-files-handler' 处发现问题 - 任一匹配器缺少必需的 'terms' 参数</yellow>"
warning.config.conflict_resolution.missing_type: "<yellow>在 config.yml 的 'resource-pack.duplicated-files-handler' 处发现问题 - 文件冲突处理器的某个解决方案缺少必需的 'type' 参数</yellow>"
warning.config.conflict_resolution.invalid_type: "<yellow>在 config.yml 的 'resource-pack.duplicated-files-handler' 处发现问题 - 文件冲突处理器的某个解决方案使用了无效类型 '<arg:0>'</yellow>"
warning.config.conflict_resolution.invalid_type: "<yellow>在 config.yml 的 'resource-pack.duplicated-files-handler' 处发现问题 - 文件冲突处理器的某个解决方案使用了无效类型 '<arg:0>'</yellow>"
warning.config.event.missing_trigger: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'on' argument for event triggers.</yellow>"
warning.config.event.invalid_trigger: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is using an invalid event trigger '<arg:2>'.</yellow>"
warning.config.event.condition.missing_type: "<yellow>在文件 <arg:0> - 配置项 '<arg:1>' 的事件条件缺少 'type' 参数</yellow>"
warning.config.event.condition.invalid_type: "<yellow>在文件 <arg:0> - 配置项 '<arg:1>' 使用了无效的事件条件类型 '<arg:2>'</yellow>"
warning.config.function.missing_type: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'type' argument for function.</yellow>"
warning.config.function.invalid_type: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is using an invalid function type '<arg:2>'.</yellow>"
warning.config.function.command.missing_command: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'command' argument for 'command' function.</yellow>"

View File

@@ -6,7 +6,7 @@ import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.core.advancement.AbstractAdvancementManager;
import net.momirealms.craftengine.core.pack.LoadingSequence;
import net.momirealms.craftengine.core.pack.Pack;
import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser;
import net.momirealms.craftengine.core.plugin.config.ConfigParser;
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
import net.momirealms.craftengine.core.util.GsonHelper;
import net.momirealms.craftengine.core.util.Key;
@@ -31,11 +31,11 @@ public class BukkitAdvancementManager extends AbstractAdvancementManager {
}
@Override
public ConfigSectionParser parser() {
public ConfigParser parser() {
return this.advancementParser;
}
public class AdvancementParser implements ConfigSectionParser {
public class AdvancementParser implements ConfigParser {
public static final String[] CONFIG_SECTION_NAME = new String[] {"advancements", "advancement"};
@Override

View File

@@ -12,11 +12,11 @@ import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.UpdateOption;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.plugin.context.ContextHolder;
import net.momirealms.craftengine.core.plugin.context.parameter.CommonParameters;
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.world.Vec3d;
import net.momirealms.craftengine.core.world.World;
import net.momirealms.craftengine.core.world.WorldEvents;
import net.momirealms.craftengine.core.world.WorldPosition;
import net.momirealms.sparrow.nbt.CompoundTag;
import org.bukkit.Location;
import org.bukkit.Material;
@@ -169,20 +169,20 @@ public final class CraftEngineBlocks {
if (state == null || state.isEmpty()) return false;
World world = new BukkitWorld(block.getWorld());
Location location = block.getLocation();
Vec3d vec3d = new Vec3d(location.getBlockX() + 0.5, location.getBlockY() + 0.5, location.getBlockZ() + 0.5);
WorldPosition position = new WorldPosition(world, location.getBlockX() + 0.5, location.getBlockY() + 0.5, location.getBlockZ() + 0.5);
if (dropLoot) {
ContextHolder.Builder builder = new ContextHolder.Builder().withParameter(CommonParameters.WORLD, world).withParameter(CommonParameters.LOCATION, vec3d);
ContextHolder.Builder builder = new ContextHolder.Builder()
.withParameter(DirectContextParameters.POSITION, position);
BukkitServerPlayer serverPlayer = BukkitCraftEngine.instance().adapt(player);
if (player != null) {
builder.withParameter(CommonParameters.PLAYER, serverPlayer);
//mark item builder.withOptionalParameter(CommonParameters.MAIN_HAND_ITEM, serverPlayer.getItemInHand(InteractionHand.MAIN_HAND));
builder.withParameter(DirectContextParameters.PLAYER, serverPlayer);
}
for (Item<?> item : state.getDrops(builder, world, serverPlayer)) {
world.dropItemNaturally(vec3d, item);
world.dropItemNaturally(position, item);
}
}
if (playSound) {
world.playBlockSound(vec3d, state.sounds().breakSound());
world.playBlockSound(position, state.sounds().breakSound());
}
if (sendParticles) {
FastNMS.INSTANCE.method$Level$levelEvent(world.serverWorld(), WorldEvents.BLOCK_BREAK_EFFECT, LocationUtils.toBlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()), state.customBlockState().registryId());

View File

@@ -5,17 +5,17 @@ import net.momirealms.craftengine.bukkit.entity.furniture.LoadedFurniture;
import net.momirealms.craftengine.bukkit.nms.CollisionEntity;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.util.LocationUtils;
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
import net.momirealms.craftengine.core.entity.furniture.AnchorType;
import net.momirealms.craftengine.core.entity.furniture.CustomFurniture;
import net.momirealms.craftengine.core.entity.furniture.FurnitureExtraData;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.loot.LootTable;
import net.momirealms.craftengine.core.plugin.context.ContextHolder;
import net.momirealms.craftengine.core.plugin.context.parameter.CommonParameters;
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.world.Vec3d;
import net.momirealms.craftengine.core.world.World;
import net.momirealms.craftengine.core.world.WorldPosition;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
@@ -41,7 +41,7 @@ public final class CraftEngineFurniture {
}
/**
* Places furniture at the certain location
* Places furniture at certain location
*
* @param location location
* @param furnitureId furniture to place
@@ -55,7 +55,7 @@ public final class CraftEngineFurniture {
}
/**
* Places furniture at the certain location
* Places furniture at certain location
*
* @param location location
* @param furnitureId furniture to place
@@ -66,11 +66,11 @@ public final class CraftEngineFurniture {
public static LoadedFurniture place(Location location, Key furnitureId, AnchorType anchorType) {
CustomFurniture furniture = byId(furnitureId);
if (furniture == null) return null;
return BukkitFurnitureManager.instance().place(furniture, location, anchorType, true);
return BukkitFurnitureManager.instance().place(location, furniture, FurnitureExtraData.builder().anchorType(anchorType).build(), true);
}
/**
* Places furniture at the certain location
* Places furniture at certain location
*
* @param location location
* @param furniture furniture to place
@@ -79,11 +79,11 @@ public final class CraftEngineFurniture {
*/
@NotNull
public static LoadedFurniture place(Location location, CustomFurniture furniture, AnchorType anchorType) {
return BukkitFurnitureManager.instance().place(furniture, location, anchorType, true);
return BukkitFurnitureManager.instance().place(location, furniture, FurnitureExtraData.builder().anchorType(anchorType).build(), true);
}
/**
* Places furniture at the certain location
* Places furniture at certain location
*
* @param location location
* @param furnitureId furniture to place
@@ -95,11 +95,11 @@ public final class CraftEngineFurniture {
public static LoadedFurniture place(Location location, Key furnitureId, AnchorType anchorType, boolean playSound) {
CustomFurniture furniture = byId(furnitureId);
if (furniture == null) return null;
return BukkitFurnitureManager.instance().place(furniture, location, anchorType, playSound);
return BukkitFurnitureManager.instance().place(location, furniture, FurnitureExtraData.builder().anchorType(anchorType).build(), playSound);
}
/**
* Places furniture at the certain location
* Places furniture at certain location
*
* @param location location
* @param furniture furniture to place
@@ -109,7 +109,7 @@ public final class CraftEngineFurniture {
*/
@NotNull
public static LoadedFurniture place(Location location, CustomFurniture furniture, AnchorType anchorType, boolean playSound) {
return BukkitFurnitureManager.instance().place(furniture, location, anchorType, playSound);
return BukkitFurnitureManager.instance().place(location, furniture, FurnitureExtraData.builder().anchorType(anchorType).build(), playSound);
}
/**
@@ -222,7 +222,7 @@ public final class CraftEngineFurniture {
}
/**
* Removes furniture by providing a plugin furniture instance
* Removes furniture by providing furniture instance
*
* @param loadedFurniture loaded furniture
* @param dropLoot whether to drop loots
@@ -235,7 +235,7 @@ public final class CraftEngineFurniture {
}
/**
* Removes furniture by providing a plugin furniture instance
* Removes furniture by providing furniture instance
*
* @param loadedFurniture loaded furniture
* @param player the player who removes the furniture
@@ -251,7 +251,7 @@ public final class CraftEngineFurniture {
}
/**
* Removes furniture by providing a plugin furniture instance
* Removes furniture by providing furniture instance
*
* @param loadedFurniture loaded furniture
* @param player the player who removes the furniture
@@ -263,26 +263,26 @@ public final class CraftEngineFurniture {
@Nullable net.momirealms.craftengine.core.entity.player.Player player,
boolean dropLoot,
boolean playSound) {
Location location = loadedFurniture.location();
Location location = loadedFurniture.dropLocation();
loadedFurniture.destroy();
LootTable<ItemStack> lootTable = (LootTable<ItemStack>) loadedFurniture.config().lootTable();
Vec3d vec3d = LocationUtils.toVec3d(location);
World world = new BukkitWorld(location.getWorld());
WorldPosition position = new WorldPosition(world, location.getX(), location.getY(), location.getZ());
if (dropLoot && lootTable != null) {
ContextHolder.Builder builder = ContextHolder.builder();
builder.withParameter(CommonParameters.LOCATION, vec3d);
builder.withParameter(CommonParameters.WORLD, world);
ContextHolder.Builder builder = ContextHolder.builder()
.withParameter(DirectContextParameters.POSITION, position)
.withParameter(DirectContextParameters.FURNITURE, loadedFurniture)
.withOptionalParameter(DirectContextParameters.FURNITURE_ITEM, loadedFurniture.extraData().item().orElse(null));
if (player != null) {
builder.withParameter(CommonParameters.PLAYER, player);
//mark item builder.withOptionalParameter(CommonParameters.MAIN_HAND_ITEM, player.getItemInHand(InteractionHand.MAIN_HAND));
builder.withParameter(DirectContextParameters.PLAYER, player);
}
List<Item<ItemStack>> items = lootTable.getRandomItems(builder.build(), world, player);
for (Item<ItemStack> item : items) {
world.dropItemNaturally(vec3d, item);
world.dropItemNaturally(position, item);
}
}
if (playSound) {
world.playBlockSound(vec3d, loadedFurniture.config().settings().sounds().breakSound());
world.playBlockSound(position, loadedFurniture.config().settings().sounds().breakSound());
}
}
}

View File

@@ -6,22 +6,27 @@ import net.momirealms.craftengine.bukkit.nms.FastNMS;
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.BukkitBlockInWorld;
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
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.CustomItem;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.loot.LootTable;
import net.momirealms.craftengine.core.plugin.config.Config;
import net.momirealms.craftengine.core.plugin.context.ContextHolder;
import net.momirealms.craftengine.core.plugin.context.parameter.CommonParameters;
import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext;
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
import net.momirealms.craftengine.core.plugin.event.EventTrigger;
import net.momirealms.craftengine.core.util.Cancellable;
import net.momirealms.craftengine.core.util.VersionHelper;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.Vec3d;
import net.momirealms.craftengine.core.world.WorldPosition;
import org.bukkit.*;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.data.BlockData;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
@@ -33,10 +38,12 @@ import org.bukkit.event.block.BlockPhysicsEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.EntityExplodeEvent;
import org.bukkit.event.player.PlayerJoinEvent;
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;
@@ -49,6 +56,14 @@ public class BlockEventListener implements Listener {
this.enableNoteBlockCheck = enableNoteBlockCheck;
}
@EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
Object packet = this.manager.cachedUpdateTagsPacket;
if (packet != null) {
this.plugin.networkManager().sendPacket(event.getPlayer(), packet);
}
}
@EventHandler(ignoreCancelled = true)
public void onPlayerAttack(EntityDamageByEntityEvent event) {
if (!VersionHelper.isOrAbove1_20_5()) {
@@ -108,17 +123,40 @@ public class BlockEventListener implements Listener {
Object blockState = BlockStateUtils.blockDataToBlockState(block.getBlockData());
int stateId = BlockStateUtils.blockStateToId(blockState);
Player player = event.getPlayer();
Location location = block.getLocation();
BukkitServerPlayer serverPlayer = this.plugin.adapt(player);
net.momirealms.craftengine.core.world.World world = new BukkitWorld(player.getWorld());
WorldPosition position = new WorldPosition(world, location.getBlockX() + 0.5, location.getBlockY() + 0.5, location.getBlockZ() + 0.5);
Item<ItemStack> itemInHand = serverPlayer.getItemInHand(InteractionHand.MAIN_HAND);
if (itemInHand != null) {
Optional<CustomItem<ItemStack>> optionalCustomItem = itemInHand.getCustomItem();
if (optionalCustomItem.isPresent()) {
Cancellable cancellable = Cancellable.of(event::isCancelled, event::setCancelled);
optionalCustomItem.get().execute(
PlayerOptionalContext.of(serverPlayer, ContextHolder.builder()
.withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block))
.withParameter(DirectContextParameters.POSITION, position)
.withParameter(DirectContextParameters.PLAYER, serverPlayer)
.withParameter(DirectContextParameters.EVENT, cancellable)
.withOptionalParameter(DirectContextParameters.ITEM_IN_HAND, itemInHand)
), EventTrigger.BREAK
);
if (cancellable.isCancelled()) {
return;
}
}
}
if (!BlockStateUtils.isVanillaBlock(stateId)) {
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
// trigger api event
CustomBlockBreakEvent customBreakEvent = new CustomBlockBreakEvent(serverPlayer, location, block, state);
boolean isCancelled = EventUtils.fireAndCheckCancel(customBreakEvent);
if (isCancelled) {
@@ -126,7 +164,20 @@ public class BlockEventListener implements Listener {
return;
}
net.momirealms.craftengine.core.world.World world = new BukkitWorld(location.getWorld());
// execute functions
Cancellable cancellable = Cancellable.of(event::isCancelled, event::setCancelled);
PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder()
.withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block))
.withParameter(DirectContextParameters.BLOCK_STATE, state)
.withParameter(DirectContextParameters.EVENT, cancellable)
.withParameter(DirectContextParameters.POSITION, position)
.withOptionalParameter(DirectContextParameters.ITEM_IN_HAND, itemInHand)
);
state.owner().value().execute(context, EventTrigger.BREAK);
if (cancellable.isCancelled()) {
return;
}
// handle waterlogged blocks
@SuppressWarnings("unchecked")
Property<Boolean> waterloggedProperty = (Property<Boolean>) state.owner().value().getProperty("waterlogged");
@@ -136,27 +187,26 @@ public class BlockEventListener implements Listener {
location.getWorld().setBlockData(location, Material.WATER.createBlockData());
}
}
// play sound
Vec3d vec3d = new Vec3d(location.getBlockX() + 0.5, location.getBlockY() + 0.5, location.getBlockZ() + 0.5);
world.playBlockSound(vec3d, state.sounds().breakSound());
world.playBlockSound(position, state.sounds().breakSound());
if (player.getGameMode() == GameMode.CREATIVE || !customBreakEvent.dropItems()) {
return;
}
Item<ItemStack> itemInHand = serverPlayer.getItemInHand(InteractionHand.MAIN_HAND);
// do not drop if it's not the correct tool
if (!BlockStateUtils.isCorrectTool(state, itemInHand)) {
return;
}
// drop items
ContextHolder.Builder builder = ContextHolder.builder()
.withParameter(CommonParameters.WORLD, world)
.withParameter(CommonParameters.LOCATION, vec3d)
.withParameter(CommonParameters.PLAYER, serverPlayer);
//mark item .withOptionalParameter(CommonParameters.MAIN_HAND_ITEM, itemInHand);
for (Item<Object> item : state.getDrops(builder, world, serverPlayer)) {
world.dropItemNaturally(vec3d, item);
ContextHolder.Builder lootContext = ContextHolder.builder()
.withParameter(DirectContextParameters.POSITION, position)
.withParameter(DirectContextParameters.PLAYER, serverPlayer)
.withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block))
.withOptionalParameter(DirectContextParameters.ITEM_IN_HAND, itemInHand);
for (Item<Object> item : state.getDrops(lootContext, world, serverPlayer)) {
world.dropItemNaturally(position, item);
}
}
} else {
@@ -167,23 +217,19 @@ public class BlockEventListener implements Listener {
event.setDropItems(false);
event.setExpToDrop(0);
}
Location location = block.getLocation();
BukkitServerPlayer serverPlayer = this.plugin.adapt(player);
net.momirealms.craftengine.core.world.World world = new BukkitWorld(player.getWorld());
Vec3d vec3d = new Vec3d(location.getBlockX() + 0.5, location.getBlockY() + 0.5, location.getBlockZ() + 0.5);
ContextHolder.Builder builder = ContextHolder.builder()
.withParameter(CommonParameters.WORLD, world)
.withParameter(CommonParameters.LOCATION, vec3d)
.withParameter(CommonParameters.PLAYER, serverPlayer);
//mark item .withOptionalParameter(CommonParameters.MAIN_HAND_ITEM, serverPlayer.getItemInHand(InteractionHand.MAIN_HAND));
ContextHolder contextHolder = builder.build();
ContextHolder lootContext = ContextHolder.builder()
.withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block))
.withParameter(DirectContextParameters.POSITION, position)
.withParameter(DirectContextParameters.PLAYER, serverPlayer)
.withOptionalParameter(DirectContextParameters.ITEM_IN_HAND, itemInHand).build();
for (LootTable<?> lootTable : it.lootTables()) {
for (Item<?> item : lootTable.getRandomItems(contextHolder, world, serverPlayer)) {
world.dropItemNaturally(vec3d, item);
for (Item<?> item : lootTable.getRandomItems(lootContext, world, serverPlayer)) {
world.dropItemNaturally(position, item);
}
}
});
}
// sound system
if (Config.enableSoundSystem()) {
Object ownerBlock = BlockStateUtils.getBlockOwner(blockState);
@@ -212,12 +258,11 @@ public class BlockEventListener implements Listener {
if (!immutableBlockState.isEmpty()) {
Location location = block.getLocation();
net.momirealms.craftengine.core.world.World world = new BukkitWorld(block.getWorld());
Vec3d vec3d = new Vec3d(location.getBlockX() + 0.5, location.getBlockY() + 0.5, location.getBlockZ() + 0.5);
ContextHolder.Builder builder = ContextHolder.builder()
.withParameter(CommonParameters.WORLD, world)
.withParameter(CommonParameters.LOCATION, vec3d);
for (Item<?> item : immutableBlockState.getDrops(builder, world, null)) {
world.dropItemNaturally(vec3d, item);
WorldPosition position = new WorldPosition(world, location.getBlockX() + 0.5, location.getBlockY() + 0.5, location.getBlockZ() + 0.5);
for (Item<?> item : immutableBlockState.getDrops(ContextHolder.builder()
.withParameter(DirectContextParameters.POSITION, position)
.withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block)), world, null)) {
world.dropItemNaturally(position, item);
}
}
} else {
@@ -227,43 +272,52 @@ public class BlockEventListener implements Listener {
event.getDrops().clear();
event.setExpToDrop(0);
}
Location location = block.getLocation();
Vec3d vec3d = new Vec3d(location.getBlockX() + 0.5, location.getBlockY() + 0.5, location.getBlockZ() + 0.5);
net.momirealms.craftengine.core.world.World world = new BukkitWorld(location.getWorld());
ContextHolder.Builder builder = ContextHolder.builder();
builder.withParameter(CommonParameters.WORLD, world);
builder.withParameter(CommonParameters.LOCATION, vec3d);
ContextHolder contextHolder = builder.build();
WorldPosition position = new WorldPosition(world, location.getBlockX() + 0.5, location.getBlockY() + 0.5, location.getBlockZ() + 0.5);
ContextHolder.Builder builder = ContextHolder.builder()
.withParameter(DirectContextParameters.POSITION, position)
.withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block));
for (LootTable<?> lootTable : it.lootTables()) {
for (Item<?> item : lootTable.getRandomItems(contextHolder, world, null)) {
world.dropItemNaturally(vec3d, item);
for (Item<?> item : lootTable.getRandomItems(builder.build(), world, null)) {
world.dropItemNaturally(position, item);
}
}
});
}
}
@EventHandler(ignoreCancelled = true)
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOW)
public void onStep(GenericGameEvent event) {
if (event.getEvent() != GameEvent.STEP) return;
Entity entity = event.getEntity();
if (!(entity instanceof Player player)) return;
BlockPos pos = EntityUtils.getOnPos(player);
Location playerLocation = player.getLocation();
BlockData blockData = player.getWorld().getBlockData(pos.x(), pos.y(), pos.z());
Object blockState = BlockStateUtils.blockDataToBlockState(blockData);
Block block = player.getWorld().getBlockAt(pos.x(), pos.y(), pos.z());
Object blockState = BlockStateUtils.blockDataToBlockState(block.getBlockData());
int stateId = BlockStateUtils.blockStateToId(blockState);
if (!BlockStateUtils.isVanillaBlock(stateId)) {
Location location = player.getLocation();
ImmutableBlockState state = manager.getImmutableBlockStateUnsafe(stateId);
player.playSound(playerLocation, state.sounds().stepSound().id().toString(), SoundCategory.BLOCKS, state.sounds().stepSound().volume(), state.sounds().stepSound().pitch());
Cancellable cancellable = Cancellable.dummy();
state.owner().value().execute(PlayerOptionalContext.of(this.plugin.adapt(player), ContextHolder.builder()
.withParameter(DirectContextParameters.EVENT, cancellable)
.withParameter(DirectContextParameters.POSITION, new WorldPosition(new BukkitWorld(event.getWorld()), LocationUtils.toVec3d(location)))
.withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block))
.withParameter(DirectContextParameters.BLOCK_STATE, state)
), EventTrigger.STEP);
if (cancellable.isCancelled()) {
event.setCancelled(true);
return;
}
player.playSound(location, state.sounds().stepSound().id().toString(), SoundCategory.BLOCKS, state.sounds().stepSound().volume(), state.sounds().stepSound().pitch());
} else if (Config.enableSoundSystem()) {
Object ownerBlock = BlockStateUtils.getBlockOwner(blockState);
if (manager.isBlockSoundRemoved(ownerBlock)) {
try {
Object soundType = Reflections.field$BlockBehaviour$soundType.get(ownerBlock);
Object stepSound = Reflections.field$SoundType$stepSound.get(soundType);
player.playSound(playerLocation, FastNMS.INSTANCE.field$SoundEvent$location(stepSound).toString(), SoundCategory.BLOCKS, 0.15f, 1f);
player.playSound(player.getLocation(), FastNMS.INSTANCE.field$SoundEvent$location(stepSound).toString(), SoundCategory.BLOCKS, 0.15f, 1f);
} catch (ReflectiveOperationException e) {
plugin.logger().warn("Failed to get sound type", e);
}
@@ -271,39 +325,6 @@ public class BlockEventListener implements Listener {
}
}
// Use BlockBreakBlock event
// @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
// public void onPistonRetract(BlockPistonRetractEvent event) {
// handlePistonEvent(event.getDirection(), event.getBlocks(), event.getBlock());
// }
//
// @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
// public void onPistonExtend(BlockPistonExtendEvent event) {
// handlePistonEvent(event.getDirection(), event.getBlocks(), event.getBlock());
// }
//
// private void handlePistonEvent(BlockFace face, List<Block> blocksList, Block piston) {
// int blocks = blocksList.size();
// net.momirealms.craftengine.core.world.World world = new BukkitWorld(piston.getWorld());
// for (int i = blocks - 1; i >= 0; --i) {
// Location oldLocation = blocksList.get(i).getLocation();
// BlockPos oldPos = new BlockPos(oldLocation.getBlockX(), oldLocation.getBlockY(), oldLocation.getBlockZ());
// Block block = blocksList.get(i);
// ImmutableBlockState blockState = manager.getImmutableBlockState(BlockStateUtils.blockDataToId(block.getBlockData()));
// if (blockState != null && blockState.pushReaction() == PushReaction.DESTROY) {
// // break actions
// ContextHolder.Builder builder = ContextHolder.builder();
// Vec3d vec3d = Vec3d.atCenterOf(oldPos);
// builder.withParameter(LootParameters.LOCATION, vec3d);
// builder.withParameter(LootParameters.WORLD, world);
// for (Item<Object> item : blockState.getDrops(builder, world)) {
// world.dropItemNaturally(vec3d, item);
// }
// world.playBlockSound(vec3d, blockState.sounds().breakSound());
// }
// }
// }
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
public void onEntityExplode(EntityExplodeEvent event) {
if (VersionHelper.isOrAbove1_21()) {
@@ -327,17 +348,17 @@ public class BlockEventListener implements Listener {
BlockPos blockPos = new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ());
ImmutableBlockState state = manager.getImmutableBlockState(BlockStateUtils.blockDataToId(block.getBlockData()));
if (state != null && !state.isEmpty()) {
ContextHolder.Builder builder = ContextHolder.builder();
Vec3d vec3d = Vec3d.atCenterOf(blockPos);
builder.withParameter(CommonParameters.LOCATION, vec3d);
builder.withParameter(CommonParameters.WORLD, world);
WorldPosition position = new WorldPosition(world, Vec3d.atCenterOf(blockPos));
ContextHolder.Builder builder = ContextHolder.builder()
.withParameter(DirectContextParameters.POSITION, position)
.withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block));
if (yield < 1f) {
builder.withParameter(CommonParameters.EXPLOSION_RADIUS, 1.0f / yield);
builder.withParameter(DirectContextParameters.EXPLOSION_RADIUS, 1.0f / yield);
}
for (Item<Object> item : state.getDrops(builder, world, null)) {
world.dropItemNaturally(vec3d, item);
world.dropItemNaturally(position, item);
}
world.playBlockSound(vec3d, state.sounds().breakSound());
world.playBlockSound(position, state.sounds().breakSound());
}
}
}

View File

@@ -11,10 +11,7 @@ import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.plugin.injector.BukkitInjector;
import net.momirealms.craftengine.bukkit.plugin.network.PacketConsumers;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.KeyUtils;
import net.momirealms.craftengine.bukkit.util.Reflections;
import net.momirealms.craftengine.bukkit.util.RegistryUtils;
import net.momirealms.craftengine.bukkit.util.*;
import net.momirealms.craftengine.core.block.*;
import net.momirealms.craftengine.core.block.properties.Properties;
import net.momirealms.craftengine.core.block.properties.Property;
@@ -25,7 +22,11 @@ import net.momirealms.craftengine.core.pack.ResourceLocation;
import net.momirealms.craftengine.core.pack.model.generation.ModelGeneration;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.config.Config;
import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser;
import net.momirealms.craftengine.core.plugin.config.ConfigParser;
import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext;
import net.momirealms.craftengine.core.plugin.context.function.Function;
import net.momirealms.craftengine.core.plugin.event.EventFunctions;
import net.momirealms.craftengine.core.plugin.event.EventTrigger;
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
import net.momirealms.craftengine.core.plugin.locale.TranslationManager;
import net.momirealms.craftengine.core.registry.BuiltInRegistries;
@@ -35,7 +36,9 @@ import net.momirealms.craftengine.core.util.*;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.block.data.BlockData;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
@@ -88,6 +91,10 @@ public class BukkitBlockManager extends AbstractBlockManager {
private final BlockEventListener blockEventListener;
private final FallingBlockRemoveListener fallingBlockRemoveListener;
private Map<Integer, List<String>> clientBoundTags = Map.of();
private Map<Integer, List<String>> previousTags = Map.of();
protected Object cachedUpdateTagsPacket;
public BukkitBlockManager(BukkitCraftEngine plugin) {
super(plugin);
this.plugin = plugin;
@@ -142,6 +149,8 @@ public class BukkitBlockManager extends AbstractBlockManager {
this.modBlockStates.clear();
if (EmptyBlock.STATE != null)
Arrays.fill(this.stateId2ImmutableBlockStates, EmptyBlock.STATE);
this.previousTags = this.clientBoundTags;
this.clientBoundTags = new HashMap<>();
}
@Override
@@ -161,6 +170,26 @@ public class BukkitBlockManager extends AbstractBlockManager {
initSuggestions();
resetPacketConsumers();
clearCache();
resendTags();
}
private void resendTags() {
// if there's no change
if (this.clientBoundTags.equals(this.previousTags)) return;
List<TagUtils.TagEntry> list = new ArrayList<>();
for (Map.Entry<Integer, List<String>> entry : this.clientBoundTags.entrySet()) {
list.add(new TagUtils.TagEntry(entry.getKey(), entry.getValue()));
}
Object packet = TagUtils.createUpdateTagsPacket(Map.of(Reflections.instance$Registries$BLOCK, list));
for (Player player : Bukkit.getOnlinePlayers()) {
this.plugin.networkManager().sendPacket(player, packet);
}
// 如果空,那么新来的玩家就没必要收到更新包了
if (list.isEmpty()) {
this.cachedUpdateTagsPacket = null;
} else {
this.cachedUpdateTagsPacket = packet;
}
}
private void clearCache() {
@@ -193,7 +222,7 @@ public class BukkitBlockManager extends AbstractBlockManager {
}
@Override
public ConfigSectionParser parser() {
public ConfigParser parser() {
return this.blockParser;
}
@@ -221,7 +250,7 @@ public class BukkitBlockManager extends AbstractBlockManager {
for (int i = 0; i < size; i++) {
states[i] = new PackedBlockState(BlockStateUtils.idToBlockState(i), i);
}
BlockRegistryMirror.init(states);
BlockRegistryMirror.init(states, new PackedBlockState(Reflections.instance$Blocks$STONE$defaultState, BlockStateUtils.blockStateToId(Reflections.instance$Blocks$STONE$defaultState)));
}
private void registerEmptyBlock() {
@@ -314,7 +343,7 @@ public class BukkitBlockManager extends AbstractBlockManager {
}
}
public class BlockParser implements ConfigSectionParser {
public class BlockParser implements ConfigParser {
public static final String[] CONFIG_SECTION_NAME = new String[] {"blocks", "block"};
@Override
@@ -329,10 +358,18 @@ public class BukkitBlockManager extends AbstractBlockManager {
@Override
public void parseSection(Pack pack, Path path, Key id, Map<String, Object> section) {
// check duplicated config
if (byId.containsKey(id)) {
throw new LocalizedResourceConfigException("warning.config.block.duplicate");
if (id.namespace().equals("minecraft") && Registry.MATERIAL.get(KeyUtils.toNamespacedKey(id)) != null) {
parseVanillaBlock(pack, path, id, section);
} else {
// check duplicated config
if (byId.containsKey(id)) {
throw new LocalizedResourceConfigException("warning.config.block.duplicate");
}
parseCustomBlock(pack, path, id, section);
}
}
private void parseCustomBlock(Pack pack, Path path, Key id, Map<String, Object> section) {
// read block settings
BlockSettings settings = BlockSettings.fromMap(MiscUtils.castToMap(section.getOrDefault("settings", Map.of()), false));
@@ -421,15 +458,19 @@ public class BukkitBlockManager extends AbstractBlockManager {
}
}
Object eventsObj = ResourceConfigUtils.get(section, "events", "event");
EnumMap<EventTrigger, List<Function<PlayerOptionalContext>>> events = EventFunctions.parseEvents(eventsObj);
Map<String, Object> behaviors = MiscUtils.castToMap(section.getOrDefault("behavior", Map.of()), false);
CustomBlock block = BukkitCustomBlock.builder(id)
.appearances(appearances)
.variantMapper(variants)
.lootTable(lootTable)
.properties(properties)
.settings(settings)
.behavior(behaviors)
.build();
.appearances(appearances)
.variantMapper(variants)
.lootTable(lootTable)
.properties(properties)
.settings(settings)
.behavior(behaviors)
.events(events)
.build();
// bind appearance and real state
for (ImmutableBlockState state : block.variantProvider().states()) {
@@ -443,7 +484,7 @@ public class BukkitBlockManager extends AbstractBlockManager {
appearanceToRealState.computeIfAbsent(state.vanillaBlockState().registryId(), k -> new ArrayList<>()).add(state.customBlockState().registryId());
}
byId.put(id, block);
BukkitBlockManager.this.byId.put(id, block);
// generate mod assets
if (Config.generateModAssets()) {
@@ -453,6 +494,23 @@ public class BukkitBlockManager extends AbstractBlockManager {
}
}
}
private void parseVanillaBlock(Pack pack, Path path, Key id, Map<String, Object> section) {
Map<String, Object> settings = MiscUtils.castToMap(section.get("settings"), true);
if (settings != null) {
Object clientBoundTags = settings.get("client-bound-tags");
if (clientBoundTags instanceof List<?> list) {
List<String> clientSideTags = MiscUtils.getAsStringList(list).stream().filter(ResourceLocation::isValid).toList();
try {
Object nmsBlock = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$BLOCK, KeyUtils.toResourceLocation(id));
FastNMS.INSTANCE.method$IdMap$getId(Reflections.instance$BuiltInRegistries$BLOCK, nmsBlock).ifPresent(i ->
BukkitBlockManager.this.clientBoundTags.put(i, clientSideTags));
} catch (ReflectiveOperationException e) {
BukkitBlockManager.this.plugin.logger().warn("Unable to get block " + id, e);
}
}
}
}
}
private Map<String, Property<?>> parseProperties(Map<String, Object> propertiesSection) {
@@ -537,7 +595,7 @@ public class BukkitBlockManager extends AbstractBlockManager {
if (singleModelMap.containsKey("weight")) json.addProperty("weight", ResourceConfigUtils.getAsInt(singleModelMap.get("weight"), "weight"));
Map<String, Object> generationMap = MiscUtils.castToMap(singleModelMap.get("generation"), true);
if (generationMap != null) {
prepareModelGeneration(new ModelGeneration(Key.of(modelPath), generationMap));
prepareModelGeneration(ModelGeneration.of(Key.of(modelPath), generationMap));
}
variants.add(json);
}

View File

@@ -9,6 +9,9 @@ import net.momirealms.craftengine.core.block.*;
import net.momirealms.craftengine.core.block.properties.Property;
import net.momirealms.craftengine.core.loot.LootTable;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext;
import net.momirealms.craftengine.core.plugin.context.function.Function;
import net.momirealms.craftengine.core.plugin.event.EventTrigger;
import net.momirealms.craftengine.core.registry.BuiltInRegistries;
import net.momirealms.craftengine.core.registry.Holder;
import net.momirealms.craftengine.core.registry.WritableRegistry;
@@ -19,12 +22,11 @@ import net.momirealms.craftengine.core.util.VersionHelper;
import net.momirealms.craftengine.shared.ObjectHolder;
import net.momirealms.craftengine.shared.block.BlockBehavior;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.*;
public class BukkitCustomBlock extends CustomBlock {
@@ -35,10 +37,11 @@ public class BukkitCustomBlock extends CustomBlock {
Map<String, Integer> appearances,
Map<String, VariantState> variantMapper,
BlockSettings settings,
Map<String, Object> behavior,
@NotNull EnumMap<EventTrigger, List<Function<PlayerOptionalContext>>> events,
@Nullable Map<String, Object> behavior,
@Nullable LootTable<?> lootTable
) {
super(id, holder, properties, appearances, variantMapper, settings, behavior, lootTable);
super(id, holder, properties, appearances, variantMapper, settings, events, behavior, lootTable);
}
@SuppressWarnings("unchecked")
@@ -158,7 +161,7 @@ public class BukkitCustomBlock extends CustomBlock {
// create or get block holder
Holder.Reference<CustomBlock> holder = BuiltInRegistries.BLOCK.get(id).orElseGet(() ->
((WritableRegistry<CustomBlock>) BuiltInRegistries.BLOCK).registerForHolder(new ResourceKey<>(BuiltInRegistries.BLOCK.key().location(), id)));
return new BukkitCustomBlock(id, holder, properties, appearances, variantMapper, settings, behavior, lootTable);
return new BukkitCustomBlock(id, holder, properties, appearances, variantMapper, settings, events, behavior, lootTable);
}
}
}

View File

@@ -7,19 +7,14 @@ import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.context.ContextHolder;
import net.momirealms.craftengine.core.plugin.context.parameter.CommonParameters;
import net.momirealms.craftengine.core.world.Vec3d;
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
import net.momirealms.craftengine.core.world.WorldPosition;
import org.bukkit.entity.FallingBlock;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
public class FallingBlockRemoveListener implements Listener {
/*
* This is not an event that would be removed
* Paper mistakenly marked it as deprecated in 1.20.4
*/
@SuppressWarnings("removal")
@EventHandler
public void onFallingBlockBreak(org.bukkit.event.entity.EntityRemoveEvent event) {
if (event.getCause() == org.bukkit.event.entity.EntityRemoveEvent.Cause.DROP && event.getEntity() instanceof FallingBlock fallingBlock) {
@@ -31,22 +26,18 @@ public class FallingBlockRemoveListener implements Listener {
int stateId = BlockStateUtils.blockStateToId(blockState);
ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(stateId);
if (immutableBlockState == null || immutableBlockState.isEmpty()) return;
ContextHolder.Builder builder = ContextHolder.builder();
builder.withParameter(CommonParameters.FALLING_BLOCK, true);
double x = Reflections.field$Entity$xo.getDouble(fallingBlockEntity);
double y = Reflections.field$Entity$yo.getDouble(fallingBlockEntity);
double z = Reflections.field$Entity$zo.getDouble(fallingBlockEntity);
Vec3d vec3d = new Vec3d(x, y, z);
net.momirealms.craftengine.core.world.World world = new BukkitWorld(fallingBlock.getWorld());
builder.withParameter(CommonParameters.LOCATION, vec3d);
builder.withParameter(CommonParameters.WORLD, world);
WorldPosition position = new WorldPosition(world, Reflections.field$Entity$xo.getDouble(fallingBlockEntity), Reflections.field$Entity$yo.getDouble(fallingBlockEntity), Reflections.field$Entity$zo.getDouble(fallingBlockEntity));
ContextHolder.Builder builder = ContextHolder.builder()
.withParameter(DirectContextParameters.FALLING_BLOCK, true)
.withParameter(DirectContextParameters.POSITION, position);
for (Item<Object> item : immutableBlockState.getDrops(builder, world, null)) {
world.dropItemNaturally(vec3d, item);
world.dropItemNaturally(position, item);
}
Object entityData = Reflections.field$Entity$entityData.get(fallingBlockEntity);
boolean isSilent = (boolean) Reflections.method$SynchedEntityData$get.invoke(entityData, Reflections.instance$Entity$DATA_SILENT);
if (!isSilent) {
world.playBlockSound(vec3d, immutableBlockState.sounds().destroySound());
world.playBlockSound(position, immutableBlockState.sounds().destroySound());
}
} catch (ReflectiveOperationException e) {
CraftEngine.instance().logger().warn("Failed to handle EntityRemoveEvent", e);

View File

@@ -12,7 +12,7 @@ import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.plugin.context.ContextHolder;
import net.momirealms.craftengine.core.plugin.context.parameter.CommonParameters;
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.MiscUtils;
import net.momirealms.craftengine.core.util.Tuple;
@@ -20,6 +20,7 @@ import net.momirealms.craftengine.core.util.VersionHelper;
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.core.world.WorldPosition;
import net.momirealms.craftengine.shared.block.BlockBehavior;
import org.bukkit.Bukkit;
import org.bukkit.Material;
@@ -67,16 +68,15 @@ public class BushBlockBehavior extends BukkitBlockBehavior {
int stateId = BlockStateUtils.blockStateToId(state);
ImmutableBlockState previousState = BukkitBlockManager.instance().getImmutableBlockState(stateId);
if (previousState != null && !previousState.isEmpty()) {
ContextHolder.Builder builder = ContextHolder.builder();
BlockPos pos = LocationUtils.fromBlockPos(blockPos);
Vec3d vec3d = Vec3d.atCenterOf(pos);
net.momirealms.craftengine.core.world.World world = new BukkitWorld(FastNMS.INSTANCE.method$Level$getCraftWorld(level));
builder.withParameter(CommonParameters.LOCATION, vec3d);
builder.withParameter(CommonParameters.WORLD, world);
WorldPosition position = new WorldPosition(world, Vec3d.atCenterOf(pos));
ContextHolder.Builder builder = ContextHolder.builder()
.withParameter(DirectContextParameters.POSITION, position);
for (Item<Object> item : previousState.getDrops(builder, world, null)) {
world.dropItemNaturally(vec3d, item);
world.dropItemNaturally(position, item);
}
world.playBlockSound(vec3d, previousState.sounds().breakSound());
world.playBlockSound(position, previousState.sounds().breakSound());
FastNMS.INSTANCE.method$Level$levelEvent(level, WorldEvents.BLOCK_BREAK_EFFECT, blockPos, stateId);
}
return Reflections.method$Block$defaultBlockState.invoke(Reflections.instance$Blocks$AIR);

View File

@@ -16,12 +16,13 @@ import net.momirealms.craftengine.core.plugin.context.ContextHolder;
import net.momirealms.craftengine.core.plugin.context.SimpleContext;
import net.momirealms.craftengine.core.plugin.context.number.NumberProvider;
import net.momirealms.craftengine.core.plugin.context.number.NumberProviders;
import net.momirealms.craftengine.core.plugin.context.parameter.CommonParameters;
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
import net.momirealms.craftengine.core.util.RandomUtils;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.util.Tuple;
import net.momirealms.craftengine.core.world.Vec3d;
import net.momirealms.craftengine.core.world.Vec3i;
import net.momirealms.craftengine.core.world.WorldPosition;
import net.momirealms.craftengine.shared.block.BlockBehavior;
import org.bukkit.World;
@@ -148,8 +149,8 @@ public class CropBlockBehavior extends BushBlockBehavior {
int i = this.getAge(immutableBlockState) + this.boneMealBonus.getInt(
SimpleContext.of(
ContextHolder.builder()
.withParameter(CommonParameters.WORLD, new BukkitWorld(world))
.withParameter(CommonParameters.LOCATION, Vec3d.atCenterOf(new Vec3i(x, y, z)))
.withParameter(DirectContextParameters.BLOCK_STATE, immutableBlockState)
.withParameter(DirectContextParameters.POSITION, new WorldPosition(new BukkitWorld(world), Vec3d.atCenterOf(new Vec3i(x, y, z))))
.build()
)
);
@@ -159,7 +160,7 @@ public class CropBlockBehavior extends BushBlockBehavior {
}
FastNMS.INSTANCE.method$LevelWriter$setBlock(level, pos, immutableBlockState.with(this.ageProperty, i).customBlockState().handle(), UpdateOption.UPDATE_ALL.flags());
if (sendParticles) {
world.spawnParticle(ParticleUtils.getParticle("HAPPY_VILLAGER"), x + 0.5, y + 0.5, z + 0.5, 15, 0.25, 0.25, 0.25);
world.spawnParticle(ParticleUtils.HAPPY_VILLAGER, x + 0.5, y + 0.5, z + 0.5, 15, 0.25, 0.25, 0.25);
}
}

View File

@@ -11,10 +11,11 @@ import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.plugin.context.ContextHolder;
import net.momirealms.craftengine.core.plugin.context.parameter.CommonParameters;
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.util.VersionHelper;
import net.momirealms.craftengine.core.world.Vec3d;
import net.momirealms.craftengine.core.world.WorldPosition;
import net.momirealms.craftengine.shared.block.BlockBehavior;
import java.util.Map;
@@ -90,22 +91,18 @@ public class FallingBlockBehavior extends BukkitBlockBehavior {
int stateId = BlockStateUtils.blockStateToId(blockState);
ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(stateId);
if (immutableBlockState == null || immutableBlockState.isEmpty()) return;
ContextHolder.Builder builder = ContextHolder.builder();
builder.withParameter(CommonParameters.FALLING_BLOCK, true);
double x = Reflections.field$Entity$xo.getDouble(fallingBlockEntity);
double y = Reflections.field$Entity$yo.getDouble(fallingBlockEntity);
double z = Reflections.field$Entity$zo.getDouble(fallingBlockEntity);
Vec3d vec3d = new Vec3d(x, y, z);
net.momirealms.craftengine.core.world.World world = new BukkitWorld(FastNMS.INSTANCE.method$Level$getCraftWorld(level));
builder.withParameter(CommonParameters.LOCATION, vec3d);
builder.withParameter(CommonParameters.WORLD, world);
WorldPosition position = new WorldPosition(world, Reflections.field$Entity$xo.getDouble(fallingBlockEntity), Reflections.field$Entity$yo.getDouble(fallingBlockEntity), Reflections.field$Entity$zo.getDouble(fallingBlockEntity));
ContextHolder.Builder builder = ContextHolder.builder()
.withParameter(DirectContextParameters.FALLING_BLOCK, true)
.withParameter(DirectContextParameters.POSITION, position);
for (Item<Object> item : immutableBlockState.getDrops(builder, world, null)) {
world.dropItemNaturally(vec3d, item);
world.dropItemNaturally(position, item);
}
Object entityData = Reflections.field$Entity$entityData.get(fallingBlockEntity);
boolean isSilent = (boolean) Reflections.method$SynchedEntityData$get.invoke(entityData, Reflections.instance$Entity$DATA_SILENT);
if (!isSilent) {
world.playBlockSound(vec3d, immutableBlockState.sounds().destroySound());
world.playBlockSound(position, immutableBlockState.sounds().destroySound());
}
}

View File

@@ -52,7 +52,7 @@ public class GrassBlockBehavior extends BukkitBlockBehavior {
int x = FastNMS.INSTANCE.field$Vec3i$x(blockPos);
int y = FastNMS.INSTANCE.field$Vec3i$y(blockPos);
int z = FastNMS.INSTANCE.field$Vec3i$z(blockPos);
world.spawnParticle(ParticleUtils.getParticle("HAPPY_VILLAGER"), x + 0.5, y + 1.5, z + 0.5, 20, 2, 0, 2);
world.spawnParticle(ParticleUtils.HAPPY_VILLAGER, x + 0.5, y + 1.5, z + 0.5, 20, 2, 0, 2);
}
return true;
}

View File

@@ -14,13 +14,14 @@ import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
import net.momirealms.craftengine.core.block.properties.Property;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.plugin.context.ContextHolder;
import net.momirealms.craftengine.core.plugin.context.parameter.CommonParameters;
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
import net.momirealms.craftengine.core.util.Direction;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.util.VersionHelper;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.Vec3d;
import net.momirealms.craftengine.core.world.WorldPosition;
import net.momirealms.craftengine.shared.block.BlockBehavior;
import org.bukkit.Bukkit;
import org.bukkit.Material;
@@ -122,13 +123,12 @@ public class LeavesBlockBehavior extends WaterLoggedBlockBehavior {
if (isWaterLogged(immutableBlockState)) {
bukkitWorld.setBlockData(pos.x(), pos.y(), pos.z(), Material.WATER.createBlockData());
}
Vec3d vec3d = Vec3d.atCenterOf(pos);
net.momirealms.craftengine.core.world.World world = new BukkitWorld(bukkitWorld);
WorldPosition position = new WorldPosition(world, Vec3d.atCenterOf(pos));
ContextHolder.Builder builder = ContextHolder.builder()
.withParameter(CommonParameters.LOCATION, vec3d)
.withParameter(CommonParameters.WORLD, world);
.withParameter(DirectContextParameters.POSITION, position);
for (Item<Object> item : immutableBlockState.getDrops(builder, world, null)) {
world.dropItemNaturally(vec3d, item);
world.dropItemNaturally(position, item);
}
}
}

View File

@@ -120,7 +120,7 @@ public class SaplingBlockBehavior extends BushBlockBehavior {
int x = FastNMS.INSTANCE.field$Vec3i$x(blockPos);
int y = FastNMS.INSTANCE.field$Vec3i$y(blockPos);
int z = FastNMS.INSTANCE.field$Vec3i$z(blockPos);
world.spawnParticle(ParticleUtils.getParticle("HAPPY_VILLAGER"), x + 0.5, y + 0.5, z + 0.5, 15, 0.25, 0.25, 0.25);
world.spawnParticle(ParticleUtils.HAPPY_VILLAGER, x + 0.5, y + 0.5, z + 0.5, 15, 0.25, 0.25, 0.25);
}
return success;
}

View File

@@ -14,11 +14,12 @@ import net.momirealms.craftengine.core.block.properties.IntegerProperty;
import net.momirealms.craftengine.core.block.properties.Property;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.plugin.context.ContextHolder;
import net.momirealms.craftengine.core.plugin.context.parameter.CommonParameters;
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
import net.momirealms.craftengine.core.util.*;
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.core.world.WorldPosition;
import net.momirealms.craftengine.shared.block.BlockBehavior;
import java.util.List;
@@ -58,15 +59,14 @@ public class SugarCaneBlockBehavior extends BushBlockBehavior {
if (currentState != null && !currentState.isEmpty()) {
// break the sugar cane
FastNMS.INSTANCE.method$Level$removeBlock(level, blockPos, false);
Vec3d vec3d = Vec3d.atCenterOf(LocationUtils.fromBlockPos(blockPos));
net.momirealms.craftengine.core.world.World world = new BukkitWorld(FastNMS.INSTANCE.method$Level$getCraftWorld(level));
WorldPosition position = new WorldPosition(world, Vec3d.atCenterOf(LocationUtils.fromBlockPos(blockPos)));
ContextHolder.Builder builder = ContextHolder.builder()
.withParameter(CommonParameters.LOCATION, vec3d)
.withParameter(CommonParameters.WORLD, world);
.withParameter(DirectContextParameters.POSITION, position);
for (Item<Object> item : currentState.getDrops(builder, world, null)) {
world.dropItemNaturally(vec3d, item);
world.dropItemNaturally(position, item);
}
world.playBlockSound(vec3d, currentState.sounds().breakSound());
world.playBlockSound(position, currentState.sounds().breakSound());
FastNMS.INSTANCE.method$Level$levelEvent(level, WorldEvents.BLOCK_BREAK_EFFECT, blockPos, stateId);
}
}

View File

@@ -2,14 +2,15 @@ package net.momirealms.craftengine.bukkit.entity;
import net.momirealms.craftengine.bukkit.util.KeyUtils;
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
import net.momirealms.craftengine.core.entity.Entity;
import net.momirealms.craftengine.core.entity.AbstractEntity;
import net.momirealms.craftengine.core.util.Direction;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.world.World;
import java.lang.ref.WeakReference;
import java.util.UUID;
public class BukkitEntity extends Entity {
public class BukkitEntity extends AbstractEntity {
private final WeakReference<org.bukkit.entity.Entity> entity;
public BukkitEntity(org.bukkit.entity.Entity entity) {
@@ -18,17 +19,17 @@ public class BukkitEntity extends Entity {
@Override
public double x() {
return literalObject().getLocation().getX();
return literalObject().getX();
}
@Override
public double y() {
return literalObject().getLocation().getY();
return literalObject().getY();
}
@Override
public double z() {
return literalObject().getLocation().getZ();
return literalObject().getZ();
}
@Override
@@ -42,12 +43,12 @@ public class BukkitEntity extends Entity {
@Override
public float getXRot() {
return literalObject().getLocation().getYaw();
return literalObject().getYaw();
}
@Override
public float getYRot() {
return literalObject().getLocation().getPitch();
return literalObject().getPitch();
}
@Override
@@ -69,4 +70,14 @@ public class BukkitEntity extends Entity {
public Key type() {
return KeyUtils.namespacedKey2Key(literalObject().getType().getKey());
}
@Override
public String name() {
return literalObject().getName();
}
@Override
public UUID uuid() {
return literalObject().getUniqueId();
}
}

View File

@@ -3,7 +3,6 @@ package net.momirealms.craftengine.bukkit.entity.data;
import net.momirealms.craftengine.core.util.VersionHelper;
public class InteractionEntityData<T> extends BaseEntityData<T> {
// Interaction only
public static final InteractionEntityData<Float> Width = of(8, EntityDataValue.Serializers$FLOAT, 1F);
public static final InteractionEntityData<Float> Height = of(9, EntityDataValue.Serializers$FLOAT, 1F);
public static final InteractionEntityData<Boolean> Responsive = of(10, EntityDataValue.Serializers$BOOLEAN, false);

View File

@@ -3,7 +3,6 @@ package net.momirealms.craftengine.bukkit.entity.data;
import net.momirealms.craftengine.bukkit.util.Reflections;
public class TextDisplayEntityData<T> extends DisplayEntityData<T> {
// Text display only
public static final DisplayEntityData<Object> Text = of(23, EntityDataValue.Serializers$COMPONENT, Reflections.instance$Component$empty);
public static final DisplayEntityData<Integer> LineWidth = of(24, EntityDataValue.Serializers$INT, 200);
public static final DisplayEntityData<Integer> BackgroundColor = of(25, EntityDataValue.Serializers$INT, 0x40000000);

View File

@@ -0,0 +1,11 @@
package net.momirealms.craftengine.bukkit.entity.data;
import net.momirealms.craftengine.bukkit.util.Reflections;
public class ThrowableItemProjectileData<T> extends BaseEntityData<T> {
public static final ThrowableItemProjectileData<Object> ItemStack = new ThrowableItemProjectileData<>(8, EntityDataValue.Serializers$ITEM_STACK, Reflections.instance$ItemStack$Air);
public ThrowableItemProjectileData(int id, Object serializer, T defaultValue) {
super(id, serializer, defaultValue);
}
}

View File

@@ -4,15 +4,16 @@ import net.momirealms.craftengine.bukkit.entity.data.ItemDisplayEntityData;
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.util.Reflections;
import net.momirealms.craftengine.core.entity.Billboard;
import net.momirealms.craftengine.core.entity.ItemDisplayContext;
import net.momirealms.craftengine.core.entity.furniture.AbstractFurnitureElement;
import net.momirealms.craftengine.core.entity.furniture.Billboard;
import net.momirealms.craftengine.core.entity.furniture.ItemDisplayContext;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.world.World;
import net.momirealms.craftengine.core.world.WorldPosition;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.joml.Quaternionf;
import org.joml.Vector3f;
@@ -22,7 +23,7 @@ import java.util.UUID;
import java.util.function.Consumer;
public class BukkitFurnitureElement extends AbstractFurnitureElement {
private List<Object> cachedValues;
private final List<Object> commonValues;
public BukkitFurnitureElement(Key item,
Billboard billboard,
@@ -30,35 +31,40 @@ public class BukkitFurnitureElement extends AbstractFurnitureElement {
Vector3f scale,
Vector3f translation,
Vector3f offset,
Quaternionf rotation) {
super(item, billboard, transform, scale, translation, offset, rotation);
Quaternionf rotation,
boolean applyDyedColor) {
super(item, billboard, transform, scale, translation, offset, rotation, applyDyedColor);
this.commonValues = new ArrayList<>();
ItemDisplayEntityData.Scale.addEntityDataIfNotDefaultValue(scale(), this.commonValues);
ItemDisplayEntityData.RotationLeft.addEntityDataIfNotDefaultValue(rotation(), this.commonValues);
ItemDisplayEntityData.BillboardConstraints.addEntityDataIfNotDefaultValue(billboard().id(), this.commonValues);
ItemDisplayEntityData.Translation.addEntityDataIfNotDefaultValue(translation(), this.commonValues);
ItemDisplayEntityData.DisplayType.addEntityDataIfNotDefaultValue(transform().id(), this.commonValues);
}
@Override
public void initPackets(int entityId, World world, double x, double y, double z, float yaw, Quaternionf conjugated, Consumer<Object> packets) {
public void initPackets(int entityId, @NotNull WorldPosition position, @NotNull Quaternionf conjugated, Integer dyedColor, Consumer<Object> packets) {
Vector3f offset = conjugated.transform(new Vector3f(position()));
packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
entityId, UUID.randomUUID(), x + offset.x, y + offset.y, z - offset.z, 0, yaw,
entityId, UUID.randomUUID(), position.x() + offset.x, position.y() + offset.y, position.z() - offset.z, 0, position.xRot(),
Reflections.instance$EntityType$ITEM_DISPLAY, 0, Reflections.instance$Vec3$Zero, 0
));
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityId, getCachedValues()));
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityId, getCachedValues(dyedColor)));
}
private synchronized List<Object> getCachedValues() {
if (this.cachedValues == null) {
this.cachedValues = new ArrayList<>();
Item<ItemStack> item = BukkitItemManager.instance().createWrappedItem(item(), null);
if (item == null) {
CraftEngine.instance().logger().warn("Failed to create furniture element for " + item() + " because item " + item() + " not found");
item = BukkitItemManager.instance().wrap(new ItemStack(Material.STONE));
private synchronized List<Object> getCachedValues(Integer color) {
List<Object> cachedValues = new ArrayList<>(this.commonValues);
Item<ItemStack> item = BukkitItemManager.instance().createWrappedItem(item(), null);
if (item == null) {
CraftEngine.instance().debug(() -> "Failed to create furniture element because item " + item() + " not found");
item = BukkitItemManager.instance().wrap(new ItemStack(Material.BARRIER));
} else {
if (color != null) {
item.dyedColor(color);
item.load();
}
ItemDisplayEntityData.DisplayedItem.addEntityDataIfNotDefaultValue(item.getLiteralObject(), this.cachedValues);
ItemDisplayEntityData.Scale.addEntityDataIfNotDefaultValue(scale(), this.cachedValues);
ItemDisplayEntityData.RotationLeft.addEntityDataIfNotDefaultValue(rotation(), this.cachedValues);
ItemDisplayEntityData.BillboardConstraints.addEntityDataIfNotDefaultValue(billboard().id(), this.cachedValues);
ItemDisplayEntityData.Translation.addEntityDataIfNotDefaultValue(translation(), this.cachedValues);
ItemDisplayEntityData.DisplayType.addEntityDataIfNotDefaultValue(transform().id(), this.cachedValues);
}
return this.cachedValues;
ItemDisplayEntityData.DisplayedItem.addEntityDataIfNotDefaultValue(item.getLiteralObject(), cachedValues);
return cachedValues;
}
}

View File

@@ -4,20 +4,29 @@ import net.momirealms.craftengine.bukkit.entity.furniture.hitbox.InteractionHitB
import net.momirealms.craftengine.bukkit.nms.CollisionEntity;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.plugin.network.handler.FurniturePacketHandler;
import net.momirealms.craftengine.bukkit.util.EntityUtils;
import net.momirealms.craftengine.bukkit.util.LocationUtils;
import net.momirealms.craftengine.bukkit.util.Reflections;
import net.momirealms.craftengine.core.entity.Billboard;
import net.momirealms.craftengine.core.entity.ItemDisplayContext;
import net.momirealms.craftengine.core.entity.furniture.*;
import net.momirealms.craftengine.core.loot.LootTable;
import net.momirealms.craftengine.core.pack.LoadingSequence;
import net.momirealms.craftengine.core.pack.Pack;
import net.momirealms.craftengine.core.plugin.config.Config;
import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser;
import net.momirealms.craftengine.core.plugin.config.ConfigParser;
import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext;
import net.momirealms.craftengine.core.plugin.context.function.Function;
import net.momirealms.craftengine.core.plugin.event.EventFunctions;
import net.momirealms.craftengine.core.plugin.event.EventTrigger;
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
import net.momirealms.craftengine.core.sound.SoundData;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.MiscUtils;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.util.VersionHelper;
import net.momirealms.craftengine.core.world.Vec3d;
import net.momirealms.craftengine.core.world.WorldPosition;
import org.bukkit.*;
import org.bukkit.entity.*;
import org.bukkit.event.HandlerList;
@@ -28,13 +37,16 @@ import org.jetbrains.annotations.NotNull;
import org.joml.Vector3f;
import javax.annotation.Nullable;
import java.io.IOException;
import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class BukkitFurnitureManager extends AbstractFurnitureManager {
public static final NamespacedKey FURNITURE_KEY = Objects.requireNonNull(NamespacedKey.fromString("craftengine:furniture_id"));
public static final NamespacedKey FURNITURE_ANCHOR_KEY = Objects.requireNonNull(NamespacedKey.fromString("craftengine:anchor_type"));
// DEPRECATED
// public static final NamespacedKey FURNITURE_ANCHOR_KEY = Objects.requireNonNull(NamespacedKey.fromString("craftengine:anchor_type"));
public static final NamespacedKey FURNITURE_EXTRA_DATA_KEY = Objects.requireNonNull(NamespacedKey.fromString("craftengine:furniture_extra_data"));
public static final NamespacedKey FURNITURE_SEAT_BASE_ENTITY_KEY = Objects.requireNonNull(NamespacedKey.fromString("craftengine:seat_to_base_entity"));
public static final NamespacedKey FURNITURE_SEAT_VECTOR_3F_KEY = Objects.requireNonNull(NamespacedKey.fromString("craftengine:seat_vector"));
public static final NamespacedKey FURNITURE_COLLISION = Objects.requireNonNull(NamespacedKey.fromString("craftengine:collision"));
@@ -63,19 +75,23 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
}
@Override
public Furniture place(CustomFurniture furniture, Vec3d vec3d, net.momirealms.craftengine.core.world.World world, AnchorType anchorType, boolean playSound) {
return this.place(furniture, new Location((World) world.platformWorld(), vec3d.x(), vec3d.y(), vec3d.z()), anchorType, playSound);
public Furniture place(WorldPosition position, CustomFurniture furniture, FurnitureExtraData extraData, boolean playSound) {
return this.place(LocationUtils.toLocation(position), furniture, extraData, playSound);
}
public LoadedFurniture place(CustomFurniture furniture, Location location, AnchorType anchorType, boolean playSound) {
if (furniture.isAllowedPlacement(anchorType)) {
anchorType = furniture.getAnyPlacement();
public LoadedFurniture place(Location location, CustomFurniture furniture, FurnitureExtraData extraData, boolean playSound) {
Optional<AnchorType> optionalAnchorType = extraData.anchorType();
if (optionalAnchorType.isEmpty() || !furniture.isAllowedPlacement(optionalAnchorType.get())) {
extraData.anchorType(furniture.getAnyPlacement());
}
AnchorType finalAnchorType = anchorType;
Entity furnitureEntity = EntityUtils.spawnEntity(location.getWorld(), location, EntityType.ITEM_DISPLAY, entity -> {
ItemDisplay display = (ItemDisplay) entity;
display.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_KEY, PersistentDataType.STRING, furniture.id().toString());
display.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_ANCHOR_KEY, PersistentDataType.STRING, finalAnchorType.name());
try {
display.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_EXTRA_DATA_KEY, PersistentDataType.BYTE_ARRAY, extraData.toBytes());
} catch (IOException e) {
this.plugin.logger().warn("Failed to set furniture PDC for " + furniture.id().toString(), e);
}
handleBaseEntityLoadEarly(display);
});
if (playSound) {
@@ -86,11 +102,11 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
}
@Override
public ConfigSectionParser parser() {
public ConfigParser parser() {
return this.furnitureParser;
}
public class FurnitureParser implements ConfigSectionParser {
public class FurnitureParser implements ConfigParser {
public static final String[] CONFIG_SECTION_NAME = new String[] { "furniture" };
@Override
@@ -124,6 +140,8 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
AnchorType anchorType = AnchorType.valueOf(entry.getKey().toUpperCase(Locale.ENGLISH));
Map<String, Object> placementArguments = MiscUtils.castToMap(entry.getValue(), true);
Optional<Vector3f> optionalLootSpawnOffset = Optional.ofNullable(placementArguments.get("loot-spawn-offset")).map(it -> MiscUtils.getAsVector3f(it, "loot-spawn-offset"));
// furniture display elements
List<FurnitureElement> elements = new ArrayList<>();
List<Map<String, Object>> elementConfigs = (List<Map<String, Object>>) placementArguments.getOrDefault("elements", List.of());
@@ -138,7 +156,8 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
MiscUtils.getAsVector3f(element.getOrDefault("scale", "1"), "scale"),
MiscUtils.getAsVector3f(element.getOrDefault("translation", "0"), "translation"),
MiscUtils.getAsVector3f(element.getOrDefault("position", "0"), "position"),
MiscUtils.getAsQuaternionf(element.getOrDefault("rotation", "0"), "rotation")
MiscUtils.getAsQuaternionf(element.getOrDefault("rotation", "0"), "rotation"),
(boolean) element.getOrDefault("apply-dyed-color", true)
);
elements.add(furnitureElement);
}
@@ -178,7 +197,8 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
hitboxes.toArray(new HitBox[0]),
rotationRule,
alignmentRule,
externalModel
externalModel,
optionalLootSpawnOffset
));
} else {
placements.put(anchorType, new CustomFurniture.Placement(
@@ -186,7 +206,8 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
hitboxes.toArray(new HitBox[0]),
RotationRule.ANY,
AlignmentRule.CENTER,
externalModel
externalModel,
optionalLootSpawnOffset
));
}
}
@@ -196,7 +217,8 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
// get loot table
LootTable<ItemStack> lootTable = lootMap == null ? null : LootTable.fromMap(lootMap);
CustomFurniture furniture = new CustomFurniture(id, settings, placements, lootTable);
EnumMap<EventTrigger, List<Function<PlayerOptionalContext>>> events = EventFunctions.parseEvents(ResourceConfigUtils.get(section, "events", "event"));
CustomFurniture furniture = new CustomFurniture(id, settings, placements, events, lootTable);
byId.put(id, furniture);
}
}
@@ -217,9 +239,6 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
handleCollisionEntityLoadOnEntitiesLoad(interaction);
} else if (entity instanceof Boat boat) {
handleCollisionEntityLoadOnEntitiesLoad(boat);
} else if (entity instanceof Shulker shulker) {
// TODO 移除这一行,预计过一个月
handleCollisionEntityLoadOnEntitiesLoad(shulker);
}
}
}
@@ -294,17 +313,17 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
boolean preventChange = FastNMS.INSTANCE.isPreventingStatusUpdates(location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4);
if (above1_20_1) {
if (!preventChange) {
LoadedFurniture furniture = addNewFurniture(display, customFurniture, getAnchorType(display, customFurniture));
LoadedFurniture furniture = addNewFurniture(display, customFurniture);
furniture.initializeColliders();
for (Player player : display.getTrackedPlayers()) {
this.plugin.adapt(player).furnitureView().computeIfAbsent(furniture.baseEntityId(), k -> new ArrayList<>()).addAll(furniture.fakeEntityIds());
this.plugin.adapt(player).entityPacketHandlers().computeIfAbsent(furniture.baseEntityId(), k -> new FurniturePacketHandler(furniture.fakeEntityIds()));
this.plugin.networkManager().sendPacket(player, furniture.spawnPacket(player));
}
}
} else {
LoadedFurniture furniture = addNewFurniture(display, customFurniture, getAnchorType(display, customFurniture));
LoadedFurniture furniture = addNewFurniture(display, customFurniture);
for (Player player : display.getTrackedPlayers()) {
this.plugin.adapt(player).furnitureView().computeIfAbsent(furniture.baseEntityId(), k -> new ArrayList<>()).addAll(furniture.fakeEntityIds());
this.plugin.adapt(player).entityPacketHandlers().computeIfAbsent(furniture.baseEntityId(), k -> new FurniturePacketHandler(furniture.fakeEntityIds()));
this.plugin.networkManager().sendPacket(player, furniture.spawnPacket(player));
}
if (preventChange) {
@@ -366,7 +385,7 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
CustomFurniture customFurniture = optionalFurniture.get();
LoadedFurniture previous = this.furnitureByRealEntityId.get(display.getEntityId());
if (previous != null) return;
LoadedFurniture furniture = addNewFurniture(display, customFurniture, getAnchorType(display, customFurniture));
LoadedFurniture furniture = addNewFurniture(display, customFurniture);
furniture.initializeColliders(); // safely do it here
}
}
@@ -387,24 +406,37 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
collisionEntity.remove();
}
private AnchorType getAnchorType(Entity baseEntity, CustomFurniture furniture) {
String anchorType = baseEntity.getPersistentDataContainer().get(FURNITURE_ANCHOR_KEY, PersistentDataType.STRING);
if (anchorType != null) {
try {
AnchorType unverified = AnchorType.valueOf(anchorType);
if (furniture.isAllowedPlacement(unverified)) {
return unverified;
}
} catch (IllegalArgumentException ignored) {
}
}
AnchorType anchorTypeEnum = furniture.getAnyPlacement();
baseEntity.getPersistentDataContainer().set(FURNITURE_ANCHOR_KEY, PersistentDataType.STRING, anchorTypeEnum.name());
return anchorTypeEnum;
private FurnitureExtraData getFurnitureExtraData(Entity baseEntity) throws IOException {
byte[] extraData = baseEntity.getPersistentDataContainer().get(FURNITURE_EXTRA_DATA_KEY, PersistentDataType.BYTE_ARRAY);
if (extraData == null) return FurnitureExtraData.builder().build();
return FurnitureExtraData.fromBytes(extraData);
}
private synchronized LoadedFurniture addNewFurniture(ItemDisplay display, CustomFurniture furniture, AnchorType anchorType) {
LoadedFurniture loadedFurniture = new LoadedFurniture(display, furniture, anchorType);
// private AnchorType getAnchorType(Entity baseEntity, CustomFurniture furniture) {
// String anchorType = baseEntity.getPersistentDataContainer().get(FURNITURE_ANCHOR_KEY, PersistentDataType.STRING);
// if (anchorType != null) {
// try {
// AnchorType unverified = AnchorType.valueOf(anchorType);
// if (furniture.isAllowedPlacement(unverified)) {
// return unverified;
// }
// } catch (IllegalArgumentException ignored) {
// }
// }
// AnchorType anchorTypeEnum = furniture.getAnyPlacement();
// baseEntity.getPersistentDataContainer().set(FURNITURE_ANCHOR_KEY, PersistentDataType.STRING, anchorTypeEnum.name());
// return anchorTypeEnum;
// }
private synchronized LoadedFurniture addNewFurniture(ItemDisplay display, CustomFurniture furniture) {
FurnitureExtraData extraData;
try {
extraData = getFurnitureExtraData(display);
} catch (IOException e) {
extraData = FurnitureExtraData.builder().build();
plugin.logger().warn("Furniture extra data could not be loaded", e);
}
LoadedFurniture loadedFurniture = new LoadedFurniture(display, furniture, extraData);
this.furnitureByRealEntityId.put(loadedFurniture.baseEntityId(), loadedFurniture);
for (int entityId : loadedFurniture.entityIds()) {
this.furnitureByEntityId.put(entityId, loadedFurniture);

View File

@@ -14,6 +14,7 @@ import net.momirealms.craftengine.core.util.QuaternionUtils;
import net.momirealms.craftengine.core.util.VersionHelper;
import net.momirealms.craftengine.core.world.Vec3d;
import net.momirealms.craftengine.core.world.World;
import net.momirealms.craftengine.core.world.WorldPosition;
import net.momirealms.craftengine.core.world.collision.AABB;
import org.bukkit.Location;
import org.bukkit.attribute.Attribute;
@@ -24,6 +25,7 @@ import org.jetbrains.annotations.Nullable;
import org.joml.Quaternionf;
import org.joml.Vector3f;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.*;
@@ -31,6 +33,7 @@ public class LoadedFurniture implements Furniture {
private final Key id;
private final CustomFurniture furniture;
private final AnchorType anchorType;
private FurnitureExtraData extraData;
// location
private final Location location;
// base entity
@@ -54,10 +57,11 @@ public class LoadedFurniture implements Furniture {
public LoadedFurniture(Entity baseEntity,
CustomFurniture furniture,
AnchorType anchorType) {
FurnitureExtraData extraData) {
this.id = furniture.id();
this.extraData = extraData;
this.baseEntityId = baseEntity.getEntityId();
this.anchorType = anchorType;
this.anchorType = extraData.anchorType().orElse(furniture.getAnyPlacement());
this.location = baseEntity.getLocation();
this.baseEntity = new WeakReference<>(baseEntity);
this.furniture = furniture;
@@ -75,9 +79,9 @@ public class LoadedFurniture implements Furniture {
} catch (Exception e) {
CraftEngine.instance().logger().warn("Failed to load external model for furniture " + id, e);
}
hasExternalModel = true;
this.hasExternalModel = true;
} else {
hasExternalModel = false;
this.hasExternalModel = false;
}
float yaw = this.location.getYaw();
@@ -92,10 +96,12 @@ public class LoadedFurniture implements Furniture {
List<Collider> colliders = new ArrayList<>();
World world = world();
WorldPosition position = new WorldPosition(world, x, y, z, yaw, 0);
Integer dyedColor = this.extraData.dyedColor().orElse(null);
for (FurnitureElement element : placement.elements()) {
int entityId = Reflections.instance$Entity$ENTITY_COUNTER.incrementAndGet();
fakeEntityIds.add(entityId);
element.initPackets(entityId, world, x, y, z, yaw, conjugated, packet -> {
element.initPackets(entityId, position, conjugated, dyedColor, packet -> {
packets.add(packet);
if (this.minimized) minimizedPackets.add(packet);
});
@@ -107,7 +113,7 @@ public class LoadedFurniture implements Furniture {
mainEntityIds.add(entityId);
this.hitBoxes.put(entityId, hitBox);
}
hitBox.initPacketsAndColliders(ids, world, x, y, z, yaw, conjugated, (packet, canBeMinimized) -> {
hitBox.initPacketsAndColliders(ids, position, conjugated, (packet, canBeMinimized) -> {
packets.add(packet);
if (this.minimized && !canBeMinimized) {
minimizedPackets.add(packet);
@@ -176,6 +182,17 @@ public class LoadedFurniture implements Furniture {
return baseEntity().isValid();
}
@NotNull
public Location dropLocation() {
Optional<Vector3f> dropOffset = config().getPlacement(this.anchorType).dropOffset();
if (dropOffset.isEmpty()) {
return location();
}
Quaternionf conjugated = QuaternionUtils.toQuaternionf(0, Math.toRadians(180 - this.location.getYaw()), 0).conjugate();
Vector3f offset = conjugated.transform(new Vector3f(dropOffset.get()));
return new Location(this.location.getWorld(), this.location.getX() + offset.x, this.location.getY() + offset.y, this.location.getZ() - offset.z);
}
@Override
public void destroy() {
if (!isValid()) {
@@ -294,7 +311,27 @@ public class LoadedFurniture implements Furniture {
spawnSeatEntityForPlayer((Player) player.platformPlayer(), seat);
}
public void spawnSeatEntityForPlayer(org.bukkit.entity.Player player, Seat seat) {
@Override
public FurnitureExtraData extraData() {
return this.extraData;
}
@Override
public void setExtraData(FurnitureExtraData extraData) {
this.extraData = extraData;
this.save();
}
@Override
public void save() {
try {
this.baseEntity().getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_EXTRA_DATA_KEY, PersistentDataType.BYTE_ARRAY, this.extraData.toBytes());
} catch (IOException e) {
CraftEngine.instance().logger().warn("Failed to save furniture data.", e);
}
}
private void spawnSeatEntityForPlayer(org.bukkit.entity.Player player, Seat seat) {
Location location = this.calculateSeatLocation(seat);
Entity seatEntity = seat.limitPlayerRotation() ?
EntityUtils.spawnEntity(player.getWorld(), VersionHelper.isOrAbove1_20_2() ? location.subtract(0,0.9875,0) : location.subtract(0,0.990625,0), EntityType.ARMOR_STAND, entity -> {

View File

@@ -9,7 +9,7 @@ import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.MiscUtils;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.util.VersionHelper;
import net.momirealms.craftengine.core.world.World;
import net.momirealms.craftengine.core.world.WorldPosition;
import net.momirealms.craftengine.core.world.collision.AABB;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
@@ -51,11 +51,11 @@ public class CustomHitBox extends AbstractHitBox {
}
@Override
public void initPacketsAndColliders(int[] entityId, World world, double x, double y, double z, float yaw, Quaternionf conjugated, BiConsumer<Object, Boolean> packets, Consumer<Collider> collider, BiConsumer<Integer, AABB> aabb) {
public void initPacketsAndColliders(int[] entityId, WorldPosition position, Quaternionf conjugated, BiConsumer<Object, Boolean> packets, Consumer<Collider> collider, BiConsumer<Integer, AABB> aabb) {
Vector3f offset = conjugated.transform(new Vector3f(position()));
try {
packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
entityId[0], UUID.randomUUID(), x + offset.x, y + offset.y, z - offset.z, 0, yaw,
entityId[0], UUID.randomUUID(), position.x() + offset.x, position.y() + offset.y, position.z() - offset.z, 0, position.xRot(),
FastNMS.INSTANCE.toNMSEntityType(this.entityType), 0, Reflections.instance$Vec3$Zero, 0
), true);
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityId[0], List.copyOf(this.cachedValues)), true);

View File

@@ -4,7 +4,7 @@ import net.momirealms.craftengine.core.entity.furniture.*;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.MiscUtils;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.world.World;
import net.momirealms.craftengine.core.world.WorldPosition;
import net.momirealms.craftengine.core.world.collision.AABB;
import org.joml.Quaternionf;
import org.joml.Vector3f;
@@ -33,7 +33,7 @@ public class HappyGhastHitBox extends AbstractHitBox {
}
@Override
public void initPacketsAndColliders(int[] entityId, World world, double x, double y, double z, float yaw, Quaternionf conjugated, BiConsumer<Object, Boolean> packets, Consumer<Collider> collider, BiConsumer<Integer, AABB> aabb) {
public void initPacketsAndColliders(int[] entityId, WorldPosition position, Quaternionf conjugated, BiConsumer<Object, Boolean> packets, Consumer<Collider> collider, BiConsumer<Integer, AABB> aabb) {
}
@Override

View File

@@ -9,7 +9,7 @@ import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.MiscUtils;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.world.Vec3d;
import net.momirealms.craftengine.core.world.World;
import net.momirealms.craftengine.core.world.WorldPosition;
import net.momirealms.craftengine.core.world.collision.AABB;
import org.joml.Quaternionf;
import org.joml.Vector3f;
@@ -53,8 +53,12 @@ public class InteractionHitBox extends AbstractHitBox {
}
@Override
public void initPacketsAndColliders(int[] entityId, World world, double x, double y, double z, float yaw, Quaternionf conjugated, BiConsumer<Object, Boolean> packets, Consumer<Collider> collider, BiConsumer<Integer, AABB> aabb) {
public void initPacketsAndColliders(int[] entityId, WorldPosition position, Quaternionf conjugated, BiConsumer<Object, Boolean> packets, Consumer<Collider> collider, BiConsumer<Integer, AABB> aabb) {
Vector3f offset = conjugated.transform(new Vector3f(position()));
double x = position.x();
double y = position.y();
double z = position.z();
float yaw = position.xRot();
packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
entityId[0], UUID.randomUUID(), x + offset.x, y + offset.y, z - offset.z, 0, yaw,
Reflections.instance$EntityType$INTERACTION, 0, Reflections.instance$Vec3$Zero, 0
@@ -66,7 +70,7 @@ public class InteractionHitBox extends AbstractHitBox {
if (blocksBuilding() || this.canBeHitByProjectile()) {
AABB ceAABB = AABB.fromInteraction(new Vec3d(x + offset.x, y + offset.y, z - offset.z), this.size.x, this.size.y);
Object nmsAABB = FastNMS.INSTANCE.constructor$AABB(ceAABB.minX, ceAABB.minY, ceAABB.minZ, ceAABB.maxX, ceAABB.maxY, ceAABB.maxZ);
collider.accept(new BukkitCollider(world.serverWorld(), nmsAABB, x, y, z, this.canBeHitByProjectile(), false, this.blocksBuilding()));
collider.accept(new BukkitCollider(position.world().serverWorld(), nmsAABB, x, y, z, this.canBeHitByProjectile(), false, this.blocksBuilding()));
}
}

View File

@@ -10,6 +10,7 @@ import net.momirealms.craftengine.core.entity.furniture.*;
import net.momirealms.craftengine.core.util.*;
import net.momirealms.craftengine.core.world.Vec3d;
import net.momirealms.craftengine.core.world.World;
import net.momirealms.craftengine.core.world.WorldPosition;
import net.momirealms.craftengine.core.world.collision.AABB;
import org.joml.Quaternionf;
import org.joml.Vector3f;
@@ -196,9 +197,13 @@ public class ShulkerHitBox extends AbstractHitBox {
}
@Override
public void initPacketsAndColliders(int[] entityIds, World world, double x, double y, double z, float yaw, Quaternionf conjugated, BiConsumer<Object, Boolean> packets, Consumer<Collider> collider, BiConsumer<Integer, AABB> aabb) {
public void initPacketsAndColliders(int[] entityIds, WorldPosition position, Quaternionf conjugated, BiConsumer<Object, Boolean> packets, Consumer<Collider> collider, BiConsumer<Integer, AABB> aabb) {
Vector3f offset = conjugated.transform(new Vector3f(position()));
try {
double x = position.x();
double y = position.y();
double z = position.z();
float yaw = position.xRot();
double originalY = y + offset.y;
double integerPart = Math.floor(originalY);
double fractionalPart = originalY - integerPart;
@@ -228,7 +233,7 @@ public class ShulkerHitBox extends AbstractHitBox {
Reflections.method$AttributeInstance$setBaseValue.invoke(attributeInstance, this.scale);
packets.accept(Reflections.constructor$ClientboundUpdateAttributesPacket0.newInstance(entityIds[1], Collections.singletonList(attributeInstance)), false);
}
this.spawner.accept(entityIds, world, x, y, z, yaw, offset, packets, collider, aabb);
this.spawner.accept(entityIds, position.world(), x, y, z, yaw, offset, packets, collider, aabb);
} catch (ReflectiveOperationException e) {
throw new RuntimeException("Failed to construct shulker hitbox spawn packet", e);
}

View File

@@ -0,0 +1,14 @@
package net.momirealms.craftengine.bukkit.entity.projectile;
import net.momirealms.craftengine.core.entity.projectile.AbstractCustomProjectile;
import net.momirealms.craftengine.core.entity.projectile.ProjectileMeta;
import net.momirealms.craftengine.core.item.Item;
import org.bukkit.entity.Projectile;
import org.bukkit.inventory.ItemStack;
public class BukkitCustomProjectile extends AbstractCustomProjectile {
public BukkitCustomProjectile(ProjectileMeta meta, Projectile projectile, Item<ItemStack> projectileItem) {
super(meta, new BukkitProjectile(projectile), projectileItem);
}
}

View File

@@ -0,0 +1,11 @@
package net.momirealms.craftengine.bukkit.entity.projectile;
import net.momirealms.craftengine.bukkit.entity.BukkitEntity;
import net.momirealms.craftengine.core.entity.projectile.Projectile;
public class BukkitProjectile extends BukkitEntity implements Projectile {
public BukkitProjectile(org.bukkit.entity.Projectile entity) {
super(entity);
}
}

View File

@@ -0,0 +1,196 @@
package net.momirealms.craftengine.bukkit.entity.projectile;
import com.destroystokyo.paper.event.entity.EntityRemoveFromWorldEvent;
import io.papermc.paper.event.player.PlayerStopUsingItemEvent;
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.plugin.scheduler.impl.FoliaTask;
import net.momirealms.craftengine.bukkit.util.ParticleUtils;
import net.momirealms.craftengine.bukkit.util.Reflections;
import net.momirealms.craftengine.core.entity.projectile.CustomProjectile;
import net.momirealms.craftengine.core.entity.projectile.ProjectileManager;
import net.momirealms.craftengine.core.entity.projectile.ProjectileMeta;
import net.momirealms.craftengine.core.item.CustomItem;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.plugin.scheduler.SchedulerTask;
import net.momirealms.craftengine.core.util.VersionHelper;
import org.bukkit.Bukkit;
import org.bukkit.entity.*;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.ProjectileLaunchEvent;
import org.bukkit.event.player.PlayerItemConsumeEvent;
import org.bukkit.event.world.EntitiesLoadEvent;
import org.bukkit.inventory.ItemStack;
import javax.annotation.Nullable;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
public class BukkitProjectileManager implements Listener, ProjectileManager {
private static BukkitProjectileManager instance;
private final BukkitCraftEngine plugin;
private final Map<Integer, BukkitCustomProjectile> projectiles;
public BukkitProjectileManager(BukkitCraftEngine plugin) {
this.plugin = plugin;
this.projectiles = new ConcurrentHashMap<>();
instance = this;
}
@Override
public void delayedInit() {
Bukkit.getPluginManager().registerEvents(this, this.plugin.bootstrap());
}
@Override
public void disable() {
HandlerList.unregisterAll(this);
}
@Override
public Optional<CustomProjectile> projectileByEntityId(int entityId) {
return Optional.ofNullable(this.projectiles.get(entityId));
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
public void onProjectileLaunch(ProjectileLaunchEvent event) {
Projectile projectile = event.getEntity();
handleProjectileLoad(projectile);
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
public void onEntitiesLoad(EntitiesLoadEvent event) {
for (Entity entity : event.getEntities()) {
if (entity instanceof Projectile projectile) {
handleProjectileLoad(projectile);
}
}
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGH)
public void onEntityRemove(EntityRemoveFromWorldEvent event) {
this.projectiles.remove(event.getEntity().getEntityId());
}
private void handleProjectileLoad(Projectile projectile) {
ItemStack projectileItem;
if (projectile instanceof ThrowableProjectile throwableProjectile) {
projectileItem = throwableProjectile.getItem();
} else if (projectile instanceof Arrow arrow) {
projectileItem = arrow.getItemStack();
} else {
return;
}
Item<ItemStack> wrapped = this.plugin.itemManager().wrap(projectileItem);
if (wrapped == null) return;
wrapped.getCustomItem().ifPresent(it -> {
ProjectileMeta meta = it.settings().projectileMeta();
if (meta != null) {
this.projectiles.put(projectile.getEntityId(), new BukkitCustomProjectile(meta, projectile, wrapped));
new ProjectileInjectTask(projectile);
}
});
}
@EventHandler
public void onPlayerInteract(PlayerItemConsumeEvent event) {
ItemStack item = event.getItem();
String type = getType(item);
if (type == null) return;
if (type.equals("bow") || type.equals("spear")) {
event.setCancelled(true);
}
}
@EventHandler
public void onPlayerStopUsingItem(PlayerStopUsingItemEvent event) {
ItemStack item = event.getItem();
String type = getType(item);
if (type == null) return;
int ticksHeldFor = event.getTicksHeldFor();
Player player = event.getPlayer();
if (type.equals("bow")) {
if (ticksHeldFor < 3) return;
// player.sendMessage("可以投出自定义弓: " + item.getType() + " 持续 " + ticksHeldFor + " 刻");
} else if (type.equals("trident")) {
if (ticksHeldFor < 10) return;
// player.sendMessage("可以投出自定义三叉戟: " + item.getType() + " 持续 " + ticksHeldFor + " 刻");
Object nmsItemStack = FastNMS.INSTANCE.field$CraftItemStack$handle(item);
Object nmsServerLevel = FastNMS.INSTANCE.field$CraftWorld$ServerLevel(player.getWorld());
Object nmsEntity = FastNMS.INSTANCE.method$CraftEntity$getHandle(player);
boolean success = TridentRelease.releaseUsing(nmsItemStack, nmsServerLevel, nmsEntity);
// player.sendMessage("释放成功: " + success);
}
}
@Nullable
private String getType(ItemStack item) {
Item<ItemStack> wrapped = BukkitItemManager.instance().wrap(item);
Optional<CustomItem<ItemStack>> optionalCustomItem = wrapped.getCustomItem();
if (optionalCustomItem.isEmpty()) return null;
CustomItem<ItemStack> customItem = optionalCustomItem.get();
ProjectileMeta meta = customItem.settings().projectileMeta();
if (meta == null) return null;
return meta.type();
}
public class ProjectileInjectTask implements Runnable {
private final Projectile projectile;
private final SchedulerTask task;
private boolean injected;
public ProjectileInjectTask(Projectile projectile) {
this.projectile = projectile;
if (VersionHelper.isFolia()) {
this.task = new FoliaTask(projectile.getScheduler().runAtFixedRate(plugin.bootstrap(), (t) -> this.run(), () -> {}, 1, 1));
} else {
this.task = plugin.scheduler().sync().runRepeating(this, 1, 1);
}
}
@Override
public void run() {
if (!this.projectile.isValid()) {
this.task.cancel();
return;
}
Object nmsEntity = FastNMS.INSTANCE.method$CraftEntity$getHandle(this.projectile);
if (!this.injected) {
Object trackedEntity = FastNMS.INSTANCE.field$Entity$trackedEntity(nmsEntity);
if (trackedEntity == null) {
return;
}
Object serverEntity = FastNMS.INSTANCE.filed$ChunkMap$TrackedEntity$serverEntity(trackedEntity);
if (serverEntity == null) {
return;
}
try {
Reflections.field$ServerEntity$updateInterval.set(serverEntity, 1);
this.injected = true;
} catch (ReflectiveOperationException e) {
plugin.logger().warn("Failed to update server entity tracking interval", e);
}
}
if (canSpawnParticle(nmsEntity)) {
this.projectile.getWorld().spawnParticle(ParticleUtils.BUBBLE, this.projectile.getLocation(), 3, 0.1, 0.1, 0.1, 0);
}
}
private static boolean canSpawnParticle(Object nmsEntity) {
if (!FastNMS.INSTANCE.field$Entity$wasTouchingWater(nmsEntity)) return false;
if (Reflections.clazz$AbstractArrow.isInstance(nmsEntity)) {
return !FastNMS.INSTANCE.method$AbstractArrow$isInGround(nmsEntity);
}
return true;
}
}
public static BukkitProjectileManager instance() {
return instance;
}
}

View File

@@ -0,0 +1,515 @@
package net.momirealms.craftengine.bukkit.entity.projectile;
import com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.util.Reflections;
import net.momirealms.craftengine.core.util.MCUtils;
import net.momirealms.craftengine.core.util.VersionHelper;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.entity.Projectile;
public class TridentRelease {
private TridentRelease() {}
public static boolean releaseUsing(Object stack, Object level, Object entity) {
if (VersionHelper.isOrAbove1_21_2()) {
return releaseUsing_1_21_2(stack, level, entity);
} else if (VersionHelper.isOrAbove1_21()) {
return releaseUsing_1_21(stack, level, entity);
} else if (VersionHelper.isOrAbove1_20_5()) {
return releaseUsing_1_20_5(stack, level, entity);
} else if (VersionHelper.isOrAbove1_20_3()) {
return releaseUsing_1_20_3(stack, level, entity);
} else if (VersionHelper.isOrAbove1_20()) {
return releaseUsing_1_20(stack, level, entity);
}
return false;
}
private static boolean releaseUsing_1_21_2(Object stack, Object level, Object entity) {
Object copyStack = FastNMS.INSTANCE.method$ItemStack$copyWithCount(stack, 1);
if (FastNMS.INSTANCE.method$ItemStack$isEmpty(copyStack)) return false;
Object copyStack1 = FastNMS.INSTANCE.method$ItemStack$copy(stack);
if (FastNMS.INSTANCE.method$ItemStack$isEmpty(copyStack1)) return false;
float spinStrength = FastNMS.INSTANCE.method$EnchantmentHelper$getTridentSpinAttackStrength(stack, entity);
if ((spinStrength > 0.0F && !FastNMS.INSTANCE.method$Entity$isInWaterOrRain(entity)) || FastNMS.INSTANCE.method$ItemStack$nextDamageWillBreak(stack)) {
return false;
}
FastNMS.INSTANCE.method$ItemStack$setDamageValue(copyStack, FastNMS.INSTANCE.method$ItemStack$getDamageValue(stack) + 1);
FastNMS.INSTANCE.method$ItemStack$setDamageValue(copyStack1, FastNMS.INSTANCE.method$ItemStack$getDamageValue(stack) + 1);
Object sound = FastNMS.INSTANCE.method$EnchantmentHelper$pickHighestLevel(stack);
if (spinStrength == 0.0F) {
Object projectile = FastNMS.INSTANCE.method$Projectile$ThrownTrident$spawnProjectileFromRotationDelayed(
level,
copyStack,
entity,
0.0F,
2.5F,
1.0F
);
PlayerLaunchProjectileEvent event = new PlayerLaunchProjectileEvent(
(Player) FastNMS.INSTANCE.method$Entity$getBukkitEntity(entity),
FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(stack),
(Projectile) FastNMS.INSTANCE.method$Entity$getBukkitEntity(FastNMS.INSTANCE.method$Projectile$Delayed$projectile(projectile))
);
if (!event.callEvent() || !FastNMS.INSTANCE.method$Projectile$Delayed$attemptSpawn(projectile)) {
FastNMS.INSTANCE.method$AbstractContainerMenu$sendAllDataToRemote(FastNMS.INSTANCE.field$Player$containerMenu(entity));
return false;
}
Object trident = FastNMS.INSTANCE.method$Projectile$Delayed$projectile(projectile);
if (event.shouldConsume()) {
FastNMS.INSTANCE.method$ItemStack$hurtWithoutBreaking(stack, 1, entity);
FastNMS.INSTANCE.method$ItemStack$consume(stack, 1, entity);
}
FastNMS.INSTANCE.field$AbstractArrow$pickupItemStack(trident, copyStack1);
if (FastNMS.INSTANCE.method$Player$hasInfiniteMaterials(entity)) {
FastNMS.INSTANCE.field$AbstractArrow$pickup(trident, Reflections.instance$AbstractArrow$Pickup$CREATIVE_ONLY);
}
FastNMS.INSTANCE.method$Level$playSound(
level,
null,
trident,
FastNMS.INSTANCE.method$Holder$value(sound),
Reflections.instance$SoundSource$PLAYERS,
1.0F, 1.0F
);
return true;
}
float yaw = FastNMS.INSTANCE.method$Entity$getYRot(entity);
float pitch = FastNMS.INSTANCE.method$Entity$getXRot(entity);
float x = -MCUtils.sin(yaw * MCUtils.DEG_TO_RAD) * MCUtils.cos(pitch * MCUtils.DEG_TO_RAD);
float y = -MCUtils.sin(pitch * MCUtils.DEG_TO_RAD);
float z = MCUtils.cos(yaw * MCUtils.DEG_TO_RAD) * MCUtils.cos(pitch * MCUtils.DEG_TO_RAD);
float length = MCUtils.sqrt(x * x + y * y + z * z);
x = x / length * spinStrength;
y = y / length * spinStrength;
z = z / length * spinStrength;
FastNMS.INSTANCE.method$CraftEventFactory$callPlayerRiptideEvent(entity, stack, x, y, z);
FastNMS.INSTANCE.method$Entity$push(entity, x, y, z);
FastNMS.INSTANCE.field$Entity$hurtMarked(entity, true);
FastNMS.INSTANCE.method$ItemStack$setDamageValue(stack, FastNMS.INSTANCE.method$ItemStack$getDamageValue(stack) + 1);
FastNMS.INSTANCE.method$Player$startAutoSpinAttack(entity, 20, 8.0F, stack);
if (FastNMS.INSTANCE.method$Entity$onGround(entity)) {
FastNMS.INSTANCE.method$Entity$move(entity, Reflections.instance$MoverType$SELF, FastNMS.INSTANCE.constructor$Vec3(0.0D, 1.1999999D, 0.0D));
}
FastNMS.INSTANCE.method$Level$playSound(
level,
null,
entity,
FastNMS.INSTANCE.method$Holder$value(sound),
Reflections.instance$SoundSource$PLAYERS,
1.0F, 1.0F
);
return true;
}
private static boolean releaseUsing_1_21(Object stack, Object level, Object entity) {
Object copyStack = FastNMS.INSTANCE.method$ItemStack$copy(stack);
if (FastNMS.INSTANCE.method$ItemStack$isEmpty(copyStack)) return false;
float spinStrength = FastNMS.INSTANCE.method$EnchantmentHelper$getTridentSpinAttackStrength(stack, entity);
if ((spinStrength > 0.0F && !FastNMS.INSTANCE.method$Entity$isInWaterOrRain(entity)) || FastNMS.INSTANCE.method$ItemStack$nextDamageWillBreak(stack)) {
return false;
}
FastNMS.INSTANCE.method$ItemStack$setDamageValue(copyStack, FastNMS.INSTANCE.method$ItemStack$getDamageValue(stack) + 1);
Object sound = FastNMS.INSTANCE.method$EnchantmentHelper$pickHighestLevel(stack);
if (spinStrength == 0.0F) {
Object entitythrowntrident = FastNMS.INSTANCE.constructor$ThrownTrident(level, entity, stack);
FastNMS.INSTANCE.method$ThrownTrident$shootFromRotation(
entitythrowntrident,
entity,
FastNMS.INSTANCE.method$Entity$getXRot(entity),
FastNMS.INSTANCE.method$Entity$getYRot(entity),
0.0F, 2.5F, 1.0F
);
if (FastNMS.INSTANCE.method$Player$hasInfiniteMaterials(entity)) {
FastNMS.INSTANCE.field$AbstractArrow$pickup(entitythrowntrident, Reflections.instance$AbstractArrow$Pickup$CREATIVE_ONLY);
}
PlayerLaunchProjectileEvent event = new PlayerLaunchProjectileEvent(
(Player) FastNMS.INSTANCE.method$Entity$getBukkitEntity(entity),
FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(stack),
(Projectile) FastNMS.INSTANCE.method$Entity$getBukkitEntity(entitythrowntrident)
);
if (!event.callEvent() || !FastNMS.INSTANCE.method$LevelWriter$addFreshEntity(level, entitythrowntrident)) {
Entity bukkitEntity = FastNMS.INSTANCE.method$Entity$getBukkitEntity(entity);
if (bukkitEntity instanceof Player player) {
player.updateInventory();
}
return false;
}
if (event.shouldConsume()) {
FastNMS.INSTANCE.method$ItemStack$hurtAndBreak(
stack, 1, entity,
FastNMS.INSTANCE.method$LivingEntity$getSlotForHand(FastNMS.INSTANCE.method$LivingEntity$getUsedItemHand(entity))
);
}
FastNMS.INSTANCE.field$AbstractArrow$pickupItemStack(entitythrowntrident, copyStack);
FastNMS.INSTANCE.method$Level$playSound(
level,
null,
entitythrowntrident,
FastNMS.INSTANCE.method$Holder$value(sound),
Reflections.instance$SoundSource$PLAYERS,
1.0F, 1.0F
);
if (event.shouldConsume() && !FastNMS.INSTANCE.method$Player$hasInfiniteMaterials(entity)) {
FastNMS.INSTANCE.method$Inventory$removeItem(FastNMS.INSTANCE.method$Player$getInventory(entity), stack);
}
return true;
}
float yaw = FastNMS.INSTANCE.method$Entity$getYRot(entity);
float pitch = FastNMS.INSTANCE.method$Entity$getXRot(entity);
float x = -MCUtils.sin(yaw * MCUtils.DEG_TO_RAD) * MCUtils.cos(pitch * MCUtils.DEG_TO_RAD);
float y = -MCUtils.sin(pitch * MCUtils.DEG_TO_RAD);
float z = MCUtils.cos(yaw * MCUtils.DEG_TO_RAD) * MCUtils.cos(pitch * MCUtils.DEG_TO_RAD);
float length = MCUtils.sqrt(x * x + y * y + z * z);
x = x / length * spinStrength;
y = y / length * spinStrength;
z = z / length * spinStrength;
FastNMS.INSTANCE.method$CraftEventFactory$callPlayerRiptideEvent(entity, stack, x, y, z);
FastNMS.INSTANCE.method$Entity$push(entity, x, y, z);
FastNMS.INSTANCE.field$Entity$hurtMarked(entity, true);
FastNMS.INSTANCE.method$ItemStack$setDamageValue(stack, FastNMS.INSTANCE.method$ItemStack$getDamageValue(stack) + 1);
FastNMS.INSTANCE.method$Player$startAutoSpinAttack(entity, 20, 8.0F, stack);
if (FastNMS.INSTANCE.method$Entity$onGround(entity)) {
FastNMS.INSTANCE.method$Entity$move(entity, Reflections.instance$MoverType$SELF, FastNMS.INSTANCE.constructor$Vec3(0.0D, 1.1999999D, 0.0D));
}
FastNMS.INSTANCE.method$Level$playSound(
level,
null,
entity,
FastNMS.INSTANCE.method$Holder$value(sound),
Reflections.instance$SoundSource$PLAYERS,
1.0F, 1.0F
);
return true;
}
private static boolean releaseUsing_1_20_5(Object stack, Object level, Object entity) {
Object copyStack = FastNMS.INSTANCE.method$ItemStack$copy(stack);
if (FastNMS.INSTANCE.method$ItemStack$isEmpty(copyStack)) return false;
float spinStrength = FastNMS.INSTANCE.method$EnchantmentHelper$getTridentSpinAttackStrength(stack, entity);
if ((spinStrength > 0.0F && !FastNMS.INSTANCE.method$Entity$isInWaterOrRain(entity)) || FastNMS.INSTANCE.method$ItemStack$nextDamageWillBreak(stack)) {
return false;
}
FastNMS.INSTANCE.method$ItemStack$setDamageValue(copyStack, FastNMS.INSTANCE.method$ItemStack$getDamageValue(stack) + 1);
if (spinStrength == 0.0F) {
Object entitythrowntrident = FastNMS.INSTANCE.constructor$ThrownTrident(level, entity, stack);
FastNMS.INSTANCE.method$ThrownTrident$shootFromRotation(
entitythrowntrident,
entity,
FastNMS.INSTANCE.method$Entity$getXRot(entity),
FastNMS.INSTANCE.method$Entity$getYRot(entity),
0.0F, 2.5F, 1.0F
);
if (FastNMS.INSTANCE.method$Player$hasInfiniteMaterials(entity)) {
FastNMS.INSTANCE.field$AbstractArrow$pickup(entitythrowntrident, Reflections.instance$AbstractArrow$Pickup$CREATIVE_ONLY);
}
PlayerLaunchProjectileEvent event = new PlayerLaunchProjectileEvent(
(Player) FastNMS.INSTANCE.method$Entity$getBukkitEntity(entity),
FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(stack),
(Projectile) FastNMS.INSTANCE.method$Entity$getBukkitEntity(entitythrowntrident)
);
if (!event.callEvent() || !FastNMS.INSTANCE.method$LevelWriter$addFreshEntity(level, entitythrowntrident)) {
Entity bukkitEntity = FastNMS.INSTANCE.method$Entity$getBukkitEntity(entity);
if (bukkitEntity instanceof Player player) {
player.updateInventory();
}
return false;
}
if (event.shouldConsume()) {
FastNMS.INSTANCE.method$ItemStack$hurtAndBreak(
stack, 1, entity,
FastNMS.INSTANCE.method$LivingEntity$getSlotForHand(FastNMS.INSTANCE.method$LivingEntity$getUsedItemHand(entity))
);
}
FastNMS.INSTANCE.field$AbstractArrow$pickupItemStack(entitythrowntrident, copyStack);
FastNMS.INSTANCE.method$Level$playSound(
level,
null,
entitythrowntrident,
Reflections.instance$SoundEvent$TRIDENT_THROW,
Reflections.instance$SoundSource$PLAYERS,
1.0F, 1.0F
);
if (event.shouldConsume() && !FastNMS.INSTANCE.method$Player$hasInfiniteMaterials(entity)) {
FastNMS.INSTANCE.method$Inventory$removeItem(FastNMS.INSTANCE.method$Player$getInventory(entity), stack);
}
return true;
}
float yaw = FastNMS.INSTANCE.method$Entity$getYRot(entity);
float pitch = FastNMS.INSTANCE.method$Entity$getXRot(entity);
float x = -MCUtils.sin(yaw * MCUtils.DEG_TO_RAD) * MCUtils.cos(pitch * MCUtils.DEG_TO_RAD);
float y = -MCUtils.sin(pitch * MCUtils.DEG_TO_RAD);
float z = MCUtils.cos(yaw * MCUtils.DEG_TO_RAD) * MCUtils.cos(pitch * MCUtils.DEG_TO_RAD);
float length = MCUtils.sqrt(x * x + y * y + z * z);
x = x / length * spinStrength;
y = y / length * spinStrength;
z = z / length * spinStrength;
FastNMS.INSTANCE.method$CraftEventFactory$callPlayerRiptideEvent(entity, stack, x, y, z);
FastNMS.INSTANCE.method$Entity$push(entity, x, y, z);
FastNMS.INSTANCE.field$Entity$hurtMarked(entity, true);
FastNMS.INSTANCE.method$ItemStack$setDamageValue(stack, FastNMS.INSTANCE.method$ItemStack$getDamageValue(stack) + 1);
FastNMS.INSTANCE.method$Player$startAutoSpinAttack(entity, 20, -1.0F, null);
if (FastNMS.INSTANCE.method$Entity$onGround(entity)) {
FastNMS.INSTANCE.method$Entity$move(entity, Reflections.instance$MoverType$SELF, FastNMS.INSTANCE.constructor$Vec3(0.0D, 1.1999999D, 0.0D));
}
Object soundeffect;
if (spinStrength >= 3) {
soundeffect = Reflections.instance$SoundEvent$TRIDENT_RIPTIDE_3;
} else if (spinStrength == 2) {
soundeffect = Reflections.instance$SoundEvent$TRIDENT_RIPTIDE_2;
} else {
soundeffect = Reflections.instance$SoundEvent$TRIDENT_RIPTIDE_1;
}
FastNMS.INSTANCE.method$Level$playSound(
level,
null,
entity,
soundeffect,
Reflections.instance$SoundSource$PLAYERS,
1.0F, 1.0F
);
return true;
}
private static boolean releaseUsing_1_20_3(Object stack, Object level, Object entity) {
Object copyStack = FastNMS.INSTANCE.method$ItemStack$copy(stack);
if (FastNMS.INSTANCE.method$ItemStack$isEmpty(copyStack)) return false;
float spinStrength = FastNMS.INSTANCE.method$EnchantmentHelper$getTridentSpinAttackStrength(stack, entity);
if ((spinStrength > 0.0F && !FastNMS.INSTANCE.method$Entity$isInWaterOrRain(entity)) || FastNMS.INSTANCE.method$ItemStack$nextDamageWillBreak(stack)) {
return false;
}
FastNMS.INSTANCE.method$ItemStack$setDamageValue(copyStack, FastNMS.INSTANCE.method$ItemStack$getDamageValue(stack) + 1);
if (spinStrength == 0.0F) {
Object entitythrowntrident = FastNMS.INSTANCE.constructor$ThrownTrident(level, entity, stack);
FastNMS.INSTANCE.method$ThrownTrident$shootFromRotation(
entitythrowntrident,
entity,
FastNMS.INSTANCE.method$Entity$getXRot(entity),
FastNMS.INSTANCE.method$Entity$getYRot(entity),
0.0F, 2.5F, 1.0F
);
if (FastNMS.INSTANCE.field$Abilities$instabuild(FastNMS.INSTANCE.method$Player$getAbilities(entity))) {
FastNMS.INSTANCE.field$AbstractArrow$pickup(entitythrowntrident, Reflections.instance$AbstractArrow$Pickup$CREATIVE_ONLY);
}
PlayerLaunchProjectileEvent event = new PlayerLaunchProjectileEvent(
(Player) FastNMS.INSTANCE.method$Entity$getBukkitEntity(entity),
FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(stack),
(Projectile) FastNMS.INSTANCE.method$Entity$getBukkitEntity(entitythrowntrident)
);
if (!event.callEvent() || !FastNMS.INSTANCE.method$LevelWriter$addFreshEntity(level, entitythrowntrident)) {
Entity bukkitEntity = FastNMS.INSTANCE.method$Entity$getBukkitEntity(entity);
if (bukkitEntity instanceof Player player) {
player.updateInventory();
}
return false;
}
if (event.shouldConsume()) {
FastNMS.INSTANCE.method$ItemStack$hurtAndBreak(
stack, 1, entity,
(player1) -> FastNMS.INSTANCE.method$LivingEntity$broadcastBreakEvent(player1, FastNMS.INSTANCE.method$LivingEntity$getUsedItemHand(entity))
);
}
FastNMS.INSTANCE.field$AbstractArrow$pickupItemStack(entitythrowntrident, copyStack);
FastNMS.INSTANCE.method$Level$playSound(
level,
null,
entitythrowntrident,
Reflections.instance$SoundEvent$TRIDENT_THROW,
Reflections.instance$SoundSource$PLAYERS,
1.0F, 1.0F
);
if (event.shouldConsume() && !FastNMS.INSTANCE.field$Abilities$instabuild(FastNMS.INSTANCE.method$Player$getAbilities(entity))) {
FastNMS.INSTANCE.method$Inventory$removeItem(FastNMS.INSTANCE.method$Player$getInventory(entity), stack);
}
return true;
}
float yaw = FastNMS.INSTANCE.method$Entity$getYRot(entity);
float pitch = FastNMS.INSTANCE.method$Entity$getXRot(entity);
float x = -MCUtils.sin(yaw * MCUtils.DEG_TO_RAD) * MCUtils.cos(pitch * MCUtils.DEG_TO_RAD);
float y = -MCUtils.sin(pitch * MCUtils.DEG_TO_RAD);
float z = MCUtils.cos(yaw * MCUtils.DEG_TO_RAD) * MCUtils.cos(pitch * MCUtils.DEG_TO_RAD);
float length = MCUtils.sqrt(x * x + y * y + z * z);
x = x / length * spinStrength;
y = y / length * spinStrength;
z = z / length * spinStrength;
FastNMS.INSTANCE.method$CraftEventFactory$callPlayerRiptideEvent(entity, stack, x, y, z);
FastNMS.INSTANCE.method$Entity$push(entity, x, y, z);
FastNMS.INSTANCE.field$Entity$hurtMarked(entity, true);
FastNMS.INSTANCE.method$ItemStack$setDamageValue(stack, FastNMS.INSTANCE.method$ItemStack$getDamageValue(stack) + 1);
FastNMS.INSTANCE.method$Player$startAutoSpinAttack(entity, 20, -1.0F, null);
if (FastNMS.INSTANCE.method$Entity$onGround(entity)) {
FastNMS.INSTANCE.method$Entity$move(entity, Reflections.instance$MoverType$SELF, FastNMS.INSTANCE.constructor$Vec3(0.0D, 1.1999999D, 0.0D));
}
Object soundeffect;
if (spinStrength >= 3) {
soundeffect = Reflections.instance$SoundEvent$TRIDENT_RIPTIDE_3;
} else if (spinStrength == 2) {
soundeffect = Reflections.instance$SoundEvent$TRIDENT_RIPTIDE_2;
} else {
soundeffect = Reflections.instance$SoundEvent$TRIDENT_RIPTIDE_1;
}
FastNMS.INSTANCE.method$Level$playSound(
level,
null,
entity,
soundeffect,
Reflections.instance$SoundSource$PLAYERS,
1.0F, 1.0F
);
return true;
}
private static boolean releaseUsing_1_20(Object stack, Object level, Object entity) {
Object copyStack = FastNMS.INSTANCE.method$ItemStack$copy(stack);
if (FastNMS.INSTANCE.method$ItemStack$isEmpty(copyStack)) return false;
float spinStrength = FastNMS.INSTANCE.method$EnchantmentHelper$getTridentSpinAttackStrength(stack, entity);
if ((spinStrength > 0.0F && !FastNMS.INSTANCE.method$Entity$isInWaterOrRain(entity)) || FastNMS.INSTANCE.method$ItemStack$nextDamageWillBreak(stack)) {
return false;
}
FastNMS.INSTANCE.method$ItemStack$setDamageValue(copyStack, FastNMS.INSTANCE.method$ItemStack$getDamageValue(stack) + 1);
if (spinStrength == 0.0F) {
Object entitythrowntrident = FastNMS.INSTANCE.constructor$ThrownTrident(level, entity, stack);
FastNMS.INSTANCE.method$ThrownTrident$shootFromRotation(
entitythrowntrident,
entity,
FastNMS.INSTANCE.method$Entity$getXRot(entity),
FastNMS.INSTANCE.method$Entity$getYRot(entity),
0.0F, 2.5F, 1.0F
);
if (FastNMS.INSTANCE.field$Abilities$instabuild(FastNMS.INSTANCE.method$Player$getAbilities(entity))) {
FastNMS.INSTANCE.field$AbstractArrow$pickup(entitythrowntrident, Reflections.instance$AbstractArrow$Pickup$CREATIVE_ONLY);
}
PlayerLaunchProjectileEvent event = new PlayerLaunchProjectileEvent(
(Player) FastNMS.INSTANCE.method$Entity$getBukkitEntity(entity),
FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(stack),
(Projectile) FastNMS.INSTANCE.method$Entity$getBukkitEntity(entitythrowntrident)
);
if (!event.callEvent() || !FastNMS.INSTANCE.method$LevelWriter$addFreshEntity(level, entitythrowntrident)) {
Entity bukkitEntity = FastNMS.INSTANCE.method$Entity$getBukkitEntity(entity);
if (bukkitEntity instanceof Player player) {
player.updateInventory();
}
return false;
}
if (event.shouldConsume()) {
FastNMS.INSTANCE.method$ItemStack$hurtAndBreak(
stack, 1, entity,
(player1) -> FastNMS.INSTANCE.method$LivingEntity$broadcastBreakEvent(player1, FastNMS.INSTANCE.method$LivingEntity$getUsedItemHand(entity))
);
}
FastNMS.INSTANCE.field$ThrownTrident$tridentItem(entitythrowntrident, copyStack);
FastNMS.INSTANCE.method$Level$playSound(
level,
null,
entitythrowntrident,
Reflections.instance$SoundEvent$TRIDENT_THROW,
Reflections.instance$SoundSource$PLAYERS,
1.0F, 1.0F
);
if (event.shouldConsume() && !FastNMS.INSTANCE.field$Abilities$instabuild(FastNMS.INSTANCE.method$Player$getAbilities(entity))) {
FastNMS.INSTANCE.method$Inventory$removeItem(FastNMS.INSTANCE.method$Player$getInventory(entity), stack);
}
return true;
}
float yaw = FastNMS.INSTANCE.method$Entity$getYRot(entity);
float pitch = FastNMS.INSTANCE.method$Entity$getXRot(entity);
float x = -MCUtils.sin(yaw * MCUtils.DEG_TO_RAD) * MCUtils.cos(pitch * MCUtils.DEG_TO_RAD);
float y = -MCUtils.sin(pitch * MCUtils.DEG_TO_RAD);
float z = MCUtils.cos(yaw * MCUtils.DEG_TO_RAD) * MCUtils.cos(pitch * MCUtils.DEG_TO_RAD);
float length = MCUtils.sqrt(x * x + y * y + z * z);
x = x / length * spinStrength;
y = y / length * spinStrength;
z = z / length * spinStrength;
FastNMS.INSTANCE.method$Entity$push(entity, x, y, z);
FastNMS.INSTANCE.field$Entity$hurtMarked(entity, true);
FastNMS.INSTANCE.method$ItemStack$setDamageValue(stack, FastNMS.INSTANCE.method$ItemStack$getDamageValue(stack) + 1);
FastNMS.INSTANCE.method$Player$startAutoSpinAttack(entity, 20, -1.0F, null);
if (FastNMS.INSTANCE.method$Entity$onGround(entity)) {
FastNMS.INSTANCE.method$Entity$move(entity, Reflections.instance$MoverType$SELF, FastNMS.INSTANCE.constructor$Vec3(0.0D, 1.1999999D, 0.0D));
}
Object soundeffect;
if (spinStrength >= 3) {
soundeffect = Reflections.instance$SoundEvent$TRIDENT_RIPTIDE_3;
} else if (spinStrength == 2) {
soundeffect = Reflections.instance$SoundEvent$TRIDENT_RIPTIDE_2;
} else {
soundeffect = Reflections.instance$SoundEvent$TRIDENT_RIPTIDE_1;
}
FastNMS.INSTANCE.method$Level$playSound(
level,
null,
entity,
soundeffect,
Reflections.instance$SoundSource$PLAYERS,
1.0F, 1.0F
);
return true;
}
}

View File

@@ -6,14 +6,15 @@ import net.momirealms.craftengine.bukkit.util.MaterialUtils;
import net.momirealms.craftengine.core.item.*;
import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
import net.momirealms.craftengine.core.item.modifier.ItemDataModifier;
import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext;
import net.momirealms.craftengine.core.plugin.context.function.Function;
import net.momirealms.craftengine.core.plugin.event.EventTrigger;
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.List;
import java.util.Map;
import java.util.*;
public class BukkitCustomItem implements CustomItem<ItemStack> {
private final Key id;
@@ -26,6 +27,7 @@ public class BukkitCustomItem implements CustomItem<ItemStack> {
private final NetworkItemDataProcessor<ItemStack>[] networkItemDataProcessors;
private final List<ItemBehavior> behaviors;
private final ItemSettings settings;
private final EnumMap<EventTrigger, List<Function<PlayerOptionalContext>>> events;
@SuppressWarnings("unchecked")
public BukkitCustomItem(Key id,
@@ -34,10 +36,12 @@ public class BukkitCustomItem implements CustomItem<ItemStack> {
List<ItemDataModifier<ItemStack>> modifiers,
List<ItemDataModifier<ItemStack>> clientBoundModifiers,
List<ItemBehavior> behaviors,
ItemSettings settings) {
ItemSettings settings,
EnumMap<EventTrigger, List<Function<PlayerOptionalContext>>> events) {
this.id = id;
this.material = material;
this.materialKey = materialKey;
this.events = events;
// unchecked cast
this.modifiers = modifiers.toArray(new ItemDataModifier[0]);
// unchecked cast
@@ -65,6 +69,13 @@ public class BukkitCustomItem implements CustomItem<ItemStack> {
this.networkItemDataProcessors = networkItemDataProcessors.toArray(new NetworkItemDataProcessor[0]);
}
@Override
public void execute(PlayerOptionalContext context, EventTrigger trigger) {
for (Function<PlayerOptionalContext> function : Optional.ofNullable(this.events.get(trigger)).orElse(Collections.emptyList())) {
function.run(context);
}
}
@Override
public Key id() {
return this.id;
@@ -145,6 +156,7 @@ public class BukkitCustomItem implements CustomItem<ItemStack> {
private Material material;
private Key materialKey;
private ItemSettings settings;
private EnumMap<EventTrigger, List<Function<PlayerOptionalContext>>> events = new EnumMap<>(EventTrigger.class);
private final List<ItemBehavior> behaviors = new ArrayList<>();
private final List<ItemDataModifier<ItemStack>> modifiers = new ArrayList<>();
private final List<ItemDataModifier<ItemStack>> clientBoundModifiers = new ArrayList<>();
@@ -204,10 +216,16 @@ public class BukkitCustomItem implements CustomItem<ItemStack> {
return this;
}
@Override
public Builder<ItemStack> events(EnumMap<EventTrigger, List<Function<PlayerOptionalContext>>> events) {
this.events = events;
return this;
}
@Override
public CustomItem<ItemStack> build() {
this.modifiers.addAll(this.settings.modifiers());
return new BukkitCustomItem(this.id, this.materialKey, this.material, this.modifiers, this.clientBoundModifiers, this.behaviors, this.settings);
return new BukkitCustomItem(this.id, this.materialKey, this.material, this.modifiers, this.clientBoundModifiers, this.behaviors, this.settings, this.events);
}
}
}

View File

@@ -1,10 +1,14 @@
package net.momirealms.craftengine.bukkit.item;
import com.saicone.rtag.item.ItemTagStream;
import net.momirealms.craftengine.bukkit.item.behavior.AxeItemBehavior;
import net.momirealms.craftengine.bukkit.item.behavior.BoneMealItemBehavior;
import net.momirealms.craftengine.bukkit.item.behavior.BucketItemBehavior;
import net.momirealms.craftengine.bukkit.item.behavior.WaterBucketItemBehavior;
import net.momirealms.craftengine.bukkit.item.factory.BukkitItemFactory;
import net.momirealms.craftengine.bukkit.item.listener.ArmorEventListener;
import net.momirealms.craftengine.bukkit.item.listener.DebugStickListener;
import net.momirealms.craftengine.bukkit.item.listener.ItemEventListener;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.util.ItemUtils;
@@ -27,8 +31,9 @@ import net.momirealms.craftengine.core.pack.model.generation.ModelGeneration;
import net.momirealms.craftengine.core.pack.model.select.ChargeTypeSelectProperty;
import net.momirealms.craftengine.core.pack.model.select.TrimMaterialSelectProperty;
import net.momirealms.craftengine.core.plugin.config.Config;
import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser;
import net.momirealms.craftengine.core.plugin.config.ConfigParser;
import net.momirealms.craftengine.core.plugin.context.ContextHolder;
import net.momirealms.craftengine.core.plugin.event.EventFunctions;
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
import net.momirealms.craftengine.core.registry.BuiltInRegistries;
import net.momirealms.craftengine.core.registry.Holder;
@@ -62,6 +67,7 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
private final BukkitCraftEngine plugin;
private final ItemEventListener itemEventListener;
private final DebugStickListener debugStickListener;
private final ArmorEventListener armorEventListener;
private final ItemParser itemParser;
public BukkitItemManager(BukkitCraftEngine plugin) {
@@ -71,6 +77,7 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
this.factory = BukkitItemFactory.create(plugin);
this.itemEventListener = new ItemEventListener(plugin);
this.debugStickListener = new DebugStickListener(plugin);
this.armorEventListener = new ArmorEventListener();
this.itemParser = new ItemParser();
this.registerAllVanillaItems();
if (plugin.hasMod()) {
@@ -129,6 +136,7 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
public void delayedInit() {
Bukkit.getPluginManager().registerEvents(this.itemEventListener, this.plugin.bootstrap());
Bukkit.getPluginManager().registerEvents(this.debugStickListener, this.plugin.bootstrap());
Bukkit.getPluginManager().registerEvents(this.armorEventListener, this.plugin.bootstrap());
}
public static BukkitItemManager instance() {
@@ -161,10 +169,16 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
this.unload();
HandlerList.unregisterAll(this.itemEventListener);
HandlerList.unregisterAll(this.debugStickListener);
HandlerList.unregisterAll(this.armorEventListener);
}
@Override
public ConfigSectionParser parser() {
public Item<ItemStack> fromByteArray(byte[] bytes) {
return this.factory.wrap(ItemTagStream.INSTANCE.fromBytes(bytes));
}
@Override
public ConfigParser parser() {
return this.itemParser;
}
@@ -224,7 +238,7 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
return wrapped.id();
}
public class ItemParser implements ConfigSectionParser {
public class ItemParser implements ConfigParser {
public static final String[] CONFIG_SECTION_NAME = new String[] {"items", "item"};
@Override
@@ -344,12 +358,15 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
itemSettings.canPlaceRelatedVanillaBlock(true);
}
itemBuilder.settings(itemSettings);
itemBuilder.events(EventFunctions.parseEvents(ResourceConfigUtils.get(section, "events", "event")));
CustomItem<ItemStack> customItem = itemBuilder.build();
customItems.put(id, customItem);
// cache command suggestions
cachedSuggestions.add(Suggestion.suggestion(id.toString()));
// TODO Deprecated 理论支持任意物品类型
if (material == Material.TOTEM_OF_UNDYING)
cachedTotemSuggestions.add(Suggestion.suggestion(id.toString()));
@@ -411,6 +428,7 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
}
if (Config.packMinVersion() < 21.39f) {
// TODO 手动指定旧版格式
List<LegacyOverridesModel> legacyOverridesModels = new ArrayList<>();
processModelRecursively(model, new LinkedHashMap<>(), legacyOverridesModels, materialId, customModelData);
TreeSet<LegacyOverridesModel> lom = legacyOverrides.computeIfAbsent(materialId, k -> new TreeSet<>());
@@ -463,6 +481,12 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
baseModel.path(),
customModelData
));
} else if (currentModel instanceof SpecialItemModel specialModel) {
resultList.add(new LegacyOverridesModel(
new LinkedHashMap<>(accumulatedPredicates),
specialModel.base(),
customModelData
));
}
}

View File

@@ -14,11 +14,11 @@ public class ComponentItemWrapper implements ItemWrapper<ItemStack> {
private final ItemStack item;
public ComponentItemWrapper(final ItemStack item) {
this.item = item;
this.item = FastNMS.INSTANCE.ensureCraftItemStack(item);
}
public ComponentItemWrapper(final ItemStack item, int count) {
this.item = item;
this.item = FastNMS.INSTANCE.ensureCraftItemStack(item);
this.item.setAmount(count);
}
@@ -71,7 +71,7 @@ public class ComponentItemWrapper implements ItemWrapper<ItemStack> {
@Override
public ItemWrapper<ItemStack> copyWithCount(int count) {
ItemStack copied = item.clone();
ItemStack copied = this.item.clone();
copied.setAmount(count);
return new ComponentItemWrapper(copied);
}

View File

@@ -25,6 +25,7 @@ public class ComponentTypes {
public static final Object REPAIR_COST = getComponentType(ComponentKeys.REPAIR_COST);
public static final Object CUSTOM_DATA = getComponentType(ComponentKeys.CUSTOM_DATA);
public static final Object PROFILE = getComponentType(ComponentKeys.PROFILE);
public static final Object DYED_COLOR = getComponentType(ComponentKeys.DYED_COLOR);
private ComponentTypes() {}

View File

@@ -1,230 +0,0 @@
package net.momirealms.craftengine.bukkit.item;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.momirealms.craftengine.bukkit.api.event.CustomBlockInteractEvent;
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
import net.momirealms.craftengine.bukkit.item.behavior.BlockItemBehavior;
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.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;
import net.momirealms.craftengine.core.util.Direction;
import net.momirealms.craftengine.core.world.BlockHitResult;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.Vec3d;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.block.Block;
import org.bukkit.block.data.BlockData;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
public class ItemEventListener implements Listener {
private final BukkitCraftEngine plugin;
public ItemEventListener(BukkitCraftEngine plugin) {
this.plugin = plugin;
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGH)
public void onInteractBlock(PlayerInteractEvent event) {
Action action = event.getAction();
if (action != Action.LEFT_CLICK_BLOCK && action != Action.RIGHT_CLICK_BLOCK) {
return;
}
Block block = Objects.requireNonNull(event.getClickedBlock());
Object blockState = BlockStateUtils.blockDataToBlockState(block.getBlockData());
int stateId = BlockStateUtils.blockStateToId(blockState);
if (BlockStateUtils.isVanillaBlock(stateId)) {
return;
}
// it's breaking the block
if (action == Action.LEFT_CLICK_BLOCK && event.getPlayer().getGameMode() == GameMode.CREATIVE) {
return;
}
CustomBlockInteractEvent interactEvent = new CustomBlockInteractEvent(
event.getPlayer(),
block.getLocation(),
event.getInteractionPoint(),
BukkitBlockManager.instance().getImmutableBlockStateUnsafe(stateId),
block,
event.getBlockFace(),
event.getHand() == EquipmentSlot.HAND ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND,
action.isRightClick() ? CustomBlockInteractEvent.Action.RIGHT_CLICK : CustomBlockInteractEvent.Action.LEFT_CLICK,
event.getItem()
);
if (EventUtils.fireAndCheckCancel(interactEvent)) {
event.setCancelled(true);
}
}
@EventHandler
public void onInteractAir(PlayerInteractEvent event) {
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()) {
return;
}
// Gets the item in hand
Item<ItemStack> itemInHand = player.getItemInHand(hand);
// should never be null
if (itemInHand == null) return;
Optional<List<ItemBehavior>> optionalItemBehaviors = itemInHand.getItemBehavior();
if (optionalItemBehaviors.isPresent()) {
for (ItemBehavior itemBehavior : optionalItemBehaviors.get()) {
InteractionResult result = itemBehavior.use(player.world(), player, hand);
if (result == InteractionResult.SUCCESS_AND_CANCEL) {
event.setCancelled(true);
return;
}
if (result != InteractionResult.PASS) {
return;
}
}
}
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onInteractAtBlock(PlayerInteractEvent event) {
if (event.getAction() != Action.RIGHT_CLICK_BLOCK) return;
if (event.useItemInHand() == Event.Result.DENY || event.useInteractedBlock() == Event.Result.DENY) return;
Location interactionPoint = event.getInteractionPoint();
if (interactionPoint == null) return;
Player bukkitPlayer = event.getPlayer();
Block clickedBlock = Objects.requireNonNull(event.getClickedBlock());
BukkitServerPlayer player = this.plugin.adapt(bukkitPlayer);
InteractionHand hand = event.getHand() == EquipmentSlot.HAND ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND;
if (cancelEventIfHasInteraction(event, player, hand)) {
return;
}
// Gets the item in hand
Item<ItemStack> itemInHand = player.getItemInHand(hand);
if (itemInHand == null) return;
Optional<List<ItemBehavior>> optionalItemBehaviors = itemInHand.getItemBehavior();
// has custom item behavior
if (optionalItemBehaviors.isPresent()) {
BlockPos pos = LocationUtils.toBlockPos(clickedBlock.getLocation());
Vec3d vec3d = new Vec3d(interactionPoint.getX(), interactionPoint.getY(), interactionPoint.getZ());
Direction direction = DirectionUtils.toDirection(event.getBlockFace());
BlockHitResult hitResult = new BlockHitResult(vec3d, direction, pos, false);
boolean interactable = InteractUtils.isInteractable(BlockStateUtils.getBlockOwnerId(clickedBlock), bukkitPlayer, clickedBlock.getBlockData(), hitResult, itemInHand);
// do not allow to place block if it's a vanilla block
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);
}
}
if (!player.isSecondaryUseActive() && interactable) {
// if it's interactable on server, cancel the custom behaviors
return;
}
// no spectator interactions
if (player.isSpectatorMode()) {
return;
}
for (ItemBehavior itemBehavior : optionalItemBehaviors.get()) {
InteractionResult result = itemBehavior.useOnBlock(new UseOnContext(player, hand, hitResult));
if (result == InteractionResult.SUCCESS_AND_CANCEL) {
event.setCancelled(true);
return;
}
int maxY = player.world().worldHeight().getMaxBuildHeight() - 1;
if (direction == Direction.UP
&& result != InteractionResult.SUCCESS
&& pos.y() >= maxY
&& itemBehavior instanceof BlockItemBehavior
) {
player.sendActionBar(Component.translatable("build.tooHigh").arguments(Component.text(maxY)).color(NamedTextColor.RED));
return;
}
if (result != InteractionResult.PASS) {
return;
}
}
return;
}
// it's a vanilla block
if (itemInHand.isBlockItem() && !itemInHand.isCustomItem()) {
// client won't have sounds if the fake block is interactable
// so we should check and resend sounds on interact
Object blockState = BlockStateUtils.blockDataToBlockState(clickedBlock.getBlockData());
int stateId = BlockStateUtils.blockStateToId(blockState);
ImmutableBlockState againstCustomBlock = BukkitBlockManager.instance().getImmutableBlockState(stateId);
if (againstCustomBlock == null || againstCustomBlock.isEmpty()) {
return;
}
BlockPos pos = LocationUtils.toBlockPos(clickedBlock.getLocation());
Vec3d vec3d = new Vec3d(interactionPoint.getX(), interactionPoint.getY(), interactionPoint.getZ());
Direction direction = DirectionUtils.toDirection(event.getBlockFace());
BlockHitResult hitResult = new BlockHitResult(vec3d, direction, pos, false);
try {
BlockData craftBlockData = BlockStateUtils.fromBlockData(againstCustomBlock.vanillaBlockState().handle());
if (InteractUtils.isInteractable(KeyUtils.namespacedKey2Key(craftBlockData.getMaterial().getKey()), bukkitPlayer, craftBlockData, hitResult, itemInHand)) {
if (!player.isSecondaryUseActive()) {
player.setResendSound();
}
} else {
if (BlockStateUtils.isReplaceable(againstCustomBlock.customBlockState().handle()) && !BlockStateUtils.isReplaceable(againstCustomBlock.vanillaBlockState().handle())) {
player.setResendSwing();
}
}
} catch (ReflectiveOperationException e) {
plugin.logger().warn("Failed to get CraftBlockData", e);
}
}
}
private boolean cancelEventIfHasInteraction(PlayerInteractEvent event, BukkitServerPlayer player, InteractionHand hand) {
if (hand == InteractionHand.OFF_HAND) {
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.lastSuccessfulInteractionTick() == currentTicks) {
event.setCancelled(true);
return true;
}
}
return false;
}
}

View File

@@ -89,7 +89,7 @@ public class AxeItemBehavior extends ItemBehavior {
bukkitPlayer.setStatistic(Statistic.USE_ITEM, material, bukkitPlayer.getStatistic(Statistic.USE_ITEM, material) + 1);
// resend swing if it's not interactable on client side
if (!InteractUtils.isInteractable(BlockStateUtils.getBlockOwnerIdFromState(state.vanillaBlockState().handle()),
if (!InteractUtils.isInteractable(
bukkitPlayer, BlockStateUtils.fromBlockData(state.vanillaBlockState().handle()),
context.getHitResult(), item
) || player.isSecondaryUseActive()) {

View File

@@ -1,11 +1,14 @@
package net.momirealms.craftengine.bukkit.item.behavior;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.momirealms.craftengine.bukkit.api.CraftEngineBlocks;
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.*;
import net.momirealms.craftengine.bukkit.world.BukkitBlockInWorld;
import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.UpdateOption;
@@ -20,11 +23,17 @@ 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.plugin.context.ContextHolder;
import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext;
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
import net.momirealms.craftengine.core.plugin.event.EventTrigger;
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
import net.momirealms.craftengine.core.util.Cancellable;
import net.momirealms.craftengine.core.util.Direction;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.MiscUtils;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.Vec3d;
import net.momirealms.craftengine.core.world.WorldPosition;
import org.bukkit.Bukkit;
import org.bukkit.GameEvent;
import org.bukkit.Location;
@@ -38,7 +47,6 @@ import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
import org.bukkit.util.Vector;
import javax.annotation.Nullable;
import java.nio.file.Path;
import java.util.Map;
import java.util.Optional;
@@ -57,34 +65,32 @@ public class BlockItemBehavior extends ItemBehavior {
}
public InteractionResult place(BlockPlaceContext context) {
if (!context.canPlace()) {
return InteractionResult.FAIL;
}
Optional<CustomBlock> optionalBlock = BukkitBlockManager.instance().blockById(this.blockId);
if (optionalBlock.isEmpty()) {
CraftEngine.instance().logger().warn("Failed to place unknown block " + this.blockId);
return InteractionResult.FAIL;
}
CustomBlock block = optionalBlock.get();
BlockPlaceContext placeContext = updatePlacementContext(context);
if (placeContext == null) {
if (!context.canPlace()) {
return InteractionResult.FAIL;
}
ImmutableBlockState blockStateToPlace = getPlacementState(placeContext, block);
CustomBlock block = optionalBlock.get();
BlockPos pos = context.getClickedPos();
int maxY = context.getLevel().worldHeight().getMaxBuildHeight() - 1;
if (context.getClickedFace() == Direction.UP && pos.y() >= maxY) {
context.getPlayer().sendActionBar(Component.translatable("build.tooHigh").arguments(Component.text(maxY)).color(NamedTextColor.RED));
return InteractionResult.FAIL;
}
ImmutableBlockState blockStateToPlace = getPlacementState(context, block);
if (blockStateToPlace == null) {
return InteractionResult.FAIL;
}
Player player = placeContext.getPlayer();
BlockPos pos = placeContext.getClickedPos();
BlockPos againstPos = placeContext.getAgainstPos();
World world = (World) placeContext.getLevel().platformWorld();
Player player = context.getPlayer();
BlockPos againstPos = context.getAgainstPos();
World world = (World) context.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();
@@ -105,6 +111,11 @@ public class BlockItemBehavior extends ItemBehavior {
}
}
int gameTicks = player.gameTicks();
if (!player.updateLastSuccessfulInteractionTick(gameTicks)) {
return InteractionResult.FAIL;
}
// trigger event
CustomBlockAttemptPlaceEvent attemptPlaceEvent = new CustomBlockAttemptPlaceEvent(bukkitPlayer, placeLocation.clone(), blockStateToPlace,
DirectionUtils.toBlockFace(context.getClickedFace()), bukkitBlock, context.getHand());
@@ -117,7 +128,7 @@ public class BlockItemBehavior extends ItemBehavior {
// place custom block
CraftEngineBlocks.place(placeLocation, blockStateToPlace, UpdateOption.UPDATE_ALL_IMMEDIATE, false);
// call bukkit event
BlockPlaceEvent bukkitPlaceEvent = new BlockPlaceEvent(bukkitBlock, previousState, againstBlock, (ItemStack) placeContext.getItem().getItem(), bukkitPlayer, true, context.getHand() == InteractionHand.MAIN_HAND ? EquipmentSlot.HAND : EquipmentSlot.OFF_HAND);
BlockPlaceEvent bukkitPlaceEvent = new BlockPlaceEvent(bukkitBlock, previousState, againstBlock, (ItemStack) context.getItem().getItem(), bukkitPlayer, true, context.getHand() == InteractionHand.MAIN_HAND ? EquipmentSlot.HAND : EquipmentSlot.OFF_HAND);
if (EventUtils.fireAndCheckCancel(bukkitPlaceEvent)) {
// revert changes
previousState.update(true, false);
@@ -132,24 +143,32 @@ public class BlockItemBehavior extends ItemBehavior {
return InteractionResult.FAIL;
}
WorldPosition position = new WorldPosition(context.getLevel(), pos.x() + 0.5, pos.y() + 0.5, pos.z() + 0.5);
Cancellable dummy = Cancellable.dummy();
PlayerOptionalContext functionContext = PlayerOptionalContext.of(player, ContextHolder.builder()
.withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(bukkitBlock))
.withParameter(DirectContextParameters.POSITION, position)
.withParameter(DirectContextParameters.EVENT, dummy)
.withParameter(DirectContextParameters.HAND, context.getHand())
.withParameter(DirectContextParameters.ITEM_IN_HAND, context.getItem())
);
block.execute(functionContext, EventTrigger.PLACE);
if (dummy.isCancelled()) {
return InteractionResult.SUCCESS_AND_CANCEL;
}
if (!player.isCreativeMode()) {
Item<?> item = placeContext.getItem();
Item<?> item = context.getItem();
item.count(item.count() - 1);
item.load();
}
player.swingHand(placeContext.getHand());
placeContext.getLevel().playBlockSound(new Vec3d(pos.x() + 0.5, pos.y() + 0.5, pos.z() + 0.5), blockStateToPlace.sounds().placeSound());
player.swingHand(context.getHand());
context.getLevel().playBlockSound(position, blockStateToPlace.sounds().placeSound());
world.sendGameEvent(bukkitPlayer, GameEvent.BLOCK_PLACE, new Vector(pos.x(), pos.y(), pos.z()));
return InteractionResult.SUCCESS;
}
// for child class to override
@Nullable
public BlockPlaceContext updatePlacementContext(BlockPlaceContext context) {
return context;
}
protected ImmutableBlockState getPlacementState(BlockPlaceContext context, CustomBlock block) {
ImmutableBlockState state = block.getStateForPlacement(context);
return state != null && this.canPlace(context, state) ? state : null;

View File

@@ -8,8 +8,10 @@ import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
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.core.entity.furniture.AnchorType;
import net.momirealms.craftengine.core.entity.furniture.CustomFurniture;
import net.momirealms.craftengine.core.entity.furniture.FurnitureExtraData;
import net.momirealms.craftengine.core.entity.furniture.HitBox;
import net.momirealms.craftengine.core.entity.player.InteractionResult;
import net.momirealms.craftengine.core.entity.player.Player;
@@ -19,6 +21,10 @@ import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory;
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.context.ContextHolder;
import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext;
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
import net.momirealms.craftengine.core.plugin.event.EventTrigger;
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
import net.momirealms.craftengine.core.util.*;
import net.momirealms.craftengine.core.world.Vec3d;
@@ -126,7 +132,15 @@ public class FurnitureItemBehavior extends ItemBehavior {
return InteractionResult.FAIL;
}
LoadedFurniture loadedFurniture = BukkitFurnitureManager.instance().place(customFurniture, furnitureLocation.clone(), anchorType, false);
Item<?> item = context.getItem();
LoadedFurniture loadedFurniture = BukkitFurnitureManager.instance().place(
furnitureLocation.clone(), customFurniture,
FurnitureExtraData.builder()
.item(item.copyWithCount(1))
.anchorType(anchorType)
.dyedColor(item.dyedColor().orElse(null))
.build(), false);
FurniturePlaceEvent placeEvent = new FurniturePlaceEvent(bukkitPlayer, loadedFurniture, furnitureLocation, context.getHand());
if (EventUtils.fireAndCheckCancel(placeEvent)) {
@@ -134,8 +148,20 @@ public class FurnitureItemBehavior extends ItemBehavior {
return InteractionResult.FAIL;
}
Cancellable dummy = Cancellable.dummy();
PlayerOptionalContext functionContext = PlayerOptionalContext.of(player, ContextHolder.builder()
.withParameter(DirectContextParameters.FURNITURE, loadedFurniture)
.withParameter(DirectContextParameters.POSITION, LocationUtils.toWorldPosition(furnitureLocation))
.withParameter(DirectContextParameters.EVENT, dummy)
.withParameter(DirectContextParameters.HAND, context.getHand())
.withParameter(DirectContextParameters.ITEM_IN_HAND, item)
);
customFurniture.execute(functionContext, EventTrigger.PLACE);
if (dummy.isCancelled()) {
return InteractionResult.SUCCESS_AND_CANCEL;
}
if (!player.isCreativeMode()) {
Item<?> item = context.getItem();
item.count(item.count() - 1);
item.load();
}

View File

@@ -1,6 +1,7 @@
package net.momirealms.craftengine.bukkit.item.factory;
import com.google.gson.JsonElement;
import com.saicone.rtag.item.ItemTagStream;
import net.momirealms.craftengine.bukkit.util.ItemTags;
import net.momirealms.craftengine.bukkit.util.Reflections;
import net.momirealms.craftengine.core.item.EquipmentData;
@@ -39,15 +40,21 @@ public abstract class BukkitItemFactory<W extends ItemWrapper<ItemStack>> extend
case "1.21.4" -> {
return new ComponentItemFactory1_21_4(plugin);
}
case "1.21.5", "1.22", "1.22.1" -> {
case "1.21.5", "1.21.6", "1.22", "1.22.1" -> {
return new ComponentItemFactory1_21_5(plugin);
}
default -> throw new IllegalStateException("Unsupported server version: " + plugin.serverVersion());
}
}
@Override
protected byte[] toByteArray(W item) {
return ItemTagStream.INSTANCE.toBytes(item.getItem());
}
@Override
protected boolean isBlockItem(W item) {
// todo 这个 isBlockItem 他考虑组件了吗???
return item.getItem().getType().isBlock();
}

View File

@@ -244,6 +244,30 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory<ComponentItemW
}
}
@Override
protected Optional<Integer> dyedColor(ComponentItemWrapper item) {
if (!item.hasComponent(ComponentTypes.DYED_COLOR)) return Optional.empty();
Object javaObj = ComponentType.encodeJava(
ComponentTypes.DYED_COLOR,
item.getComponent(ComponentTypes.DYED_COLOR)
).orElse(null);
if (javaObj instanceof Integer integer) {
return Optional.of(integer);
} else if (javaObj instanceof Map<?, ?> map) {
return Optional.of((int) map.get("rgb"));
}
return Optional.empty();
}
@Override
protected void dyedColor(ComponentItemWrapper item, Integer color) {
if (color == null) {
item.resetComponent(ComponentTypes.DYED_COLOR);
} else {
item.setJavaComponent(ComponentTypes.DYED_COLOR, color);
}
}
@Override
protected Optional<Integer> maxDamage(ComponentItemWrapper item) {
if (!item.hasComponent(ComponentTypes.MAX_DAMAGE)) return Optional.of((int) item.getItem().getType().getMaxDurability());
@@ -309,7 +333,7 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory<ComponentItemW
Object enchant = item.getComponent(ComponentTypes.ENCHANTMENTS);
try {
Map<String, Integer> map = EnchantmentUtils.toMap(enchant);
map.put(enchantment.toString(), enchantment.level());
map.put(enchantment.id().toString(), enchantment.level());
item.setJavaComponent(ComponentTypes.ENCHANTMENTS, map);
} catch (ReflectiveOperationException e) {
plugin.logger().warn("Failed to add enchantment", e);
@@ -321,7 +345,7 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory<ComponentItemW
Object enchant = item.getComponent(ComponentTypes.STORED_ENCHANTMENTS);
try {
Map<String, Integer> map = EnchantmentUtils.toMap(enchant);
map.put(enchantment.toString(), enchantment.level());
map.put(enchantment.id().toString(), enchantment.level());
item.setJavaComponent(ComponentTypes.STORED_ENCHANTMENTS, map);
} catch (ReflectiveOperationException e) {
plugin.logger().warn("Failed to add stored enchantment", e);

View File

@@ -152,6 +152,21 @@ public class UniversalItemFactory extends BukkitItemFactory<LegacyItemWrapper> {
item.set(damage, "Damage");
}
@Override
protected Optional<Integer> dyedColor(LegacyItemWrapper item) {
if (!item.hasTag("display", "color")) return Optional.empty();
return Optional.of(item.get("display", "color"));
}
@Override
protected void dyedColor(LegacyItemWrapper item, Integer color) {
if (color == null) {
item.remove("display", "color");
} else {
item.set(color, "display", "color");
}
}
@Override
protected Optional<Integer> maxDamage(LegacyItemWrapper item) {
return Optional.of((int) item.getItem().getType().getMaxDurability());
@@ -198,9 +213,9 @@ public class UniversalItemFactory extends BukkitItemFactory<LegacyItemWrapper> {
return;
}
}
item.add(Map.of("id", enchantment.toString(), "lvl", (short) enchantment.level()), "Enchantments");
item.add(Map.of("id", enchantment.id().toString(), "lvl", (short) enchantment.level()), "Enchantments");
} else {
item.set(List.of(Map.of("id", enchantment.toString(), "lvl", (short) enchantment.level())), "Enchantments");
item.set(List.of(Map.of("id", enchantment.id().toString(), "lvl", (short) enchantment.level())), "Enchantments");
}
}
@@ -214,9 +229,9 @@ public class UniversalItemFactory extends BukkitItemFactory<LegacyItemWrapper> {
return;
}
}
item.add(Map.of("id", enchantment.toString(), "lvl", (short) enchantment.level()), "StoredEnchantments");
item.add(Map.of("id", enchantment.id().toString(), "lvl", (short) enchantment.level()), "StoredEnchantments");
} else {
item.set(List.of(Map.of("id", enchantment.toString(), "lvl", (short) enchantment.level())), "StoredEnchantments");
item.set(List.of(Map.of("id", enchantment.id().toString(), "lvl", (short) enchantment.level())), "StoredEnchantments");
}
}

View File

@@ -0,0 +1,102 @@
package net.momirealms.craftengine.bukkit.item.listener;
import net.momirealms.craftengine.bukkit.api.CraftEngineItems;
import net.momirealms.craftengine.bukkit.util.ItemUtils;
import net.momirealms.craftengine.core.util.VersionHelper;
import org.bukkit.Material;
import org.bukkit.entity.Horse;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryAction;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryDragEvent;
import org.bukkit.event.player.PlayerInteractEntityEvent;
import org.bukkit.inventory.HorseInventory;
import org.bukkit.inventory.ItemStack;
import java.util.Map;
public class ArmorEventListener implements Listener {
// 只有在没有equippable组件的版本才生效阻止自定义物品放到马上
// 低版本没有自定义盔甲,所以完全不需要考虑能放置上去的情况
@EventHandler(ignoreCancelled = true)
public void onInteractHorse(PlayerInteractEntityEvent event) {
if (VersionHelper.isOrAbove1_21_2()) return;
if (event.getRightClicked() instanceof Horse horse) {
ItemStack itemInHand = event.getPlayer().getInventory().getItem(event.getHand());
if (horse.getInventory().getArmor() == null) {
switch (itemInHand.getType()) {
case LEATHER_HORSE_ARMOR, IRON_HORSE_ARMOR, GOLDEN_HORSE_ARMOR, DIAMOND_HORSE_ARMOR -> {
if (CraftEngineItems.isCustomItem(itemInHand)) {
event.setCancelled(true);
}
}
}
} else if (horse.getInventory().getSaddle() == null) {
if (itemInHand.getType() == Material.SADDLE) {
if (CraftEngineItems.isCustomItem(itemInHand)) {
event.setCancelled(true);
}
}
}
}
}
// 处理低版本的马物品栏
@EventHandler(ignoreCancelled = true)
public void onMoveItemInHorseInventory(InventoryClickEvent event) {
if (VersionHelper.isOrAbove1_21_2()) return;
if (!(event.getInventory() instanceof HorseInventory horseInventory)) {
return;
}
if (event.getClickedInventory() == event.getWhoClicked().getInventory()) {
ItemStack currentItem = event.getCurrentItem();
if (event.getAction() == InventoryAction.MOVE_TO_OTHER_INVENTORY) {
if (currentItem != null && CraftEngineItems.isCustomItem(currentItem)) {
event.setCancelled(true);
}
}
} else if (event.getClickedInventory() == horseInventory) {
ItemStack itemInCursor = event.getCursor();
if (event.getAction() == InventoryAction.SWAP_WITH_CURSOR || event.getAction() == InventoryAction.PLACE_ALL || event.getAction() == InventoryAction.PLACE_ONE) {
if (!ItemUtils.isEmpty(itemInCursor) && CraftEngineItems.isCustomItem(itemInCursor)) {
event.setCancelled(true);
return;
}
}
if (event.getAction() == InventoryAction.HOTBAR_SWAP) {
int slot = event.getHotbarButton();
if (slot != -1) {
ItemStack itemInHotBar = event.getWhoClicked().getInventory().getItem(slot);
if (!ItemUtils.isEmpty(itemInHotBar) && CraftEngineItems.isCustomItem(itemInHotBar)) {
event.setCancelled(true);
return;
}
} else {
ItemStack offHand = event.getWhoClicked().getInventory().getItemInOffHand();
if (!ItemUtils.isEmpty(offHand) && CraftEngineItems.isCustomItem(offHand)) {
event.setCancelled(true);
return;
}
}
}
}
}
@EventHandler(ignoreCancelled = true)
public void onMoveItemInHorseInventory(InventoryDragEvent event) {
if (VersionHelper.isOrAbove1_21_2()) return;
if (!(event.getInventory() instanceof HorseInventory horseInventory)) {
return;
}
for (Map.Entry<Integer, ItemStack> item : event.getNewItems().entrySet()) {
if (item.getKey() == 0 || item.getKey() == 1) {
if (!ItemUtils.isEmpty(item.getValue()) && CraftEngineItems.isCustomItem(item.getValue())) {
event.setCancelled(true);
return;
}
}
}
}
}

View File

@@ -1,8 +1,9 @@
package net.momirealms.craftengine.bukkit.item;
package net.momirealms.craftengine.bukkit.item.listener;
import net.kyori.adventure.text.Component;
import net.momirealms.craftengine.bukkit.api.CraftEngineBlocks;
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;

View File

@@ -0,0 +1,312 @@
package net.momirealms.craftengine.bukkit.item.listener;
import net.momirealms.craftengine.bukkit.api.event.CustomBlockInteractEvent;
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
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.BukkitBlockInWorld;
import net.momirealms.craftengine.core.block.CustomBlock;
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;
import net.momirealms.craftengine.core.plugin.context.ContextHolder;
import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext;
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
import net.momirealms.craftengine.core.plugin.event.EventTrigger;
import net.momirealms.craftengine.core.util.Cancellable;
import net.momirealms.craftengine.core.util.Direction;
import net.momirealms.craftengine.core.world.BlockHitResult;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.Vec3d;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.block.Block;
import org.bukkit.block.data.BlockData;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerItemConsumeEvent;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
public class ItemEventListener implements Listener {
private final BukkitCraftEngine plugin;
public ItemEventListener(BukkitCraftEngine plugin) {
this.plugin = plugin;
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
public void onInteractBlock(PlayerInteractEvent event) {
Action action = event.getAction();
Player player = event.getPlayer();
if (
(action != Action.LEFT_CLICK_BLOCK && action != Action.RIGHT_CLICK_BLOCK) || /* block is required */
(player.getGameMode() == GameMode.SPECTATOR) || /* no spectator interactions */
(action == Action.LEFT_CLICK_BLOCK && player.getGameMode() == GameMode.CREATIVE) /* it's breaking the block */
) {
return;
}
BukkitServerPlayer serverPlayer = this.plugin.adapt(player);
InteractionHand hand = event.getHand() == EquipmentSlot.HAND ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND;
// 如果本tick内主手已被处理则不处理副手
// 这是因为客户端可能会同时发主副手交互包,但实际上只能处理其中一个
if (this.cancelEventIfHasInteraction(event, serverPlayer, hand)) {
return;
}
// some common data
Block block = Objects.requireNonNull(event.getClickedBlock());
BlockData blockData = block.getBlockData();
Object blockState = BlockStateUtils.blockDataToBlockState(blockData);
ImmutableBlockState immutableBlockState = null;
int stateId = BlockStateUtils.blockStateToId(blockState);
Item<ItemStack> itemInHand = serverPlayer.getItemInHand(hand);
// 处理自定义方块
if (!BlockStateUtils.isVanillaBlock(stateId)) {
immutableBlockState = BukkitBlockManager.instance().getImmutableBlockStateUnsafe(stateId);
// call the event if it's custom
CustomBlockInteractEvent interactEvent = new CustomBlockInteractEvent(
player,
block.getLocation(),
event.getInteractionPoint(),
immutableBlockState,
block,
event.getBlockFace(),
hand,
action.isRightClick() ? CustomBlockInteractEvent.Action.RIGHT_CLICK : CustomBlockInteractEvent.Action.LEFT_CLICK,
event.getItem()
);
if (EventUtils.fireAndCheckCancel(interactEvent)) {
event.setCancelled(true);
return;
}
Cancellable dummy = Cancellable.dummy();
// run custom functions
CustomBlock customBlock = immutableBlockState.owner().value();
PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder()
.withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block))
.withParameter(DirectContextParameters.BLOCK_STATE, immutableBlockState)
.withParameter(DirectContextParameters.HAND, hand)
.withParameter(DirectContextParameters.EVENT, dummy)
.withParameter(DirectContextParameters.POSITION, LocationUtils.toWorldPosition(block.getLocation()))
.withOptionalParameter(DirectContextParameters.ITEM_IN_HAND, itemInHand)
);
if (action.isRightClick()) customBlock.execute(context, EventTrigger.RIGHT_CLICK);
else customBlock.execute(context, EventTrigger.LEFT_CLICK);
if (dummy.isCancelled()) {
event.setCancelled(true);
return;
}
}
Optional<CustomItem<ItemStack>> optionalCustomItem = itemInHand == null ? Optional.empty() : itemInHand.getCustomItem();
boolean hasItem = itemInHand != null;
boolean hasCustomItem = optionalCustomItem.isPresent();
// interact block with items
if (hasItem && action == Action.RIGHT_CLICK_BLOCK) {
Location interactionPoint = Objects.requireNonNull(event.getInteractionPoint(), "interaction point should not be null");
Direction direction = DirectionUtils.toDirection(event.getBlockFace());
BlockPos pos = LocationUtils.toBlockPos(block.getLocation());
Vec3d vec3d = new Vec3d(interactionPoint.getX(), interactionPoint.getY(), interactionPoint.getZ());
BlockHitResult hitResult = new BlockHitResult(vec3d, direction, pos, false);
// handle block item
if (itemInHand.isBlockItem()) {
// vanilla item
if (!hasCustomItem) {
// interact a custom block
if (immutableBlockState != null) {
// client won't have sounds if the clientside block is interactable
// so we should check and resend sounds on BlockPlaceEvent
BlockData craftBlockData = BlockStateUtils.fromBlockData(immutableBlockState.vanillaBlockState().handle());
if (InteractUtils.isInteractable(player, craftBlockData, hitResult, itemInHand)) {
if (!serverPlayer.isSecondaryUseActive()) {
serverPlayer.setResendSound();
}
} else {
if (BlockStateUtils.isReplaceable(immutableBlockState.customBlockState().handle()) && !BlockStateUtils.isReplaceable(immutableBlockState.vanillaBlockState().handle())) {
serverPlayer.setResendSwing();
}
}
}
}
// custom item
else {
if (optionalCustomItem.get().settings().canPlaceRelatedVanillaBlock()) {
// 如果用户设置了允许放置对应的原版方块,那么直接返回。
// 这种情况下最好是return以避免同时触发多个behavior发生冲突
// 当用户选择其作为原版方块放下时,自定义行为可能已经不重要了?
return;
} else {
// todo 实际上这里的处理并不正确,因为判断玩家是否能够放置那个方块需要更加细节的判断。比如玩家无法对着树叶放置火把,但是交互事件依然触发,此情况下不可丢弃自定义行为。
if (serverPlayer.isSecondaryUseActive() || !InteractUtils.isInteractable(player, blockData, hitResult, itemInHand)) {
event.setCancelled(true);
}
}
}
}
// execute item right click functions
if (hasCustomItem) {
Cancellable dummy = Cancellable.dummy();
PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder()
.withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block))
.withOptionalParameter(DirectContextParameters.BLOCK_STATE, immutableBlockState)
.withOptionalParameter(DirectContextParameters.ITEM_IN_HAND, itemInHand)
.withParameter(DirectContextParameters.POSITION, LocationUtils.toWorldPosition(block.getLocation()))
.withParameter(DirectContextParameters.HAND, hand)
.withParameter(DirectContextParameters.EVENT, dummy)
);
CustomItem<ItemStack> customItem = optionalCustomItem.get();
customItem.execute(context, EventTrigger.RIGHT_CLICK);
if (dummy.isCancelled()) {
event.setCancelled(true);
return;
}
}
// 检查其他的物品行为,物品行为理论只在交互时处理
Optional<List<ItemBehavior>> optionalItemBehaviors = itemInHand.getItemBehavior();
// 物品类型是否包含自定义物品行为,行为不一定来自于自定义物品,部分原版物品也包含了新的行为
if (optionalItemBehaviors.isPresent()) {
// 检测是否可交互应当只判断原版方块,因为自定义方块早就判断过了,如果可交互不可能到这一步
boolean interactable = immutableBlockState == null && InteractUtils.isInteractable(player, blockData, hitResult, itemInHand);
// 如果方块可交互但是玩家没shift那么原版的方块交互优先取消自定义物品的behavior
// todo 如果我的物品行为允许某些交互呢?是否值得进一步处理?
if (!serverPlayer.isSecondaryUseActive() && interactable) {
return;
}
// 依次执行物品行为
for (ItemBehavior itemBehavior : optionalItemBehaviors.get()) {
InteractionResult result = itemBehavior.useOnBlock(new UseOnContext(serverPlayer, hand, hitResult));
if (result == InteractionResult.SUCCESS_AND_CANCEL) {
event.setCancelled(true);
return;
}
// 非pass的情况直接结束
if (result != InteractionResult.PASS) {
return;
}
}
}
}
if (hasCustomItem && action == Action.LEFT_CLICK_BLOCK) {
Cancellable dummy = Cancellable.dummy();
PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder()
.withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block))
.withOptionalParameter(DirectContextParameters.BLOCK_STATE, immutableBlockState)
.withOptionalParameter(DirectContextParameters.ITEM_IN_HAND, itemInHand)
.withParameter(DirectContextParameters.POSITION, LocationUtils.toWorldPosition(block.getLocation()))
.withParameter(DirectContextParameters.HAND, hand)
);
CustomItem<ItemStack> customItem = optionalCustomItem.get();
customItem.execute(context, EventTrigger.LEFT_CLICK);
if (dummy.isCancelled()) {
event.setCancelled(true);
return;
}
}
}
@EventHandler
public void onInteractAir(PlayerInteractEvent event) {
Action action = event.getAction();
if (action != Action.RIGHT_CLICK_AIR && action != Action.LEFT_CLICK_AIR)
return;
Player player = event.getPlayer();
BukkitServerPlayer serverPlayer = this.plugin.adapt(player);
if (serverPlayer.isSpectatorMode())
return;
// Gets the item in hand
InteractionHand hand = event.getHand() == EquipmentSlot.HAND ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND;
Item<ItemStack> itemInHand = serverPlayer.getItemInHand(hand);
// should never be null
if (itemInHand == null) return;
// todo 真的需要这个吗
// if (cancelEventIfHasInteraction(event, serverPlayer, hand)) {
// return;
// }
Optional<CustomItem<ItemStack>> optionalCustomItem = itemInHand.getCustomItem();
if (optionalCustomItem.isPresent()) {
PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder()
.withParameter(DirectContextParameters.HAND, hand)
.withOptionalParameter(DirectContextParameters.ITEM_IN_HAND, itemInHand)
);
CustomItem<ItemStack> customItem = optionalCustomItem.get();
if (action.isRightClick()) customItem.execute(context, EventTrigger.RIGHT_CLICK);
else customItem.execute(context, EventTrigger.LEFT_CLICK);
}
if (action.isRightClick()) {
Optional<List<ItemBehavior>> optionalItemBehaviors = itemInHand.getItemBehavior();
if (optionalItemBehaviors.isPresent()) {
for (ItemBehavior itemBehavior : optionalItemBehaviors.get()) {
InteractionResult result = itemBehavior.use(serverPlayer.world(), serverPlayer, hand);
if (result == InteractionResult.SUCCESS_AND_CANCEL) {
event.setCancelled(true);
return;
}
if (result != InteractionResult.PASS) {
return;
}
}
}
}
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOW)
public void onConsumeItem(PlayerItemConsumeEvent event) {
ItemStack consumedItem = event.getItem();
if (ItemUtils.isEmpty(consumedItem)) return;
Item<ItemStack> wrapped = this.plugin.itemManager().wrap(consumedItem);
Optional<CustomItem<ItemStack>> optionalCustomItem = wrapped.getCustomItem();
if (optionalCustomItem.isEmpty()) {
return;
}
Cancellable dummy = Cancellable.dummy();
CustomItem<ItemStack> customItem = optionalCustomItem.get();
PlayerOptionalContext context = PlayerOptionalContext.of(this.plugin.adapt(event.getPlayer()), ContextHolder.builder()
.withParameter(DirectContextParameters.CONSUMED_ITEM, wrapped)
.withParameter(DirectContextParameters.EVENT, dummy)
.withParameter(DirectContextParameters.HAND, event.getHand() == EquipmentSlot.HAND ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND)
);
customItem.execute(context, EventTrigger.CONSUME);
if (dummy.isCancelled()) {
event.setCancelled(true);
return;
}
}
private boolean cancelEventIfHasInteraction(PlayerInteractEvent event, BukkitServerPlayer player, InteractionHand hand) {
if (hand == InteractionHand.OFF_HAND) {
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.lastSuccessfulInteractionTick() == currentTicks) {
event.setCancelled(true);
return true;
}
}
return false;
}
}

View File

@@ -330,14 +330,12 @@ public class RecipeEventListener implements Listener {
if (clicked == null) return;
Material type = clicked.getType();
if (type != Material.CAMPFIRE && type != Material.SOUL_CAMPFIRE) return;
if (!VersionHelper.isOrAbove1_21_2()) {
if (clicked.getState() instanceof Campfire campfire) {
try {
Object blockEntity = Reflections.field$CraftBlockEntityState$tileEntity.get(campfire);
BukkitInjector.injectCookingBlockEntity(blockEntity);
} catch (Exception e) {
this.plugin.logger().warn("Failed to inject cooking block entity", e);
}
if (clicked.getState() instanceof Campfire campfire) {
try {
Object blockEntity = Reflections.field$CraftBlockEntityState$tileEntity.get(campfire);
BukkitInjector.injectCookingBlockEntity(blockEntity);
} catch (Exception e) {
this.plugin.logger().warn("Failed to inject cooking block entity", e);
}
}
@@ -704,59 +702,72 @@ public class RecipeEventListener implements Listener {
try {
Object mcRecipe = Reflections.field$CraftComplexRecipe$recipe.get(complexRecipe);
if (!Reflections.clazz$RepairItemRecipe.isInstance(mcRecipe)) {
// Repair recipe
if (Reflections.clazz$RepairItemRecipe.isInstance(mcRecipe)) {
// repair item
ItemStack[] itemStacks = inventory.getMatrix();
Pair<ItemStack, ItemStack> onlyTwoItems = getTheOnlyTwoItem(itemStacks);
if (onlyTwoItems.left() == null || onlyTwoItems.right() == null) {
inventory.setResult(null);
return;
}
Item<ItemStack> left = plugin.itemManager().wrap(onlyTwoItems.left());
Item<ItemStack> right = plugin.itemManager().wrap(onlyTwoItems.right());
if (!left.id().equals(right.id())) {
inventory.setResult(null);
return;
}
int totalDamage = right.damage().orElse(0) + left.damage().orElse(0);
int totalMaxDamage = left.maxDamage().get() + right.maxDamage().get();
// should be impossible, but take care
if (totalDamage >= totalMaxDamage) {
inventory.setResult(null);
return;
}
Player player;
try {
player = (Player) Reflections.method$InventoryView$getPlayer.invoke(event.getView());
} catch (ReflectiveOperationException e) {
plugin.logger().warn("Failed to get inventory viewer", e);
return;
}
Optional<CustomItem<ItemStack>> customItemOptional = plugin.itemManager().getCustomItem(left.id());
if (customItemOptional.isEmpty()) {
inventory.setResult(null);
return;
}
CustomItem<ItemStack> customItem = customItemOptional.get();
if (!customItem.settings().canRepair()) {
inventory.setResult(null);
return;
}
Item<ItemStack> newItem = customItem.buildItem(ItemBuildContext.of(plugin.adapt(player)));
int remainingDurability = totalMaxDamage - totalDamage;
int newItemDamage = Math.max(0, newItem.maxDamage().get() - remainingDurability);
newItem.damage(newItemDamage);
inventory.setResult(newItem.load());
} else if (Reflections.clazz$ArmorDyeRecipe.isInstance(mcRecipe)) {
ItemStack[] itemStacks = inventory.getMatrix();
for (ItemStack itemStack : itemStacks) {
if (itemStack == null) continue;
Item<ItemStack> item = plugin.itemManager().wrap(itemStack);
Optional<CustomItem<ItemStack>> optionalCustomItem = item.getCustomItem();
if (optionalCustomItem.isPresent() && !optionalCustomItem.get().settings().dyeable()) {
inventory.setResult(null);
return;
}
}
} else {
inventory.setResult(null);
return;
}
// repair item
ItemStack[] itemStacks = inventory.getMatrix();
Pair<ItemStack, ItemStack> onlyTwoItems = getTheOnlyTwoItem(itemStacks);
if (onlyTwoItems.left() == null || onlyTwoItems.right() == null) {
inventory.setResult(null);
return;
}
Item<ItemStack> left = plugin.itemManager().wrap(onlyTwoItems.left());
Item<ItemStack> right = plugin.itemManager().wrap(onlyTwoItems.right());
if (!left.id().equals(right.id())) {
inventory.setResult(null);
return;
}
int totalDamage = right.damage().orElse(0) + left.damage().orElse(0);
int totalMaxDamage = left.maxDamage().get() + right.maxDamage().get();
// should be impossible, but take care
if (totalDamage >= totalMaxDamage) {
inventory.setResult(null);
return;
}
Player player;
try {
player = (Player) Reflections.method$InventoryView$getPlayer.invoke(event.getView());
} catch (ReflectiveOperationException e) {
plugin.logger().warn("Failed to get inventory viewer", e);
return;
}
Optional<CustomItem<ItemStack>> customItemOptional = plugin.itemManager().getCustomItem(left.id());
if (customItemOptional.isEmpty()) {
inventory.setResult(null);
return;
}
CustomItem<ItemStack> customItem = customItemOptional.get();
if (!customItem.settings().canRepair()) {
inventory.setResult(null);
return;
}
Item<ItemStack> newItem = customItem.buildItem(ItemBuildContext.of(plugin.adapt(player)));
int remainingDurability = totalMaxDamage - totalDamage;
int newItemDamage = Math.max(0, newItem.maxDamage().get() - remainingDurability);
newItem.damage(newItemDamage);
inventory.setResult(newItem.load());
} catch (Exception e) {
this.plugin.logger().warn("Failed to handle minecraft custom recipe", e);
}

View File

@@ -12,12 +12,12 @@ import net.momirealms.craftengine.core.loot.LootTable;
import net.momirealms.craftengine.core.loot.VanillaLoot;
import net.momirealms.craftengine.core.pack.LoadingSequence;
import net.momirealms.craftengine.core.pack.Pack;
import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser;
import net.momirealms.craftengine.core.plugin.config.ConfigParser;
import net.momirealms.craftengine.core.plugin.context.ContextHolder;
import net.momirealms.craftengine.core.plugin.context.parameter.CommonParameters;
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
import net.momirealms.craftengine.core.util.*;
import net.momirealms.craftengine.core.world.Vec3d;
import net.momirealms.craftengine.core.world.WorldPosition;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
@@ -65,33 +65,31 @@ public class BukkitVanillaLootManager extends AbstractVanillaLootManager impleme
}
Location location = entity.getLocation();
net.momirealms.craftengine.core.world.World world = new BukkitWorld(entity.getWorld());
Vec3d vec3d = new Vec3d(location.getBlockX() + 0.5, location.getBlockY() + 0.5, location.getBlockZ() + 0.5);
ContextHolder.Builder builder = ContextHolder.builder();
builder.withParameter(CommonParameters.WORLD, world);
builder.withParameter(CommonParameters.LOCATION, vec3d);
WorldPosition position = new WorldPosition(world, location.getX(), location.getY(), location.getZ());
ContextHolder.Builder builder = ContextHolder.builder()
.withParameter(DirectContextParameters.POSITION, position);
BukkitServerPlayer optionalPlayer = null;
if (VersionHelper.isOrAbove1_20_5()) {
if (event.getDamageSource().getCausingEntity() instanceof Player player) {
optionalPlayer = this.plugin.adapt(player);
builder.withParameter(CommonParameters.PLAYER, optionalPlayer);
//mark item builder.withOptionalParameter(CommonParameters.MAIN_HAND_ITEM, serverPlayer.getItemInHand(InteractionHand.MAIN_HAND));
builder.withParameter(DirectContextParameters.PLAYER, optionalPlayer);
}
}
ContextHolder contextHolder = builder.build();
for (LootTable<?> lootTable : loot.lootTables()) {
for (Item<?> item : lootTable.getRandomItems(contextHolder, world, optionalPlayer)) {
world.dropItemNaturally(vec3d, item);
world.dropItemNaturally(position, item);
}
}
});
}
@Override
public ConfigSectionParser parser() {
public ConfigParser parser() {
return this.vanillaLootParser;
}
public class VanillaLootParser implements ConfigSectionParser {
public class VanillaLootParser implements ConfigParser {
public static final String[] CONFIG_SECTION_NAME = new String[] {"vanilla-loots", "vanilla-loot", "loots", "loot"};
@Override

View File

@@ -7,6 +7,7 @@ import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
import net.momirealms.craftengine.bukkit.block.behavior.BukkitBlockBehaviors;
import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager;
import net.momirealms.craftengine.bukkit.entity.furniture.hitbox.BukkitHitBoxTypes;
import net.momirealms.craftengine.bukkit.entity.projectile.BukkitProjectileManager;
import net.momirealms.craftengine.bukkit.font.BukkitFontManager;
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
import net.momirealms.craftengine.bukkit.item.behavior.BukkitItemBehaviors;
@@ -40,7 +41,6 @@ import net.momirealms.craftengine.core.util.ReflectionUtils;
import net.momirealms.craftengine.core.util.VersionHelper;
import org.bstats.bukkit.Metrics;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.bukkit.inventory.ItemStack;
@@ -166,6 +166,7 @@ public class BukkitCraftEngine extends CraftEngine {
super.vanillaLootManager = new BukkitVanillaLootManager(this);
super.fontManager = new BukkitFontManager(this);
super.advancementManager = new BukkitAdvancementManager(this);
super.projectileManager = new BukkitProjectileManager(this);
super.onPluginEnable();
super.compatibilityManager().onEnable();
}
@@ -192,15 +193,7 @@ public class BukkitCraftEngine extends CraftEngine {
new Metrics(this.bootstrap(), 24333);
}
// tick task
if (VersionHelper.isFolia()) {
this.tickTask = this.scheduler().sync().runRepeating(() -> {
for (BukkitServerPlayer serverPlayer : networkManager().onlineUsers()) {
org.bukkit.entity.Player player = serverPlayer.platformPlayer();
Location location = player.getLocation();
scheduler().sync().run(serverPlayer::tick, player.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4);
}
}, 1, 1);
} else {
if (!VersionHelper.isFolia()) {
this.tickTask = this.scheduler().sync().runRepeating(() -> {
for (BukkitServerPlayer serverPlayer : networkManager().onlineUsers()) {
serverPlayer.tick();

View File

@@ -0,0 +1,38 @@
package net.momirealms.craftengine.bukkit.plugin.classpath;
import net.momirealms.craftengine.bukkit.util.Reflections;
import net.momirealms.craftengine.core.plugin.Plugin;
import net.momirealms.craftengine.core.plugin.classpath.ClassPathAppender;
import net.momirealms.craftengine.core.plugin.classpath.URLClassLoaderAccess;
import java.net.MalformedURLException;
import java.net.URLClassLoader;
import java.nio.file.Path;
public class BukkitClassPathAppender implements ClassPathAppender {
private final URLClassLoaderAccess classLoaderAccess;
public BukkitClassPathAppender(ClassLoader classLoader) throws IllegalAccessException {
if (Reflections.clazz$PaperPluginClassLoader != null && Reflections.clazz$PaperPluginClassLoader.isInstance(classLoader)) {
URLClassLoader libraryClassLoader = (URLClassLoader) Reflections.field$PaperPluginClassLoader$libraryLoader.get(classLoader);
this.classLoaderAccess = URLClassLoaderAccess.create(libraryClassLoader);
} else if (classLoader instanceof URLClassLoader) {
this.classLoaderAccess = URLClassLoaderAccess.create((URLClassLoader) classLoader);
} else {
throw new IllegalStateException("ClassLoader is not instance of URLClassLoader");
}
}
public BukkitClassPathAppender(Plugin plugin) throws IllegalAccessException {
this(plugin.getClass().getClassLoader());
}
@Override
public void addJarToClasspath(Path file) {
try {
this.classLoaderAccess.addURL(file.toUri().toURL());
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -7,15 +7,10 @@ import net.momirealms.craftengine.bukkit.plugin.injector.BukkitInjector;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager;
import net.momirealms.craftengine.core.plugin.command.sender.Sender;
import net.momirealms.craftengine.core.plugin.context.ContextHolder;
import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext;
import net.momirealms.craftengine.core.util.AdventureHelper;
import net.momirealms.craftengine.core.world.chunk.CESection;
import org.bukkit.Chunk;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.incendo.cloud.Command;
import org.incendo.cloud.parser.standard.StringParser;
public class DebugIsSectionInjectedCommand extends BukkitCommandFeature<CommandSender> {

View File

@@ -1,5 +1,6 @@
package net.momirealms.craftengine.bukkit.plugin.command.feature;
import net.momirealms.craftengine.bukkit.api.CraftEngineFurniture;
import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager;
import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature;
import net.momirealms.craftengine.bukkit.util.KeyUtils;
@@ -55,7 +56,7 @@ public class DebugSpawnFurnitureCommand extends BukkitCommandFeature<CommandSend
CustomFurniture customFurniture = optionalCustomFurniture.get();
AnchorType anchorType = (AnchorType) context.optional("anchor-type").orElse(customFurniture.getAnyPlacement());
boolean playSound = context.flags().hasFlag("silent");
furnitureManager.place(customFurniture, location, anchorType, !playSound);
CraftEngineFurniture.place(location, customFurniture, anchorType, playSound);
});
}

View File

@@ -54,7 +54,6 @@ public class ReloadCommand extends BukkitCommandFeature<CommandSender> {
Component.text(reloadResult.asyncTime()),
Component.text(reloadResult.syncTime())
);
});
} catch (Exception e) {
handleFeedback(context, MessageConstants.COMMAND_RELOAD_CONFIG_FAILURE);
@@ -88,6 +87,9 @@ public class ReloadCommand extends BukkitCommandFeature<CommandSender> {
Component.text(reloadResult.syncTime()),
Component.text(packTime)
);
} catch (Exception e) {
handleFeedback(context, MessageConstants.COMMAND_RELOAD_PACK_FAILURE);
plugin().logger().warn("Failed to generate resource pack", e);
} finally {
RELOAD_PACK_FLAG = false;
}

View File

@@ -3,15 +3,32 @@ package net.momirealms.craftengine.bukkit.plugin.command.feature;
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.context.ContextHolder;
import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext;
import net.momirealms.craftengine.core.util.AdventureHelper;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.incendo.cloud.Command;
import org.incendo.cloud.bukkit.parser.NamespacedKeyParser;
import org.incendo.cloud.context.CommandContext;
import org.incendo.cloud.context.CommandInput;
import org.incendo.cloud.parser.standard.BooleanParser;
import org.incendo.cloud.parser.standard.StringParser;
import org.incendo.cloud.suggestion.Suggestion;
import org.incendo.cloud.suggestion.SuggestionProvider;
import java.util.Collection;
import java.util.HashSet;
import java.util.concurrent.CompletableFuture;
public class TestCommand extends BukkitCommandFeature<CommandSender> {
public static final Collection<Suggestion> TARGET_BLOCK_SUGGESTIONS = new HashSet<>();
static {
for (Material material : Material.values()) {
TARGET_BLOCK_SUGGESTIONS.add(Suggestion.suggestion(material.getKey().toString()));
}
}
public TestCommand(CraftEngineCommandManager<CommandSender> commandManager, CraftEngine plugin) {
super(commandManager, plugin);
@@ -21,11 +38,19 @@ public class TestCommand extends BukkitCommandFeature<CommandSender> {
public Command.Builder<? extends CommandSender> assembleCommand(org.incendo.cloud.CommandManager<CommandSender> manager, Command.Builder<CommandSender> builder) {
return builder
.senderType(Player.class)
.required("text", StringParser.greedyStringParser())
.required("reset", BooleanParser.booleanParser())
.required("setTag", NamespacedKeyParser.namespacedKeyParser())
.required("targetBlock", StringParser.stringComponent(StringParser.StringMode.GREEDY_FLAG_YIELDING).suggestionProvider(new SuggestionProvider<>() {
@Override
public @NonNull CompletableFuture<? extends @NonNull Iterable<? extends @NonNull Suggestion>> suggestionsFuture(@NonNull CommandContext<Object> context, @NonNull CommandInput input) {
return CompletableFuture.completedFuture(TARGET_BLOCK_SUGGESTIONS);
}
}))
.handler(context -> {
String text = "<red><arg:1:0912012><papi:player_name></red>";
PlayerOptionalContext context1 = PlayerOptionalContext.of(plugin().adapt(context.sender()), ContextHolder.builder());
plugin().senderFactory().wrap(context.sender()).sendMessage(AdventureHelper.customMiniMessage().deserialize(text, context1.tagResolvers()));
Player player = context.sender();
player.sendMessage("开始测试");
NamespacedKey key = context.get("setTag");
player.sendMessage("结束测试");
});
}

View File

@@ -1,13 +1,10 @@
package net.momirealms.craftengine.bukkit.plugin.gui;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.util.LegacyInventoryUtils;
import net.momirealms.craftengine.core.plugin.gui.AbstractGui;
import net.momirealms.craftengine.core.plugin.gui.Gui;
import net.momirealms.craftengine.core.plugin.gui.GuiManager;
import net.momirealms.craftengine.core.plugin.gui.Inventory;
import net.momirealms.craftengine.core.plugin.scheduler.SchedulerTask;
import net.momirealms.craftengine.core.util.VersionHelper;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
@@ -19,7 +16,6 @@ import org.bukkit.event.inventory.InventoryDragEvent;
public class BukkitGuiManager implements GuiManager, Listener {
private final BukkitCraftEngine plugin;
private SchedulerTask timerTask;
public BukkitGuiManager(BukkitCraftEngine plugin) {
this.plugin = plugin;
@@ -28,35 +24,11 @@ public class BukkitGuiManager implements GuiManager, Listener {
@Override
public void delayedInit() {
Bukkit.getPluginManager().registerEvents(this, plugin.bootstrap());
this.timerTask = plugin.scheduler().sync().runRepeating(this::timerTask, 30, 30);
}
@Override
public void disable() {
HandlerList.unregisterAll(this);
if (this.timerTask != null && !this.timerTask.cancelled()) {
this.timerTask.cancel();
}
}
public void timerTask() {
if (VersionHelper.isFolia()) {
for (Player player : Bukkit.getOnlinePlayers()) {
this.plugin.scheduler().sync().run(() -> {
org.bukkit.inventory.Inventory top = !VersionHelper.isOrAbove1_21() ? LegacyInventoryUtils.getTopInventory(player) : player.getOpenInventory().getTopInventory();
if (top.getHolder() instanceof CraftEngineInventoryHolder holder) {
holder.gui().onTimer();
}
}, player.getWorld(), player.getLocation().getBlockX() >> 4, player.getLocation().getBlockZ() >> 4);
}
} else {
for (Player player : Bukkit.getOnlinePlayers()) {
org.bukkit.inventory.Inventory top = !VersionHelper.isOrAbove1_21() ? LegacyInventoryUtils.getTopInventory(player) : player.getOpenInventory().getTopInventory();
if (top.getHolder() instanceof CraftEngineInventoryHolder holder) {
holder.gui().onTimer();
}
}
}
}
@Override

View File

@@ -68,7 +68,6 @@ import java.lang.reflect.Modifier;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.function.Consumer;
@@ -827,8 +826,8 @@ public class BukkitInjector {
if (previousLight != newLight || (clientSideNewState != null && (BlockStateUtils.isOcclude(newState) != BlockStateUtils.isOcclude(clientSideNewState)))) {
CEWorld world = thisObj.ceChunk().world();
SectionPos sectionPos = thisObj.cePos();
Set<SectionPos> posSet = SectionPosUtils.calculateAffectedRegions((sectionPos.x() << 4) + x, (sectionPos.y() << 4) + y, (sectionPos.z() << 4) + z, Math.max(newLight, previousLight));
world.sectionLightUpdated(posSet);
List<SectionPos> pos = SectionPosUtils.calculateAffectedRegions((sectionPos.x() << 4) + x, (sectionPos.y() << 4) + y, (sectionPos.z() << 4) + z, Math.max(newLight, previousLight));
world.sectionLightUpdated(pos);
}
}

View File

@@ -14,9 +14,7 @@ import net.momirealms.craftengine.bukkit.plugin.network.id.PacketIds1_20_5;
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
import net.momirealms.craftengine.bukkit.util.Reflections;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.network.ConnectionState;
import net.momirealms.craftengine.core.plugin.network.NetWorkUser;
import net.momirealms.craftengine.core.plugin.network.NetworkManager;
import net.momirealms.craftengine.core.plugin.network.*;
import net.momirealms.craftengine.core.util.*;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
@@ -141,7 +139,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
registerNMSPacketConsumer(PacketConsumers.RESPAWN, Reflections.clazz$ClientboundRespawnPacket);
registerNMSPacketConsumer(PacketConsumers.INTERACT_ENTITY, Reflections.clazz$ServerboundInteractPacket);
registerNMSPacketConsumer(PacketConsumers.SYNC_ENTITY_POSITION, Reflections.clazz$ClientboundEntityPositionSyncPacket);
registerNMSPacketConsumer(PacketConsumers.MOVE_ENTITY, Reflections.clazz$ClientboundMoveEntityPacket$Pos);
registerNMSPacketConsumer(PacketConsumers.MOVE_POS_ENTITY, Reflections.clazz$ClientboundMoveEntityPacket$Pos);
registerNMSPacketConsumer(PacketConsumers.PICK_ITEM_FROM_ENTITY, Reflections.clazz$ServerboundPickItemFromEntityPacket);
registerNMSPacketConsumer(PacketConsumers.RENAME_ITEM, Reflections.clazz$ServerboundRenameItemPacket);
registerNMSPacketConsumer(PacketConsumers.SIGN_UPDATE, Reflections.clazz$ServerboundSignUpdatePacket);
@@ -152,10 +150,11 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
registerNMSPacketConsumer(PacketConsumers.LOGIN_ACKNOWLEDGED, Reflections.clazz$ServerboundLoginAcknowledgedPacket);
registerNMSPacketConsumer(PacketConsumers.RESOURCE_PACK_RESPONSE, Reflections.clazz$ServerboundResourcePackPacket);
registerNMSPacketConsumer(PacketConsumers.ENTITY_EVENT, Reflections.clazz$ClientboundEntityEventPacket);
registerNMSPacketConsumer(PacketConsumers.MOVE_POS_AND_ROTATE_ENTITY, Reflections.clazz$ClientboundMoveEntityPacket$PosRot);
registerByteBufPacketConsumer(PacketConsumers.LEVEL_CHUNK_WITH_LIGHT, this.packetIds.clientboundLevelChunkWithLightPacket());
registerByteBufPacketConsumer(PacketConsumers.SECTION_BLOCK_UPDATE, this.packetIds.clientboundSectionBlocksUpdatePacket());
registerByteBufPacketConsumer(PacketConsumers.BLOCK_UPDATE, this.packetIds.clientboundBlockUpdatePacket());
registerByteBufPacketConsumer(VersionHelper.isOrAbove1_21_3() ? PacketConsumers.LEVEL_PARTICLE_1_21_3 : (VersionHelper.isOrAbove1_20_5() ? PacketConsumers.LEVEL_PARTICLE_1_20_5 : PacketConsumers.LEVEL_PARTICLE_1_20), this.packetIds.clientboundLevelParticlesPacket());
registerByteBufPacketConsumer(VersionHelper.isOrAbove1_21_4() ? PacketConsumers.LEVEL_PARTICLE_1_21_4 : (VersionHelper.isOrAbove1_20_5() ? PacketConsumers.LEVEL_PARTICLE_1_20_5 : PacketConsumers.LEVEL_PARTICLE_1_20), this.packetIds.clientboundLevelParticlesPacket());
registerByteBufPacketConsumer(PacketConsumers.LEVEL_EVENT, this.packetIds.clientboundLevelEventPacket());
registerByteBufPacketConsumer(VersionHelper.isOrAbove1_20_3() ? PacketConsumers.OPEN_SCREEN_1_20_3 : PacketConsumers.OPEN_SCREEN_1_20, this.packetIds.clientboundOpenScreenPacket());
registerByteBufPacketConsumer(VersionHelper.isOrAbove1_20_3() ? PacketConsumers.SET_TITLE_TEXT_1_20_3 : PacketConsumers.SET_TITLE_TEXT_1_20, this.packetIds.clientboundSetTitleTextPacket());
@@ -185,6 +184,10 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
user.setPlayer(player);
this.onlineUsers.put(player.getUniqueId(), user);
this.resetUserArray();
if (VersionHelper.isFolia()) {
player.getScheduler().runAtFixedRate(plugin.bootstrap(), (t) -> user.tick(),
() -> plugin.debug(() -> "Player " + player.getName() + "'s entity scheduler is retired"), 1, 1);
}
}
}
@@ -544,7 +547,8 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
FriendlyByteBuf buf = new FriendlyByteBuf(buffer);
int preProcessIndex = buf.readerIndex();
int packetId = buf.readVarInt();
ByteBufPacketEvent event = new ByteBufPacketEvent(packetId, buf);
int preIndex = buf.readerIndex();
ByteBufPacketEvent event = new ByteBufPacketEvent(packetId, buf, preIndex);
BukkitNetworkManager.this.handleByteBufPacket(this.player, event);
if (event.isCancelled()) {
buf.clear();

View File

@@ -0,0 +1,64 @@
package net.momirealms.craftengine.bukkit.plugin.network;
import net.momirealms.craftengine.core.util.FriendlyByteBuf;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Function;
public class NetWorkDataTypes<T> {
private static final Map<Integer, NetWorkDataTypes<?>> id2NetWorkDataTypes = new HashMap<>();
public static final NetWorkDataTypes<Integer> CLIENT_CUSTOM_BLOCK =
new NetWorkDataTypes<>(0, FriendlyByteBuf::readInt, FriendlyByteBuf::writeInt);
public static final NetWorkDataTypes<Boolean> CANCEL_BLOCK_UPDATE =
new NetWorkDataTypes<>(1, FriendlyByteBuf::readBoolean, FriendlyByteBuf::writeBoolean);
static {
register(CLIENT_CUSTOM_BLOCK);
register(CANCEL_BLOCK_UPDATE);
}
private static void register(NetWorkDataTypes<?> type) {
id2NetWorkDataTypes.put(type.id, type);
}
private final int id;
private final Function<FriendlyByteBuf, T> decoder;
private final BiConsumer<FriendlyByteBuf, T> encoder;
public NetWorkDataTypes(int id, Function<FriendlyByteBuf, T> decoder, BiConsumer<FriendlyByteBuf, T> encoder) {
this.id = id;
this.decoder = decoder;
this.encoder = encoder;
}
public T decode(FriendlyByteBuf buf) {
return decoder.apply(buf);
}
public void encode(FriendlyByteBuf buf, T data) {
encoder.accept(buf, data);
}
public int id() {
return id;
}
public void writeType(FriendlyByteBuf buf) {
buf.writeVarInt(id);
}
public static NetWorkDataTypes<?> readType(FriendlyByteBuf buf) {
int id = buf.readVarInt();
return id2NetWorkDataTypes.get(id);
}
@SuppressWarnings("unchecked")
public <R> NetWorkDataTypes<R> as(Class<R> clazz) {
return (NetWorkDataTypes<R>) this;
}
}

View File

@@ -12,11 +12,13 @@ import net.momirealms.craftengine.bukkit.api.event.FurnitureInteractEvent;
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager;
import net.momirealms.craftengine.bukkit.entity.furniture.LoadedFurniture;
import net.momirealms.craftengine.bukkit.entity.projectile.BukkitProjectileManager;
import net.momirealms.craftengine.bukkit.item.behavior.FurnitureItemBehavior;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.pack.BukkitPackManager;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.plugin.injector.BukkitInjector;
import net.momirealms.craftengine.bukkit.plugin.network.handler.*;
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
import net.momirealms.craftengine.bukkit.util.*;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
@@ -31,15 +33,13 @@ import net.momirealms.craftengine.core.pack.host.ResourcePackDownloadData;
import net.momirealms.craftengine.core.pack.host.ResourcePackHost;
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.plugin.network.NetWorkUser;
import net.momirealms.craftengine.core.plugin.network.NetworkManager;
import net.momirealms.craftengine.core.plugin.network.ProtocolVersion;
import net.momirealms.craftengine.core.plugin.context.ContextHolder;
import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext;
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
import net.momirealms.craftengine.core.plugin.event.EventTrigger;
import net.momirealms.craftengine.core.plugin.network.*;
import net.momirealms.craftengine.core.util.*;
import net.momirealms.craftengine.core.world.BlockHitResult;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.EntityHitResult;
import net.momirealms.craftengine.core.world.WorldEvents;
import net.momirealms.craftengine.core.world.*;
import net.momirealms.craftengine.core.world.chunk.Palette;
import net.momirealms.craftengine.core.world.chunk.PalettedContainer;
import net.momirealms.craftengine.core.world.chunk.packet.BlockEntityData;
@@ -47,6 +47,7 @@ import net.momirealms.craftengine.core.world.chunk.packet.MCSection;
import net.momirealms.craftengine.core.world.collision.AABB;
import net.momirealms.sparrow.nbt.Tag;
import org.bukkit.*;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.data.BlockData;
import org.bukkit.entity.Player;
@@ -420,7 +421,8 @@ public class PacketConsumers {
return;
}
EnumSet<? extends Enum<?>> enums = FastNMS.INSTANCE.field$ClientboundPlayerInfoUpdatePacket$actions(packet);
outer: {
outer:
{
for (Object entry : enums) {
if (entry == Reflections.instance$ClientboundPlayerInfoUpdatePacket$Action$UPDATE_DISPLAY_NAME) {
break outer;
@@ -1051,7 +1053,7 @@ public class PacketConsumers {
}
};
public static final BiConsumer<NetWorkUser, ByteBufPacketEvent> LEVEL_PARTICLE_1_21_3 = (user, event) -> {
public static final BiConsumer<NetWorkUser, ByteBufPacketEvent> LEVEL_PARTICLE_1_21_4 = (user, event) -> {
try {
FriendlyByteBuf buf = event.getBuffer();
boolean overrideLimiter = buf.readBoolean();
@@ -1577,9 +1579,11 @@ public class PacketConsumers {
buf.writeShort(za);
}
} else if (type == Reflections.instance$EntityType$BLOCK_DISPLAY$registryId) {
user.entityView().put(id, Reflections.instance$EntityType$BLOCK_DISPLAY);
user.entityPacketHandlers().put(id, BlockDisplayPacketHandler.INSTANCE);
} else if (type == Reflections.instance$EntityType$TEXT_DISPLAY$registryId) {
user.entityView().put(id, Reflections.instance$EntityType$TEXT_DISPLAY);
user.entityPacketHandlers().put(id, TextDisplayPacketHandler.INSTANCE);
} else if (type == Reflections.instance$EntityType$ARMOR_STAND$registryId) {
user.entityPacketHandlers().put(id, ArmorStandPacketHandler.INSTANCE);
}
} catch (Exception e) {
CraftEngine.instance().logger().warn("Failed to handle ClientboundAddEntityPacket", e);
@@ -1589,12 +1593,12 @@ public class PacketConsumers {
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> ADD_ENTITY = (user, event, packet) -> {
try {
Object entityType = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$type(packet);
int entityId = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$entityId(packet);
if (entityType == Reflections.instance$EntityType$ITEM_DISPLAY) {
// Furniture
int entityId = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$entityId(packet);
LoadedFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(entityId);
if (furniture != null) {
user.furnitureView().computeIfAbsent(furniture.baseEntityId(), k -> new ArrayList<>()).addAll(furniture.fakeEntityIds());
user.entityPacketHandlers().computeIfAbsent(entityId, k -> new FurniturePacketHandler(furniture.fakeEntityIds()));
user.sendPacket(furniture.spawnPacket((Player) user.platformPlayer()), false);
if (Config.hideBaseEntity() && !furniture.hasExternalModel()) {
event.setCancelled(true);
@@ -1602,40 +1606,36 @@ public class PacketConsumers {
}
} else if (entityType == BukkitFurnitureManager.NMS_COLLISION_ENTITY_TYPE) {
// Cancel collider entity packet
int entityId = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$entityId(packet);
LoadedFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(entityId);
if (furniture != null) {
event.setCancelled(true);
user.entityPacketHandlers().put(entityId, FurnitureCollisionPacketHandler.INSTANCE);
}
} else {
BukkitProjectileManager.instance().projectileByEntityId(entityId).ifPresent(customProjectile -> {
ProjectilePacketHandler handler = new ProjectilePacketHandler(customProjectile, entityId);
event.replacePacket(handler.convertAddCustomProjectilePacket(packet));
user.entityPacketHandlers().put(entityId, handler);
});
}
} catch (Exception e) {
CraftEngine.instance().logger().warn("Failed to handle ClientboundAddEntityPacket", e);
}
};
// 1.21.3+
// 1.21.2+
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> SYNC_ENTITY_POSITION = (user, event, packet) -> {
try {
int entityId = FastNMS.INSTANCE.method$ClientboundEntityPositionSyncPacket$id(packet);
if (BukkitFurnitureManager.instance().isFurnitureRealEntity(entityId)) {
event.setCancelled(true);
EntityPacketHandler handler = user.entityPacketHandlers().get(entityId);
if (handler != null) {
handler.handleSyncEntityPosition(user, event, packet);
}
} catch (Exception e) {
CraftEngine.instance().logger().warn("Failed to handle ClientboundEntityPositionSyncPacket", e);
}
};
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> MOVE_ENTITY = (user, event, packet) -> {
try {
int entityId = BukkitInjector.internalFieldAccessor().field$ClientboundMoveEntityPacket$entityId(packet);
if (BukkitFurnitureManager.instance().isFurnitureRealEntity(entityId)) {
event.setCancelled(true);
}
} catch (Exception e) {
CraftEngine.instance().logger().warn("Failed to handle ClientboundMoveEntityPacket$Pos", e);
}
};
public static final BiConsumer<NetWorkUser, ByteBufPacketEvent> REMOVE_ENTITY = (user, event) -> {
try {
FriendlyByteBuf buf = event.getBuffer();
@@ -1643,12 +1643,9 @@ public class PacketConsumers {
IntList intList = buf.readIntIdList();
for (int i = 0, size = intList.size(); i < size; i++) {
int entityId = intList.getInt(i);
user.entityView().remove(entityId);
List<Integer> entities = user.furnitureView().remove(entityId);
if (entities == null) continue;
for (int subEntityId : entities) {
EntityPacketHandler handler = user.entityPacketHandlers().remove(entityId);
if (handler != null && handler.handleEntitiesRemove(intList)) {
isChange = true;
intList.add(subEntityId);
}
}
if (isChange) {
@@ -1693,6 +1690,15 @@ public class PacketConsumers {
if (EventUtils.fireAndCheckCancel(breakEvent)) {
return;
}
// execute functions
PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder()
.withParameter(DirectContextParameters.FURNITURE, furniture)
.withParameter(DirectContextParameters.POSITION, new WorldPosition(furniture.world(), furniture.position()))
);
furniture.config().execute(context, EventTrigger.LEFT_CLICK);
furniture.config().execute(context, EventTrigger.BREAK);
CraftEngineFurniture.remove(furniture, serverPlayer, !serverPlayer.isCreativeMode(), true);
}
} else if (actionType == Reflections.instance$ServerboundInteractPacket$ActionType$INTERACT_AT) {
@@ -1715,6 +1721,13 @@ public class PacketConsumers {
return;
}
// execute functions
PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder()
.withParameter(DirectContextParameters.FURNITURE, furniture)
.withParameter(DirectContextParameters.POSITION, new WorldPosition(furniture.world(), furniture.position()))
);
furniture.config().execute(context, EventTrigger.RIGHT_CLICK);;
if (player.isSneaking()) {
// try placing another furniture above it
AABB hitBox = furniture.aabbByEntityId(entityId);
@@ -1745,7 +1758,7 @@ public class PacketConsumers {
} else {
furniture.findFirstAvailableSeat(entityId).ifPresent(seatPos -> {
if (furniture.tryOccupySeat(seatPos)) {
furniture.spawnSeatEntityForPlayer(Objects.requireNonNull(player.getPlayer()), seatPos);
furniture.spawnSeatEntityForPlayer(serverPlayer, seatPos);
}
});
}
@@ -1795,7 +1808,7 @@ public class PacketConsumers {
buf.writeLong(seed);
}
} else {
Optional<Object> optionalSound = FastNMS.INSTANCE.method$BuiltInRegistries$byId(Reflections.instance$BuiltInRegistries$SOUND_EVENT, id - 1);
Optional<Object> optionalSound = FastNMS.INSTANCE.method$IdMap$byId(Reflections.instance$BuiltInRegistries$SOUND_EVENT, id - 1);
if (optionalSound.isEmpty()) return;
Object soundEvent = optionalSound.get();
Key soundId = Key.of(FastNMS.INSTANCE.method$SoundEvent$location(soundEvent));
@@ -1966,29 +1979,38 @@ public class PacketConsumers {
} else {
data = (byte[]) Reflections.method$DiscardedPayload$dataByteArray.invoke(payload);
}
String decodeData = new String(data, StandardCharsets.UTF_8);
if (!decodeData.endsWith("init")) return;
int firstColon = decodeData.indexOf(':');
if (firstColon == -1) return;
int secondColon = decodeData.indexOf(':', firstColon + 1);
if (secondColon == -1) return;
int clientBlockRegistrySize = Integer.parseInt(decodeData.substring(firstColon + 1, secondColon));
int serverBlockRegistrySize = RegistryUtils.currentBlockRegistrySize();
if (clientBlockRegistrySize != serverBlockRegistrySize) {
Object kickPacket = Reflections.constructor$ClientboundDisconnectPacket.newInstance(
ComponentUtils.adventureToMinecraft(
Component.translatable(
"disconnect.craftengine.block_registry_mismatch",
TranslationArgument.numeric(clientBlockRegistrySize),
TranslationArgument.numeric(serverBlockRegistrySize)
)
)
);
user.nettyChannel().writeAndFlush(kickPacket);
user.nettyChannel().disconnect();
return;
FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.wrappedBuffer(data));
NetWorkDataTypes<?> dataType = NetWorkDataTypes.readType(buf);
if (dataType == NetWorkDataTypes.CLIENT_CUSTOM_BLOCK) {
int clientBlockRegistrySize = dataType.as(Integer.class).decode(buf);
int serverBlockRegistrySize = RegistryUtils.currentBlockRegistrySize();
if (clientBlockRegistrySize != serverBlockRegistrySize) {
Object kickPacket = Reflections.constructor$ClientboundDisconnectPacket.newInstance(
ComponentUtils.adventureToMinecraft(
Component.translatable(
"disconnect.craftengine.block_registry_mismatch",
TranslationArgument.numeric(clientBlockRegistrySize),
TranslationArgument.numeric(serverBlockRegistrySize)
)
)
);
user.nettyChannel().writeAndFlush(kickPacket);
user.nettyChannel().disconnect();
return;
}
user.setClientModState(true);
} else if (dataType == NetWorkDataTypes.CANCEL_BLOCK_UPDATE) {
if (!VersionHelper.isOrAbove1_20_2()) return;
if (dataType.as(Boolean.class).decode(buf)) {
FriendlyByteBuf bufPayload = new FriendlyByteBuf(Unpooled.buffer());
dataType.writeType(bufPayload);
dataType.as(Boolean.class).encode(bufPayload, true);
Object channelKey = KeyUtils.toResourceLocation(Key.of(NetworkManager.MOD_CHANNEL));
Object dataPayload = Reflections.constructor$DiscardedPayload.newInstance(channelKey, bufPayload.array());
Object responsePacket = Reflections.constructor$ClientboundCustomPayloadPacket.newInstance(dataPayload);
user.nettyChannel().writeAndFlush(responsePacket);
}
}
user.setClientModState(true);
}
} catch (Exception e) {
CraftEngine.instance().logger().warn("Failed to handle ServerboundCustomPayloadPacket", e);
@@ -2000,121 +2022,12 @@ public class PacketConsumers {
try {
FriendlyByteBuf buf = event.getBuffer();
int id = buf.readVarInt();
Object entityType = user.entityView().get(id);
if (entityType == Reflections.instance$EntityType$BLOCK_DISPLAY) {
boolean isChanged = false;
List<Object> packedItems = FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$unpack(buf);
for (int i = 0; i < packedItems.size(); i++) {
Object packedItem = packedItems.get(i);
int entityDataId = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$id(packedItem);
if (entityDataId == EntityDataUtils.BLOCK_STATE_DATA_ID) {
Object blockState = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem);
int stateId = BlockStateUtils.blockStateToId(blockState);
int newStateId;
if (!user.clientModEnabled()) {
newStateId = remap(stateId);
} else {
newStateId = remapMOD(stateId);
}
Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem);
packedItems.set(i, FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue(
entityDataId, serializer, BlockStateUtils.idToBlockState(newStateId)
));
isChanged = true;
} else if (Config.interceptEntityName() && entityDataId == EntityDataUtils.CUSTOM_NAME_DATA_ID) {
Optional<Object> optionalTextComponent = (Optional<Object>) FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem);
if (optionalTextComponent.isPresent()) {
Object textComponent = optionalTextComponent.get();
String json = ComponentUtils.minecraftToJson(textComponent);
Map<String, Component> tokens = CraftEngine.instance().fontManager().matchTags(json);
if (!tokens.isEmpty()) {
Component component = AdventureHelper.jsonToComponent(json);
for (Map.Entry<String, Component> token : tokens.entrySet()) {
component = component.replaceText(b -> b.matchLiteral(token.getKey()).replacement(token.getValue()));
}
Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem);
packedItems.set(i, FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue(
entityDataId, serializer, Optional.of(ComponentUtils.adventureToMinecraft(component))
));
isChanged = true;
}
}
}
}
if (isChanged) {
event.setChanged(true);
buf.clear();
buf.writeVarInt(event.packetID());
buf.writeVarInt(id);
FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$pack(packedItems, buf);
}
} else if (entityType == Reflections.instance$EntityType$TEXT_DISPLAY) {
if (Config.interceptTextDisplay()) {
boolean isChanged = false;
List<Object> packedItems = FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$unpack(buf);
for (int i = 0; i < packedItems.size(); i++) {
Object packedItem = packedItems.get(i);
int entityDataId = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$id(packedItem);
if (entityDataId == EntityDataUtils.TEXT_DATA_ID) {
Object textComponent = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem);
if (textComponent == Reflections.instance$Component$empty) break;
String json = ComponentUtils.minecraftToJson(textComponent);
Map<String, Component> tokens = CraftEngine.instance().fontManager().matchTags(json);
if (!tokens.isEmpty()) {
Component component = AdventureHelper.jsonToComponent(json);
for (Map.Entry<String, Component> token : tokens.entrySet()) {
component = component.replaceText(b -> b.matchLiteral(token.getKey()).replacement(token.getValue()));
}
Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem);
packedItems.set(i, FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue(entityDataId, serializer, ComponentUtils.adventureToMinecraft(component)));
isChanged = true;
break;
}
}
}
if (isChanged) {
event.setChanged(true);
buf.clear();
buf.writeVarInt(event.packetID());
buf.writeVarInt(id);
FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$pack(packedItems, buf);
}
}
} else if (entityType == Reflections.instance$EntityType$ARMOR_STAND) {
if (Config.interceptArmorStand()) {
boolean isChanged = false;
List<Object> packedItems = FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$unpack(buf);
for (int i = 0; i < packedItems.size(); i++) {
Object packedItem = packedItems.get(i);
int entityDataId = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$id(packedItem);
if (entityDataId == EntityDataUtils.CUSTOM_NAME_DATA_ID) {
Optional<Object> optionalTextComponent = (Optional<Object>) FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem);
if (optionalTextComponent.isPresent()) {
Object textComponent = optionalTextComponent.get();
String json = ComponentUtils.minecraftToJson(textComponent);
Map<String, Component> tokens = CraftEngine.instance().fontManager().matchTags(json);
if (!tokens.isEmpty()) {
Component component = AdventureHelper.jsonToComponent(json);
for (Map.Entry<String, Component> token : tokens.entrySet()) {
component = component.replaceText(b -> b.matchLiteral(token.getKey()).replacement(token.getValue()));
}
Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem);
packedItems.set(i, FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue(entityDataId, serializer, Optional.of(ComponentUtils.adventureToMinecraft(component))));
isChanged = true;
break;
}
}
}
}
if (isChanged) {
event.setChanged(true);
buf.clear();
buf.writeVarInt(event.packetID());
buf.writeVarInt(id);
FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$pack(packedItems, buf);
}
}
} else if (Config.interceptEntityName()) {
EntityPacketHandler handler = user.entityPacketHandlers().get(id);
if (handler != null) {
handler.handleSetEntityData(user, event);
return;
}
if (Config.interceptEntityName()) {
boolean isChanged = false;
List<Object> packedItems = FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$unpack(buf);
for (int i = 0; i < packedItems.size(); i++) {
@@ -2165,7 +2078,8 @@ public class PacketConsumers {
if (hasDisplay) {
displayName = buf.readNbt(false);
}
outside : if (displayName != null) {
outside:
if (displayName != null) {
Map<String, Component> tokens = CraftEngine.instance().fontManager().matchTags(displayName.getAsString());
if (tokens.isEmpty()) break outside;
Component component = AdventureHelper.tagToComponent(displayName);
@@ -2318,4 +2232,27 @@ public class PacketConsumers {
CraftEngine.instance().logger().warn("Failed to handle ClientboundEntityEventPacket", e);
}
};
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> MOVE_POS_ENTITY = (user, event, packet) -> {
try {
int entityId = BukkitInjector.internalFieldAccessor().field$ClientboundMoveEntityPacket$entityId(packet);
if (BukkitFurnitureManager.instance().isFurnitureRealEntity(entityId)) {
event.setCancelled(true);
}
} catch (Exception e) {
CraftEngine.instance().logger().warn("Failed to handle ClientboundMoveEntityPacket", e);
}
};
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> MOVE_POS_AND_ROTATE_ENTITY = (user, event, packet) -> {
try {
int entityId = BukkitInjector.internalFieldAccessor().field$ClientboundMoveEntityPacket$entityId(packet);
EntityPacketHandler handler = user.entityPacketHandlers().get(entityId);
if (handler != null) {
handler.handleMoveAndRotate(user, event, packet);
}
} catch (Exception e) {
CraftEngine.instance().logger().warn("Failed to handle ClientboundMoveEntityPacket$PosRot", e);
}
};
}

View File

@@ -0,0 +1,62 @@
package net.momirealms.craftengine.bukkit.plugin.network.handler;
import net.kyori.adventure.text.Component;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.util.ComponentUtils;
import net.momirealms.craftengine.bukkit.util.EntityDataUtils;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.config.Config;
import net.momirealms.craftengine.core.plugin.network.ByteBufPacketEvent;
import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler;
import net.momirealms.craftengine.core.plugin.network.NetWorkUser;
import net.momirealms.craftengine.core.util.AdventureHelper;
import net.momirealms.craftengine.core.util.FriendlyByteBuf;
import java.util.List;
import java.util.Map;
import java.util.Optional;
public class ArmorStandPacketHandler implements EntityPacketHandler {
public static final ArmorStandPacketHandler INSTANCE = new ArmorStandPacketHandler();
@Override
public void handleSetEntityData(NetWorkUser user, ByteBufPacketEvent event) {
if (!Config.interceptArmorStand()) {
return;
}
FriendlyByteBuf buf = event.getBuffer();
int id = buf.readVarInt();
boolean isChanged = false;
List<Object> packedItems = FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$unpack(buf);
for (int i = 0; i < packedItems.size(); i++) {
Object packedItem = packedItems.get(i);
int entityDataId = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$id(packedItem);
if (entityDataId == EntityDataUtils.CUSTOM_NAME_DATA_ID) {
@SuppressWarnings("unchecked")
Optional<Object> optionalTextComponent = (Optional<Object>) FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem);
if (optionalTextComponent.isPresent()) {
Object textComponent = optionalTextComponent.get();
String json = ComponentUtils.minecraftToJson(textComponent);
Map<String, Component> tokens = CraftEngine.instance().fontManager().matchTags(json);
if (!tokens.isEmpty()) {
Component component = AdventureHelper.jsonToComponent(json);
for (Map.Entry<String, Component> token : tokens.entrySet()) {
component = component.replaceText(b -> b.matchLiteral(token.getKey()).replacement(token.getValue()));
}
Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem);
packedItems.set(i, FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue(entityDataId, serializer, Optional.of(ComponentUtils.adventureToMinecraft(component))));
isChanged = true;
break;
}
}
}
}
if (isChanged) {
event.setChanged(true);
buf.clear();
buf.writeVarInt(event.packetID());
buf.writeVarInt(id);
FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$pack(packedItems, buf);
}
}
}

View File

@@ -0,0 +1,76 @@
package net.momirealms.craftengine.bukkit.plugin.network.handler;
import net.kyori.adventure.text.Component;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.network.PacketConsumers;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.ComponentUtils;
import net.momirealms.craftengine.bukkit.util.EntityDataUtils;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.config.Config;
import net.momirealms.craftengine.core.plugin.network.ByteBufPacketEvent;
import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler;
import net.momirealms.craftengine.core.plugin.network.NetWorkUser;
import net.momirealms.craftengine.core.util.AdventureHelper;
import net.momirealms.craftengine.core.util.FriendlyByteBuf;
import java.util.List;
import java.util.Map;
import java.util.Optional;
public class BlockDisplayPacketHandler implements EntityPacketHandler {
public static final BlockDisplayPacketHandler INSTANCE = new BlockDisplayPacketHandler();
@Override
public void handleSetEntityData(NetWorkUser user, ByteBufPacketEvent event) {
FriendlyByteBuf buf = event.getBuffer();
int id = buf.readVarInt();
boolean isChanged = false;
List<Object> packedItems = FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$unpack(buf);
for (int i = 0; i < packedItems.size(); i++) {
Object packedItem = packedItems.get(i);
int entityDataId = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$id(packedItem);
if (entityDataId == EntityDataUtils.BLOCK_STATE_DATA_ID) {
Object blockState = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem);
int stateId = BlockStateUtils.blockStateToId(blockState);
int newStateId;
if (!user.clientModEnabled()) {
newStateId = PacketConsumers.remap(stateId);
} else {
newStateId = PacketConsumers.remapMOD(stateId);
}
Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem);
packedItems.set(i, FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue(
entityDataId, serializer, BlockStateUtils.idToBlockState(newStateId)
));
isChanged = true;
} else if (Config.interceptEntityName() && entityDataId == EntityDataUtils.CUSTOM_NAME_DATA_ID) {
@SuppressWarnings("unchecked")
Optional<Object> optionalTextComponent = (Optional<Object>) FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem);
if (optionalTextComponent.isPresent()) {
Object textComponent = optionalTextComponent.get();
String json = ComponentUtils.minecraftToJson(textComponent);
Map<String, Component> tokens = CraftEngine.instance().fontManager().matchTags(json);
if (!tokens.isEmpty()) {
Component component = AdventureHelper.jsonToComponent(json);
for (Map.Entry<String, Component> token : tokens.entrySet()) {
component = component.replaceText(b -> b.matchLiteral(token.getKey()).replacement(token.getValue()));
}
Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem);
packedItems.set(i, FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue(
entityDataId, serializer, Optional.of(ComponentUtils.adventureToMinecraft(component))
));
isChanged = true;
}
}
}
}
if (isChanged) {
event.setChanged(true);
buf.clear();
buf.writeVarInt(event.packetID());
buf.writeVarInt(id);
FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$pack(packedItems, buf);
}
}
}

View File

@@ -0,0 +1,14 @@
package net.momirealms.craftengine.bukkit.plugin.network.handler;
import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler;
import net.momirealms.craftengine.core.plugin.network.NMSPacketEvent;
import net.momirealms.craftengine.core.plugin.network.NetWorkUser;
public class FurnitureCollisionPacketHandler implements EntityPacketHandler {
public static final FurnitureCollisionPacketHandler INSTANCE = new FurnitureCollisionPacketHandler();
@Override
public void handleSyncEntityPosition(NetWorkUser user, NMSPacketEvent event, Object packet) {
event.setCancelled(true);
}
}

View File

@@ -0,0 +1,27 @@
package net.momirealms.craftengine.bukkit.plugin.network.handler;
import it.unimi.dsi.fastutil.ints.IntList;
import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler;
import net.momirealms.craftengine.core.plugin.network.NMSPacketEvent;
import net.momirealms.craftengine.core.plugin.network.NetWorkUser;
import java.util.List;
public class FurniturePacketHandler implements EntityPacketHandler {
private final List<Integer> fakeEntities;
public FurniturePacketHandler(List<Integer> fakeEntities) {
this.fakeEntities = fakeEntities;
}
@Override
public boolean handleEntitiesRemove(IntList entityIds) {
entityIds.addAll(this.fakeEntities);
return true;
}
@Override
public void handleSyncEntityPosition(NetWorkUser user, NMSPacketEvent event, Object packet) {
event.setCancelled(true);
}
}

View File

@@ -0,0 +1,140 @@
package net.momirealms.craftengine.bukkit.plugin.network.handler;
import net.momirealms.craftengine.bukkit.entity.data.ItemDisplayEntityData;
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.injector.BukkitInjector;
import net.momirealms.craftengine.bukkit.util.Reflections;
import net.momirealms.craftengine.core.entity.projectile.CustomProjectile;
import net.momirealms.craftengine.core.entity.projectile.ProjectileMeta;
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.plugin.network.ByteBufPacketEvent;
import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler;
import net.momirealms.craftengine.core.plugin.network.NMSPacketEvent;
import net.momirealms.craftengine.core.plugin.network.NetWorkUser;
import net.momirealms.craftengine.core.util.FriendlyByteBuf;
import net.momirealms.craftengine.core.util.MCUtils;
import net.momirealms.craftengine.core.util.VersionHelper;
import org.bukkit.inventory.ItemStack;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
public class ProjectilePacketHandler implements EntityPacketHandler {
private final CustomProjectile projectile;
private final Object cachedPacket;
private final List<Object> cachedData;
public ProjectilePacketHandler(CustomProjectile projectile, int entityId) {
this.projectile = projectile;
this.cachedData = createCustomProjectileEntityDataValues();
this.cachedPacket = FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityId, this.cachedData);
}
@Override
public void handleSetEntityData(NetWorkUser user, ByteBufPacketEvent event) {
FriendlyByteBuf buf = event.getBuffer();
int id = buf.readVarInt();
event.setChanged(true);
buf.clear();
buf.writeVarInt(event.packetID());
buf.writeVarInt(id);
FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$pack(this.cachedData, buf);
}
@Override
public void handleSyncEntityPosition(NetWorkUser user, NMSPacketEvent event, Object packet) {
Object converted = convertCustomProjectilePositionSyncPacket(packet);
event.replacePacket(converted);
}
@Override
public void handleMoveAndRotate(NetWorkUser user, NMSPacketEvent event, Object packet) {
int entityId = BukkitInjector.internalFieldAccessor().field$ClientboundMoveEntityPacket$entityId(packet);
event.replacePacket(FastNMS.INSTANCE.constructor$ClientboundBundlePacket(List.of(
this.cachedPacket,
convertCustomProjectileMovePacket(packet, entityId)
)));
}
public Object convertAddCustomProjectilePacket(Object packet) {
int entityId = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$entityId(packet);
UUID uuid = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$uuid(packet);
double x = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$x(packet);
double y = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$y(packet);
double z = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$z(packet);
float yRot = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$yRot(packet);
float xRot = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$xRot(packet);
Object type = Reflections.instance$EntityType$ITEM_DISPLAY;
int data = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$data(packet);
double xa = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$xa(packet);
double ya = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$ya(packet);
double za = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$za(packet);
double yHeadRot = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$yHeadRot(packet);
return FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(entityId, uuid, x, y, z, MCUtils.clamp(-xRot, -90.0F, 90.0F), -yRot, type, data, FastNMS.INSTANCE.constructor$Vec3(xa, ya, za), yHeadRot);
}
private Object convertCustomProjectilePositionSyncPacket(Object packet) {
int entityId = FastNMS.INSTANCE.method$ClientboundEntityPositionSyncPacket$id(packet);
Object positionMoveRotation = FastNMS.INSTANCE.field$ClientboundEntityPositionSyncPacket$values(packet);
boolean onGround = FastNMS.INSTANCE.field$ClientboundEntityPositionSyncPacket$onGround(packet);
Object position = FastNMS.INSTANCE.field$PositionMoveRotation$position(positionMoveRotation);
Object deltaMovement = FastNMS.INSTANCE.field$PositionMoveRotation$deltaMovement(positionMoveRotation);
float yRot = FastNMS.INSTANCE.field$PositionMoveRotation$yRot(positionMoveRotation);
float xRot = FastNMS.INSTANCE.field$PositionMoveRotation$xRot(positionMoveRotation);
Object newPositionMoveRotation = FastNMS.INSTANCE.constructor$PositionMoveRotation(position, deltaMovement, -yRot, Math.clamp(-xRot, -90.0F, 90.0F));
return FastNMS.INSTANCE.constructor$ClientboundEntityPositionSyncPacket(entityId, newPositionMoveRotation, onGround);
}
public List<Object> createCustomProjectileEntityDataValues() {
List<Object> itemDisplayValues = new ArrayList<>();
Optional<CustomItem<ItemStack>> customItem = BukkitItemManager.instance().getCustomItem(this.projectile.metadata().item());
if (customItem.isEmpty()) return itemDisplayValues;
ProjectileMeta meta = this.projectile.metadata();
Item<?> displayedItem = customItem.get().buildItem(ItemBuildContext.EMPTY);
// 我们应当使用新的展示物品的组件覆盖原物品的组件,以完成附魔,附魔光效等组件的继承
displayedItem = this.projectile.item().mergeCopy(displayedItem);
ItemDisplayEntityData.InterpolationDelay.addEntityDataIfNotDefaultValue(-1, itemDisplayValues);
ItemDisplayEntityData.Translation.addEntityDataIfNotDefaultValue(meta.translation(), itemDisplayValues);
ItemDisplayEntityData.Scale.addEntityDataIfNotDefaultValue(meta.scale(), itemDisplayValues);
ItemDisplayEntityData.RotationLeft.addEntityDataIfNotDefaultValue(meta.rotation(), itemDisplayValues);
if (VersionHelper.isOrAbove1_20_2()) {
ItemDisplayEntityData.TransformationInterpolationDuration.addEntityDataIfNotDefaultValue(1, itemDisplayValues);
ItemDisplayEntityData.PositionRotationInterpolationDuration.addEntityDataIfNotDefaultValue(1, itemDisplayValues);
} else {
ItemDisplayEntityData.InterpolationDuration.addEntityDataIfNotDefaultValue(1, itemDisplayValues);
}
ItemDisplayEntityData.DisplayedItem.addEntityDataIfNotDefaultValue(displayedItem.getLiteralObject(), itemDisplayValues);
ItemDisplayEntityData.DisplayType.addEntityDataIfNotDefaultValue(meta.displayType().id(), itemDisplayValues);
return itemDisplayValues;
}
private Object convertCustomProjectileMovePacket(Object packet, int entityId) {
short xa = FastNMS.INSTANCE.field$ClientboundMoveEntityPacket$xa(packet);
short ya = FastNMS.INSTANCE.field$ClientboundMoveEntityPacket$ya(packet);
short za = FastNMS.INSTANCE.field$ClientboundMoveEntityPacket$za(packet);
float xRot = MCUtils.unpackDegrees(FastNMS.INSTANCE.field$ClientboundMoveEntityPacket$xRot(packet));
float yRot = MCUtils.unpackDegrees(FastNMS.INSTANCE.field$ClientboundMoveEntityPacket$yRot(packet));
boolean onGround = FastNMS.INSTANCE.field$ClientboundMoveEntityPacket$onGround(packet);
return FastNMS.INSTANCE.constructor$ClientboundMoveEntityPacket$PosRot(
entityId, xa, ya, za,
MCUtils.packDegrees(-yRot), MCUtils.packDegrees(MCUtils.clamp(-xRot, -90.0F, 90.0F)),
onGround
);
}
private Object convertCustomProjectileTeleportPacket(Object packet, int entityId) {
float xRot = MCUtils.unpackDegrees(FastNMS.INSTANCE.field$ClientboundMoveEntityPacket$xRot(packet));
float yRot = MCUtils.unpackDegrees(FastNMS.INSTANCE.field$ClientboundMoveEntityPacket$yRot(packet));
boolean onGround = FastNMS.INSTANCE.field$ClientboundMoveEntityPacket$onGround(packet);
return FastNMS.INSTANCE.constructor$ClientboundTeleportEntityPacket(
entityId, this.projectile.projectile().x(), this.projectile.projectile().y(), this.projectile.projectile().z(),
MCUtils.packDegrees(-yRot), MCUtils.packDegrees(MCUtils.clamp(-xRot, -90.0F, 90.0F)),
onGround
);
}
}

View File

@@ -0,0 +1,59 @@
package net.momirealms.craftengine.bukkit.plugin.network.handler;
import net.kyori.adventure.text.Component;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.util.ComponentUtils;
import net.momirealms.craftengine.bukkit.util.EntityDataUtils;
import net.momirealms.craftengine.bukkit.util.Reflections;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.config.Config;
import net.momirealms.craftengine.core.plugin.network.ByteBufPacketEvent;
import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler;
import net.momirealms.craftengine.core.plugin.network.NetWorkUser;
import net.momirealms.craftengine.core.util.AdventureHelper;
import net.momirealms.craftengine.core.util.FriendlyByteBuf;
import java.util.List;
import java.util.Map;
public class TextDisplayPacketHandler implements EntityPacketHandler {
public static final TextDisplayPacketHandler INSTANCE = new TextDisplayPacketHandler();
@Override
public void handleSetEntityData(NetWorkUser user, ByteBufPacketEvent event) {
if (!Config.interceptTextDisplay()) {
return;
}
FriendlyByteBuf buf = event.getBuffer();
int id = buf.readVarInt();
boolean isChanged = false;
List<Object> packedItems = FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$unpack(buf);
for (int i = 0; i < packedItems.size(); i++) {
Object packedItem = packedItems.get(i);
int entityDataId = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$id(packedItem);
if (entityDataId == EntityDataUtils.TEXT_DATA_ID) {
Object textComponent = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem);
if (textComponent == Reflections.instance$Component$empty) break;
String json = ComponentUtils.minecraftToJson(textComponent);
Map<String, Component> tokens = CraftEngine.instance().fontManager().matchTags(json);
if (!tokens.isEmpty()) {
Component component = AdventureHelper.jsonToComponent(json);
for (Map.Entry<String, Component> token : tokens.entrySet()) {
component = component.replaceText(b -> b.matchLiteral(token.getKey()).replacement(token.getValue()));
}
Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem);
packedItems.set(i, FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue(entityDataId, serializer, ComponentUtils.adventureToMinecraft(component)));
isChanged = true;
break;
}
}
}
if (isChanged) {
event.setChanged(true);
buf.clear();
buf.writeVarInt(event.packetID());
buf.writeVarInt(id);
FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$pack(packedItems, buf);
}
}
}

View File

@@ -7,17 +7,20 @@ import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.plugin.gui.CraftEngineInventoryHolder;
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.GameMode;
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.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.config.Config;
import net.momirealms.craftengine.core.plugin.network.ConnectionState;
import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler;
import net.momirealms.craftengine.core.plugin.network.ProtocolVersion;
import net.momirealms.craftengine.core.util.Direction;
import net.momirealms.craftengine.core.util.Key;
@@ -25,7 +28,10 @@ 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.WorldEvents;
import org.bukkit.*;
import org.bukkit.FluidCollisionMode;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.SoundCategory;
import org.bukkit.attribute.Attribute;
import org.bukkit.attribute.AttributeInstance;
import org.bukkit.block.Block;
@@ -89,9 +95,8 @@ public class BukkitServerPlayer extends Player {
// cache interaction range here
private int lastUpdateInteractionRangeTick;
private double cachedInteractionRange;
// for better fake furniture visual sync
private final Map<Integer, List<Integer>> furnitureView = new ConcurrentHashMap<>();
private final Map<Integer, Object> entityTypeView = new ConcurrentHashMap<>();
private final Map<Integer, EntityPacketHandler> entityTypeView = new ConcurrentHashMap<>();
public BukkitServerPlayer(BukkitCraftEngine plugin, Channel channel) {
this.channel = channel;
@@ -103,13 +108,6 @@ public class BukkitServerPlayer extends Player {
this.serverPlayerRef = new WeakReference<>(FastNMS.INSTANCE.method$CraftPlayer$getHandle(player));
this.uuid = player.getUniqueId();
this.name = player.getName();
// 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
@@ -151,18 +149,18 @@ public class BukkitServerPlayer extends Player {
}
@Override
public boolean isCreativeMode() {
return platformPlayer().getGameMode() == GameMode.CREATIVE;
public GameMode gameMode() {
return switch (platformPlayer().getGameMode()) {
case CREATIVE -> GameMode.CREATIVE;
case SPECTATOR -> GameMode.SPECTATOR;
case ADVENTURE -> GameMode.ADVENTURE;
case SURVIVAL -> GameMode.SURVIVAL;
};
}
@Override
public boolean isSpectatorMode() {
return platformPlayer().getGameMode() == GameMode.SPECTATOR;
}
@Override
public boolean isAdventureMode() {
return platformPlayer().getGameMode() == GameMode.ADVENTURE;
public void setGameMode(GameMode gameMode) {
platformPlayer().setGameMode(Objects.requireNonNull(org.bukkit.GameMode.getByValue(gameMode.id())));
}
@Override
@@ -177,12 +175,22 @@ public class BukkitServerPlayer extends Player {
@Override
public void sendActionBar(Component text) {
try {
Object packet = Reflections.constructor$ClientboundSetActionBarTextPacket.newInstance(ComponentUtils.adventureToMinecraft(text));
sendPacket(packet, false);
} catch (ReflectiveOperationException e) {
plugin.logger().warn("Failed to send action bar", e);
}
Object packet = FastNMS.INSTANCE.constructor$ClientboundActionBarPacket(ComponentUtils.adventureToMinecraft(text));
sendPacket(packet, false);
}
@Override
public void sendTitle(Component title, Component subtitle, int fadeIn, int stay, int fadeOut) {
Object titlePacket = FastNMS.INSTANCE.constructor$ClientboundSetTitleTextPacket(ComponentUtils.adventureToMinecraft(title));
Object subtitlePacket = FastNMS.INSTANCE.constructor$ClientboundSetSubtitleTextPacket(ComponentUtils.adventureToMinecraft(subtitle));
Object timePacket = FastNMS.INSTANCE.constructor$ClientboundSetTitlesAnimationPacket(fadeIn, stay, fadeOut);
sendPackets(List.of(titlePacket, subtitlePacket, timePacket), false);
}
@Override
public void sendMessage(Component text, boolean overlay) {
Object packet = FastNMS.INSTANCE.constructor$ClientboundSystemChatPacket(ComponentUtils.adventureToMinecraft(text), overlay);
sendPacket(packet, false);
}
@Override
@@ -334,6 +342,9 @@ public class BukkitServerPlayer extends Player {
} else {
this.gameTicks = FastNMS.INSTANCE.field$MinecraftServer$currentTick();
}
if (this.gameTicks % 30 == 0) {
this.updateGUI();
}
if (this.isDestroyingBlock) {
this.tickBlockDestroy();
}
@@ -350,6 +361,13 @@ public class BukkitServerPlayer extends Player {
}
}
private void updateGUI() {
org.bukkit.inventory.Inventory top = !VersionHelper.isOrAbove1_21() ? LegacyInventoryUtils.getTopInventory(platformPlayer()) : platformPlayer().getOpenInventory().getTopInventory();
if (top.getHolder() instanceof CraftEngineInventoryHolder holder) {
holder.gui().onTimer();
}
}
@Override
public float getDestroyProgress(Object blockState, BlockPos pos) {
return FastNMS.INSTANCE.method$BlockStateBase$getDestroyProgress(blockState, serverPlayer(), FastNMS.INSTANCE.field$CraftWorld$ServerLevel(platformPlayer().getWorld()), LocationUtils.toBlockPos(pos));
@@ -653,12 +671,12 @@ public class BukkitServerPlayer extends Player {
@Override
public float getYRot() {
return platformPlayer().getLocation().getPitch();
return platformPlayer().getPitch();
}
@Override
public float getXRot() {
return platformPlayer().getLocation().getYaw();
return platformPlayer().getYaw();
}
@Override
@@ -685,17 +703,17 @@ public class BukkitServerPlayer extends Player {
@Override
public double x() {
return platformPlayer().getLocation().getX();
return platformPlayer().getX();
}
@Override
public double y() {
return platformPlayer().getLocation().getY();
return platformPlayer().getY();
}
@Override
public double z() {
return platformPlayer().getLocation().getZ();
return platformPlayer().getZ();
}
@Override
@@ -716,12 +734,7 @@ public class BukkitServerPlayer extends Player {
}
@Override
public Map<Integer, List<Integer>> furnitureView() {
return this.furnitureView;
}
@Override
public Map<Integer, Object> entityView() {
public Map<Integer, EntityPacketHandler> entityPacketHandlers() {
return this.entityTypeView;
}
@@ -787,7 +800,6 @@ public class BukkitServerPlayer extends Player {
@Override
public void clearView() {
this.entityTypeView.clear();
this.furnitureView.clear();
}
@Override
@@ -816,4 +828,9 @@ public class BukkitServerPlayer extends Player {
return LegacyAttributeUtils.getLuck(platformPlayer());
}
}
@Override
public boolean isFlying() {
return platformPlayer().isFlying();
}
}

View File

@@ -101,8 +101,11 @@ public class BlockStateUtils {
}
public static Key getBlockOwnerId(Block block) {
BlockData data = block.getBlockData();
Object blockState = blockDataToBlockState(data);
return getBlockOwnerId(block.getBlockData());
}
public static Key getBlockOwnerId(BlockData block) {
Object blockState = blockDataToBlockState(block);
return getBlockOwnerIdFromState(blockState);
}
@@ -188,8 +191,12 @@ public class BlockStateUtils {
Reflections.field$BlockStateBase$replaceable.set(state, replaceable);
}
public static boolean isReplaceable(Object state) throws ReflectiveOperationException {
return (boolean) Reflections.field$BlockStateBase$replaceable.get(state);
public static boolean isReplaceable(Object state) {
try {
return (boolean) Reflections.field$BlockStateBase$replaceable.get(state);
} catch (ReflectiveOperationException e) {
throw new RuntimeException("Failed to get replaceable property", e);
}
}
public static void setCanOcclude(Object state, boolean canOcclude) throws ReflectiveOperationException {

View File

@@ -273,9 +273,10 @@ public class InteractUtils {
}
}
public static boolean isInteractable(Key block, org.bukkit.entity.Player player, BlockData state, BlockHitResult hit, Item<ItemStack> item) {
if (INTERACTIONS.containsKey(block)) {
return INTERACTIONS.get(block).apply(player, item, state, hit);
public static boolean isInteractable(Player player, BlockData state, BlockHitResult hit, Item<ItemStack> item) {
Key blockType = BlockStateUtils.getBlockOwnerId(state);
if (INTERACTIONS.containsKey(blockType)) {
return INTERACTIONS.get(blockType).apply(player, item, state, hit);
} else {
return false;
}

View File

@@ -23,4 +23,8 @@ public class KeyUtils {
public static Object toResourceLocation(Key key) {
return toResourceLocation(key.namespace(), key.value());
}
public static NamespacedKey toNamespacedKey(Key key) {
return new NamespacedKey(key.namespace(), key.value());
}
}

View File

@@ -23,16 +23,11 @@ public class LightUtils {
List<Object> players = FastNMS.INSTANCE.method$ChunkHolder$getPlayers(chunkHolder);
if (players.isEmpty()) continue;
Object lightEngine = Reflections.field$ChunkHolder$lightEngine.get(chunkHolder);
BitSet blockChangedLightSectionFilter = (BitSet) Reflections.field$ChunkHolder$blockChangedLightSectionFilter.get(chunkHolder);
blockChangedLightSectionFilter.or(entry.getValue());
BitSet skyChangedLightSectionFilter = (BitSet) Reflections.field$ChunkHolder$skyChangedLightSectionFilter.get(chunkHolder);
Object chunkPos = FastNMS.INSTANCE.constructor$ChunkPos((int) chunkKey, (int) (chunkKey >> 32));
Object lightPacket = FastNMS.INSTANCE.constructor$ClientboundLightUpdatePacket(chunkPos, lightEngine, skyChangedLightSectionFilter, blockChangedLightSectionFilter);
Object lightPacket = FastNMS.INSTANCE.constructor$ClientboundLightUpdatePacket(chunkPos, lightEngine, entry.getValue(), entry.getValue());
for (Object player : players) {
FastNMS.INSTANCE.sendPacket(player, lightPacket);
}
blockChangedLightSectionFilter.clear();
skyChangedLightSectionFilter.clear();
}
} catch (Exception e) {
CraftEngine.instance().logger().warn("Could not update light for world " + world.getName());

View File

@@ -1,15 +1,26 @@
package net.momirealms.craftengine.bukkit.util;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.Vec3d;
import net.momirealms.craftengine.core.world.WorldPosition;
import org.bukkit.Location;
import org.bukkit.World;
import org.jetbrains.annotations.NotNull;
public class LocationUtils {
private LocationUtils() {}
public static Location toLocation(WorldPosition position) {
return new Location((World) position.world().platformWorld(), position.x(), position.y(), position.z(), position.xRot(), position.yRot());
}
public static WorldPosition toWorldPosition(Location location) {
return new WorldPosition(new BukkitWorld(location.getWorld()), location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch());
}
public static Vec3d toVec3d(Location loc) {
return new Vec3d(loc.getX(), loc.getY(), loc.getZ());
}
@@ -27,11 +38,7 @@ public class LocationUtils {
}
public static Object above(Object blockPos) throws ReflectiveOperationException {
return toBlockPos(
FastNMS.INSTANCE.field$Vec3i$x(blockPos),
FastNMS.INSTANCE.field$Vec3i$y(blockPos) + 1,
FastNMS.INSTANCE.field$Vec3i$z(blockPos)
);
return toBlockPos(FastNMS.INSTANCE.field$Vec3i$x(blockPos), FastNMS.INSTANCE.field$Vec3i$y(blockPos) + 1, FastNMS.INSTANCE.field$Vec3i$z(blockPos));
}
public static Object toBlockPos(int x, int y, int z) {

View File

@@ -3,7 +3,8 @@ package net.momirealms.craftengine.bukkit.util;
import net.momirealms.craftengine.core.util.VersionHelper;
import org.bukkit.Particle;
public class ParticleUtils {
public final class ParticleUtils {
private ParticleUtils() {}
public static Particle getParticle(String particle) {
try {
@@ -12,8 +13,12 @@ public class ParticleUtils {
return switch (particle) {
case "REDSTONE" -> Particle.valueOf("DUST");
case "VILLAGER_HAPPY", "HAPPY_VILLAGER" -> Particle.valueOf(VersionHelper.isOrAbove1_20_5() ? "HAPPY_VILLAGER" : "VILLAGER_HAPPY");
case "BUBBLE", "WATER_BUBBLE" -> Particle.valueOf(VersionHelper.isOrAbove1_20_5() ? "BUBBLE" : "WATER_BUBBLE");
default -> Particle.valueOf(particle);
};
}
}
public static final Particle HAPPY_VILLAGER = getParticle("HAPPY_VILLAGER");
public static final Particle BUBBLE = getParticle("BUBBLE");
}

View File

@@ -27,6 +27,7 @@ import sun.misc.Unsafe;
import java.io.BufferedReader;
import java.lang.invoke.VarHandle;
import java.lang.reflect.*;
import java.net.URLClassLoader;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.CompletableFuture;
@@ -558,7 +559,6 @@ public class Reflections {
)
);
@Deprecated
public static final Field field$ClientboundAddEntityPacket$type = requireNonNull(
ReflectionUtils.getDeclaredField(
clazz$ClientboundAddEntityPacket, clazz$EntityType, 0
@@ -3368,11 +3368,23 @@ public class Reflections {
);
public static final Object instance$SoundEvent$EMPTY;
public static final Object instance$SoundEvent$TRIDENT_RIPTIDE_1;
public static final Object instance$SoundEvent$TRIDENT_RIPTIDE_2;
public static final Object instance$SoundEvent$TRIDENT_RIPTIDE_3;
public static final Object instance$SoundEvent$TRIDENT_THROW;
static {
try {
Object key = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "intentionally_empty");
instance$SoundEvent$EMPTY = method$Registry$get.invoke(instance$BuiltInRegistries$SOUND_EVENT, key);
Object intentionallyEmpty = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "intentionally_empty");
instance$SoundEvent$EMPTY = method$Registry$get.invoke(instance$BuiltInRegistries$SOUND_EVENT, intentionallyEmpty);
Object tridentRiptide1 = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "item.trident_riptide_1");
instance$SoundEvent$TRIDENT_RIPTIDE_1 = method$Registry$get.invoke(instance$BuiltInRegistries$SOUND_EVENT, tridentRiptide1);
Object tridentRiptide2 = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "item.trident_riptide_2");
instance$SoundEvent$TRIDENT_RIPTIDE_2 = method$Registry$get.invoke(instance$BuiltInRegistries$SOUND_EVENT, tridentRiptide2);
Object tridentRiptide3 = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "item.trident.riptide_3");
instance$SoundEvent$TRIDENT_RIPTIDE_3 = method$Registry$get.invoke(instance$BuiltInRegistries$SOUND_EVENT, tridentRiptide3);
Object tridentThrow = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "item.trident.throw");
instance$SoundEvent$TRIDENT_THROW = method$Registry$get.invoke(instance$BuiltInRegistries$SOUND_EVENT, tridentThrow);
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
@@ -3810,6 +3822,8 @@ public class Reflections {
public static final Object instance$EntityType$INTERACTION;
public static final Object instance$EntityType$SHULKER;
public static final Object instance$EntityType$OAK_BOAT;
public static final Object instance$EntityType$TRIDENT;
public static final Object instance$EntityType$SNOWBALL;
static {
try {
@@ -3829,6 +3843,10 @@ public class Reflections {
instance$EntityType$ARMOR_STAND = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, armorStand);
Object oakBoat = VersionHelper.isOrAbove1_21_2() ? FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "oak_boat") : FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "boat");
instance$EntityType$OAK_BOAT = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, oakBoat);
Object trident = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "trident");
instance$EntityType$TRIDENT = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, trident);
Object snowball = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "snowball");
instance$EntityType$SNOWBALL = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, snowball);
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
@@ -3979,7 +3997,7 @@ public class Reflections {
)
);
// 1.21.3+
// 1.21.2+
public static final Class<?> clazz$ClientboundEntityPositionSyncPacket =
ReflectionUtils.getClazz(
BukkitReflectionUtils.assembleMCClass("network.protocol.game.ClientboundEntityPositionSyncPacket")
@@ -5264,6 +5282,13 @@ public class Reflections {
)
);
public static final Class<?> clazz$ArmorDyeRecipe = requireNonNull(
BukkitReflectionUtils.findReobfOrMojmapClass(
"world.item.crafting.RecipeArmorDye",
"world.item.crafting.ArmorDyeRecipe"
)
);
public static final Field field$CraftComplexRecipe$recipe = requireNonNull(
ReflectionUtils.getDeclaredField(
clazz$CraftComplexRecipe, clazz$CustomRecipe, 0
@@ -6098,6 +6123,10 @@ public class Reflections {
)
);
public static final Constructor<?> constructor$ClientboundMoveEntityPacket$PosRot = requireNonNull(
ReflectionUtils.getTheOnlyConstructor(clazz$ClientboundMoveEntityPacket$PosRot)
);
public static final Class<?> clazz$ClientboundRotateHeadPacket = requireNonNull(
BukkitReflectionUtils.findReobfOrMojmapClass(
"network.protocol.game.PacketPlayOutEntityHeadRotation",
@@ -6395,14 +6424,20 @@ public class Reflections {
);
public static final int instance$EntityType$BLOCK_DISPLAY$registryId;
public static final int instance$EntityType$ITEM_DISPLAY$registryId;
public static final int instance$EntityType$TEXT_DISPLAY$registryId;
public static final int instance$EntityType$FALLING_BLOCK$registryId;
public static final int instance$EntityType$TRIDENT$registryId;
public static final int instance$EntityType$ARMOR_STAND$registryId;
static {
try {
instance$EntityType$BLOCK_DISPLAY$registryId = (int) Reflections.method$Registry$getId.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, instance$EntityType$BLOCK_DISPLAY);
instance$EntityType$ITEM_DISPLAY$registryId = (int) Reflections.method$Registry$getId.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, instance$EntityType$ITEM_DISPLAY);
instance$EntityType$TEXT_DISPLAY$registryId = (int) Reflections.method$Registry$getId.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, instance$EntityType$TEXT_DISPLAY);
instance$EntityType$FALLING_BLOCK$registryId = (int) Reflections.method$Registry$getId.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, instance$EntityType$FALLING_BLOCK);
instance$EntityType$TRIDENT$registryId = (int) Reflections.method$Registry$getId.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, instance$EntityType$TRIDENT);
instance$EntityType$ARMOR_STAND$registryId = (int) Reflections.method$Registry$getId.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, instance$EntityType$ARMOR_STAND);
} catch (Exception e) {
throw new RuntimeException(e);
}
@@ -6618,4 +6653,143 @@ public class Reflections {
BukkitReflectionUtils.assembleCBClass("block.CraftBlockStates$BlockEntityStateFactory")
)
);
public static final Class<?> clazz$ServerEntity = requireNonNull(
BukkitReflectionUtils.findReobfOrMojmapClass(
"server.level.EntityTrackerEntry",
"server.level.ServerEntity")
);
public static final Field field$ServerEntity$updateInterval = requireNonNull(
ReflectionUtils.getInstanceDeclaredField(
clazz$ServerEntity, int.class, 0
)
);
public static final Class<?> clazz$AbstractArrow = requireNonNull(
BukkitReflectionUtils.findReobfOrMojmapClass(
"world.entity.projectile.EntityArrow",
"world.entity.projectile.AbstractArrow"
)
);
public static final Class<?> clazz$ClientboundCustomPayloadPacket = requireNonNull(
BukkitReflectionUtils.findReobfOrMojmapClass(
List.of("network.protocol.game.PacketPlayOutCustomPayload", "network.protocol.common.ClientboundCustomPayloadPacket"),
List.of("network.protocol.game.ClientboundCustomPayloadPacket", "network.protocol.common.ClientboundCustomPayloadPacket")
)
);
public static final Constructor<?> constructor$ClientboundCustomPayloadPacket = requireNonNull(
ReflectionUtils.getConstructor(clazz$ClientboundCustomPayloadPacket, 0)
);
// 1.20.2+
public static final Constructor<?> constructor$DiscardedPayload = Optional.ofNullable(clazz$DiscardedPayload)
.map(clazz -> ReflectionUtils.getTheOnlyConstructor(clazz))
.orElse(null);
public static final Class<?> clazz$PaperPluginClassLoader = ReflectionUtils.getClazz(
"io.papermc.paper.plugin.entrypoint.classloader.PaperPluginClassLoader"
);
public static final Field field$PaperPluginClassLoader$libraryLoader = Optional.ofNullable(clazz$PaperPluginClassLoader)
.map(it -> ReflectionUtils.getDeclaredField(it, URLClassLoader.class, 0))
.orElse(null);
public static final Method method$SoundSource$values = requireNonNull(
ReflectionUtils.getStaticMethod(
clazz$SoundSource, clazz$SoundSource.arrayType()
)
);
public static final Object instance$SoundSource$MASTER;
public static final Object instance$SoundSource$MUSIC;
public static final Object instance$SoundSource$RECORDS;
public static final Object instance$SoundSource$WEATHER;
public static final Object instance$SoundSource$BLOCKS;
public static final Object instance$SoundSource$HOSTILE;
public static final Object instance$SoundSource$NEUTRAL;
public static final Object instance$SoundSource$PLAYERS;
public static final Object instance$SoundSource$AMBIENT;
public static final Object instance$SoundSource$VOICE;
static {
try {
Object[] values = (Object[]) method$SoundSource$values.invoke(null);
instance$SoundSource$MASTER = values[0];
instance$SoundSource$MUSIC = values[1];
instance$SoundSource$RECORDS = values[2];
instance$SoundSource$WEATHER = values[3];
instance$SoundSource$BLOCKS = values[4];
instance$SoundSource$HOSTILE = values[5];
instance$SoundSource$NEUTRAL = values[6];
instance$SoundSource$PLAYERS = values[7];
instance$SoundSource$AMBIENT = values[8];
instance$SoundSource$VOICE = values[9];
} catch (ReflectiveOperationException e) {
throw new AssertionError(e);
}
}
public static final Class<?> clazz$MoverType = requireNonNull(
BukkitReflectionUtils.findReobfOrMojmapClass(
"world.entity.EnumMoveType",
"world.entity.MoverType"
)
);
public static final Method method$MoverType$values = requireNonNull(
ReflectionUtils.getStaticMethod(
clazz$MoverType, clazz$MoverType.arrayType()
)
);
public static final Object instance$MoverType$SELF;
public static final Object instance$MoverType$PLAYER;
public static final Object instance$MoverType$PISTON;
public static final Object instance$MoverType$SHULKER_BOX;
public static final Object instance$MoverType$SHULKER;
static {
try {
Object[] values = (Object[]) method$MoverType$values.invoke(null);
instance$MoverType$SELF = values[0];
instance$MoverType$PLAYER = values[1];
instance$MoverType$PISTON = values[2];
instance$MoverType$SHULKER_BOX = values[3];
instance$MoverType$SHULKER = values[4];
} catch (ReflectiveOperationException e) {
throw new AssertionError(e);
}
}
public static final Class<?> clazz$AbstractArrow$Pickup = requireNonNull(
BukkitReflectionUtils.findReobfOrMojmapClass(
"world.entity.projectile.EntityArrow$PickupStatus",
"world.entity.projectile.AbstractArrow$Pickup"
)
);
public static final Method method$AbstractArrow$Pickup$values = requireNonNull(
ReflectionUtils.getStaticMethod(
clazz$AbstractArrow$Pickup, clazz$AbstractArrow$Pickup.arrayType()
)
);
public static final Object instance$AbstractArrow$Pickup$DISALLOWED;
public static final Object instance$AbstractArrow$Pickup$ALLOWED;
public static final Object instance$AbstractArrow$Pickup$CREATIVE_ONLY;
static {
try {
Object[] values = (Object[]) method$AbstractArrow$Pickup$values.invoke(null);
instance$AbstractArrow$Pickup$DISALLOWED = values[0];
instance$AbstractArrow$Pickup$ALLOWED = values[1];
instance$AbstractArrow$Pickup$CREATIVE_ONLY = values[2];
} catch (ReflectiveOperationException e) {
throw new AssertionError(e);
}
}
}

View File

@@ -0,0 +1,86 @@
package net.momirealms.craftengine.bukkit.util;
import io.netty.buffer.Unpooled;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.core.util.FriendlyByteBuf;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public final class TagUtils {
private TagUtils() {}
/**
* 构建模拟标签更新数据包(用于向客户端添加虚拟标签)
*
* @param tags 需要添加的标签数据,结构为嵌套映射:
* <pre>{@code
* Map结构示例:
* {
* 注册表键1 (如BuiltInRegistries.ITEM.key) -> {
* "命名空间:值1" -> IntList.of(1, 2, 3), // 该命名空间下生效的物品ID列表
* "命名空间:值2" -> IntList.of(5, 7)
* },
* 注册表键2 (如BuiltInRegistries.BLOCK.key) -> {
* "minecraft:beacon_base_blocks" -> IntList.of(1024, 2048)
* },
* ....
* }
* }</pre>
* 其中:</br>
* - 外层键注册表ResourceKey</br>
* - 中间层键:标签的命名空间:值(字符串)</br>
* - 值包含注册表内项目数字ID的IntList
*
* @return 可发送给客户端的 ClientboundUpdateTagsPacket 数据包对象
*/
@SuppressWarnings("unchecked")
public static Object createUpdateTagsPacket(Map<Object, List<TagEntry>> tags) {
Map<Object, Object> registriesNetworkPayload = (Map<Object, Object>) FastNMS.INSTANCE.method$TagNetworkSerialization$serializeTagsToNetwork();
Map<Object, Object> modified = new HashMap<>();
for (Map.Entry<Object, List<TagEntry>> entry : tags.entrySet()) {
Object existingPayload = registriesNetworkPayload.get(entry.getKey());
if (existingPayload == null) continue;
FriendlyByteBuf deserializeBuf = new FriendlyByteBuf(Unpooled.buffer());
FastNMS.INSTANCE.method$TagNetworkSerialization$NetworkPayload$write(existingPayload, deserializeBuf);
Map<String, IntList> originalTags = deserializeBuf.readMap(
FriendlyByteBuf::readUtf,
FriendlyByteBuf::readIntIdList
);
Map<Integer, List<String>> reversedTags = new HashMap<>();
for (Map.Entry<String, IntList> tagEntry : originalTags.entrySet()) {
for (int id : tagEntry.getValue()) {
reversedTags.computeIfAbsent(id, k -> new ArrayList<>()).add(tagEntry.getKey());
}
}
for (TagEntry tagEntry : entry.getValue()) {
reversedTags.remove(tagEntry.id);
for (String tag : tagEntry.tags) {
reversedTags.computeIfAbsent(tagEntry.id, k -> new ArrayList<>()).add(tag);
}
}
Map<String, IntList> processedTags = new HashMap<>();
for (Map.Entry<Integer, List<String>> tagEntry : reversedTags.entrySet()) {
for (String tag : tagEntry.getValue()) {
processedTags.computeIfAbsent(tag, k -> new IntArrayList()).addLast(tagEntry.getKey());
}
}
FriendlyByteBuf serializeBuf = new FriendlyByteBuf(Unpooled.buffer());
serializeBuf.writeMap(processedTags,
FriendlyByteBuf::writeUtf,
FriendlyByteBuf::writeIntIdList
);
Object mergedPayload = FastNMS.INSTANCE.method$TagNetworkSerialization$NetworkPayload$read(serializeBuf);
modified.put(entry.getKey(), mergedPayload);
}
return FastNMS.INSTANCE.constructor$ClientboundUpdateTagsPacket(modified);
}
public record TagEntry(int id, List<String> tags) {
}
}

View File

@@ -6,9 +6,9 @@ import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
import net.momirealms.craftengine.bukkit.item.behavior.BlockItemBehavior;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.KeyUtils;
import net.momirealms.craftengine.bukkit.util.LocationUtils;
import net.momirealms.craftengine.bukkit.util.Reflections;
import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.item.CustomItem;
import net.momirealms.craftengine.core.item.Item;
@@ -86,21 +86,17 @@ public class BukkitBlockInWorld implements BlockInWorld {
}
@Override
public String getAsString() {
ImmutableBlockState state = CraftEngineBlocks.getCustomBlockState(this.block);
if (state != null) {
return state.toString();
}
return this.block.getBlockData().getAsString();
public ImmutableBlockState customBlockState() {
return CraftEngineBlocks.getCustomBlockState(this.block);
}
@Override
public Key owner() {
public CustomBlock customBlock() {
ImmutableBlockState state = CraftEngineBlocks.getCustomBlockState(this.block);
if (state != null) {
return state.owner().value().id();
return state.owner().value();
}
return KeyUtils.namespacedKey2Key(this.block.getType().getKey());
return null;
}
public Block block() {

View File

@@ -4,10 +4,13 @@ import net.momirealms.craftengine.bukkit.util.LightUtils;
import net.momirealms.craftengine.core.plugin.config.Config;
import net.momirealms.craftengine.core.util.SectionPosUtils;
import net.momirealms.craftengine.core.world.CEWorld;
import net.momirealms.craftengine.core.world.SectionPos;
import net.momirealms.craftengine.core.world.World;
import net.momirealms.craftengine.core.world.chunk.storage.StorageAdaptor;
import net.momirealms.craftengine.core.world.chunk.storage.WorldDataStorage;
import java.util.HashSet;
public class BukkitCEWorld extends CEWorld {
public BukkitCEWorld(World world, StorageAdaptor adaptor) {
@@ -20,9 +23,19 @@ public class BukkitCEWorld extends CEWorld {
@Override
public void tick() {
HashSet<SectionPos> poses;
synchronized (super.updatedSectionSet) {
poses = new HashSet<>(super.updatedSectionSet);
super.updatedSectionSet.clear();
}
if (Config.enableLightSystem()) {
LightUtils.updateChunkLight((org.bukkit.World) world.platformWorld(), SectionPosUtils.toMap(super.updatedSectionPositions, world.worldHeight().getMinSection() - 1, world.worldHeight().getMaxSection() + 1));
super.updatedSectionPositions.clear();
LightUtils.updateChunkLight(
(org.bukkit.World) world.platformWorld(),
SectionPosUtils.toMap(poses,
world.worldHeight().getMinSection() - 1,
world.worldHeight().getMaxSection() + 1
)
);
}
}
}

View File

@@ -7,7 +7,7 @@ import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.VersionHelper;
import net.momirealms.craftengine.core.world.BlockInWorld;
import net.momirealms.craftengine.core.world.Vec3d;
import net.momirealms.craftengine.core.world.Position;
import net.momirealms.craftengine.core.world.World;
import net.momirealms.craftengine.core.world.WorldHeight;
import org.bukkit.Location;
@@ -35,11 +35,7 @@ public class BukkitWorld implements World {
@Override
public Object serverWorld() {
try {
return FastNMS.INSTANCE.field$CraftWorld$ServerLevel(platformWorld());
} catch (Exception e) {
throw new RuntimeException("Failed to get server world", e);
}
return FastNMS.INSTANCE.field$CraftWorld$ServerLevel(platformWorld());
}
@Override
@@ -71,7 +67,7 @@ public class BukkitWorld implements World {
}
@Override
public void dropItemNaturally(Vec3d location, Item<?> item) {
public void dropItemNaturally(Position location, Item<?> item) {
ItemStack itemStack = (ItemStack) item.load();
if (ItemUtils.isEmpty(itemStack)) return;
if (VersionHelper.isOrAbove1_21_2()) {
@@ -82,7 +78,7 @@ public class BukkitWorld implements World {
}
@Override
public void dropExp(Vec3d location, int amount) {
public void dropExp(Position location, int amount) {
if (amount <= 0) return;
EntityUtils.spawnEntity(platformWorld(), new Location(platformWorld(), location.x(), location.y(), location.z()), EntityType.EXPERIENCE_ORB, (e) -> {
ExperienceOrb orb = (ExperienceOrb) e;
@@ -91,7 +87,12 @@ public class BukkitWorld implements World {
}
@Override
public void playBlockSound(Vec3d location, Key sound, float volume, float pitch) {
public void playBlockSound(Position location, Key sound, float volume, float pitch) {
platformWorld().playSound(new Location(null, location.x(), location.y(), location.z()), sound.toString(), SoundCategory.BLOCKS, volume, pitch);
}
@Override
public long time() {
return platformWorld().getTime();
}
}

View File

@@ -32,6 +32,7 @@ import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class BukkitWorldManager implements WorldManager, Listener {
@@ -45,6 +46,7 @@ public class BukkitWorldManager implements WorldManager, Listener {
private UUID lastVisitedUUID;
private CEWorld lastVisitedWorld;
private StorageAdaptor storageAdaptor;
private boolean isTicking = false;
public BukkitWorldManager(BukkitCraftEngine plugin) {
instance = this;
@@ -90,12 +92,20 @@ public class BukkitWorldManager implements WorldManager, Listener {
public void delayedInit() {
// events and tasks
Bukkit.getPluginManager().registerEvents(this, plugin.bootstrap());
this.tickTask = plugin.scheduler().sync().runRepeating(() -> {
for (CEWorld world : worldArray) {
world.tick();
Bukkit.getPluginManager().registerEvents(this, this.plugin.bootstrap());
this.tickTask = this.plugin.scheduler().asyncRepeating(() -> {
try {
if (this.isTicking) {
return;
}
this.isTicking = true;
for (CEWorld world : this.worldArray) {
world.tick();
}
} finally {
this.isTicking = false;
}
}, 1, 1);
}, 50, 50, TimeUnit.MILLISECONDS);
// load loaded chunks
this.worldMapLock.writeLock().lock();
try {
@@ -125,7 +135,6 @@ public class BukkitWorldManager implements WorldManager, Listener {
if (this.tickTask != null && !this.tickTask.cancelled()) {
this.tickTask.cancel();
}
for (World world : Bukkit.getWorlds()) {
CEWorld ceWorld = getWorld(world.getUID());
for (Chunk chunk : world.getLoadedChunks()) {
@@ -299,7 +308,7 @@ public class BukkitWorldManager implements WorldManager, Listener {
}
}
}
if (unsaved && !FastNMS.INSTANCE.method$LevelChunk$isUnsaved(levelChunk)) {
if (unsaved /*&& !FastNMS.INSTANCE.method$LevelChunk$isUnsaved(levelChunk)*/) {
FastNMS.INSTANCE.method$LevelChunk$markUnsaved(levelChunk);
}
ceChunk.unload();

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