@@ -5,9 +5,7 @@ plugins {
|
||||
|
||||
repositories {
|
||||
maven("https://jitpack.io/")
|
||||
maven("https://repo.extendedclip.com/content/repositories/placeholderapi/") // papi
|
||||
maven("https://repo.papermc.io/repository/maven-public/")
|
||||
maven("https://maven.enginehub.org/repo/") // worldguard worldedit
|
||||
maven("https://repo.momirealms.net/releases/")
|
||||
mavenCentral()
|
||||
}
|
||||
@@ -24,8 +22,6 @@ dependencies {
|
||||
compileOnly("net.momirealms:sparrow-util:${rootProject.properties["sparrow_util_version"]}")
|
||||
// NMS
|
||||
compileOnly("net.momirealms:craft-engine-nms-helper:${rootProject.properties["nms_helper_version"]}")
|
||||
// Placeholder
|
||||
compileOnly("me.clip:placeholderapi:${rootProject.properties["placeholder_api_version"]}")
|
||||
// Platform
|
||||
compileOnly("dev.folia:folia-api:${rootProject.properties["paper_version"]}-R0.1-SNAPSHOT")
|
||||
// OpenGL Math
|
||||
@@ -56,8 +52,6 @@ dependencies {
|
||||
compileOnly("net.kyori:adventure-text-serializer-gson:${rootProject.properties["adventure_bundle_version"]}") {
|
||||
exclude("com.google.code.gson", "gson")
|
||||
}
|
||||
compileOnly("com.sk89q.worldedit:worldedit-core:7.2.19")
|
||||
compileOnly("com.sk89q.worldedit:worldedit-bukkit:7.2.19")
|
||||
// Data Fixer Upper
|
||||
compileOnly("com.mojang:datafixerupper:${rootProject.properties["datafixerupper_version"]}")
|
||||
// BStats
|
||||
|
||||
@@ -2,14 +2,27 @@ repositories {
|
||||
mavenCentral()
|
||||
maven("https://repo.papermc.io/repository/maven-public/")
|
||||
maven("https://r.irepo.space/maven/")
|
||||
maven("https://repo.extendedclip.com/content/repositories/placeholderapi/") // papi
|
||||
maven("https://maven.enginehub.org/repo/") // worldguard worldedit
|
||||
maven("https://repo.rapture.pw/repository/maven-releases/") // slime world
|
||||
maven("https://repo.infernalsuite.com/repository/maven-snapshots/") // slime world
|
||||
maven("https://repo.momirealms.net/releases/")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(project(":core"))
|
||||
compileOnly("net.momirealms:sparrow-nbt:${rootProject.properties["sparrow_nbt_version"]}")
|
||||
// Platform
|
||||
compileOnly("dev.folia:folia-api:${rootProject.properties["paper_version"]}-R0.1-SNAPSHOT")
|
||||
// NeigeItems
|
||||
compileOnly("pers.neige.neigeitems:NeigeItems:1.21.42")
|
||||
// Placeholder
|
||||
compileOnly("me.clip:placeholderapi:${rootProject.properties["placeholder_api_version"]}")
|
||||
// WorldEdit
|
||||
compileOnly("com.sk89q.worldedit:worldedit-core:7.2.19")
|
||||
compileOnly("com.sk89q.worldedit:worldedit-bukkit:7.2.19")
|
||||
// SlimeWorld
|
||||
compileOnly("com.infernalsuite.asp:api:4.0.0-SNAPSHOT")
|
||||
}
|
||||
|
||||
java {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package net.momirealms.craftengine.bukkit.plugin.papi;
|
||||
package net.momirealms.craftengine.bukkit.compatibility.papi;
|
||||
|
||||
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
|
||||
import net.momirealms.craftengine.core.font.BitmapImage;
|
||||
@@ -1,6 +1,7 @@
|
||||
package net.momirealms.craftengine.bukkit.util;
|
||||
package net.momirealms.craftengine.bukkit.compatibility.papi;
|
||||
|
||||
import me.clip.placeholderapi.PlaceholderAPI;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
|
||||
public class PlaceholderAPIUtils {
|
||||
@@ -10,4 +11,9 @@ public class PlaceholderAPIUtils {
|
||||
public static String parse(OfflinePlayer player, String text) {
|
||||
return PlaceholderAPI.setPlaceholders(player, text);
|
||||
}
|
||||
|
||||
public static void registerExpansions(CraftEngine plugin) {
|
||||
new ImageExpansion(plugin).register();
|
||||
new ShiftExpansion(plugin).register();
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package net.momirealms.craftengine.bukkit.plugin.papi;
|
||||
package net.momirealms.craftengine.bukkit.compatibility.papi;
|
||||
|
||||
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
@@ -0,0 +1,63 @@
|
||||
package net.momirealms.craftengine.bukkit.compatibility.slimeworld;
|
||||
|
||||
import com.infernalsuite.asp.api.AdvancedSlimePaperAPI;
|
||||
import com.infernalsuite.asp.api.events.LoadSlimeWorldEvent;
|
||||
import com.infernalsuite.asp.api.world.SlimeWorld;
|
||||
import net.momirealms.craftengine.core.util.ReflectionUtils;
|
||||
import net.momirealms.craftengine.core.world.World;
|
||||
import net.momirealms.craftengine.core.world.WorldManager;
|
||||
import net.momirealms.craftengine.core.world.chunk.storage.DefaultStorageAdaptor;
|
||||
import net.momirealms.craftengine.core.world.chunk.storage.WorldDataStorage;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class SlimeFormatStorageAdaptor extends DefaultStorageAdaptor implements Listener {
|
||||
private final WorldManager worldManager;
|
||||
private final Class<?> byteArrayTagClass = ReflectionUtils.getClazz("net{}kyori{}adventure{}nbt{}ByteArrayBinaryTag".replace("{}", "."));
|
||||
private final Method method$ByteArrayBinaryTag$byteArrayBinaryTag = ReflectionUtils.getStaticMethod(byteArrayTagClass, byteArrayTagClass, byte.class.arrayType());
|
||||
private final Method method$ByteArrayBinaryTag$value = ReflectionUtils.getMethod(byteArrayTagClass, byte.class.arrayType());
|
||||
|
||||
@EventHandler
|
||||
public void onWorldLoad(LoadSlimeWorldEvent event) {
|
||||
org.bukkit.World world = Bukkit.getWorld(event.getSlimeWorld().getName());
|
||||
this.worldManager.loadWorld(this.worldManager.createWorld(this.worldManager.wrap(world), new SlimeWorldDataStorage(event.getSlimeWorld(), this)));
|
||||
}
|
||||
|
||||
public SlimeFormatStorageAdaptor(WorldManager worldManager) {
|
||||
this.worldManager = worldManager;
|
||||
}
|
||||
|
||||
public SlimeWorld getWorld(String name) {
|
||||
return AdvancedSlimePaperAPI.instance().getLoadedWorld(name);
|
||||
}
|
||||
|
||||
// 请注意,在加载事件的时候,无法通过AdvancedSlimePaperAPI.instance().getLoadedWorld来判断是否为slime世界
|
||||
@Override
|
||||
public @NotNull WorldDataStorage adapt(@NotNull World world) {
|
||||
SlimeWorld slimeWorld = getWorld(world.name());
|
||||
if (slimeWorld == null) {
|
||||
return super.adapt(world);
|
||||
}
|
||||
return new SlimeWorldDataStorage(slimeWorld, this);
|
||||
}
|
||||
|
||||
public byte[] byteArrayTagToBytes(Object byteArrayTag) {
|
||||
try {
|
||||
return (byte[]) method$ByteArrayBinaryTag$value.invoke(byteArrayTag);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException("Failed to convert byte array tag to byte[]", e);
|
||||
}
|
||||
}
|
||||
|
||||
public Object bytesToByteArrayTag(byte[] bytes) {
|
||||
try {
|
||||
return method$ByteArrayBinaryTag$byteArrayBinaryTag.invoke(null, (Object) bytes);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException("Failed to convert byte array tag to byte[]", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package net.momirealms.craftengine.bukkit.compatibility.slimeworld;
|
||||
|
||||
import com.infernalsuite.asp.api.world.SlimeChunk;
|
||||
import com.infernalsuite.asp.api.world.SlimeWorld;
|
||||
import net.momirealms.craftengine.core.world.ChunkPos;
|
||||
import net.momirealms.craftengine.core.world.chunk.storage.WorldDataStorage;
|
||||
import net.momirealms.sparrow.nbt.CompoundTag;
|
||||
import net.momirealms.sparrow.nbt.NBT;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Map;
|
||||
|
||||
public class SlimeWorldDataStorage implements WorldDataStorage {
|
||||
private final WeakReference<SlimeWorld> slimeWorld;
|
||||
private final SlimeFormatStorageAdaptor adaptor;
|
||||
|
||||
public SlimeWorldDataStorage(SlimeWorld slimeWorld, SlimeFormatStorageAdaptor adaptor) {
|
||||
this.slimeWorld = new WeakReference<>(slimeWorld);
|
||||
this.adaptor = adaptor;
|
||||
}
|
||||
|
||||
public SlimeWorld getWorld() {
|
||||
return slimeWorld.get();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public CompoundTag readChunkTagAt(ChunkPos pos) {
|
||||
SlimeChunk slimeChunk = getWorld().getChunk(pos.x, pos.z);
|
||||
if (slimeChunk == null) return null;
|
||||
Object tag = slimeChunk.getExtraData().get("craftengine");
|
||||
if (tag == null) return null;
|
||||
try {
|
||||
return NBT.readCompound(new DataInputStream(new ByteArrayInputStream(adaptor.byteArrayTagToBytes(tag))));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to read chunk tag from slime world. " + pos, e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void writeChunkTagAt(ChunkPos pos, @Nullable CompoundTag nbt) {
|
||||
SlimeChunk slimeChunk = getWorld().getChunk(pos.x, pos.z);
|
||||
if (slimeChunk == null) return;
|
||||
if (nbt == null) {
|
||||
slimeChunk.getExtraData().remove("craftengine");
|
||||
} else {
|
||||
try {
|
||||
Object tag = adaptor.bytesToByteArrayTag(NBT.toBytes(nbt));
|
||||
Map<String, ?> data1 = slimeChunk.getExtraData();
|
||||
Map<String, Object> data2 = (Map<String, Object>) data1;
|
||||
data2.put("craftengine", tag);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to write chunk tag to slime world. " + pos, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package net.momirealms.craftengine.bukkit.block.worldedit;
|
||||
package net.momirealms.craftengine.bukkit.compatibility.worldedit;
|
||||
|
||||
import com.sk89q.worldedit.bukkit.BukkitBlockRegistry;
|
||||
import com.sk89q.worldedit.util.concurrency.LazyReference;
|
||||
@@ -14,9 +14,7 @@
|
||||
# Q: When do I need to configure this file?
|
||||
# A: When the number of real block IDs is insufficient, but there are still available appearances.
|
||||
|
||||
# By default, the plugin only registers an additional 112 oak leaf block states (for the default configuration needs [>=28 states]).
|
||||
minecraft:oak_leaves: 112
|
||||
|
||||
minecraft:oak_sapling: 1
|
||||
minecraft:birch_sapling: 1
|
||||
minecraft:spruce_sapling: 1
|
||||
@@ -24,5 +22,5 @@ minecraft:jungle_sapling: 1
|
||||
minecraft:dark_oak_sapling: 1
|
||||
minecraft:acacia_sapling: 1
|
||||
minecraft:cherry_sapling: 1
|
||||
|
||||
minecraft:anvil: 2
|
||||
minecraft:anvil: 2
|
||||
minecraft:sugarcane: 14
|
||||
@@ -59,5 +59,7 @@ categories:
|
||||
- default:netherite_anvil
|
||||
- default:fairy_flower
|
||||
- default:reed
|
||||
- default:flame_cane
|
||||
- default:gunpowder_block
|
||||
- default:solid_gunpowder_block
|
||||
- default:solid_gunpowder_block
|
||||
- default:ender_pearl_flower_seeds
|
||||
@@ -3,6 +3,8 @@ i18n:
|
||||
item.chinese_lantern: "Chinese Lantern"
|
||||
item.fairy_flower: "Fairy Flower"
|
||||
item.reed: "Reed"
|
||||
item.flame_cane: "Flame Cane"
|
||||
item.ender_pearl_flower_seeds: "Ender Pearl Flower Seeds"
|
||||
item.bench: "Bench"
|
||||
item.table_lamp: "Table Lamp"
|
||||
item.wooden_chair: "Wooden Chair"
|
||||
@@ -41,6 +43,8 @@ i18n:
|
||||
item.chinese_lantern: "灯笼"
|
||||
item.fairy_flower: "仙灵花"
|
||||
item.reed: "芦苇"
|
||||
item.flame_cane: "烈焰甘蔗"
|
||||
item.ender_pearl_flower_seeds: "末影珍珠花种子"
|
||||
item.bench: "长椅"
|
||||
item.table_lamp: "台灯"
|
||||
item.wooden_chair: "木椅"
|
||||
|
||||
@@ -2,7 +2,7 @@ images:
|
||||
default:icons:
|
||||
height: 10
|
||||
ascent: 9
|
||||
font: minecraft:default # Consider using other fonts if other plugins support custom font!
|
||||
font: minecraft:icons # Do not use 'minecraft:default' unless other plugins don't support custom font!
|
||||
file: minecraft:font/image/icons.png
|
||||
chars:
|
||||
- '\ub000\ub001'
|
||||
|
||||
@@ -23,6 +23,30 @@ items:
|
||||
behavior:
|
||||
type: liquid_collision_block_item
|
||||
block: default:reed
|
||||
default:flame_cane:
|
||||
material: paper
|
||||
custom-model-data: 4002
|
||||
data:
|
||||
item-name: "<!i><i18n:item.flame_cane>"
|
||||
model:
|
||||
template: default:model/simplified_generated
|
||||
arguments:
|
||||
path: "minecraft:item/custom/flame_cane"
|
||||
behavior:
|
||||
type: block_item
|
||||
block: default:flame_cane
|
||||
default:ender_pearl_flower_seeds:
|
||||
material: paper
|
||||
custom-model-data: 4003
|
||||
data:
|
||||
item-name: "<!i><i18n:item.ender_pearl_flower_seeds>"
|
||||
model:
|
||||
template: default:model/simplified_generated
|
||||
arguments:
|
||||
path: "minecraft:item/custom/ender_pearl_flower_seeds"
|
||||
behavior:
|
||||
type: block_item
|
||||
block: default:ender_pearl_flower
|
||||
blocks:
|
||||
default:fairy_flower:
|
||||
settings:
|
||||
@@ -32,8 +56,12 @@ blocks:
|
||||
overrides:
|
||||
item: default:fairy_flower
|
||||
push-reaction: DESTROY
|
||||
map-color: 19
|
||||
behavior:
|
||||
type: bush_block
|
||||
bottom-block-tags:
|
||||
- minecraft:dirt
|
||||
- minecraft:farmland
|
||||
loot:
|
||||
template: "default:loot_table/basic"
|
||||
arguments:
|
||||
@@ -70,6 +98,7 @@ blocks:
|
||||
overrides:
|
||||
item: default:reed
|
||||
push-reaction: DESTROY
|
||||
map-color: 60
|
||||
behavior:
|
||||
type: on_liquid_block
|
||||
liquid-type: water
|
||||
@@ -82,6 +111,137 @@ blocks:
|
||||
state: sugar_cane:1
|
||||
model:
|
||||
path: "minecraft:block/custom/reed"
|
||||
default:flame_cane:
|
||||
settings:
|
||||
template:
|
||||
- default:hardness/none
|
||||
- default:sound/grass
|
||||
overrides:
|
||||
item: default:flame_cane
|
||||
push-reaction: DESTROY
|
||||
map-color: 15
|
||||
is-randomly-ticking: true
|
||||
behavior:
|
||||
type: sugar_cane_block
|
||||
max-height: 4
|
||||
required-adjacent-liquids: lava
|
||||
grow-speed: 0.333
|
||||
bottom-blocks:
|
||||
- minecraft:netherrack
|
||||
- minecraft:soul_sand
|
||||
- minecraft:soul_soil
|
||||
- minecraft:magma_block
|
||||
- minecraft:warped_nylium
|
||||
- minecraft:crimson_nylium
|
||||
- minecraft:basalt
|
||||
loot:
|
||||
template: "default:loot_table/basic"
|
||||
arguments:
|
||||
item: default:flame_cane
|
||||
states:
|
||||
properties:
|
||||
age:
|
||||
type: int
|
||||
default: 0
|
||||
range: 0~5
|
||||
appearances:
|
||||
default:
|
||||
state: "sugar_cane:2"
|
||||
models:
|
||||
- path: "minecraft:block/custom/flame_cane_1"
|
||||
weight: 1
|
||||
generation:
|
||||
parent: "minecraft:block/sugar_cane"
|
||||
textures:
|
||||
"cross": "minecraft:block/custom/flame_cane_1"
|
||||
- path: "minecraft:block/custom/flame_cane_2"
|
||||
weight: 1
|
||||
generation:
|
||||
parent: "minecraft:block/sugar_cane"
|
||||
textures:
|
||||
"cross": "minecraft:block/custom/flame_cane_2"
|
||||
variants:
|
||||
age=0:
|
||||
appearance: default
|
||||
id: 2
|
||||
age=1:
|
||||
appearance: default
|
||||
id: 3
|
||||
age=2:
|
||||
appearance: default
|
||||
id: 4
|
||||
age=3:
|
||||
appearance: default
|
||||
id: 5
|
||||
age=4:
|
||||
appearance: default
|
||||
id: 6
|
||||
age=5:
|
||||
appearance: default
|
||||
id: 7
|
||||
default:ender_pearl_flower:
|
||||
settings:
|
||||
template:
|
||||
- default:hardness/none
|
||||
- default:sound/grass
|
||||
overrides:
|
||||
item: default:ender_pearl_flower_seeds
|
||||
push-reaction: DESTROY
|
||||
map-color: 24
|
||||
is-randomly-ticking: true
|
||||
behavior:
|
||||
type: crop_block
|
||||
grow-speed: 0.25
|
||||
light-requirement: 9
|
||||
bottom-blocks:
|
||||
- minecraft:end_stone
|
||||
loot:
|
||||
template: default:loot_table/seed_crop
|
||||
arguments:
|
||||
crop_item: minecraft:ender_pearl
|
||||
crop_seed: default:ender_pearl_flower_seeds
|
||||
ripe_age: 2
|
||||
states:
|
||||
properties:
|
||||
age:
|
||||
type: int
|
||||
default: 0
|
||||
range: 0~2
|
||||
appearances:
|
||||
stage_0:
|
||||
state: "tripwire:1"
|
||||
models:
|
||||
- path: "minecraft:block/custom/ender_pearl_flower_stage_0"
|
||||
generation:
|
||||
parent: "minecraft:block/cross"
|
||||
textures:
|
||||
"cross": "minecraft:block/custom/ender_pearl_flower_stage_0"
|
||||
stage_1:
|
||||
state: "tripwire:0"
|
||||
models:
|
||||
- path: "minecraft:block/custom/ender_pearl_flower_stage_1"
|
||||
generation:
|
||||
parent: "minecraft:block/cross"
|
||||
textures:
|
||||
"cross": "minecraft:block/custom/ender_pearl_flower_stage_1"
|
||||
stage_2:
|
||||
state: "sugar_cane:3"
|
||||
models:
|
||||
- path: "minecraft:block/custom/ender_pearl_flower_stage_2"
|
||||
generation:
|
||||
parent: "minecraft:block/cross"
|
||||
textures:
|
||||
"cross": "minecraft:block/custom/ender_pearl_flower_stage_2"
|
||||
variants:
|
||||
age=0:
|
||||
appearance: stage_0
|
||||
id: 0
|
||||
age=1:
|
||||
appearance: stage_1
|
||||
id: 1
|
||||
age=2:
|
||||
appearance: stage_2
|
||||
id: 8
|
||||
recipes:
|
||||
default:paper_from_reed:
|
||||
type: shaped
|
||||
@@ -91,4 +251,46 @@ recipes:
|
||||
A: "default:reed"
|
||||
result:
|
||||
id: minecraft:paper
|
||||
count: 3
|
||||
count: 3
|
||||
default:magma_cream:
|
||||
type: shaped
|
||||
pattern:
|
||||
- " A "
|
||||
- "ABA"
|
||||
- " A "
|
||||
ingredients:
|
||||
A: "default:flame_cane"
|
||||
B: "minecraft:slime_ball"
|
||||
result:
|
||||
id: minecraft:magma_cream
|
||||
count: 1
|
||||
default:magma_block:
|
||||
type: shapeless
|
||||
ingredients:
|
||||
A1: "minecraft:cobblestone"
|
||||
A2: "minecraft:cobblestone"
|
||||
B1: "default:flame_cane"
|
||||
B2: "default:flame_cane"
|
||||
result:
|
||||
id: minecraft:magma_block
|
||||
count: 2
|
||||
|
||||
vanilla-loots:
|
||||
minecraft:ender_pearl_flower_seeds_from_endermite:
|
||||
type: entity
|
||||
target: "minecraft:endermite"
|
||||
override: false
|
||||
loot:
|
||||
pools:
|
||||
- rolls: 1
|
||||
conditions:
|
||||
- type: table_bonus
|
||||
enchantment: minecraft:looting
|
||||
chances:
|
||||
- 0.1
|
||||
- 0.5
|
||||
- 0.8
|
||||
- 1
|
||||
entries:
|
||||
- type: item
|
||||
item: "default:ender_pearl_flower_seeds"
|
||||
@@ -953,7 +953,12 @@ templates#recipes:
|
||||
|
||||
# loot tables
|
||||
templates#loot_tables:
|
||||
|
||||
# drop one item
|
||||
|
||||
# template: default:loot_table/basic
|
||||
# arguments:
|
||||
# item: the item
|
||||
default:loot_table/basic:
|
||||
pools:
|
||||
- rolls: 1
|
||||
@@ -962,7 +967,12 @@ templates#loot_tables:
|
||||
entries:
|
||||
- type: item
|
||||
item: "{item}"
|
||||
|
||||
# drop with silk touch
|
||||
|
||||
# template: default:loot_table/silk_touch
|
||||
# arguments:
|
||||
# item: the item
|
||||
default:loot_table/silk_touch:
|
||||
pools:
|
||||
- rolls: 1
|
||||
@@ -972,7 +982,78 @@ templates#loot_tables:
|
||||
entries:
|
||||
- type: item
|
||||
item: "{item}"
|
||||
# drop ores
|
||||
|
||||
# crop drops
|
||||
|
||||
# template: default:loot_table/seed_crop
|
||||
# arguments:
|
||||
# crop_item: the ripe crop
|
||||
# crop_seed: the seed of the crop
|
||||
# ripe_age: the max age
|
||||
default:loot_table/seed_crop:
|
||||
pools:
|
||||
- rolls: 1
|
||||
entries:
|
||||
- type: alternatives
|
||||
children:
|
||||
- type: item
|
||||
item: "{crop_item}"
|
||||
conditions:
|
||||
- type: match_block_property
|
||||
properties:
|
||||
age: "{ripe_age}"
|
||||
- type: item
|
||||
item: "{crop_seed}"
|
||||
- rolls: 1
|
||||
conditions:
|
||||
- type: match_block_property
|
||||
properties:
|
||||
age: "{ripe_age}"
|
||||
entries:
|
||||
- type: item
|
||||
item: "{crop_seed}"
|
||||
functions:
|
||||
- type: apply_bonus
|
||||
enchantment: minecraft:fortune
|
||||
formula:
|
||||
type: binomial_with_bonus_count
|
||||
extra: 3
|
||||
probability: 0.5714286
|
||||
|
||||
# template: default:loot_table/crop
|
||||
# arguments:
|
||||
# crop_item: the ripe crop
|
||||
# ripe_age: the max age
|
||||
default:loot_table/crop:
|
||||
pools:
|
||||
- rolls: 1
|
||||
entries:
|
||||
- type: item
|
||||
item: "{crop_item}"
|
||||
- rolls: 1
|
||||
conditions:
|
||||
- type: match_block_property
|
||||
properties:
|
||||
age: "{ripe_age}"
|
||||
entries:
|
||||
- type: item
|
||||
item: "{crop_item}"
|
||||
functions:
|
||||
- type: apply_bonus
|
||||
enchantment: minecraft:fortune
|
||||
formula:
|
||||
type: binomial_with_bonus_count
|
||||
extra: 3
|
||||
probability: 0.5714286
|
||||
|
||||
# ore drops
|
||||
|
||||
# template: default:loot_table/ore
|
||||
# arguments:
|
||||
# ore_block: the ore block
|
||||
# ore_drop: the drops of the ore
|
||||
# min_exp: the min exp to drop
|
||||
# max_exp: the max exp to drop
|
||||
default:loot_table/ore:
|
||||
pools:
|
||||
- rolls: 1
|
||||
@@ -997,7 +1078,37 @@ templates#loot_tables:
|
||||
type: uniform
|
||||
min: "{min_exp}"
|
||||
max: "{max_exp}"
|
||||
# leaves
|
||||
|
||||
# template: default:loot_table/ore_no_exp
|
||||
# arguments:
|
||||
# ore_block: the ore block
|
||||
# ore_drop: the drops of the ore
|
||||
default:loot_table/ore_no_exp:
|
||||
pools:
|
||||
- rolls: 1
|
||||
entries:
|
||||
- type: alternatives
|
||||
children:
|
||||
- type: item
|
||||
item: "{ore_block}"
|
||||
conditions:
|
||||
- type: enchantment
|
||||
predicate: minecraft:silk_touch>=1
|
||||
- type: item
|
||||
item: "{ore_drop}"
|
||||
functions:
|
||||
- type: apply_bonus
|
||||
enchantment: minecraft:fortune
|
||||
formula:
|
||||
type: ore_drops
|
||||
- type: explosion_decay
|
||||
|
||||
# leaves drops
|
||||
|
||||
# template: default:loot_table/leaves
|
||||
# arguments:
|
||||
# leaves: the leaves block
|
||||
# sapling: the sapling item
|
||||
default:loot_table/leaves:
|
||||
pools:
|
||||
- rolls: 1
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"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"}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 183 B |
|
After Width: | Height: | Size: 331 B |
|
After Width: | Height: | Size: 455 B |
|
After Width: | Height: | Size: 362 B |
|
After Width: | Height: | Size: 290 B |
|
After Width: | Height: | Size: 293 B |
|
After Width: | Height: | Size: 280 B |
|
After Width: | Height: | Size: 267 B |
@@ -1,6 +1,7 @@
|
||||
package net.momirealms.craftengine.bukkit.api;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
|
||||
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.BlockStateUtils;
|
||||
@@ -108,9 +109,9 @@ public final class CraftEngineBlocks {
|
||||
boolean success;
|
||||
try {
|
||||
Object worldServer = Reflections.field$CraftWorld$ServerLevel.get(location.getWorld());
|
||||
Object blockPos = Reflections.constructor$BlockPos.newInstance(location.getBlockX(), location.getBlockY(), location.getBlockZ());
|
||||
Object blockPos = FastNMS.INSTANCE.constructor$BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ());
|
||||
Object blockState = block.customBlockState().handle();
|
||||
Object oldBlockState = Reflections.method$BlockGetter$getBlockState.invoke(worldServer, blockPos);
|
||||
Object oldBlockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(worldServer, blockPos);
|
||||
success = (boolean) Reflections.method$LevelWriter$setBlock.invoke(worldServer, blockPos, blockState, option.flags());
|
||||
if (success) {
|
||||
Reflections.method$BlockStateBase$onPlace.invoke(blockState, worldServer, blockPos, oldBlockState, true);
|
||||
|
||||
@@ -318,7 +318,7 @@ public class BlockEventListener implements Listener {
|
||||
if (direction == BlockFace.UP || direction == BlockFace.DOWN) {
|
||||
Object serverLevel = Reflections.field$CraftWorld$ServerLevel.get(world);
|
||||
Object chunkSource = Reflections.field$ServerLevel$chunkSource.get(serverLevel);
|
||||
Object blockPos = Reflections.constructor$BlockPos.newInstance(location.getBlockX(), location.getBlockY(), location.getBlockZ());
|
||||
Object blockPos = LocationUtils.toBlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ());
|
||||
Reflections.method$ServerChunkCache$blockChanged.invoke(chunkSource, blockPos);
|
||||
if (direction == BlockFace.UP) {
|
||||
NoteBlockChainUpdateUtils.noteBlockChainUpdate(serverLevel, chunkSource, Reflections.instance$Direction$UP, blockPos, 0);
|
||||
|
||||
@@ -7,8 +7,7 @@ import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import dev.dejvokep.boostedyaml.YamlDocument;
|
||||
import net.momirealms.craftengine.bukkit.block.worldedit.WorldEditBlockRegister;
|
||||
import net.momirealms.craftengine.bukkit.block.worldedit.WorldEditCommandHelper;
|
||||
import net.momirealms.craftengine.bukkit.compatibility.worldedit.WorldEditBlockRegister;
|
||||
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
|
||||
import net.momirealms.craftengine.bukkit.plugin.injector.BukkitInjector;
|
||||
import net.momirealms.craftengine.bukkit.plugin.network.PacketConsumers;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package net.momirealms.craftengine.bukkit.block.worldedit;
|
||||
package net.momirealms.craftengine.bukkit.block;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
|
||||
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
|
||||
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
|
||||
import net.momirealms.craftengine.core.block.BlockStateParser;
|
||||
@@ -6,6 +6,7 @@ import net.momirealms.craftengine.shared.block.EmptyBlockBehavior;
|
||||
|
||||
public class BukkitBlockBehaviors extends BlockBehaviors {
|
||||
public static final Key BUSH_BLOCK = Key.from("craftengine:bush_block");
|
||||
public static final Key HANGING_BLOCK = Key.from("craftengine:hanging_block");
|
||||
public static final Key FALLING_BLOCK = Key.from("craftengine:falling_block");
|
||||
public static final Key LEAVES_BLOCK = Key.from("craftengine:leaves_block");
|
||||
public static final Key STRIPPABLE_BLOCK = Key.from("craftengine:strippable_block");
|
||||
@@ -13,16 +14,21 @@ public class BukkitBlockBehaviors extends BlockBehaviors {
|
||||
public static final Key ON_LIQUID_BLOCK = Key.from("craftengine:on_liquid_block");
|
||||
public static final Key WATERLOGGED_BLOCK = Key.from("craftengine:waterlogged_block");
|
||||
public static final Key CONCRETE_POWDER_BLOCK = Key.from("craftengine:concrete_powder_block");
|
||||
public static final Key SUGARCANE_BLOCK = Key.from("craftengine:sugar_cane_block");
|
||||
public static final Key CROP_BLOCK = Key.from("craftengine:crop_block");
|
||||
|
||||
public static void init() {
|
||||
register(EMPTY, (block, args) -> EmptyBlockBehavior.INSTANCE);
|
||||
register(FALLING_BLOCK, FallingBlockBehavior.FACTORY);
|
||||
register(BUSH_BLOCK, BushBlockBehavior.FACTORY);
|
||||
register(HANGING_BLOCK, HangingBlockBehavior.FACTORY);
|
||||
register(LEAVES_BLOCK, LeavesBlockBehavior.FACTORY);
|
||||
register(STRIPPABLE_BLOCK, StrippableBlockBehavior.FACTORY);
|
||||
register(SAPLING_BLOCK, SaplingBlockBehavior.FACTORY);
|
||||
register(ON_LIQUID_BLOCK, OnLiquidBlockBehavior.FACTORY);
|
||||
register(WATERLOGGED_BLOCK, WaterLoggedBlockBehavior.FACTORY);
|
||||
register(CONCRETE_POWDER_BLOCK, ConcretePowderBlockBehavior.FACTORY);
|
||||
register(SUGARCANE_BLOCK, SugarCaneBlockBehavior.FACTORY);
|
||||
register(CROP_BLOCK, CropBlockBehavior.FACTORY);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package net.momirealms.craftengine.bukkit.block.behavior;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.BlockTags;
|
||||
import net.momirealms.craftengine.bukkit.util.LocationUtils;
|
||||
@@ -14,26 +15,32 @@ import net.momirealms.craftengine.core.item.Item;
|
||||
import net.momirealms.craftengine.core.loot.parameter.LootParameters;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.MiscUtils;
|
||||
import net.momirealms.craftengine.core.util.Tuple;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
import net.momirealms.craftengine.core.util.context.ContextHolder;
|
||||
import net.momirealms.craftengine.core.world.BlockPos;
|
||||
import net.momirealms.craftengine.core.world.Vec3d;
|
||||
import net.momirealms.craftengine.shared.block.BlockBehavior;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.Registry;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
public class BushBlockBehavior extends AbstractBlockBehavior {
|
||||
public static final Factory FACTORY = new Factory();
|
||||
protected static final Object DIRT_TAG = BlockTags.getOrCreate(Key.of("minecraft", "dirt"));
|
||||
protected static final Object FARMLAND = BlockTags.getOrCreate(Key.of("minecraft", "farmland"));
|
||||
public static final BushBlockBehavior INSTANCE = new BushBlockBehavior(List.of(DIRT_TAG, FARMLAND));
|
||||
protected final List<Object> tagsCanSurviveOn;
|
||||
protected final Set<Object> blocksCansSurviveOn;
|
||||
protected final Set<String> customBlocksCansSurviveOn;
|
||||
protected final boolean any;
|
||||
|
||||
public BushBlockBehavior(List<Object> tagsCanSurviveOn) {
|
||||
public BushBlockBehavior(List<Object> tagsCanSurviveOn, Set<Object> blocksCansSurviveOn, Set<String> customBlocksCansSurviveOn) {
|
||||
this.tagsCanSurviveOn = tagsCanSurviveOn;
|
||||
this.blocksCansSurviveOn = blocksCansSurviveOn;
|
||||
this.customBlocksCansSurviveOn = customBlocksCansSurviveOn;
|
||||
this.any = this.tagsCanSurviveOn.isEmpty() && this.blocksCansSurviveOn.isEmpty() && this.customBlocksCansSurviveOn.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -61,7 +68,7 @@ public class BushBlockBehavior extends AbstractBlockBehavior {
|
||||
ContextHolder.Builder builder = ContextHolder.builder();
|
||||
BlockPos pos = LocationUtils.fromBlockPos(blockPos);
|
||||
Vec3d vec3d = Vec3d.atCenterOf(pos);
|
||||
net.momirealms.craftengine.core.world.World world = new BukkitWorld((World) Reflections.method$Level$getCraftWorld.invoke(level));
|
||||
net.momirealms.craftengine.core.world.World world = new BukkitWorld(FastNMS.INSTANCE.method$Level$getCraftWorld(level));
|
||||
builder.withParameter(LootParameters.LOCATION, vec3d);
|
||||
builder.withParameter(LootParameters.WORLD, world);
|
||||
for (Item<Object> item : previousState.getDrops(builder, world)) {
|
||||
@@ -82,33 +89,72 @@ public class BushBlockBehavior extends AbstractBlockBehavior {
|
||||
}
|
||||
|
||||
public static class Factory implements BlockBehaviorFactory {
|
||||
|
||||
@Override
|
||||
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
|
||||
if (arguments.containsKey("bottom-block-tags")) {
|
||||
return new BushBlockBehavior(MiscUtils.getAsStringList(arguments.get("bottom-block-tags")).stream().map(it -> BlockTags.getOrCreate(Key.of(it))).toList());
|
||||
} else if (arguments.containsKey("tags")) {
|
||||
return new BushBlockBehavior(MiscUtils.getAsStringList(arguments.get("tags")).stream().map(it -> BlockTags.getOrCreate(Key.of(it))).toList());
|
||||
} else {
|
||||
return INSTANCE;
|
||||
}
|
||||
Tuple<List<Object>, Set<Object>, Set<String>> tuple = readTagsAndState(arguments);
|
||||
return new BushBlockBehavior(tuple.left(), tuple.mid(), tuple.right());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean canSurvive(Object thisBlock, Object state, Object world, Object blockPos) throws ReflectiveOperationException {
|
||||
int y = Reflections.field$Vec3i$y.getInt(blockPos);
|
||||
int x = Reflections.field$Vec3i$x.getInt(blockPos);
|
||||
int z = Reflections.field$Vec3i$z.getInt(blockPos);
|
||||
Object belowPos = Reflections.constructor$BlockPos.newInstance(x, y - 1, z);
|
||||
Object belowState = Reflections.method$BlockGetter$getBlockState.invoke(world, belowPos);
|
||||
public static Tuple<List<Object>, Set<Object>, Set<String>> readTagsAndState(Map<String, Object> arguments) {
|
||||
List<Object> mcTags = new ArrayList<>();
|
||||
for (String tag : MiscUtils.getAsStringList(arguments.getOrDefault("bottom-block-tags", List.of()))) {
|
||||
mcTags.add(BlockTags.getOrCreate(Key.of(tag)));
|
||||
}
|
||||
Set<Object> mcBlocks = new HashSet<>();
|
||||
Set<String> customBlocks = new HashSet<>();
|
||||
for (String blockStateStr : MiscUtils.getAsStringList(arguments.getOrDefault("bottom-blocks", List.of()))) {
|
||||
int index = blockStateStr.indexOf('[');
|
||||
Key blockType = index != -1 ? Key.from(blockStateStr.substring(0, index)) : Key.from(blockStateStr);
|
||||
Material material = Registry.MATERIAL.get(new NamespacedKey(blockType.namespace(), blockType.value()));
|
||||
if (material != null) {
|
||||
if (index == -1) {
|
||||
// vanilla
|
||||
mcBlocks.addAll(BlockStateUtils.getAllVanillaBlockStates(blockType));
|
||||
} else {
|
||||
mcBlocks.add(BlockStateUtils.blockDataToBlockState(Bukkit.createBlockData(blockStateStr)));
|
||||
}
|
||||
} else {
|
||||
// custom maybe
|
||||
customBlocks.add(blockStateStr);
|
||||
}
|
||||
}
|
||||
return new Tuple<>(mcTags, mcBlocks, customBlocks);
|
||||
}
|
||||
|
||||
protected boolean canSurvive(Object thisBlock, Object state, Object world, Object blockPos) throws ReflectiveOperationException {
|
||||
int y = FastNMS.INSTANCE.field$Vec3i$y(blockPos);
|
||||
int x = FastNMS.INSTANCE.field$Vec3i$x(blockPos);
|
||||
int z = FastNMS.INSTANCE.field$Vec3i$z(blockPos);
|
||||
Object belowPos = FastNMS.INSTANCE.constructor$BlockPos(x, y - 1, z);
|
||||
Object belowState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(world, belowPos);
|
||||
return mayPlaceOn(belowState, world, belowPos);
|
||||
}
|
||||
|
||||
protected boolean mayPlaceOn(Object belowState, Object world, Object belowPos) throws ReflectiveOperationException {
|
||||
if (this.any) return true;
|
||||
for (Object tag : this.tagsCanSurviveOn) {
|
||||
if ((boolean) Reflections.method$BlockStateBase$hasTag.invoke(belowState, tag)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
int id = BlockStateUtils.blockStateToId(belowState);
|
||||
if (BlockStateUtils.isVanillaBlock(id)) {
|
||||
if (!this.blocksCansSurviveOn.isEmpty() && this.blocksCansSurviveOn.contains(belowState)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
ImmutableBlockState previousState = BukkitBlockManager.instance().getImmutableBlockState(id);
|
||||
if (previousState != null && !previousState.isEmpty()) {
|
||||
if (this.customBlocksCansSurviveOn.contains(previousState.owner().value().id().toString())) {
|
||||
return true;
|
||||
}
|
||||
if (this.customBlocksCansSurviveOn.contains(previousState.toString())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package net.momirealms.craftengine.bukkit.block.behavior;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.EventUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.LocationUtils;
|
||||
@@ -65,7 +66,7 @@ public class ConcretePowderBlockBehavior extends FallingBlockBehavior {
|
||||
Object level = context.getLevel().serverWorld();
|
||||
Object blockPos = LocationUtils.toBlockPos(context.getClickedPos());
|
||||
try {
|
||||
Object previousState = Reflections.method$BlockGetter$getBlockState.invoke(level, blockPos);
|
||||
Object previousState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, blockPos);
|
||||
if (!shouldSolidify(level, blockPos, previousState)) {
|
||||
return super.updateStateForPlacement(context, state);
|
||||
} else {
|
||||
@@ -137,10 +138,10 @@ public class ConcretePowderBlockBehavior extends FallingBlockBehavior {
|
||||
int j = Direction.values().length;
|
||||
for (int k = 0; k < j; k++) {
|
||||
Object direction = Reflections.instance$Directions[k];
|
||||
Object blockState = Reflections.method$BlockGetter$getBlockState.invoke(level, mutablePos);
|
||||
Object blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, mutablePos);
|
||||
if (direction != Reflections.instance$Direction$DOWN || canSolidify(blockState)) {
|
||||
Reflections.method$MutableBlockPos$setWithOffset.invoke(mutablePos, pos, direction);
|
||||
blockState = Reflections.method$BlockGetter$getBlockState.invoke(level, mutablePos);
|
||||
blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, mutablePos);
|
||||
if (canSolidify(blockState) && !(boolean) Reflections.method$BlockStateBase$isFaceSturdy.invoke(blockState, level, pos, Reflections.getOppositeDirection(direction), Reflections.instance$SupportType$FULL)) {
|
||||
flag = true;
|
||||
break;
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
package net.momirealms.craftengine.bukkit.block.behavior;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.ParticleUtils;
|
||||
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.block.UpdateOption;
|
||||
import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
|
||||
import net.momirealms.craftengine.core.block.properties.IntegerProperty;
|
||||
import net.momirealms.craftengine.core.block.properties.Property;
|
||||
import net.momirealms.craftengine.core.util.MiscUtils;
|
||||
import net.momirealms.craftengine.core.util.RandomUtils;
|
||||
import net.momirealms.craftengine.core.util.Tuple;
|
||||
import net.momirealms.craftengine.shared.block.BlockBehavior;
|
||||
import org.bukkit.World;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
public class CropBlockBehavior extends BushBlockBehavior {
|
||||
public static final Factory FACTORY = new Factory();
|
||||
private final IntegerProperty ageProperty;
|
||||
private final float growSpeed;
|
||||
private final int minGrowLight;
|
||||
|
||||
public CropBlockBehavior(List<Object> tagsCanSurviveOn, Set<Object> blocksCansSurviveOn, Set<String> customBlocksCansSurviveOn,
|
||||
Property<Integer> ageProperty, float growSpeed, int minGrowLight) {
|
||||
super(tagsCanSurviveOn, blocksCansSurviveOn, customBlocksCansSurviveOn);
|
||||
this.ageProperty = (IntegerProperty) ageProperty;
|
||||
this.growSpeed = growSpeed;
|
||||
this.minGrowLight = minGrowLight;
|
||||
}
|
||||
|
||||
public final int getAge(ImmutableBlockState state) {
|
||||
return state.get(ageProperty);
|
||||
}
|
||||
|
||||
public boolean isMaxAge(ImmutableBlockState state) {
|
||||
return state.get(ageProperty) == ageProperty.max;
|
||||
}
|
||||
|
||||
private static int getRawBrightness(Object level, Object pos) throws InvocationTargetException, IllegalAccessException {
|
||||
return (int) Reflections.method$BlockAndTintGetter$getRawBrightness.invoke(level, pos, 0);
|
||||
}
|
||||
|
||||
private boolean hasSufficientLight(Object level, Object pos) throws InvocationTargetException, IllegalAccessException {
|
||||
return getRawBrightness(level, pos) >= this.minGrowLight - 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void randomTick(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
|
||||
Object state = args[0];
|
||||
Object level = args[1];
|
||||
Object pos = args[2];
|
||||
if (getRawBrightness(level, pos) >= this.minGrowLight) {
|
||||
ImmutableBlockState currentState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(state));
|
||||
if (currentState != null && !currentState.isEmpty()) {
|
||||
int age = this.getAge(currentState);
|
||||
if (age < this.ageProperty.max && RandomUtils.generateRandomFloat(0, 1) < this.growSpeed) {
|
||||
Reflections.method$Level$setBlock.invoke(level, pos, currentState.with(this.ageProperty, age + 1).customBlockState().handle(), UpdateOption.UPDATE_ALL.flags());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canSurvive(Object thisBlock, Object state, Object world, Object blockPos) throws ReflectiveOperationException {
|
||||
return hasSufficientLight(world, blockPos) && super.canSurvive(thisBlock, state, world, blockPos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBoneMealSuccess(Object thisBlock, Object[] args) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValidBoneMealTarget(Object thisBlock, Object[] args) {
|
||||
Object state = args[2];
|
||||
ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(state));
|
||||
if (immutableBlockState != null && !immutableBlockState.isEmpty()) {
|
||||
return getAge(immutableBlockState) != this.ageProperty.max;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performBoneMeal(Object thisBlock, Object[] args) throws Exception {
|
||||
this.performBoneMeal(args[0], args[2], args[3]);
|
||||
}
|
||||
|
||||
private void performBoneMeal(Object level, Object pos, Object state) throws InvocationTargetException, IllegalAccessException {
|
||||
ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(state));
|
||||
if (immutableBlockState == null || immutableBlockState.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
boolean sendParticles = false;
|
||||
Object visualState = immutableBlockState.vanillaBlockState().handle();
|
||||
Object visualStateBlock = Reflections.method$BlockStateBase$getBlock.invoke(visualState);
|
||||
if (Reflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) {
|
||||
boolean is = (boolean) Reflections.method$BonemealableBlock$isValidBonemealTarget.invoke(visualStateBlock, level, pos, visualState);
|
||||
if (!is) {
|
||||
sendParticles = true;
|
||||
}
|
||||
} else {
|
||||
sendParticles = true;
|
||||
}
|
||||
|
||||
int i = this.getAge(immutableBlockState) + RandomUtils.generateRandomInt(2, 5);
|
||||
int maxAge = this.ageProperty.max;
|
||||
if (i > maxAge) {
|
||||
i = maxAge;
|
||||
}
|
||||
Reflections.method$Level$setBlock.invoke(level, pos, immutableBlockState.with(this.ageProperty, i).customBlockState().handle(), UpdateOption.UPDATE_NONE.flags());
|
||||
if (sendParticles) {
|
||||
World world = FastNMS.INSTANCE.method$Level$getCraftWorld(level);
|
||||
int x = FastNMS.INSTANCE.field$Vec3i$x(pos);
|
||||
int y = FastNMS.INSTANCE.field$Vec3i$y(pos);
|
||||
int z = FastNMS.INSTANCE.field$Vec3i$z(pos);
|
||||
world.spawnParticle(ParticleUtils.getParticle("HAPPY_VILLAGER"), x + 0.5, y + 0.5, z + 0.5, 12, 0.25, 0.25, 0.25);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Factory implements BlockBehaviorFactory {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
|
||||
Tuple<List<Object>, Set<Object>, Set<String>> tuple = readTagsAndState(arguments);
|
||||
Property<Integer> ageProperty = (Property<Integer>) block.getProperty("age");
|
||||
if (ageProperty == null) {
|
||||
throw new IllegalArgumentException("age property not set for crop");
|
||||
}
|
||||
// 存活条件是最小生长亮度-1
|
||||
int minGrowLight = MiscUtils.getAsInt(arguments.getOrDefault("light-requirement", 9));
|
||||
float growSpeed = MiscUtils.getAsFloat(arguments.getOrDefault("grow-speed", 0.25f));
|
||||
return new CropBlockBehavior(tuple.left(), tuple.mid(), tuple.right(), ageProperty, growSpeed, minGrowLight);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package net.momirealms.craftengine.bukkit.block.behavior;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.LocationUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.Reflections;
|
||||
@@ -16,7 +17,6 @@ import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
import net.momirealms.craftengine.core.util.context.ContextHolder;
|
||||
import net.momirealms.craftengine.core.world.Vec3d;
|
||||
import net.momirealms.craftengine.shared.block.BlockBehavior;
|
||||
import org.bukkit.World;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
@@ -56,17 +56,17 @@ public class FallingBlockBehavior extends AbstractBlockBehavior {
|
||||
@Override
|
||||
public void tick(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
|
||||
Object blockPos = args[2];
|
||||
int y = Reflections.field$Vec3i$y.getInt(blockPos);
|
||||
int y = FastNMS.INSTANCE.field$Vec3i$y(blockPos);
|
||||
Object world = args[1];
|
||||
Object dimension = Reflections.method$$LevelReader$dimensionType.invoke(world);
|
||||
int minY = Reflections.field$DimensionType$minY.getInt(dimension);
|
||||
if (y < minY) {
|
||||
return;
|
||||
}
|
||||
int x = Reflections.field$Vec3i$x.getInt(blockPos);
|
||||
int z = Reflections.field$Vec3i$z.getInt(blockPos);
|
||||
Object belowPos = Reflections.constructor$BlockPos.newInstance(x, y - 1, z);
|
||||
Object belowState = Reflections.method$BlockGetter$getBlockState.invoke(world, belowPos);
|
||||
int x = FastNMS.INSTANCE.field$Vec3i$x(blockPos);
|
||||
int z = FastNMS.INSTANCE.field$Vec3i$z(blockPos);
|
||||
Object belowPos = LocationUtils.toBlockPos(x, y - 1, z);
|
||||
Object belowState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(world, belowPos);
|
||||
boolean isFree = (boolean) Reflections.method$FallingBlock$isFree.invoke(null, belowState);
|
||||
if (!isFree) {
|
||||
return;
|
||||
@@ -96,7 +96,7 @@ public class FallingBlockBehavior extends AbstractBlockBehavior {
|
||||
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((World) Reflections.method$Level$getCraftWorld.invoke(level));
|
||||
net.momirealms.craftengine.core.world.World world = new BukkitWorld(FastNMS.INSTANCE.method$Level$getCraftWorld(level));
|
||||
builder.withParameter(LootParameters.LOCATION, vec3d);
|
||||
builder.withParameter(LootParameters.WORLD, world);
|
||||
for (Item<Object> item : immutableBlockState.getDrops(builder, world)) {
|
||||
@@ -121,7 +121,7 @@ public class FallingBlockBehavior extends AbstractBlockBehavior {
|
||||
if (immutableBlockState == null || immutableBlockState.isEmpty()) return;
|
||||
Object level = args[0];
|
||||
Object pos = args[1];
|
||||
net.momirealms.craftengine.core.world.World world = new BukkitWorld((World) Reflections.method$Level$getCraftWorld.invoke(level));
|
||||
net.momirealms.craftengine.core.world.World world = new BukkitWorld(FastNMS.INSTANCE.method$Level$getCraftWorld(level));
|
||||
world.playBlockSound(Vec3d.atCenterOf(LocationUtils.fromBlockPos(pos)), immutableBlockState.sounds().landSound());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package net.momirealms.craftengine.bukkit.block.behavior;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.core.block.CustomBlock;
|
||||
import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
|
||||
import net.momirealms.craftengine.core.util.Tuple;
|
||||
import net.momirealms.craftengine.shared.block.BlockBehavior;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class HangingBlockBehavior extends BushBlockBehavior {
|
||||
public static final Factory FACTORY = new Factory();
|
||||
|
||||
public HangingBlockBehavior(List<Object> tagsCanSurviveOn, Set<Object> blocksCansSurviveOn, Set<String> customBlocksCansSurviveOn) {
|
||||
super(tagsCanSurviveOn, blocksCansSurviveOn, customBlocksCansSurviveOn);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canSurvive(Object thisBlock, Object state, Object world, Object blockPos) throws ReflectiveOperationException {
|
||||
int y = FastNMS.INSTANCE.field$Vec3i$y(blockPos);
|
||||
int x = FastNMS.INSTANCE.field$Vec3i$x(blockPos);
|
||||
int z = FastNMS.INSTANCE.field$Vec3i$z(blockPos);
|
||||
Object belowPos = FastNMS.INSTANCE.constructor$BlockPos(x, y + 1, z);
|
||||
Object belowState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(world, belowPos);
|
||||
return mayPlaceOn(belowState, world, belowPos);
|
||||
}
|
||||
|
||||
public static class Factory implements BlockBehaviorFactory {
|
||||
|
||||
@Override
|
||||
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
|
||||
Tuple<List<Object>, Set<Object>, Set<String>> tuple = readTagsAndState(arguments);
|
||||
return new HangingBlockBehavior(tuple.left(), tuple.mid(), tuple.right());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package net.momirealms.craftengine.bukkit.block.behavior;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.BlockTags;
|
||||
import net.momirealms.craftengine.bukkit.util.LocationUtils;
|
||||
@@ -108,7 +109,7 @@ public class LeavesBlockBehavior extends WaterLoggedBlockBehavior {
|
||||
Object blockPos = args[2];
|
||||
ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(args[0]));
|
||||
if (immutableBlockState != null && immutableBlockState.behavior() instanceof LeavesBlockBehavior behavior && behavior.isDecaying(immutableBlockState)) {
|
||||
World bukkitWorld = (World) Reflections.method$Level$getCraftWorld.invoke(level);
|
||||
World bukkitWorld = FastNMS.INSTANCE.method$Level$getCraftWorld(level);
|
||||
BlockPos pos = LocationUtils.fromBlockPos(blockPos);
|
||||
// call bukkit event
|
||||
LeavesDecayEvent event = new LeavesDecayEvent(bukkitWorld.getBlockAt(pos.x(), pos.y(), pos.z()));
|
||||
@@ -142,7 +143,7 @@ public class LeavesBlockBehavior extends WaterLoggedBlockBehavior {
|
||||
for (int k = 0; k < j; ++k) {
|
||||
Object direction = Reflections.instance$Directions[k];
|
||||
Reflections.method$MutableBlockPos$setWithOffset.invoke(mutablePos, blockPos, direction);
|
||||
Object blockState = Reflections.method$BlockGetter$getBlockState.invoke(world, mutablePos);
|
||||
Object blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(world, mutablePos);
|
||||
i = Math.min(i, getDistanceAt(blockState) + 1);
|
||||
if (i == 1) {
|
||||
break;
|
||||
|
||||
@@ -9,14 +9,15 @@ import net.momirealms.craftengine.shared.block.BlockBehavior;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class OnLiquidBlockBehavior extends BushBlockBehavior {
|
||||
public static final Factory FACTORY = new Factory();
|
||||
private final boolean onWater;
|
||||
private final boolean onLava;
|
||||
|
||||
public OnLiquidBlockBehavior(List<Object> tagsCanSurviveOn, boolean onWater, boolean onLava) {
|
||||
super(tagsCanSurviveOn);
|
||||
public OnLiquidBlockBehavior(boolean onWater, boolean onLava) {
|
||||
super(List.of(), Set.of(), Set.of());
|
||||
this.onWater = onWater;
|
||||
this.onLava = onLava;
|
||||
}
|
||||
@@ -33,7 +34,7 @@ public class OnLiquidBlockBehavior extends BushBlockBehavior {
|
||||
@Override
|
||||
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
|
||||
List<String> liquidTypes = MiscUtils.getAsStringList(arguments.getOrDefault("liquid-type", List.of("water")));
|
||||
return new OnLiquidBlockBehavior(List.of(), liquidTypes.contains("water"), liquidTypes.contains("lava"));
|
||||
return new OnLiquidBlockBehavior(liquidTypes.contains("water"), liquidTypes.contains("lava"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package net.momirealms.craftengine.bukkit.block.behavior;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.api.CraftEngineBlocks;
|
||||
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.util.*;
|
||||
import net.momirealms.craftengine.core.block.CustomBlock;
|
||||
import net.momirealms.craftengine.core.block.ImmutableBlockState;
|
||||
@@ -12,6 +13,7 @@ import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.MiscUtils;
|
||||
import net.momirealms.craftengine.core.util.RandomUtils;
|
||||
import net.momirealms.craftengine.core.util.Tuple;
|
||||
import net.momirealms.craftengine.shared.block.BlockBehavior;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
@@ -19,6 +21,7 @@ import org.bukkit.World;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
public class SaplingBlockBehavior extends BushBlockBehavior {
|
||||
@@ -26,12 +29,14 @@ public class SaplingBlockBehavior extends BushBlockBehavior {
|
||||
private final Key feature;
|
||||
private final Property<Integer> stageProperty;
|
||||
private final double boneMealSuccessChance;
|
||||
private final float growSpeed;
|
||||
|
||||
public SaplingBlockBehavior(Key feature, Property<Integer> stageProperty, List<Object> tagsCanSurviveOn, double boneMealSuccessChance) {
|
||||
super(tagsCanSurviveOn);
|
||||
public SaplingBlockBehavior(Key feature, Property<Integer> stageProperty, List<Object> tagsCanSurviveOn, Set<Object> blocksCansSurviveOn, Set<String> customBlocksCansSurviveOn, double boneMealSuccessChance, float growSpeed) {
|
||||
super(tagsCanSurviveOn, blocksCansSurviveOn, customBlocksCansSurviveOn);
|
||||
this.feature = feature;
|
||||
this.stageProperty = stageProperty;
|
||||
this.boneMealSuccessChance = boneMealSuccessChance;
|
||||
this.growSpeed = growSpeed;
|
||||
}
|
||||
|
||||
public Key treeFeature() {
|
||||
@@ -43,11 +48,8 @@ public class SaplingBlockBehavior extends BushBlockBehavior {
|
||||
Object world = args[1];
|
||||
Object blockPos = args[2];
|
||||
Object blockState = args[0];
|
||||
int x = (int) Reflections.field$Vec3i$x.get(blockPos);
|
||||
int y = (int) Reflections.field$Vec3i$y.get(blockPos);
|
||||
int z = (int) Reflections.field$Vec3i$z.get(blockPos);
|
||||
Object aboveBlockPos = LocationUtils.toBlockPos(x, y + 1, z);
|
||||
if ((int) Reflections.method$LevelReader$getMaxLocalRawBrightness.invoke(world, aboveBlockPos) >= 9 && (float) Reflections.method$RandomSource$nextFloat.invoke(args[3]) < (1.0f / 7.0f)) {
|
||||
Object aboveBlockPos = LocationUtils.above(blockPos);
|
||||
if ((int) Reflections.method$LevelReader$getMaxLocalRawBrightness.invoke(world, aboveBlockPos) >= 9 && (float) RandomUtils.generateRandomFloat(0, 1) < growSpeed) {
|
||||
increaseStage(world, blockPos, blockState, args[3]);
|
||||
}
|
||||
}
|
||||
@@ -58,10 +60,10 @@ public class SaplingBlockBehavior extends BushBlockBehavior {
|
||||
int currentStage = immutableBlockState.get(this.stageProperty);
|
||||
if (currentStage != this.stageProperty.possibleValues().get(this.stageProperty.possibleValues().size() - 1)) {
|
||||
ImmutableBlockState nextStage = immutableBlockState.cycle(this.stageProperty);
|
||||
World bukkitWorld = (World) Reflections.method$Level$getCraftWorld.invoke(world);
|
||||
int x = (int) Reflections.field$Vec3i$x.get(blockPos);
|
||||
int y = (int) Reflections.field$Vec3i$y.get(blockPos);
|
||||
int z = (int) Reflections.field$Vec3i$z.get(blockPos);
|
||||
World bukkitWorld = FastNMS.INSTANCE.method$Level$getCraftWorld(world);
|
||||
int x = FastNMS.INSTANCE.field$Vec3i$x(blockPos);
|
||||
int y = FastNMS.INSTANCE.field$Vec3i$y(blockPos);
|
||||
int z = FastNMS.INSTANCE.field$Vec3i$z(blockPos);
|
||||
CraftEngineBlocks.place(new Location(bukkitWorld, x, y, z), nextStage, UpdateOption.UPDATE_NONE, false);
|
||||
} else {
|
||||
generateTree(world, blockPos, blockState, randomSource);
|
||||
@@ -83,7 +85,7 @@ public class SaplingBlockBehavior extends BushBlockBehavior {
|
||||
Object legacyState = Reflections.method$FluidState$createLegacyBlock.invoke(fluidState);
|
||||
Reflections.method$Level$setBlock.invoke(world, blockPos, legacyState, UpdateOption.UPDATE_NONE.flags());
|
||||
if ((boolean) Reflections.method$ConfiguredFeature$place.invoke(configuredFeature, world, chunkGenerator, randomSource, blockPos)) {
|
||||
if (Reflections.method$BlockGetter$getBlockState.invoke(world, blockPos) == legacyState) {
|
||||
if (FastNMS.INSTANCE.method$BlockGetter$getBlockState(world, blockPos) == legacyState) {
|
||||
Reflections.method$ServerLevel$sendBlockUpdated.invoke(world, blockPos, blockState, legacyState, 2);
|
||||
}
|
||||
} else {
|
||||
@@ -93,8 +95,34 @@ public class SaplingBlockBehavior extends BushBlockBehavior {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBoneMealSuccess(Object thisBlock, Object[] args) {
|
||||
return RandomUtils.generateRandomDouble(0d, 1d) < this.boneMealSuccessChance;
|
||||
public boolean isBoneMealSuccess(Object thisBlock, Object[] args) throws Exception {
|
||||
boolean success = RandomUtils.generateRandomDouble(0d, 1d) < this.boneMealSuccessChance;
|
||||
Object level = args[0];
|
||||
Object blockPos = args[2];
|
||||
Object blockState = args[3];
|
||||
ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(blockState));
|
||||
if (immutableBlockState == null || immutableBlockState.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
boolean sendParticles = false;
|
||||
Object visualState = immutableBlockState.vanillaBlockState().handle();
|
||||
Object visualStateBlock = Reflections.method$BlockStateBase$getBlock.invoke(visualState);
|
||||
if (Reflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) {
|
||||
boolean is = (boolean) Reflections.method$BonemealableBlock$isValidBonemealTarget.invoke(visualStateBlock, level, blockPos, visualState);
|
||||
if (!is) {
|
||||
sendParticles = true;
|
||||
}
|
||||
} else {
|
||||
sendParticles = true;
|
||||
}
|
||||
if (sendParticles) {
|
||||
World world = FastNMS.INSTANCE.method$Level$getCraftWorld(level);
|
||||
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, 12, 0.25, 0.25, 0.25);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -121,13 +149,9 @@ public class SaplingBlockBehavior extends BushBlockBehavior {
|
||||
throw new IllegalArgumentException("stage property not set for sapling");
|
||||
}
|
||||
double boneMealSuccessChance = MiscUtils.getAsDouble(arguments.getOrDefault("bone-meal-success-chance", 0.45));
|
||||
if (arguments.containsKey("bottom-block-tags")) {
|
||||
return new SaplingBlockBehavior(Key.of(feature), stageProperty, MiscUtils.getAsStringList(arguments.get("bottom-block-tags")).stream().map(it -> BlockTags.getOrCreate(Key.of(it))).toList(), boneMealSuccessChance);
|
||||
} else if (arguments.containsKey("tags")) {
|
||||
return new SaplingBlockBehavior(Key.of(feature), stageProperty, MiscUtils.getAsStringList(arguments.get("tags")).stream().map(it -> BlockTags.getOrCreate(Key.of(it))).toList(), boneMealSuccessChance);
|
||||
} else {
|
||||
return new SaplingBlockBehavior(Key.of(feature), stageProperty, List.of(DIRT_TAG, FARMLAND), boneMealSuccessChance);
|
||||
}
|
||||
Tuple<List<Object>, Set<Object>, Set<String>> tuple = readTagsAndState(arguments);
|
||||
return new SaplingBlockBehavior(Key.of(feature), stageProperty, tuple.left(), tuple.mid(), tuple.right(), boneMealSuccessChance,
|
||||
MiscUtils.getAsFloat(arguments.getOrDefault("grow-speed", 1.0 / 7.0)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
package net.momirealms.craftengine.bukkit.block.behavior;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.LocationUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.Reflections;
|
||||
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
|
||||
import net.momirealms.craftengine.core.block.CustomBlock;
|
||||
import net.momirealms.craftengine.core.block.ImmutableBlockState;
|
||||
import net.momirealms.craftengine.core.block.UpdateOption;
|
||||
import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
|
||||
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.loot.parameter.LootParameters;
|
||||
import net.momirealms.craftengine.core.util.MiscUtils;
|
||||
import net.momirealms.craftengine.core.util.RandomUtils;
|
||||
import net.momirealms.craftengine.core.util.Tuple;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
import net.momirealms.craftengine.core.util.context.ContextHolder;
|
||||
import net.momirealms.craftengine.core.world.BlockPos;
|
||||
import net.momirealms.craftengine.core.world.Vec3d;
|
||||
import net.momirealms.craftengine.shared.block.BlockBehavior;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
public class SugarCaneBlockBehavior extends BushBlockBehavior {
|
||||
public static final Factory FACTORY = new Factory();
|
||||
private static final List<Object> WATER = List.of(Reflections.instance$Fluids$WATER, Reflections.instance$Fluids$FLOWING_WATER);
|
||||
private static final List<Object> LAVA = List.of(Reflections.instance$Fluids$LAVA, Reflections.instance$Fluids$FLOWING_LAVA);
|
||||
private static final List<Object> HORIZON_DIRECTIONS = List.of(Reflections.instance$Direction$NORTH, Reflections.instance$Direction$EAST, Reflections.instance$Direction$SOUTH, Reflections.instance$Direction$WEST);
|
||||
private final int maxHeight;
|
||||
private final boolean nearWater;
|
||||
private final boolean nearLava;
|
||||
private final IntegerProperty ageProperty;
|
||||
private final float growSpeed;
|
||||
private final CustomBlock customBlock;
|
||||
|
||||
public SugarCaneBlockBehavior(CustomBlock customBlock, List<Object> tagsCanSurviveOn, Set<Object> blocksCansSurviveOn, Set<String> customBlocksCansSurviveOn, Property<Integer> ageProperty,
|
||||
int maxHeight, boolean nearWater, boolean nearLava, float growSpeed) {
|
||||
super(tagsCanSurviveOn, blocksCansSurviveOn, customBlocksCansSurviveOn);
|
||||
this.nearWater = nearWater;
|
||||
this.nearLava = nearLava;
|
||||
this.maxHeight = maxHeight;
|
||||
this.ageProperty = (IntegerProperty) ageProperty;
|
||||
this.growSpeed = growSpeed;
|
||||
this.customBlock = customBlock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
|
||||
Object blockState = args[0];
|
||||
Object level = args[1];
|
||||
Object blockPos = args[2];
|
||||
if (!canSurvive(thisBlock, blockState, level, blockPos)) {
|
||||
ImmutableBlockState currentState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(blockState));
|
||||
if (currentState != null && !currentState.isEmpty()) {
|
||||
// break the sugar cane
|
||||
Reflections.method$Level$removeBlock.invoke(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));
|
||||
// TODO client side particles?
|
||||
ContextHolder.Builder builder = ContextHolder.builder()
|
||||
.withParameter(LootParameters.LOCATION, vec3d)
|
||||
.withParameter(LootParameters.WORLD, world);
|
||||
for (Item<Object> item : currentState.getDrops(builder, world)) {
|
||||
world.dropItemNaturally(vec3d, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object updateShape(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
|
||||
Object world;
|
||||
Object blockPos;
|
||||
if (VersionHelper.isVersionNewerThan1_21_2()) {
|
||||
world = args[1];
|
||||
blockPos = args[3];
|
||||
} else {
|
||||
world = args[3];
|
||||
blockPos = args[4];
|
||||
}
|
||||
Reflections.method$LevelAccessor$scheduleTick.invoke(world, blockPos, thisBlock, 1);
|
||||
// return state, do not call super.
|
||||
return superMethod.call();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void randomTick(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
|
||||
Object blockState = args[0];
|
||||
Object level = args[1];
|
||||
Object blockPos = args[2];
|
||||
// above block is empty
|
||||
if (FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, LocationUtils.above(blockPos)) == Reflections.instance$Blocks$AIR$defaultState) {
|
||||
int currentHeight = 1;
|
||||
BlockPos currentPos = LocationUtils.fromBlockPos(blockPos);
|
||||
ImmutableBlockState currentState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(blockState));
|
||||
if (currentState != null && !currentState.isEmpty()) {
|
||||
while (true) {
|
||||
Object belowPos = LocationUtils.toBlockPos(currentPos.x(), currentPos.y() - currentHeight, currentPos.z());
|
||||
Object belowState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, belowPos);
|
||||
ImmutableBlockState belowImmutableState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(belowState));
|
||||
if (belowImmutableState != null && !belowImmutableState.isEmpty() && belowImmutableState.owner() == currentState.owner()) {
|
||||
currentHeight++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentHeight < this.maxHeight) {
|
||||
int age = currentState.get(ageProperty);
|
||||
if (age >= this.ageProperty.max || RandomUtils.generateRandomFloat(0, 1) < this.growSpeed) {
|
||||
Object abovePos = LocationUtils.above(blockPos);
|
||||
if (VersionHelper.isVersionNewerThan1_21_5()) {
|
||||
Reflections.method$CraftEventFactory$handleBlockGrowEvent.invoke(null, level, abovePos, customBlock.defaultState().customBlockState().handle(), UpdateOption.UPDATE_ALL.flags());
|
||||
} else {
|
||||
Reflections.method$CraftEventFactory$handleBlockGrowEvent.invoke(null, level, abovePos, customBlock.defaultState().customBlockState().handle());
|
||||
}
|
||||
Reflections.method$Level$setBlock.invoke(level, blockPos, currentState.with(this.ageProperty, this.ageProperty.min).customBlockState().handle(), UpdateOption.UPDATE_NONE.flags());
|
||||
} else if (RandomUtils.generateRandomFloat(0, 1) < this.growSpeed) {
|
||||
Reflections.method$Level$setBlock.invoke(level, blockPos, currentState.with(this.ageProperty, age + 1).customBlockState().handle(), UpdateOption.UPDATE_NONE.flags());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canSurvive(Object thisBlock, Object state, Object world, Object blockPos) throws ReflectiveOperationException {
|
||||
int y = FastNMS.INSTANCE.field$Vec3i$y(blockPos);
|
||||
int x = FastNMS.INSTANCE.field$Vec3i$x(blockPos);
|
||||
int z = FastNMS.INSTANCE.field$Vec3i$z(blockPos);
|
||||
Object belowPos = FastNMS.INSTANCE.constructor$BlockPos(x, y - 1, z);
|
||||
Object belowState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(world, belowPos);
|
||||
int id = BlockStateUtils.blockStateToId(belowState);
|
||||
// 如果下方是同种方块
|
||||
if (!BlockStateUtils.isVanillaBlock(id)) {
|
||||
ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockStateUnsafe(id);
|
||||
if (immutableBlockState.owner().value() == this.customBlock) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (!super.mayPlaceOn(belowState, world, belowPos)) {
|
||||
return false;
|
||||
}
|
||||
// 如果不需要依靠流体
|
||||
if (!this.nearWater && !this.nearLava) {
|
||||
return true;
|
||||
}
|
||||
// 需要流体
|
||||
if (this.nearWater) {
|
||||
if (hasNearbyLiquid(world, belowPos, true)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (this.nearLava) {
|
||||
if (hasNearbyLiquid(world, belowPos, false)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean hasNearbyLiquid(Object world, Object blockPos, boolean waterOrLava) throws ReflectiveOperationException {
|
||||
for (Object direction : HORIZON_DIRECTIONS) {
|
||||
Object relativePos = Reflections.method$BlockPos$relative.invoke(blockPos, direction);
|
||||
if (waterOrLava) {
|
||||
// water
|
||||
Object blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(world, relativePos);
|
||||
if (Reflections.method$BlockStateBase$getBlock.invoke(blockState) == Reflections.instance$Blocks$ICE) {
|
||||
return true;
|
||||
}
|
||||
Object fluidState = Reflections.method$Level$getFluidState.invoke(world, relativePos);
|
||||
Object fluidType = Reflections.method$FluidState$getType.invoke(fluidState);
|
||||
if (WATER.contains(fluidType)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// lava
|
||||
Object fluidState = Reflections.method$Level$getFluidState.invoke(world, relativePos);
|
||||
Object fluidType = Reflections.method$FluidState$getType.invoke(fluidState);
|
||||
if (LAVA.contains(fluidType)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static class Factory implements BlockBehaviorFactory {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
|
||||
Tuple<List<Object>, Set<Object>, Set<String>> tuple = readTagsAndState(arguments);
|
||||
Property<Integer> ageProperty = (Property<Integer>) block.getProperty("age");
|
||||
if (ageProperty == null) {
|
||||
throw new IllegalArgumentException("age property not set for sugar cane");
|
||||
}
|
||||
int maxHeight = MiscUtils.getAsInt(arguments.getOrDefault("max-height", 3));
|
||||
List<String> nearbyLiquids = MiscUtils.getAsStringList(arguments.getOrDefault("required-adjacent-liquids", List.of()));
|
||||
boolean nearWater = nearbyLiquids.contains("water");
|
||||
boolean nearLava = nearbyLiquids.contains("lava");
|
||||
return new SugarCaneBlockBehavior(block, tuple.left(), tuple.mid(), tuple.right(), ageProperty, maxHeight, nearWater, nearLava,
|
||||
MiscUtils.getAsFloat(arguments.getOrDefault("grow-speed", 1)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package net.momirealms.craftengine.bukkit.item;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.compatibility.item.NeigeItemsProvider;
|
||||
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;
|
||||
@@ -50,6 +51,7 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
|
||||
registerVanillaItemExtraBehavior(AxeItemBehavior.INSTANCE, ItemKeys.AXES);
|
||||
registerVanillaItemExtraBehavior(WaterBucketItemBehavior.INSTANCE, ItemKeys.WATER_BUCKETS);
|
||||
registerVanillaItemExtraBehavior(BucketItemBehavior.INSTANCE, ItemKeys.BUCKET);
|
||||
registerVanillaItemExtraBehavior(BoneMealItemBehavior.INSTANCE, ItemKeys.BONE_MEAL);
|
||||
}
|
||||
|
||||
private static BukkitItemManager instance;
|
||||
|
||||
@@ -40,7 +40,6 @@ import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
@@ -147,12 +146,7 @@ public class BlockItemBehavior extends ItemBehavior {
|
||||
|
||||
protected boolean canPlace(BlockPlaceContext context, ImmutableBlockState state) {
|
||||
try {
|
||||
Object player;
|
||||
try {
|
||||
player = Reflections.method$CraftPlayer$getHandle.invoke(context.getPlayer().platformPlayer());
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
throw new RuntimeException("Failed to get server player", e);
|
||||
}
|
||||
Object player = context.getPlayer().serverPlayer();
|
||||
Object blockState = state.customBlockState().handle();
|
||||
Object blockPos = LocationUtils.toBlockPos(context.getClickedPos());
|
||||
Object voxelShape = Reflections.method$CollisionContext$of.invoke(null, player);
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
package net.momirealms.craftengine.bukkit.item.behavior;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
|
||||
import net.momirealms.craftengine.bukkit.block.behavior.CropBlockBehavior;
|
||||
import net.momirealms.craftengine.bukkit.block.behavior.SaplingBlockBehavior;
|
||||
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.LocationUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.Reflections;
|
||||
import net.momirealms.craftengine.bukkit.world.BukkitWorldBlock;
|
||||
import net.momirealms.craftengine.core.block.ImmutableBlockState;
|
||||
import net.momirealms.craftengine.core.entity.player.InteractionResult;
|
||||
import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
|
||||
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.util.Key;
|
||||
import org.bukkit.block.Block;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Map;
|
||||
|
||||
public class BoneMealItemBehavior extends ItemBehavior {
|
||||
public static final Factory FACTORY = new Factory();
|
||||
public static final BoneMealItemBehavior INSTANCE = new BoneMealItemBehavior();
|
||||
|
||||
@Override
|
||||
public InteractionResult useOnBlock(UseOnContext context) {
|
||||
BukkitWorldBlock clicked = (BukkitWorldBlock) context.getLevel().getBlockAt(context.getClickedPos());
|
||||
Block block = clicked.block();
|
||||
ImmutableBlockState state = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockDataToId(block.getBlockData()));
|
||||
if (state == null || state.isEmpty()) return InteractionResult.PASS;
|
||||
|
||||
boolean shouldHandle =false;
|
||||
if (state.behavior() instanceof CropBlockBehavior blockBehavior) {
|
||||
if (!blockBehavior.isMaxAge(state)) {
|
||||
shouldHandle = true;
|
||||
}
|
||||
} else if (state.behavior() instanceof SaplingBlockBehavior) {
|
||||
shouldHandle = true;
|
||||
}
|
||||
|
||||
if (!shouldHandle) return InteractionResult.PASS;
|
||||
|
||||
boolean sendSwing = false;
|
||||
try {
|
||||
Object visualState = state.vanillaBlockState().handle();
|
||||
Object visualStateBlock = Reflections.method$BlockStateBase$getBlock.invoke(visualState);
|
||||
if (Reflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) {
|
||||
boolean is = (boolean) Reflections.method$BonemealableBlock$isValidBonemealTarget.invoke(visualStateBlock, context.getLevel().serverWorld(), LocationUtils.toBlockPos(context.getClickedPos()), visualState);
|
||||
if (!is) {
|
||||
sendSwing = true;
|
||||
}
|
||||
} else {
|
||||
sendSwing = true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
CraftEngine.instance().logger().warn("Failed to check visual state bone meal state", e);
|
||||
}
|
||||
if (sendSwing) {
|
||||
context.getPlayer().swingHand(context.getHand());
|
||||
}
|
||||
return InteractionResult.SUCCESS;
|
||||
}
|
||||
|
||||
public static class Factory implements ItemBehaviorFactory {
|
||||
|
||||
@Override
|
||||
public ItemBehavior create(Pack pack, Path path, Key id, Map<String, Object> arguments) {
|
||||
return INSTANCE;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ public class BukkitItemBehaviors extends ItemBehaviors {
|
||||
public static final Key AXE_ITEM = Key.from("craftengine:axe_item");
|
||||
public static final Key WATER_BUCKET_ITEM = Key.from("craftengine:water_bucket_item");
|
||||
public static final Key BUCKET_ITEM = Key.from("craftengine:bucket_item");
|
||||
public static final Key BONE_MEAL_ITEM = Key.from("craftengine:bone_meal_item");
|
||||
|
||||
public static void init() {
|
||||
register(EMPTY, EmptyItemBehavior.FACTORY);
|
||||
@@ -20,5 +21,6 @@ public class BukkitItemBehaviors extends ItemBehaviors {
|
||||
register(AXE_ITEM, AxeItemBehavior.FACTORY);
|
||||
register(WATER_BUCKET_ITEM, WaterBucketItemBehavior.FACTORY);
|
||||
register(BUCKET_ITEM, BucketItemBehavior.FACTORY);
|
||||
register(BONE_MEAL_ITEM, BoneMealItemBehavior.FACTORY);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package net.momirealms.craftengine.bukkit.item.behavior;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.util.LocationUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.Reflections;
|
||||
import net.momirealms.craftengine.core.entity.player.InteractionHand;
|
||||
@@ -41,7 +42,7 @@ public class LiquidCollisionBlockItemBehavior extends BlockItemBehavior {
|
||||
try {
|
||||
Object blockHitResult = Reflections.method$Item$getPlayerPOVHitResult.invoke(null, world.serverWorld(), player.serverPlayer(), Reflections.instance$ClipContext$Fluid$SOURCE_ONLY);
|
||||
Object blockPos = Reflections.field$BlockHitResul$blockPos.get(blockHitResult);
|
||||
BlockPos above = new BlockPos(Reflections.field$Vec3i$x.getInt(blockPos), Reflections.field$Vec3i$y.getInt(blockPos) + offsetY, Reflections.field$Vec3i$z.getInt(blockPos));
|
||||
BlockPos above = new BlockPos(FastNMS.INSTANCE.field$Vec3i$x(blockPos), FastNMS.INSTANCE.field$Vec3i$y(blockPos) + offsetY, FastNMS.INSTANCE.field$Vec3i$z(blockPos));
|
||||
Direction direction = Direction.values()[(int) Reflections.method$Direction$ordinal.invoke(Reflections.field$BlockHitResul$direction.get(blockHitResult))];
|
||||
boolean miss = Reflections.field$BlockHitResul$miss.getBoolean(blockHitResult);
|
||||
Vec3d hitPos = LocationUtils.fromVec(Reflections.field$HitResult$location.get(blockHitResult));
|
||||
|
||||
@@ -39,6 +39,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.Reader;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
@@ -988,8 +989,22 @@ public class BukkitRecipeManager implements RecipeManager<ItemStack> {
|
||||
}
|
||||
}
|
||||
|
||||
// 1.21.5+
|
||||
private static Object toTransmuteResult(ItemStack item) throws InvocationTargetException, IllegalAccessException, InstantiationException {
|
||||
Object itemStack = Reflections.method$CraftItemStack$asNMSCopy.invoke(null, item);
|
||||
Object nmsItem = Reflections.method$ItemStack$getItem.invoke(itemStack);
|
||||
return Reflections.constructor$TransmuteResult.newInstance(nmsItem);
|
||||
}
|
||||
|
||||
private static Object createMinecraftSmithingTransformRecipe(CustomSmithingTransformRecipe<ItemStack> ceRecipe) throws ReflectiveOperationException {
|
||||
if (VersionHelper.isVersionNewerThan1_21_2()) {
|
||||
if (VersionHelper.isVersionNewerThan1_21_5()) {
|
||||
return Reflections.constructor$SmithingTransformRecipe.newInstance(
|
||||
toOptionalMinecraftIngredient(ceRecipe.template()),
|
||||
toMinecraftIngredient(ceRecipe.base()),
|
||||
toOptionalMinecraftIngredient(ceRecipe.addition()),
|
||||
toTransmuteResult(ceRecipe.result(ItemBuildContext.EMPTY))
|
||||
);
|
||||
} else if (VersionHelper.isVersionNewerThan1_21_2()) {
|
||||
return Reflections.constructor$SmithingTransformRecipe.newInstance(
|
||||
toOptionalMinecraftIngredient(ceRecipe.template()),
|
||||
toOptionalMinecraftIngredient(ceRecipe.base()),
|
||||
|
||||
@@ -118,7 +118,7 @@ public class BukkitVanillaLootManager implements VanillaLootManager, Listener {
|
||||
VanillaLoot vanillaLoot = this.blockLoots.computeIfAbsent(BlockStateUtils.blockStateToId(blockState), k -> new VanillaLoot(VanillaLoot.Type.BLOCK));
|
||||
vanillaLoot.addLootTable(lootTable);
|
||||
} else {
|
||||
for (Object blockState : BlockStateUtils.getAllBlockStates(Key.of(target))) {
|
||||
for (Object blockState : BlockStateUtils.getAllVanillaBlockStates(Key.of(target))) {
|
||||
if (blockState == Reflections.instance$Blocks$AIR$defaultState) {
|
||||
this.plugin.logger().warn(path, "Failed to load " + id + ". Invalid target " + target);
|
||||
return;
|
||||
|
||||
@@ -4,6 +4,7 @@ import net.momirealms.antigrieflib.AntiGriefLib;
|
||||
import net.momirealms.craftengine.bukkit.api.event.CraftEngineReloadEvent;
|
||||
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
|
||||
import net.momirealms.craftengine.bukkit.block.behavior.BukkitBlockBehaviors;
|
||||
import net.momirealms.craftengine.bukkit.compatibility.papi.PlaceholderAPIUtils;
|
||||
import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager;
|
||||
import net.momirealms.craftengine.bukkit.font.BukkitImageManager;
|
||||
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
|
||||
@@ -17,13 +18,10 @@ import net.momirealms.craftengine.bukkit.plugin.command.BukkitSenderFactory;
|
||||
import net.momirealms.craftengine.bukkit.plugin.gui.BukkitGuiManager;
|
||||
import net.momirealms.craftengine.bukkit.plugin.injector.BukkitInjector;
|
||||
import net.momirealms.craftengine.bukkit.plugin.network.BukkitNetworkManager;
|
||||
import net.momirealms.craftengine.bukkit.plugin.papi.ImageExpansion;
|
||||
import net.momirealms.craftengine.bukkit.plugin.papi.ShiftExpansion;
|
||||
import net.momirealms.craftengine.bukkit.plugin.scheduler.BukkitSchedulerAdapter;
|
||||
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
|
||||
import net.momirealms.craftengine.bukkit.sound.BukkitSoundManager;
|
||||
import net.momirealms.craftengine.bukkit.util.EventUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.PlaceholderAPIUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.Reflections;
|
||||
import net.momirealms.craftengine.bukkit.world.BukkitWorldManager;
|
||||
import net.momirealms.craftengine.core.entity.player.Player;
|
||||
@@ -176,8 +174,7 @@ public class BukkitCraftEngine extends CraftEngine {
|
||||
// compatibility
|
||||
// register expansion
|
||||
if (this.isPluginEnabled("PlaceholderAPI")) {
|
||||
new ShiftExpansion(this).register();
|
||||
new ImageExpansion(this).register();
|
||||
PlaceholderAPIUtils.registerExpansions(this);
|
||||
this.hasPlaceholderAPI = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,6 +96,8 @@ public class BukkitInjector {
|
||||
.method(ElementMatchers.any()
|
||||
.and(ElementMatchers.not(ElementMatchers.is(Reflections.method$PalettedContainer$getAndSet)))
|
||||
.and(ElementMatchers.not(ElementMatchers.isDeclaredBy(Object.class)))
|
||||
// TODO Requires Paper Patch
|
||||
//.and(ElementMatchers.not(ElementMatchers.named("get").and(ElementMatchers.takesArguments(int.class)).and(ElementMatchers.returns(Object.class))))
|
||||
)
|
||||
.intercept(MethodDelegation.toField("target"))
|
||||
.method(ElementMatchers.is(Reflections.method$PalettedContainer$getAndSet))
|
||||
|
||||
@@ -6,7 +6,7 @@ import io.netty.handler.codec.MessageToMessageDecoder;
|
||||
import io.netty.handler.codec.MessageToMessageEncoder;
|
||||
import io.netty.util.internal.logging.InternalLogger;
|
||||
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
|
||||
import net.momirealms.craftengine.bukkit.plugin.network.impl.*;
|
||||
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
|
||||
@@ -72,7 +72,9 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
|
||||
|
||||
public BukkitNetworkManager(BukkitCraftEngine plugin) {
|
||||
this.plugin = plugin;
|
||||
if (VersionHelper.isVersionNewerThan1_21_2()) {
|
||||
if (VersionHelper.isVersionNewerThan1_21_5()) {
|
||||
this.packetIds = new PacketIds1_21_5();
|
||||
} else if (VersionHelper.isVersionNewerThan1_21_2()) {
|
||||
this.packetIds = new PacketIds1_21_2();
|
||||
} else if (VersionHelper.isVersionNewerThan1_20_5()) {
|
||||
this.packetIds = new PacketIds1_20_5();
|
||||
@@ -254,7 +256,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
|
||||
return (Channel) Reflections.field$Channel.get(
|
||||
Reflections.field$NetworkManager.get(
|
||||
Reflections.field$ServerPlayer$connection.get(
|
||||
Reflections.method$CraftPlayer$getHandle.invoke(player)
|
||||
FastNMS.INSTANCE.method$CraftPlayer$getHandle(player)
|
||||
)
|
||||
)
|
||||
);
|
||||
@@ -265,7 +267,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
|
||||
|
||||
public void sendPacket(@NotNull Player player, @NotNull Object packet) {
|
||||
try {
|
||||
Object serverPlayer = Reflections.method$CraftPlayer$getHandle.invoke(player);
|
||||
Object serverPlayer = FastNMS.INSTANCE.method$CraftPlayer$getHandle(player);
|
||||
this.immediatePacketConsumer.accept(serverPlayer, packet);
|
||||
} catch (Exception e) {
|
||||
this.plugin.logger().warn("Failed to send packet", e);
|
||||
|
||||
@@ -11,6 +11,7 @@ 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.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
|
||||
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
|
||||
import net.momirealms.craftengine.bukkit.util.*;
|
||||
@@ -268,11 +269,7 @@ public class PacketConsumers {
|
||||
Player platformPlayer = player.platformPlayer();
|
||||
World world = platformPlayer.getWorld();
|
||||
Object blockPos = Reflections.field$ServerboundPlayerActionPacket$pos.get(packet);
|
||||
BlockPos pos = new BlockPos(
|
||||
(int) Reflections.field$Vec3i$x.get(blockPos),
|
||||
(int) Reflections.field$Vec3i$y.get(blockPos),
|
||||
(int) Reflections.field$Vec3i$z.get(blockPos)
|
||||
);
|
||||
BlockPos pos = LocationUtils.fromBlockPos(blockPos);
|
||||
if (VersionHelper.isFolia()) {
|
||||
BukkitCraftEngine.instance().scheduler().sync().run(() -> {
|
||||
try {
|
||||
@@ -293,7 +290,7 @@ public class PacketConsumers {
|
||||
Object action = Reflections.field$ServerboundPlayerActionPacket$action.get(packet);
|
||||
if (action == Reflections.instance$ServerboundPlayerActionPacket$Action$START_DESTROY_BLOCK) {
|
||||
Object serverLevel = Reflections.field$CraftWorld$ServerLevel.get(world);
|
||||
Object blockState = Reflections.method$BlockGetter$getBlockState.invoke(serverLevel, LocationUtils.toBlockPos(pos));
|
||||
Object blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(serverLevel, LocationUtils.toBlockPos(pos));
|
||||
int stateId = BlockStateUtils.blockStateToId(blockState);
|
||||
// not a custom block
|
||||
if (BlockStateUtils.isVanillaBlock(stateId)) {
|
||||
@@ -311,7 +308,7 @@ public class PacketConsumers {
|
||||
}
|
||||
if (player.isAdventureMode()) {
|
||||
Object itemStack = Reflections.method$CraftItemStack$asNMSCopy.invoke(null, player.platformPlayer().getInventory().getItemInMainHand());
|
||||
Object blockPos = Reflections.constructor$BlockPos.newInstance(pos.x(), pos.y(), pos.z());
|
||||
Object blockPos = LocationUtils.toBlockPos(pos);
|
||||
Object blockInWorld = Reflections.constructor$BlockInWorld.newInstance(serverLevel, blockPos, false);
|
||||
if (VersionHelper.isVersionNewerThan1_20_5()) {
|
||||
if (Reflections.method$ItemStack$canBreakBlockInAdventureMode != null
|
||||
@@ -523,8 +520,8 @@ public class PacketConsumers {
|
||||
if (player == null) return;
|
||||
Object pos = Reflections.field$ServerboundPickItemFromBlockPacket$pos.get(packet);
|
||||
if (VersionHelper.isFolia()) {
|
||||
int x = (int) Reflections.field$Vec3i$x.get(pos);
|
||||
int z = (int) Reflections.field$Vec3i$z.get(pos);
|
||||
int x = FastNMS.INSTANCE.field$Vec3i$x(pos);
|
||||
int z = FastNMS.INSTANCE.field$Vec3i$z(pos);
|
||||
BukkitCraftEngine.instance().scheduler().sync().run(() -> {
|
||||
try {
|
||||
handlePickItemFromBlockPacketOnMainThread(player, pos);
|
||||
@@ -548,7 +545,7 @@ public class PacketConsumers {
|
||||
|
||||
private static void handlePickItemFromBlockPacketOnMainThread(Player player, Object pos) throws Exception {
|
||||
Object serverLevel = Reflections.field$CraftWorld$ServerLevel.get(player.getWorld());
|
||||
Object blockState = Reflections.method$BlockGetter$getBlockState.invoke(serverLevel, pos);
|
||||
Object blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(serverLevel, pos);
|
||||
ImmutableBlockState state = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(blockState));
|
||||
if (state == null) return;
|
||||
Key itemId = state.settings().itemId();
|
||||
@@ -603,7 +600,7 @@ public class PacketConsumers {
|
||||
}
|
||||
assert Reflections.method$ServerGamePacketListenerImpl$tryPickItem != null;
|
||||
Reflections.method$ServerGamePacketListenerImpl$tryPickItem.invoke(
|
||||
Reflections.field$ServerPlayer$connection.get(Reflections.method$CraftPlayer$getHandle.invoke(player)), Reflections.method$CraftItemStack$asNMSCopy.invoke(null, itemStack));
|
||||
Reflections.field$ServerPlayer$connection.get(FastNMS.INSTANCE.method$CraftPlayer$getHandle(player)), Reflections.method$CraftItemStack$asNMSCopy.invoke(null, itemStack));
|
||||
}
|
||||
|
||||
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> ADD_ENTITY = (user, event, packet) -> {
|
||||
@@ -909,9 +906,14 @@ public class PacketConsumers {
|
||||
Object id = Reflections.method$CustomPacketPayload$Type$id.invoke(type);
|
||||
String channel = id.toString();
|
||||
if (!channel.equals(NetworkManager.MOD_CHANNEL)) return;
|
||||
ByteBuf buf = (ByteBuf) Reflections.method$DiscardedPayload$data.invoke(payload);
|
||||
byte[] data = new byte[buf.readableBytes()];
|
||||
buf.readBytes(data);
|
||||
byte[] data;
|
||||
if (Reflections.method$DiscardedPayload$data != null) {
|
||||
ByteBuf buf = (ByteBuf) Reflections.method$DiscardedPayload$data.invoke(payload);
|
||||
data = new byte[buf.readableBytes()];
|
||||
buf.readBytes(data);
|
||||
} 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(':');
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package net.momirealms.craftengine.bukkit.plugin.network.impl;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.plugin.network.PacketIds;
|
||||
|
||||
public class PacketIds1_21_5 implements PacketIds {
|
||||
|
||||
@Override
|
||||
public int clientboundBlockUpdatePacket() {
|
||||
return 8;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int clientboundSectionBlocksUpdatePacket() {
|
||||
return 72;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int clientboundLevelParticlesPacket() {
|
||||
return 40;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int clientboundLevelEventPacket() {
|
||||
return 39;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int clientboundAddEntityPacket() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import com.google.common.collect.Lists;
|
||||
import io.netty.channel.Channel;
|
||||
import net.kyori.adventure.text.Component;
|
||||
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.util.*;
|
||||
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
|
||||
@@ -75,11 +76,7 @@ public class BukkitServerPlayer extends Player {
|
||||
|
||||
public void setPlayer(org.bukkit.entity.Player player) {
|
||||
playerRef = new WeakReference<>(player);
|
||||
try {
|
||||
serverPlayerRef = new WeakReference<>(Reflections.method$CraftPlayer$getHandle.invoke(player));
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
serverPlayerRef = new WeakReference<>(FastNMS.INSTANCE.method$CraftPlayer$getHandle(player));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -298,7 +295,7 @@ public class BukkitServerPlayer extends Player {
|
||||
public float getDestroyProgress(Object blockState, BlockPos pos) {
|
||||
try {
|
||||
Object serverPlayer = serverPlayer();
|
||||
Object blockPos = Reflections.constructor$BlockPos.newInstance(pos.x(), pos.y(), pos.z());
|
||||
Object blockPos = LocationUtils.toBlockPos(pos.x(), pos.y(), pos.z());
|
||||
return (float) Reflections.method$BlockStateBase$getDestroyProgress.invoke(blockState, serverPlayer, Reflections.method$Entity$level.invoke(serverPlayer), blockPos);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
this.plugin.logger().warn("Failed to get destroy progress for player " + platformPlayer().getName());
|
||||
|
||||
@@ -1,18 +1,23 @@
|
||||
package net.momirealms.craftengine.bukkit.util;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.core.block.BlockStateParser;
|
||||
import net.momirealms.craftengine.core.block.CustomBlock;
|
||||
import net.momirealms.craftengine.core.block.ImmutableBlockState;
|
||||
import net.momirealms.craftengine.core.block.PushReaction;
|
||||
import net.momirealms.craftengine.core.util.Instrument;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.MapColor;
|
||||
import net.momirealms.craftengine.core.world.BlockPos;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.event.block.BlockPhysicsEvent;
|
||||
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class BlockStateUtils {
|
||||
public static final IdentityHashMap<Object, Object> CLIENT_SIDE_NOTE_BLOCKS = new IdentityHashMap<>();
|
||||
@@ -27,8 +32,36 @@ public class BlockStateUtils {
|
||||
hasInit = true;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static List<Object> getAllBlockStates(String blockState) {
|
||||
int index = blockState.indexOf('[');
|
||||
if (index == -1) {
|
||||
return getAllBlockStates(Key.of(blockState));
|
||||
} else {
|
||||
String blockTypeString = blockState.substring(0, index);
|
||||
Key block = Key.of(blockTypeString);
|
||||
Optional<CustomBlock> optionalCustomBlock = BukkitBlockManager.instance().getBlock(block);
|
||||
if (optionalCustomBlock.isPresent()) {
|
||||
ImmutableBlockState state = BlockStateParser.deserialize(blockState);
|
||||
if (state == null) {
|
||||
return List.of();
|
||||
} else {
|
||||
return List.of(state.customBlockState().handle());
|
||||
}
|
||||
} else {
|
||||
BlockData blockData = Bukkit.createBlockData(blockState);
|
||||
return List.of(blockDataToBlockState(blockData));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static List<Object> getAllBlockStates(Key block) {
|
||||
Optional<CustomBlock> optionalCustomBlock = BukkitBlockManager.instance().getBlock(block);
|
||||
return optionalCustomBlock.map(customBlock -> customBlock.variantProvider().states().stream().map(it -> it.customBlockState().handle()).toList())
|
||||
.orElseGet(() -> getAllVanillaBlockStates(block));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static List<Object> getAllVanillaBlockStates(Key block) {
|
||||
try {
|
||||
Object blockIns = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$BLOCK, Reflections.method$ResourceLocation$fromNamespaceAndPath.invoke(null, block.namespace(), block.value()));
|
||||
Object definition = Reflections.field$Block$StateDefinition.get(blockIns);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package net.momirealms.craftengine.bukkit.util;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.core.world.BlockPos;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
@@ -14,13 +15,9 @@ public class EntityUtils {
|
||||
|
||||
public static BlockPos getOnPos(Player player) {
|
||||
try {
|
||||
Object serverPlayer = Reflections.method$CraftPlayer$getHandle.invoke(player);
|
||||
Object serverPlayer = FastNMS.INSTANCE.method$CraftPlayer$getHandle(player);
|
||||
Object blockPos = Reflections.method$Entity$getOnPos.invoke(serverPlayer, 1.0E-5F);
|
||||
return new BlockPos(
|
||||
(int) Reflections.field$Vec3i$x.get(blockPos),
|
||||
(int) Reflections.field$Vec3i$y.get(blockPos),
|
||||
(int) Reflections.field$Vec3i$z.get(blockPos)
|
||||
);
|
||||
return LocationUtils.fromBlockPos(blockPos);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ public class LightUtils {
|
||||
|
||||
private LightUtils() {}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static void updateChunkLight(World world, Map<Long, BitSet> sectionPosSet) {
|
||||
try {
|
||||
Object serverLevel = Reflections.field$CraftWorld$ServerLevel.get(world);
|
||||
@@ -19,8 +20,13 @@ public class LightUtils {
|
||||
long chunkKey = entry.getKey();
|
||||
Object chunkHolder = Reflections.method$ServerChunkCache$getVisibleChunkIfPresent.invoke(chunkSource, chunkKey);
|
||||
if (chunkHolder == null) continue;
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Object> players = (List<Object>) Reflections.method$ChunkHolder$getPlayers.invoke(chunkHolder, false);
|
||||
List<Object> players;
|
||||
if (Reflections.method$ChunkHolder$getPlayers != null) {
|
||||
players = (List<Object>) Reflections.method$ChunkHolder$getPlayers.invoke(chunkHolder, false);
|
||||
} else {
|
||||
Object chunkHolder$PlayerProvider = Reflections.field$ChunkHolder$playerProvider.get(chunkHolder);
|
||||
players = (List<Object>) Reflections.method$ChunkHolder$PlayerProvider$getPlayers.invoke(chunkHolder$PlayerProvider, false);
|
||||
}
|
||||
if (players.isEmpty()) continue;
|
||||
Object lightEngine = Reflections.field$ChunkHolder$lightEngine.get(chunkHolder);
|
||||
BitSet blockChangedLightSectionFilter = (BitSet) Reflections.field$ChunkHolder$blockChangedLightSectionFilter.get(chunkHolder);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package net.momirealms.craftengine.bukkit.util;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.core.world.BlockPos;
|
||||
import net.momirealms.craftengine.core.world.Vec3d;
|
||||
import org.bukkit.Location;
|
||||
@@ -13,48 +14,39 @@ public class LocationUtils {
|
||||
return new Vec3d(loc.getX(), loc.getY(), loc.getZ());
|
||||
}
|
||||
|
||||
public static Vec3d fromVec(Object vec) throws ReflectiveOperationException {
|
||||
public static Vec3d fromVec(Object vec) {
|
||||
return new Vec3d(
|
||||
Reflections.field$Vec3$x.getDouble(vec),
|
||||
Reflections.field$Vec3$y.getDouble(vec),
|
||||
Reflections.field$Vec3$z.getDouble(vec)
|
||||
FastNMS.INSTANCE.field$Vec3$x(vec),
|
||||
FastNMS.INSTANCE.field$Vec3$y(vec),
|
||||
FastNMS.INSTANCE.field$Vec3$y(vec)
|
||||
);
|
||||
}
|
||||
|
||||
public static Object toBlockPos(BlockPos pos) {
|
||||
try {
|
||||
return Reflections.constructor$BlockPos.newInstance(pos.x(), pos.y(), pos.z());
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException("Failed to create BlockPos", e);
|
||||
}
|
||||
return toBlockPos(pos.x(), pos.y(), pos.z());
|
||||
}
|
||||
|
||||
public static Object above(Object blockPos) throws ReflectiveOperationException {
|
||||
return toBlockPos(
|
||||
Reflections.field$Vec3i$x.getInt(blockPos),
|
||||
Reflections.field$Vec3i$y.getInt(blockPos) + 1,
|
||||
Reflections.field$Vec3i$z.getInt(blockPos)
|
||||
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) {
|
||||
try {
|
||||
return Reflections.constructor$BlockPos.newInstance(x, y, z);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException("Failed to create BlockPos", e);
|
||||
}
|
||||
return FastNMS.INSTANCE.constructor$BlockPos(x, y, z);
|
||||
}
|
||||
|
||||
|
||||
public static BlockPos toBlockPos(Location pos) {
|
||||
return new BlockPos(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ());
|
||||
}
|
||||
|
||||
public static BlockPos fromBlockPos(Object pos) throws ReflectiveOperationException {
|
||||
return new BlockPos(
|
||||
Reflections.field$Vec3i$x.getInt(pos),
|
||||
Reflections.field$Vec3i$y.getInt(pos),
|
||||
Reflections.field$Vec3i$z.getInt(pos)
|
||||
FastNMS.INSTANCE.field$Vec3i$x(pos),
|
||||
FastNMS.INSTANCE.field$Vec3i$y(pos),
|
||||
FastNMS.INSTANCE.field$Vec3i$z(pos)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package net.momirealms.craftengine.bukkit.util;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.core.plugin.config.ConfigManager;
|
||||
|
||||
public class NoteBlockChainUpdateUtils {
|
||||
@@ -9,7 +10,7 @@ public class NoteBlockChainUpdateUtils {
|
||||
public static void noteBlockChainUpdate(Object level, Object chunkSource, Object direction, Object blockPos, int times) throws ReflectiveOperationException {
|
||||
if (times >= ConfigManager.maxChainUpdate()) return;
|
||||
Object relativePos = Reflections.method$BlockPos$relative.invoke(blockPos, direction);
|
||||
Object state = Reflections.method$BlockGetter$getBlockState.invoke(level, relativePos);
|
||||
Object state = FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, relativePos);
|
||||
if (BlockStateUtils.isClientSideNoteBlock(state)) {
|
||||
Reflections.method$ServerChunkCache$blockChanged.invoke(chunkSource, relativePos);
|
||||
noteBlockChainUpdate(level, chunkSource, direction, relativePos, times+1);
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package net.momirealms.craftengine.bukkit.util;
|
||||
|
||||
import org.bukkit.Particle;
|
||||
|
||||
public class ParticleUtils {
|
||||
|
||||
public static Particle getParticle(String particle) {
|
||||
try {
|
||||
return Particle.valueOf(particle);
|
||||
} catch (IllegalArgumentException e) {
|
||||
return switch (particle) {
|
||||
case "REDSTONE" -> Particle.valueOf("DUST");
|
||||
case "VILLAGER_HAPPY" -> Particle.valueOf("HAPPY_VILLAGER");
|
||||
default -> Particle.valueOf(particle);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -471,9 +471,9 @@ public class Reflections {
|
||||
BukkitReflectionUtils.assembleCBClass("entity.CraftPlayer")
|
||||
));
|
||||
|
||||
public static final Method method$CraftPlayer$getHandle = requireNonNull(
|
||||
ReflectionUtils.getMethod(clazz$CraftPlayer, new String[] { "getHandle" })
|
||||
);
|
||||
// public static final Method method$CraftPlayer$getHandle = requireNonNull(
|
||||
// ReflectionUtils.getMethod(clazz$CraftPlayer, new String[] { "getHandle" })
|
||||
// );
|
||||
|
||||
public static final Field field$ServerPlayer$connection = requireNonNull(
|
||||
ReflectionUtils.getInstanceDeclaredField(clazz$ServerPlayer, clazz$ServerGamePacketListenerImpl, 0)
|
||||
@@ -1263,9 +1263,18 @@ public class Reflections {
|
||||
)
|
||||
);
|
||||
|
||||
public static final Class<?> clazz$Team$Visibility = requireNonNull(
|
||||
ReflectionUtils.getClazz(
|
||||
BukkitReflectionUtils.assembleMCClass("world.scores.Team$Visibility"),
|
||||
BukkitReflectionUtils.assembleMCClass("world.scores.ScoreboardTeamBase$EnumTeamPush")
|
||||
)
|
||||
);
|
||||
|
||||
public static final Field field$ClientboundSetPlayerTeamPacket$Parameters$nametagVisibility = requireNonNull(
|
||||
ReflectionUtils.getInstanceDeclaredField(
|
||||
clazz$ClientboundSetPlayerTeamPacket$Parameters, String.class, 0
|
||||
clazz$ClientboundSetPlayerTeamPacket$Parameters,
|
||||
VersionHelper.isVersionNewerThan1_21_5() ? clazz$Team$Visibility : String.class,
|
||||
0
|
||||
)
|
||||
);
|
||||
|
||||
@@ -1355,17 +1364,17 @@ public class Reflections {
|
||||
)
|
||||
);
|
||||
|
||||
public static final Field field$Vec3i$x = requireNonNull(
|
||||
ReflectionUtils.getDeclaredField(clazz$Vec3i, int.class, 0)
|
||||
);
|
||||
|
||||
public static final Field field$Vec3i$y = requireNonNull(
|
||||
ReflectionUtils.getDeclaredField(clazz$Vec3i, int.class, 1)
|
||||
);
|
||||
|
||||
public static final Field field$Vec3i$z = requireNonNull(
|
||||
ReflectionUtils.getDeclaredField(clazz$Vec3i, int.class, 2)
|
||||
);
|
||||
// public static final Field field$Vec3i$x = requireNonNull(
|
||||
// ReflectionUtils.getDeclaredField(clazz$Vec3i, int.class, 0)
|
||||
// );
|
||||
//
|
||||
// public static final Field field$Vec3i$y = requireNonNull(
|
||||
// ReflectionUtils.getDeclaredField(clazz$Vec3i, int.class, 1)
|
||||
// );
|
||||
//
|
||||
// public static final Field field$Vec3i$z = requireNonNull(
|
||||
// ReflectionUtils.getDeclaredField(clazz$Vec3i, int.class, 2)
|
||||
// );
|
||||
|
||||
public static final Class<?> clazz$BlockState = requireNonNull(
|
||||
ReflectionUtils.getClazz(
|
||||
@@ -1588,11 +1597,11 @@ public class Reflections {
|
||||
)
|
||||
);
|
||||
|
||||
public static final Constructor<?> constructor$BlockPos = requireNonNull(
|
||||
ReflectionUtils.getConstructor(
|
||||
clazz$BlockPos, int.class, int.class, int.class
|
||||
)
|
||||
);
|
||||
// public static final Constructor<?> constructor$BlockPos = requireNonNull(
|
||||
// ReflectionUtils.getConstructor(
|
||||
// clazz$BlockPos, int.class, int.class, int.class
|
||||
// )
|
||||
// );
|
||||
|
||||
public static final Method method$Vec3i$relative = requireNonNull(
|
||||
ReflectionUtils.getMethod(
|
||||
@@ -2462,11 +2471,11 @@ public class Reflections {
|
||||
)
|
||||
);
|
||||
|
||||
public static final Method method$BlockGetter$getBlockState = requireNonNull(
|
||||
ReflectionUtils.getMethod(
|
||||
clazz$BlockGetter, clazz$BlockState, clazz$BlockPos
|
||||
)
|
||||
);
|
||||
// public static final Method method$BlockGetter$getBlockState = requireNonNull(
|
||||
// ReflectionUtils.getMethod(
|
||||
// clazz$BlockGetter, clazz$BlockState, clazz$BlockPos
|
||||
// )
|
||||
// );
|
||||
|
||||
public static final Method method$LevelAccessor$scheduleTick = requireNonNull(
|
||||
ReflectionUtils.getMethod(
|
||||
@@ -2660,12 +2669,31 @@ public class Reflections {
|
||||
)
|
||||
);
|
||||
|
||||
public static final Method method$ChunkHolder$getPlayers = requireNonNull(
|
||||
ReflectionUtils.getMethod(
|
||||
clazz$ChunkHolder, List.class, boolean.class
|
||||
public static final Class<?> clazz$ChunkHolder$PlayerProvider = requireNonNull(
|
||||
ReflectionUtils.getClazz(
|
||||
BukkitReflectionUtils.assembleMCClass("server.level.ChunkHolder$PlayerProvider"),
|
||||
BukkitReflectionUtils.assembleMCClass("server.level.PlayerChunk$d")
|
||||
)
|
||||
);
|
||||
|
||||
public static final Field field$ChunkHolder$playerProvider = requireNonNull(
|
||||
ReflectionUtils.getDeclaredField(
|
||||
clazz$ChunkHolder, clazz$ChunkHolder$PlayerProvider, 0
|
||||
)
|
||||
);
|
||||
|
||||
public static final Method method$ChunkHolder$PlayerProvider$getPlayers = requireNonNull(
|
||||
ReflectionUtils.getMethod(
|
||||
clazz$ChunkHolder$PlayerProvider, List.class, clazz$ChunkPos, boolean.class
|
||||
)
|
||||
);
|
||||
|
||||
// 1.20 ~ 1.21.4
|
||||
public static final Method method$ChunkHolder$getPlayers =
|
||||
ReflectionUtils.getMethod(
|
||||
clazz$ChunkHolder, List.class, boolean.class
|
||||
);
|
||||
|
||||
public static final Field field$ChunkHolder$lightEngine = requireNonNull(
|
||||
ReflectionUtils.getDeclaredField(
|
||||
clazz$ChunkHolder, clazz$LevelLightEngine, 0
|
||||
@@ -3407,6 +3435,7 @@ public class Reflections {
|
||||
)
|
||||
);
|
||||
|
||||
@Deprecated
|
||||
public static final Method method$Level$getCraftWorld = requireNonNull(
|
||||
ReflectionUtils.getMethod(
|
||||
clazz$Level, clazz$CraftWorld
|
||||
@@ -3923,9 +3952,9 @@ public class Reflections {
|
||||
);
|
||||
|
||||
public static final Method method$CraftEventFactory$callBlockPlaceEvent = requireNonNull(
|
||||
ReflectionUtils.getStaticMethod(
|
||||
clazz$CraftEventFactory, BlockPlaceEvent.class, clazz$ServerLevel, clazz$Player, clazz$InteractionHand, BlockState.class, int.class, int.class, int.class
|
||||
)
|
||||
VersionHelper.isVersionNewerThan1_21_5()
|
||||
? ReflectionUtils.getStaticMethod(clazz$CraftEventFactory, BlockPlaceEvent.class, clazz$ServerLevel, clazz$Player, clazz$InteractionHand, BlockState.class, clazz$BlockPos)
|
||||
: ReflectionUtils.getStaticMethod(clazz$CraftEventFactory, BlockPlaceEvent.class, clazz$ServerLevel, clazz$Player, clazz$InteractionHand, BlockState.class, int.class, int.class, int.class)
|
||||
);
|
||||
|
||||
public static final Class<?> clazz$Abilities = requireNonNull(
|
||||
@@ -4816,9 +4845,9 @@ public class Reflections {
|
||||
);
|
||||
|
||||
public static final Method method$ServerLevel$levelEvent = requireNonNull(
|
||||
ReflectionUtils.getMethod(
|
||||
clazz$ServerLevel, void.class, clazz$Player, int.class, clazz$BlockPos, int.class
|
||||
)
|
||||
VersionHelper.isVersionNewerThan1_21_5()
|
||||
? ReflectionUtils.getMethod(clazz$ServerLevel, void.class, clazz$Entity, int.class, clazz$BlockPos, int.class)
|
||||
: ReflectionUtils.getMethod(clazz$ServerLevel, void.class, clazz$Player, int.class, clazz$BlockPos, int.class)
|
||||
);
|
||||
|
||||
public static final Method method$PalettedContainer$getAndSet = Objects.requireNonNull(
|
||||
@@ -5104,12 +5133,24 @@ public class Reflections {
|
||||
)
|
||||
);
|
||||
|
||||
// 1.21.5+
|
||||
public static final Class<?> clazz$TransmuteResult =
|
||||
ReflectionUtils.getClazz(
|
||||
BukkitReflectionUtils.assembleMCClass("world.item.crafting.TransmuteResult")
|
||||
);
|
||||
|
||||
public static final Constructor<?> constructor$TransmuteResult = Optional.ofNullable(clazz$TransmuteResult)
|
||||
.map(it -> ReflectionUtils.getConstructor(it, clazz$Item))
|
||||
.orElse(null);
|
||||
|
||||
public static final Constructor<?> constructor$SmithingTransformRecipe = requireNonNull(
|
||||
VersionHelper.isVersionNewerThan1_21_2() ?
|
||||
ReflectionUtils.getConstructor(clazz$SmithingTransformRecipe, Optional.class, Optional.class, Optional.class, clazz$ItemStack) :
|
||||
VersionHelper.isVersionNewerThan1_20_2() ?
|
||||
ReflectionUtils.getConstructor(clazz$SmithingTransformRecipe, clazz$Ingredient, clazz$Ingredient, clazz$Ingredient, clazz$ItemStack) :
|
||||
ReflectionUtils.getConstructor(clazz$SmithingTransformRecipe, clazz$ResourceLocation, clazz$Ingredient, clazz$Ingredient, clazz$Ingredient, clazz$ItemStack)
|
||||
VersionHelper.isVersionNewerThan1_21_5()
|
||||
? ReflectionUtils.getConstructor(clazz$SmithingTransformRecipe, Optional.class, clazz$Ingredient, Optional.class, clazz$TransmuteResult)
|
||||
: VersionHelper.isVersionNewerThan1_21_2()
|
||||
? ReflectionUtils.getConstructor(clazz$SmithingTransformRecipe, Optional.class, Optional.class, Optional.class, clazz$ItemStack)
|
||||
: VersionHelper.isVersionNewerThan1_20_2()
|
||||
? ReflectionUtils.getConstructor(clazz$SmithingTransformRecipe, clazz$Ingredient, clazz$Ingredient, clazz$Ingredient, clazz$ItemStack)
|
||||
: ReflectionUtils.getConstructor(clazz$SmithingTransformRecipe, clazz$ResourceLocation, clazz$Ingredient, clazz$Ingredient, clazz$Ingredient, clazz$ItemStack)
|
||||
);
|
||||
|
||||
public static final Method method$RecipeManager$addRecipe = requireNonNull(
|
||||
@@ -5415,13 +5456,11 @@ public class Reflections {
|
||||
);
|
||||
|
||||
public static final Method method$SimpleWaterloggedBlock$canPlaceLiquid = requireNonNull(
|
||||
VersionHelper.isVersionNewerThan1_20_2() ?
|
||||
ReflectionUtils.getMethod(
|
||||
clazz$SimpleWaterloggedBlock, boolean.class, clazz$Player, clazz$BlockGetter, clazz$BlockPos, clazz$BlockState, clazz$Fluid
|
||||
) :
|
||||
ReflectionUtils.getMethod(
|
||||
clazz$SimpleWaterloggedBlock, boolean.class, clazz$BlockGetter, clazz$BlockPos, clazz$BlockState, clazz$Fluid
|
||||
)
|
||||
VersionHelper.isVersionNewerThan1_21_5()
|
||||
? ReflectionUtils.getMethod(clazz$SimpleWaterloggedBlock, boolean.class, clazz$LivingEntity, clazz$BlockGetter, clazz$BlockPos, clazz$BlockState, clazz$Fluid)
|
||||
: VersionHelper.isVersionNewerThan1_20_2()
|
||||
? ReflectionUtils.getMethod(clazz$SimpleWaterloggedBlock, boolean.class, clazz$Player, clazz$BlockGetter, clazz$BlockPos, clazz$BlockState, clazz$Fluid)
|
||||
: ReflectionUtils.getMethod(clazz$SimpleWaterloggedBlock, boolean.class, clazz$BlockGetter, clazz$BlockPos, clazz$BlockState, clazz$Fluid)
|
||||
);
|
||||
|
||||
public static final Method method$SimpleWaterloggedBlock$placeLiquid = requireNonNull(
|
||||
@@ -5431,13 +5470,11 @@ public class Reflections {
|
||||
);
|
||||
|
||||
public static final Method method$SimpleWaterloggedBlock$pickupBlock = requireNonNull(
|
||||
VersionHelper.isVersionNewerThan1_20_2() ?
|
||||
ReflectionUtils.getMethod(
|
||||
clazz$SimpleWaterloggedBlock, clazz$ItemStack, clazz$Player, clazz$LevelAccessor, clazz$BlockPos, clazz$BlockState
|
||||
) :
|
||||
ReflectionUtils.getMethod(
|
||||
clazz$SimpleWaterloggedBlock, clazz$ItemStack, clazz$LevelAccessor, clazz$BlockPos, clazz$BlockState
|
||||
)
|
||||
VersionHelper.isVersionNewerThan1_21_5()
|
||||
? ReflectionUtils.getMethod(clazz$SimpleWaterloggedBlock, clazz$ItemStack, clazz$LivingEntity, clazz$LevelAccessor, clazz$BlockPos, clazz$BlockState)
|
||||
: VersionHelper.isVersionNewerThan1_20_2()
|
||||
? ReflectionUtils.getMethod(clazz$SimpleWaterloggedBlock, clazz$ItemStack, clazz$Player, clazz$LevelAccessor, clazz$BlockPos, clazz$BlockState)
|
||||
: ReflectionUtils.getMethod(clazz$SimpleWaterloggedBlock, clazz$ItemStack, clazz$LevelAccessor, clazz$BlockPos, clazz$BlockState)
|
||||
);
|
||||
|
||||
public static final Method method$Fluid$getTickDelay = requireNonNull(
|
||||
@@ -5667,11 +5704,19 @@ public class Reflections {
|
||||
.map(it -> ReflectionUtils.getMethod(it, clazz$ResourceLocation))
|
||||
.orElse(null);
|
||||
|
||||
// 1.20.5+
|
||||
// 1.20.5~1.21.4#221
|
||||
public static final Method method$DiscardedPayload$data = Optional.ofNullable(clazz$DiscardedPayload)
|
||||
.map(it -> ReflectionUtils.getMethod(it, ByteBuf.class))
|
||||
.orElse(null);
|
||||
|
||||
// 1.21.4#222+
|
||||
public static final Method method$DiscardedPayload$dataByteArray = Optional.ofNullable(method$DiscardedPayload$data)
|
||||
.map(m -> (Method) null)
|
||||
.orElseGet(() -> Optional.ofNullable(clazz$DiscardedPayload)
|
||||
.map(clazz -> ReflectionUtils.getMethod(clazz, byte[].class))
|
||||
.orElse(null)
|
||||
);
|
||||
|
||||
public static final Class<?> clazz$ClientboundDisconnectPacket = requireNonNull(
|
||||
ReflectionUtils.getClazz(
|
||||
BukkitReflectionUtils.assembleMCClass("network.protocol.common.ClientboundDisconnectPacket"),
|
||||
@@ -5684,4 +5729,46 @@ public class Reflections {
|
||||
clazz$ClientboundDisconnectPacket, clazz$Component
|
||||
)
|
||||
);
|
||||
|
||||
public static final Method method$CraftEventFactory$handleBlockGrowEvent = requireNonNull(
|
||||
VersionHelper.isVersionNewerThan1_21_5() ?
|
||||
ReflectionUtils.getStaticMethod(
|
||||
clazz$CraftEventFactory, boolean.class, clazz$Level, clazz$BlockPos, clazz$BlockState, int.class
|
||||
) :
|
||||
ReflectionUtils.getStaticMethod(
|
||||
clazz$CraftEventFactory, boolean.class, clazz$Level, clazz$BlockPos, clazz$BlockState
|
||||
)
|
||||
);
|
||||
|
||||
public static final Class<?> clazz$BlockAndTintGetter = requireNonNull(
|
||||
ReflectionUtils.getClazz(
|
||||
BukkitReflectionUtils.assembleMCClass("world.level.BlockAndTintGetter"),
|
||||
BukkitReflectionUtils.assembleMCClass("world.level.IBlockLightAccess")
|
||||
)
|
||||
);
|
||||
|
||||
public static final Method method$BlockAndTintGetter$getRawBrightness = requireNonNull(
|
||||
ReflectionUtils.getMethod(
|
||||
clazz$BlockAndTintGetter, int.class, clazz$BlockPos, int.class
|
||||
)
|
||||
);
|
||||
|
||||
public static final Field field$Level$random = requireNonNull(
|
||||
ReflectionUtils.getDeclaredField(
|
||||
clazz$Level, clazz$RandomSource, 0
|
||||
)
|
||||
);
|
||||
|
||||
public static final Class<?> clazz$Mth = requireNonNull(
|
||||
ReflectionUtils.getClazz(
|
||||
BukkitReflectionUtils.assembleMCClass("util.Mth"),
|
||||
BukkitReflectionUtils.assembleMCClass("util.MathHelper")
|
||||
)
|
||||
);
|
||||
|
||||
public static final Method method$nextInt = requireNonNull(
|
||||
ReflectionUtils.getMethod(
|
||||
clazz$Mth, int.class, clazz$RandomSource, int.class, int.class
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,11 +5,17 @@ import net.momirealms.craftengine.core.plugin.config.ConfigManager;
|
||||
import net.momirealms.craftengine.core.util.SectionPosUtils;
|
||||
import net.momirealms.craftengine.core.world.CEWorld;
|
||||
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;
|
||||
|
||||
public class BukkitCEWorld extends CEWorld {
|
||||
|
||||
public BukkitCEWorld(World world) {
|
||||
super(world);
|
||||
public BukkitCEWorld(World world, StorageAdaptor adaptor) {
|
||||
super(world, adaptor);
|
||||
}
|
||||
|
||||
public BukkitCEWorld(World world, WorldDataStorage dataStorage) {
|
||||
super(world, dataStorage);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package net.momirealms.craftengine.bukkit.world;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.compatibility.slimeworld.SlimeFormatStorageAdaptor;
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
|
||||
import net.momirealms.craftengine.bukkit.plugin.injector.BukkitInjector;
|
||||
@@ -8,6 +9,7 @@ import net.momirealms.craftengine.bukkit.util.Reflections;
|
||||
import net.momirealms.craftengine.core.block.ImmutableBlockState;
|
||||
import net.momirealms.craftengine.core.plugin.config.ConfigManager;
|
||||
import net.momirealms.craftengine.core.plugin.scheduler.SchedulerTask;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
import net.momirealms.craftengine.core.world.CEWorld;
|
||||
import net.momirealms.craftengine.core.world.ChunkPos;
|
||||
import net.momirealms.craftengine.core.world.SectionPos;
|
||||
@@ -15,6 +17,9 @@ import net.momirealms.craftengine.core.world.WorldManager;
|
||||
import net.momirealms.craftengine.core.world.chunk.CEChunk;
|
||||
import net.momirealms.craftengine.core.world.chunk.CESection;
|
||||
import net.momirealms.craftengine.core.world.chunk.serialization.ChunkSerializer;
|
||||
import net.momirealms.craftengine.core.world.chunk.storage.DefaultStorageAdaptor;
|
||||
import net.momirealms.craftengine.core.world.chunk.storage.StorageAdaptor;
|
||||
import net.momirealms.craftengine.core.world.chunk.storage.WorldDataStorage;
|
||||
import net.momirealms.sparrow.nbt.CompoundTag;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Chunk;
|
||||
@@ -27,6 +32,7 @@ import org.bukkit.event.world.ChunkLoadEvent;
|
||||
import org.bukkit.event.world.ChunkUnloadEvent;
|
||||
import org.bukkit.event.world.WorldLoadEvent;
|
||||
import org.bukkit.event.world.WorldUnloadEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
@@ -38,18 +44,34 @@ public class BukkitWorldManager implements WorldManager, Listener {
|
||||
private static BukkitWorldManager instance;
|
||||
private final BukkitCraftEngine plugin;
|
||||
private final Map<UUID, CEWorld> worlds;
|
||||
private CEWorld[] worldArray;
|
||||
private CEWorld[] worldArray = new CEWorld[0];
|
||||
private final ReentrantReadWriteLock worldMapLock = new ReentrantReadWriteLock();
|
||||
private SchedulerTask tickTask;
|
||||
// cache
|
||||
private UUID lastVisitedUUID;
|
||||
private CEWorld lastVisitedWorld;
|
||||
private StorageAdaptor storageAdaptor;
|
||||
|
||||
public BukkitWorldManager(BukkitCraftEngine plugin) {
|
||||
instance = this;
|
||||
this.plugin = plugin;
|
||||
this.worlds = new HashMap<>();
|
||||
resetWorldArray();
|
||||
if (VersionHelper.isVersionNewerThan1_21_4()) {
|
||||
try {
|
||||
Class.forName("com.infernalsuite.asp.api.AdvancedSlimePaperAPI");
|
||||
SlimeFormatStorageAdaptor adaptor = new SlimeFormatStorageAdaptor(this);
|
||||
this.storageAdaptor = adaptor;
|
||||
Bukkit.getPluginManager().registerEvents(adaptor, plugin.bootstrap());
|
||||
return;
|
||||
} catch (ClassNotFoundException ignored) {
|
||||
}
|
||||
}
|
||||
this.storageAdaptor = new DefaultStorageAdaptor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStorageAdaptor(@NotNull StorageAdaptor storageAdaptor) {
|
||||
this.storageAdaptor = storageAdaptor;
|
||||
}
|
||||
|
||||
public static BukkitWorldManager instance() {
|
||||
@@ -95,7 +117,7 @@ public class BukkitWorldManager implements WorldManager, Listener {
|
||||
this.worldMapLock.writeLock().lock();
|
||||
try {
|
||||
for (World world : Bukkit.getWorlds()) {
|
||||
CEWorld ceWorld = new BukkitCEWorld(new BukkitWorld(world));
|
||||
CEWorld ceWorld = new BukkitCEWorld(new BukkitWorld(world), this.storageAdaptor);
|
||||
this.worlds.put(world.getUID(), ceWorld);
|
||||
this.resetWorldArray();
|
||||
for (Chunk chunk : world.getLoadedChunks()) {
|
||||
@@ -110,8 +132,11 @@ public class BukkitWorldManager implements WorldManager, Listener {
|
||||
@Override
|
||||
public void disable() {
|
||||
HandlerList.unregisterAll(this);
|
||||
if (tickTask != null && !tickTask.cancelled()) {
|
||||
tickTask.cancel();
|
||||
if (this.storageAdaptor instanceof Listener listener) {
|
||||
HandlerList.unregisterAll(listener);
|
||||
}
|
||||
if (this.tickTask != null && !this.tickTask.cancelled()) {
|
||||
this.tickTask.cancel();
|
||||
}
|
||||
|
||||
for (World world : Bukkit.getWorlds()) {
|
||||
@@ -125,14 +150,18 @@ public class BukkitWorldManager implements WorldManager, Listener {
|
||||
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
|
||||
public void onWorldLoad(WorldLoadEvent event) {
|
||||
World world = event.getWorld();
|
||||
CEWorld ceWorld = new BukkitCEWorld(new BukkitWorld(world));
|
||||
this.loadWorld(new BukkitWorld(event.getWorld()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadWorld(net.momirealms.craftengine.core.world.World world) {
|
||||
this.worldMapLock.writeLock().lock();
|
||||
try {
|
||||
if (this.worlds.containsKey(world.getUID())) return;
|
||||
this.worlds.put(event.getWorld().getUID(), ceWorld);
|
||||
if (this.worlds.containsKey(world.uuid())) return;
|
||||
CEWorld ceWorld = new BukkitCEWorld(world, this.storageAdaptor);
|
||||
this.worlds.put(world.uuid(), ceWorld);
|
||||
this.resetWorldArray();
|
||||
for (Chunk chunk : world.getLoadedChunks()) {
|
||||
for (Chunk chunk : ((World) world.platformWorld()).getLoadedChunks()) {
|
||||
handleChunkLoad(ceWorld, chunk);
|
||||
}
|
||||
} finally {
|
||||
@@ -140,13 +169,37 @@ public class BukkitWorldManager implements WorldManager, Listener {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadWorld(CEWorld world) {
|
||||
this.worldMapLock.writeLock().lock();
|
||||
try {
|
||||
if (this.worlds.containsKey(world.world().uuid())) return;
|
||||
this.worlds.put(world.world().uuid(), world);
|
||||
this.resetWorldArray();
|
||||
for (Chunk chunk : ((World) world.world().platformWorld()).getLoadedChunks()) {
|
||||
handleChunkLoad(world, chunk);
|
||||
}
|
||||
} finally {
|
||||
this.worldMapLock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CEWorld createWorld(net.momirealms.craftengine.core.world.World world, WorldDataStorage storage) {
|
||||
return new BukkitCEWorld(world, storage);
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
|
||||
public void onWorldUnload(WorldUnloadEvent event) {
|
||||
World world = event.getWorld();
|
||||
unloadWorld(new BukkitWorld(event.getWorld()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unloadWorld(net.momirealms.craftengine.core.world.World world) {
|
||||
CEWorld ceWorld;
|
||||
this.worldMapLock.writeLock().lock();
|
||||
try {
|
||||
ceWorld = this.worlds.remove(world.getUID());
|
||||
ceWorld = this.worlds.remove(world.uuid());
|
||||
if (ceWorld == null) {
|
||||
return;
|
||||
}
|
||||
@@ -158,11 +211,20 @@ public class BukkitWorldManager implements WorldManager, Listener {
|
||||
} finally {
|
||||
this.worldMapLock.writeLock().unlock();
|
||||
}
|
||||
for (Chunk chunk : world.getLoadedChunks()) {
|
||||
for (Chunk chunk : ((World) world.platformWorld()).getLoadedChunks()) {
|
||||
handleChunkUnload(ceWorld, chunk);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> net.momirealms.craftengine.core.world.World wrap(T world) {
|
||||
if (world instanceof World w) {
|
||||
return new BukkitWorld(w);
|
||||
} else {
|
||||
throw new IllegalArgumentException(world.getClass() + " is not a Bukkit World");
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
|
||||
public void onChunkLoad(ChunkLoadEvent event) {
|
||||
this.worldMapLock.readLock().lock();
|
||||
|
||||
@@ -6,6 +6,7 @@ import org.apache.logging.log4j.core.Logger;
|
||||
import org.apache.logging.log4j.core.filter.AbstractFilter;
|
||||
|
||||
public class LoggerFilter {
|
||||
|
||||
public static void filter() {
|
||||
Logger rootLogger = (Logger) LogManager.getRootLogger();
|
||||
rootLogger.addFilter(new AbstractFilter() {
|
||||
|
||||
@@ -14,9 +14,7 @@
|
||||
# Q: When do I need to configure this file?
|
||||
# A: When the number of real block IDs is insufficient, but there are still available appearances.
|
||||
|
||||
# By default, the plugin only registers an additional 112 oak leaf block states (for the default configuration needs [>=28 states]).
|
||||
minecraft:oak_leaves: 112
|
||||
|
||||
minecraft:oak_sapling: 1
|
||||
minecraft:birch_sapling: 1
|
||||
minecraft:spruce_sapling: 1
|
||||
@@ -24,5 +22,5 @@ minecraft:jungle_sapling: 1
|
||||
minecraft:dark_oak_sapling: 1
|
||||
minecraft:acacia_sapling: 1
|
||||
minecraft:cherry_sapling: 1
|
||||
|
||||
minecraft:anvil: 2
|
||||
minecraft:anvil: 2
|
||||
minecraft:sugarcane: 14
|
||||
@@ -10,6 +10,7 @@ import net.momirealms.craftengine.core.loot.function.LootFunction;
|
||||
import net.momirealms.craftengine.core.loot.function.LootFunctions;
|
||||
import net.momirealms.craftengine.core.loot.number.NumberProvider;
|
||||
import net.momirealms.craftengine.core.loot.number.NumberProviders;
|
||||
import net.momirealms.craftengine.core.util.MiscUtils;
|
||||
import net.momirealms.craftengine.core.util.context.ContextHolder;
|
||||
import net.momirealms.craftengine.core.world.World;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -43,19 +44,19 @@ public class LootTable<T> {
|
||||
NumberProvider rolls = NumberProviders.fromObject(pool.getOrDefault("rolls", 1));
|
||||
NumberProvider bonus_rolls = NumberProviders.fromObject(pool.getOrDefault("bonus_rolls", 0));
|
||||
List<LootCondition> conditions = Optional.ofNullable(pool.get("conditions"))
|
||||
.map(it -> LootConditions.fromMapList((List<Map<String, Object>>) it))
|
||||
.map(it -> LootConditions.fromMapList(MiscUtils.castToMapListOrThrow(it, () -> new RuntimeException("'conditions' should be a map list, current type: " + it.getClass().getSimpleName()))))
|
||||
.orElse(Lists.newArrayList());
|
||||
List<LootEntryContainer<T>> containers = Optional.ofNullable(pool.get("entries"))
|
||||
.map(it -> (List<LootEntryContainer<T>>) new ArrayList<LootEntryContainer<T>>(LootEntryContainers.fromMapList((List<Map<String, Object>>) it)))
|
||||
.map(it -> (List<LootEntryContainer<T>>) new ArrayList<LootEntryContainer<T>>(LootEntryContainers.fromMapList(MiscUtils.castToMapListOrThrow(it, () -> new RuntimeException("'entries' should be a map list, current type: " + it.getClass().getSimpleName())))))
|
||||
.orElse(Lists.newArrayList());
|
||||
List<LootFunction<T>> functions = Optional.ofNullable(pool.get("functions"))
|
||||
.map(it -> (List<LootFunction<T>>) new ArrayList<LootFunction<T>>(LootFunctions.fromMapList((List<Map<String, Object>>) it)))
|
||||
.map(it -> (List<LootFunction<T>>) new ArrayList<LootFunction<T>>(LootFunctions.fromMapList(MiscUtils.castToMapListOrThrow(it, () -> new RuntimeException("'functions' should be a map list, current type: " + it.getClass().getSimpleName())))))
|
||||
.orElse(Lists.newArrayList());
|
||||
lootPools.add(new LootPool<>(containers, conditions, functions, rolls, bonus_rolls));
|
||||
}
|
||||
return new LootTable<>(lootPools,
|
||||
Optional.ofNullable(map.get("functions"))
|
||||
.map(it -> (List<LootFunction<T>>) new ArrayList<LootFunction<T>>(LootFunctions.fromMapList((List<Map<String, Object>>) it)))
|
||||
.map(it -> (List<LootFunction<T>>) new ArrayList<LootFunction<T>>(LootFunctions.fromMapList(MiscUtils.castToMapListOrThrow(it, () -> new RuntimeException("'functions' should be a map list, current type: " + it.getClass().getSimpleName())))))
|
||||
.orElse(Lists.newArrayList())
|
||||
);
|
||||
}
|
||||
|
||||
@@ -14,8 +14,10 @@ import java.util.function.Predicate;
|
||||
|
||||
public class LootConditions {
|
||||
public static final Key MATCH_ITEM = Key.from("craftengine:match_item");
|
||||
public static final Key MATCH_BLOCK_PROPERTY = Key.from("craftengine:match_block_property");
|
||||
public static final Key TABLE_BONUS = Key.from("craftengine:table_bonus");
|
||||
public static final Key SURVIVES_EXPLOSION = Key.from("craftengine:survives_explosion");
|
||||
public static final Key RANDOM = Key.from("craftengine:random");
|
||||
public static final Key ANY_OF = Key.from("craftengine:any_of");
|
||||
public static final Key ALL_OF = Key.from("craftengine:all_of");
|
||||
public static final Key ENCHANTMENT = Key.from("craftengine:enchantment");
|
||||
@@ -24,6 +26,7 @@ public class LootConditions {
|
||||
|
||||
static {
|
||||
register(MATCH_ITEM, MatchItemCondition.FACTORY);
|
||||
register(MATCH_BLOCK_PROPERTY, MatchBlockPropertyCondition.FACTORY);
|
||||
register(TABLE_BONUS, TableBonusCondition.FACTORY);
|
||||
register(SURVIVES_EXPLOSION, SurvivesExplosionCondition.FACTORY);
|
||||
register(ANY_OF, AnyOfCondition.FACTORY);
|
||||
@@ -31,6 +34,7 @@ public class LootConditions {
|
||||
register(ENCHANTMENT, EnchantmentCondition.FACTORY);
|
||||
register(INVERTED, InvertedCondition.FACTORY);
|
||||
register(FALLING_BLOCK, FallingCondition.FACTORY);
|
||||
register(RANDOM, RandomCondition.FACTORY);
|
||||
}
|
||||
|
||||
public static void register(Key key, LootConditionFactory factory) {
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
package net.momirealms.craftengine.core.loot.condition;
|
||||
|
||||
import net.momirealms.craftengine.core.block.CustomBlock;
|
||||
import net.momirealms.craftengine.core.block.properties.Property;
|
||||
import net.momirealms.craftengine.core.loot.LootContext;
|
||||
import net.momirealms.craftengine.core.loot.parameter.LootParameters;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.Pair;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
public class MatchBlockPropertyCondition implements LootCondition {
|
||||
public static final Factory FACTORY = new Factory();
|
||||
private final List<Pair<String, String>> properties;
|
||||
|
||||
public MatchBlockPropertyCondition(List<Pair<String, String>> properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key type() {
|
||||
return LootConditions.MATCH_BLOCK_PROPERTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(LootContext lootContext) {
|
||||
return lootContext.getOptionalParameter(LootParameters.BLOCK_STATE).map(state -> {
|
||||
CustomBlock block = state.owner().value();
|
||||
for (Pair<String, String> property : this.properties) {
|
||||
Property<?> propertyIns = block.getProperty(property.left());
|
||||
if (propertyIns == null) {
|
||||
return false;
|
||||
}
|
||||
if (!state.get(propertyIns).toString().toLowerCase(Locale.ENGLISH).equals(property.right())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}).orElse(false);
|
||||
}
|
||||
|
||||
public static class Factory implements LootConditionFactory {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public LootCondition create(Map<String, Object> arguments) {
|
||||
Map<String, Object> properties = (Map<String, Object>) arguments.get("properties");
|
||||
if (properties == null) {
|
||||
throw new IllegalArgumentException("Missing 'properties' argument for 'match_block_property'");
|
||||
}
|
||||
List<Pair<String, String>> propertyList = new ArrayList<>();
|
||||
for (Map.Entry<String, Object> entry : properties.entrySet()) {
|
||||
propertyList.add(new Pair<>(entry.getKey(), entry.getValue().toString()));
|
||||
}
|
||||
return new MatchBlockPropertyCondition(propertyList);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package net.momirealms.craftengine.core.loot.condition;
|
||||
|
||||
import net.momirealms.craftengine.core.loot.LootContext;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.MiscUtils;
|
||||
import net.momirealms.craftengine.core.util.RandomUtils;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class RandomCondition implements LootCondition {
|
||||
public static final Factory FACTORY = new Factory();
|
||||
private final float chance;
|
||||
|
||||
public RandomCondition(float chance) {
|
||||
this.chance = chance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key type() {
|
||||
return LootConditions.RANDOM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(LootContext lootContext) {
|
||||
return RandomUtils.generateRandomFloat(0, 1) < this.chance;
|
||||
}
|
||||
|
||||
public static class Factory implements LootConditionFactory {
|
||||
@Override
|
||||
public LootCondition create(Map<String, Object> arguments) {
|
||||
float chance = MiscUtils.getAsFloat(arguments.getOrDefault("value", 0.5f));
|
||||
return new RandomCondition(chance);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import net.momirealms.craftengine.core.registry.Registries;
|
||||
import net.momirealms.craftengine.core.registry.WritableRegistry;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.MiscUtils;
|
||||
import net.momirealms.craftengine.core.util.RandomUtils;
|
||||
import net.momirealms.craftengine.core.util.ResourceKey;
|
||||
|
||||
import java.util.Collections;
|
||||
@@ -78,9 +79,11 @@ public class ApplyBonusCountFunction<T> extends AbstractLootConditionalFunction<
|
||||
|
||||
public static class Formulas {
|
||||
public static final Key ORE_DROPS = Key.of("craftengine:ore_drops");
|
||||
public static final Key CROP_DROPS = Key.of("craftengine:binomial_with_bonus_count");
|
||||
|
||||
static {
|
||||
register(ORE_DROPS, OreDrops.FACTORY);
|
||||
register(CROP_DROPS, CropDrops.FACTORY);
|
||||
}
|
||||
|
||||
public static void register(Key key, FormulaFactory factory) {
|
||||
@@ -133,4 +136,40 @@ public class ApplyBonusCountFunction<T> extends AbstractLootConditionalFunction<
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class CropDrops implements Formula {
|
||||
public static final Factory FACTORY = new Factory();
|
||||
private final int extra;
|
||||
private final float probability;
|
||||
|
||||
public CropDrops(int extra, float probability) {
|
||||
this.extra = extra;
|
||||
this.probability = probability;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int apply(int initialCount, int enchantmentLevel) {
|
||||
for (int i = 0; i < enchantmentLevel + this.extra; i++) {
|
||||
if (RandomUtils.generateRandomFloat(0,1) < this.probability) {
|
||||
initialCount++;
|
||||
}
|
||||
}
|
||||
return initialCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key type() {
|
||||
return Formulas.CROP_DROPS;
|
||||
}
|
||||
|
||||
public static class Factory implements FormulaFactory {
|
||||
|
||||
@Override
|
||||
public Formula create(Map<String, Object> arguments) {
|
||||
int extra = MiscUtils.getAsInt(arguments.getOrDefault("extra", 1));
|
||||
float probability = MiscUtils.getAsFloat(arguments.getOrDefault("probability", 0.5f));
|
||||
return new CropDrops(extra, probability);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -331,8 +331,15 @@ public abstract class AbstractPackManager implements PackManager {
|
||||
plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/fairy_flower_3.png");
|
||||
plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/fairy_flower_4.png");
|
||||
plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/reed.png");
|
||||
plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/flame_cane_1.png");
|
||||
plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/flame_cane_2.png");
|
||||
plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/ender_pearl_flower_stage_0.png");
|
||||
plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/ender_pearl_flower_stage_1.png");
|
||||
plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/ender_pearl_flower_stage_2.png");
|
||||
plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/item/custom/fairy_flower.png");
|
||||
plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/item/custom/reed.png");
|
||||
plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/item/custom/flame_cane.png");
|
||||
plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/item/custom/ender_pearl_flower_seeds.png");
|
||||
plugin.saveResource("resources/default/resourcepack/assets/minecraft/models/block/custom/fairy_flower_1.json");
|
||||
plugin.saveResource("resources/default/resourcepack/assets/minecraft/models/block/custom/reed.json");
|
||||
// furniture
|
||||
|
||||
@@ -16,6 +16,7 @@ import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.PluginProperties;
|
||||
import net.momirealms.craftengine.core.plugin.Reloadable;
|
||||
import net.momirealms.craftengine.core.plugin.locale.TranslationManager;
|
||||
import net.momirealms.craftengine.core.plugin.logger.filter.DisconnectLogFilter;
|
||||
import net.momirealms.craftengine.core.util.AdventureHelper;
|
||||
import net.momirealms.craftengine.core.util.MiscUtils;
|
||||
import net.momirealms.craftengine.core.util.ReflectionUtils;
|
||||
@@ -183,6 +184,7 @@ public class ConfigManager implements Reloadable {
|
||||
metrics = config.getBoolean("metrics", false);
|
||||
checkUpdate = config.getBoolean("update-checker", false);
|
||||
filterConfigurationPhaseDisconnect = config.getBoolean("filter-configuration-phase-disconnect", false);
|
||||
DisconnectLogFilter.instance().setEnable(filterConfigurationPhaseDisconnect);
|
||||
|
||||
// resource pack
|
||||
resource_pack$override_uniform_font = config.getBoolean("resource-pack.override-uniform-font", false);
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package net.momirealms.craftengine.core.plugin.logger.filter;
|
||||
|
||||
|
||||
import net.momirealms.craftengine.core.plugin.config.ConfigManager;
|
||||
import org.apache.logging.log4j.core.Filter;
|
||||
import org.apache.logging.log4j.core.LogEvent;
|
||||
import org.apache.logging.log4j.core.config.Node;
|
||||
@@ -13,10 +11,24 @@ import org.apache.logging.log4j.core.impl.MutableLogEvent;
|
||||
public class DisconnectLogFilter extends AbstractFilter {
|
||||
private static final String TARGET_LOGGER = "net.minecraft.server.network.ServerConfigurationPacketListenerImpl";
|
||||
private static final String TARGET_MESSAGE_PATTERN = "{} lost connection: {}";
|
||||
private static DisconnectLogFilter instance;
|
||||
private boolean enable = false;
|
||||
|
||||
public DisconnectLogFilter() {
|
||||
instance = this;
|
||||
}
|
||||
|
||||
public static DisconnectLogFilter instance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public void setEnable(boolean enable) {
|
||||
this.enable = enable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result filter(LogEvent event) {
|
||||
if (!ConfigManager.filterConfigurationPhaseDisconnect()) {
|
||||
if (!enable) {
|
||||
return Result.NEUTRAL;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,8 +3,6 @@ package net.momirealms.craftengine.core.plugin.network;
|
||||
import io.netty.channel.Channel;
|
||||
import net.momirealms.craftengine.core.entity.player.Player;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public interface NetworkManager {
|
||||
String MOD_CHANNEL = "craftengine:payload";
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import org.joml.Vector3f;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class MiscUtils {
|
||||
|
||||
@@ -22,6 +23,15 @@ public class MiscUtils {
|
||||
throw new IllegalArgumentException("Expected Map, got: " + obj.getClass().getSimpleName());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static List<Map<String, Object>> castToMapListOrThrow(Object obj, Supplier<RuntimeException> exceptionSupplier) {
|
||||
if (obj instanceof List<?> list) {
|
||||
return (List<Map<String, Object>>) list;
|
||||
} else {
|
||||
throw exceptionSupplier.get();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static List<Object> castToList(Object obj, boolean allowNull) {
|
||||
if (allowNull && obj == null) {
|
||||
|
||||
@@ -26,6 +26,10 @@ public class RandomUtils {
|
||||
return min + (max - min) * getInstance().random.nextFloat();
|
||||
}
|
||||
|
||||
public static int generateRandomInt(int min, int max) {
|
||||
return min >= max ? min : getInstance().random.nextInt(max - min + 1) + min;
|
||||
}
|
||||
|
||||
public static boolean generateRandomBoolean() {
|
||||
return getInstance().random.nextBoolean();
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package net.momirealms.craftengine.core.world;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
import net.momirealms.craftengine.core.block.ImmutableBlockState;
|
||||
import net.momirealms.craftengine.core.world.chunk.CEChunk;
|
||||
import net.momirealms.craftengine.core.world.chunk.storage.DefaultRegionFileStorage;
|
||||
import net.momirealms.craftengine.core.world.chunk.storage.StorageAdaptor;
|
||||
import net.momirealms.craftengine.core.world.chunk.storage.WorldDataStorage;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -25,10 +25,18 @@ public abstract class CEWorld {
|
||||
private CEChunk lastChunk;
|
||||
private long lastChunkPos;
|
||||
|
||||
public CEWorld(World world) {
|
||||
public CEWorld(World world, StorageAdaptor adaptor) {
|
||||
this.world = world;
|
||||
this.loadedChunkMap = new Long2ObjectOpenHashMap<>(1024, 0.5f);
|
||||
this.worldDataStorage = new DefaultRegionFileStorage(world.directory().resolve(REGION_DIRECTORY));
|
||||
this.worldDataStorage = adaptor.adapt(world);
|
||||
this.worldHeightAccessor = world.worldHeight();
|
||||
this.lastChunkPos = ChunkPos.INVALID_CHUNK_POS;
|
||||
}
|
||||
|
||||
public CEWorld(World world, WorldDataStorage dataStorage) {
|
||||
this.world = world;
|
||||
this.loadedChunkMap = new Long2ObjectOpenHashMap<>(1024, 0.5f);
|
||||
this.worldDataStorage = dataStorage;
|
||||
this.worldHeightAccessor = world.worldHeight();
|
||||
this.lastChunkPos = ChunkPos.INVALID_CHUNK_POS;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,27 @@
|
||||
package net.momirealms.craftengine.core.world;
|
||||
|
||||
import net.momirealms.craftengine.core.plugin.Reloadable;
|
||||
import net.momirealms.craftengine.core.world.chunk.storage.StorageAdaptor;
|
||||
import net.momirealms.craftengine.core.world.chunk.storage.WorldDataStorage;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public interface WorldManager extends Reloadable {
|
||||
|
||||
void setStorageAdaptor(@NotNull StorageAdaptor storageAdaptor);
|
||||
|
||||
CEWorld getWorld(UUID uuid);
|
||||
|
||||
void delayedInit();
|
||||
|
||||
void loadWorld(World world);
|
||||
|
||||
void loadWorld(CEWorld world);
|
||||
|
||||
CEWorld createWorld(World world, WorldDataStorage storage);
|
||||
|
||||
void unloadWorld(World world);
|
||||
|
||||
<T> World wrap(T world);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package net.momirealms.craftengine.core.world.chunk.storage;
|
||||
|
||||
import net.momirealms.craftengine.core.world.CEWorld;
|
||||
import net.momirealms.craftengine.core.world.World;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class DefaultStorageAdaptor implements StorageAdaptor {
|
||||
|
||||
@Override
|
||||
public @NotNull WorldDataStorage adapt(@NotNull World world) {
|
||||
return new DefaultRegionFileStorage(world.directory().resolve(CEWorld.REGION_DIRECTORY));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package net.momirealms.craftengine.core.world.chunk.storage;
|
||||
|
||||
import net.momirealms.craftengine.core.world.World;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public interface StorageAdaptor {
|
||||
|
||||
@NotNull
|
||||
WorldDataStorage adapt(@NotNull World world);
|
||||
}
|
||||
@@ -2,7 +2,7 @@ org.gradle.jvmargs=-Xmx1G
|
||||
|
||||
# Project settings
|
||||
# Rule: [major update].[feature update].[bug fix]
|
||||
project_version=0.0.39
|
||||
project_version=0.0.40
|
||||
config_version=18
|
||||
lang_version=3
|
||||
project_group=net.momirealms
|
||||
@@ -40,7 +40,7 @@ geantyref_version=1.3.16
|
||||
zstd_version=1.5.6-9
|
||||
commons_io_version=2.17.0
|
||||
sparrow_nbt_version=0.3
|
||||
sparrow_util_version=0.33
|
||||
sparrow_util_version=0.34
|
||||
fastutil_version=8.5.15
|
||||
netty_version=4.1.119.Final
|
||||
joml_version=1.10.8
|
||||
@@ -49,7 +49,7 @@ mojang_brigadier_version=1.0.18
|
||||
byte_buddy_version=1.15.11
|
||||
snake_yaml_version=2.3
|
||||
anti_grief_version=0.13
|
||||
nms_helper_version=0.6
|
||||
nms_helper_version=0.12
|
||||
# Ignite Dependencies
|
||||
mixinextras_version=0.4.1
|
||||
mixin_version=0.15.2+mixin.0.8.7
|
||||
|
||||