mirror of
https://github.com/Xiao-MoMi/craft-engine.git
synced 2025-12-26 10:29:20 +00:00
11
README.md
11
README.md
@@ -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>
|
||||
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
plugins {
|
||||
id("com.gradleup.shadow") version "9.0.0-beta11"
|
||||
id("com.gradleup.shadow") version "9.0.0-beta13"
|
||||
}
|
||||
|
||||
repositories {
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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: "黄玉"
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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"}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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]
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 362 B |
Binary file not shown.
|
After Width: | Height: | Size: 231 B |
Binary file not shown.
|
After Width: | Height: | Size: 548 B |
@@ -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>"
|
||||
@@ -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>"
|
||||
|
||||
@@ -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>"
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 -> {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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() {}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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> {
|
||||
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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("结束测试");
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
}
|
||||
}
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user