9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-22 16:39:28 +00:00

Merge pull request #53 from Xiao-MoMi/dev

0.0.36 fix
This commit is contained in:
XiaoMoMi
2025-03-23 04:48:56 +08:00
committed by GitHub
69 changed files with 1008 additions and 161 deletions

View File

@@ -18,6 +18,7 @@ dependencies {
implementation(project(":core"))
implementation(project(":bukkit"))
implementation(project(":bukkit:legacy"))
implementation(project(":bukkit:compatibility"))
implementation("net.kyori:adventure-platform-bukkit:${rootProject.properties["adventure_platform_version"]}")
implementation("com.saicone.rtag:rtag-item:${rootProject.properties["rtag_version"]}")

View File

@@ -92,8 +92,6 @@ resource-pack:
suffix: "minecraft/items"
- type: parent_path_suffix
suffix: "minecraft/models/item"
- type: parent_path_suffix
suffix: "minecraft/atlases"
resolution:
type: merge_json
deeply: true
@@ -117,6 +115,11 @@ resource-pack:
resolution:
type: merge_json
deeply: false
- term:
type: parent_path_suffix
suffix: "minecraft/atlases"
resolution:
type: merge_atlas
item:
# Add a <!i> tag on item name and lore

View File

@@ -1,7 +1,7 @@
items:
default:chinese_lantern:
material: paper
custom-model-data: 3001
custom-model-data: 3000
data:
item-name: "<!i><i18n:item.chinese_lantern>"
model:

View File

@@ -56,4 +56,5 @@ categories:
icon: default:chinese_lantern
list:
- default:chinese_lantern
- default:fairy_flower
- default:fairy_flower
- default:reed

View File

@@ -2,6 +2,7 @@ i18n:
en:
item.chinese_lantern: "Chinese Lantern"
item.fairy_flower: "Fairy Flower"
item.reed: "Reed"
item.bench: "Bench"
item.table_lamp: "Table Lamp"
item.wooden_chair: "Wooden Chair"
@@ -36,6 +37,7 @@ i18n:
zh_cn:
item.chinese_lantern: "灯笼"
item.fairy_flower: "仙灵花"
item.reed: "芦苇"
item.bench: "长椅"
item.table_lamp: "台灯"
item.wooden_chair: "木椅"

View File

@@ -235,7 +235,7 @@ items:
# To prevent errors, we use tree feature from vanilla here
feature: minecraft:fancy_oak
bone-meal-success-chance: 0.45
tags:
bottom-block-tags:
- minecraft:dirt
- minecraft:farmland
- minecraft:sand

View File

@@ -1,7 +1,7 @@
items:
default:fairy_flower:
material: paper
custom-model-data: 3000
custom-model-data: 3001
data:
item-name: "<!i><i18n:item.fairy_flower>"
model:
@@ -11,6 +11,18 @@ items:
behavior:
type: block_item
block: default:fairy_flower
default:reed:
material: paper
custom-model-data: 3002
data:
item-name: "<!i><i18n:item.reed>"
model:
template: default:model/simplified_generated
arguments:
path: "minecraft:item/custom/reed"
behavior:
type: liquid_collision_block_item
block: default:reed
blocks:
default:fairy_flower:
settings:
@@ -19,6 +31,7 @@ blocks:
- default:sound/grass
overrides:
item: default:fairy_flower
push-reaction: DESTROY
behavior:
type: bush_block
loot:
@@ -27,7 +40,7 @@ blocks:
item: default:fairy_flower
state:
id: 0
state: tripwire:0
state: sugar_cane:0
models:
- path: "minecraft:block/custom/fairy_flower_1"
weight: 100
@@ -48,4 +61,24 @@ blocks:
generation:
parent: "minecraft:block/custom/fairy_flower_1"
textures:
"0": "minecraft:block/custom/fairy_flower_4"
"0": "minecraft:block/custom/fairy_flower_4"
default:reed:
settings:
template:
- default:hardness/none
- default:sound/grass
overrides:
item: default:reed
push-reaction: DESTROY
behavior:
type: on_liquid_block
liquid-type: water
loot:
template: "default:loot_table/basic"
arguments:
item: default:reed
state:
id: 1
state: sugar_cane:1
model:
path: "minecraft:block/custom/reed"

View File

@@ -34,6 +34,7 @@
"from": [7, 28, 7],
"to": [9, 30, 9],
"rotation": {"angle": 0, "axis": "y", "origin": [7, 28, 7]},
"shade": false,
"faces": {
"north": {"uv": [15, 0, 16, 1], "texture": "#0"},
"east": {"uv": [15, 0, 16, 1], "texture": "#0"},
@@ -43,15 +44,5 @@
"down": {"uv": [15, 0, 16, 1], "texture": "#0"}
}
}
],
"display": {
"head": {
"translation": [0, 18.5, 0]
},
"fixed": {
"rotation": [-90, 0, 0],
"translation": [0, 0, -15],
"scale": [2, 2, 2]
}
}
]
}

View File

@@ -0,0 +1,37 @@
{
"ambientocclusion": false,
"textures": {
"0": "block/custom/reed",
"particle": "block/custom/reed"
},
"elements": [
{
"from": [-0.5, -3, 0.25],
"to": [19.5, 29, 0.25],
"shade": false,
"rotation": {"angle": -45, "axis": "y", "origin": [-0.5, -3, 2.25]},
"faces": {
"north": {"uv": [0, 0, 10, 16], "texture": "#0"},
"east": {"uv": [0, 0, 0, 16], "texture": "#0"},
"south": {"uv": [0, 0, 10, 16], "texture": "#0"},
"west": {"uv": [0, 0, 0, 16], "texture": "#0"},
"up": {"uv": [0, 0, 0, 10], "rotation": 270, "texture": "#0"},
"down": {"uv": [0, 0, 0, 10], "rotation": 90, "texture": "#0"}
}
},
{
"from": [2, -3, 14.75],
"to": [22, 29, 14.75],
"shade": false,
"rotation": {"angle": 45, "axis": "y", "origin": [2, -3, 16.75]},
"faces": {
"north": {"uv": [0, 0, 10, 16], "texture": "#0"},
"east": {"uv": [0, 0, 0, 16], "texture": "#0"},
"south": {"uv": [0, 0, 10, 16], "texture": "#0"},
"west": {"uv": [0, 0, 0, 16], "texture": "#0"},
"up": {"uv": [0, 0, 0, 10], "rotation": 270, "texture": "#0"},
"down": {"uv": [0, 0, 0, 10], "rotation": 90, "texture": "#0"}
}
}
]
}

View File

@@ -1,5 +1,4 @@
{
"texture_size": [64, 64],
"textures": {
"0": "item/custom/bench",
"particle": "item/custom/bench"

View File

@@ -1,5 +1,4 @@
{
"texture_size": [32, 32],
"textures": {
"0": "item/custom/table_lamp",
"particle": "item/custom/table_lamp"

View File

@@ -1,5 +1,4 @@
{
"texture_size": [32, 32],
"textures": {
"0": "item/custom/wooden_chair",
"particle": "item/custom/wooden_chair"

Binary file not shown.

After

Width:  |  Height:  |  Size: 695 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -15,6 +15,7 @@ repositories {
dependencies {
compileOnly(project(":core"))
compileOnly(project(":shared"))
compileOnly(project(":bukkit:compatibility"))
compileOnly(project(":bukkit:legacy"))
// Anti Grief
compileOnly("com.github.Xiao-MoMi:AntiGriefLib:${rootProject.properties["anti_grief_version"]}")

View File

@@ -0,0 +1,28 @@
repositories {
mavenCentral()
maven("https://repo.papermc.io/repository/maven-public/")
maven("https://r.irepo.space/maven/")
}
dependencies {
compileOnly(project(":core"))
// Platform
compileOnly("dev.folia:folia-api:${rootProject.properties["paper_version"]}-R0.1-SNAPSHOT")
// NeigeItems
compileOnly("pers.neige.neigeitems:NeigeItems:1.21.42")
}
java {
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
withSourcesJar()
}
tasks.withType<JavaCompile> {
options.encoding = "UTF-8"
options.release.set(21)
dependsOn(tasks.clean)
}

View File

@@ -0,0 +1,22 @@
package net.momirealms.craftengine.bukkit.compatibility.item;
import net.momirealms.craftengine.core.item.ExternalItemProvider;
import net.momirealms.craftengine.core.item.ItemBuildContext;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import pers.neige.neigeitems.manager.ItemManager;
import java.util.Optional;
public class NeigeItemsProvider implements ExternalItemProvider<ItemStack> {
@Override
public String plugin() {
return "NeigeItems";
}
@Override
public ItemStack build(String id, ItemBuildContext context) {
return ItemManager.INSTANCE.getItemStack(id, Optional.ofNullable(context.player()).map(it -> (Player) it.platformPlayer()).orElse(null));
}
}

View File

@@ -5,12 +5,12 @@ import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.shared.block.EmptyBlockBehavior;
public class BukkitBlockBehaviors extends BlockBehaviors {
public static final Key EMPTY = Key.from("craftengine:empty");
public static final Key BUSH_BLOCK = Key.from("craftengine:bush_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");
public static final Key SAPLING_BLOCK = Key.from("craftengine:sapling_block");
public static final Key ON_LIQUID_BLOCK = Key.from("craftengine:on_liquid_block");
public static void init() {
register(EMPTY, (block, args) -> EmptyBlockBehavior.INSTANCE);
@@ -19,5 +19,6 @@ public class BukkitBlockBehaviors extends BlockBehaviors {
register(LEAVES_BLOCK, LeavesBlockBehavior.FACTORY);
register(STRIPPABLE_BLOCK, StrippableBlockBehavior.FACTORY);
register(SAPLING_BLOCK, SaplingBlockBehavior.FACTORY);
register(ON_LIQUID_BLOCK, OnLiquidBlockBehavior.FACTORY);
}
}

View File

@@ -83,7 +83,9 @@ public class BushBlockBehavior extends BlockBehavior {
public static class Factory implements BlockBehaviorFactory {
@Override
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
if (arguments.containsKey("tags")) {
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;
@@ -100,7 +102,7 @@ public class BushBlockBehavior extends BlockBehavior {
return mayPlaceOn(belowState, world, belowPos);
}
private boolean mayPlaceOn(Object belowState, Object world, Object blockPos) throws ReflectiveOperationException {
protected boolean mayPlaceOn(Object belowState, Object world, Object belowPos) throws ReflectiveOperationException {
for (Object tag : this.tagsCanSurviveOn) {
if ((boolean) Reflections.method$BlockStateBase$hasTag.invoke(belowState, tag)) {
return true;

View File

@@ -0,0 +1,55 @@
package net.momirealms.craftengine.bukkit.block.behavior;
import net.momirealms.craftengine.bukkit.util.LocationUtils;
import net.momirealms.craftengine.bukkit.util.Reflections;
import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
import net.momirealms.craftengine.core.util.MiscUtils;
import net.momirealms.craftengine.shared.block.BlockBehavior;
import java.util.List;
import java.util.Map;
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);
this.onWater = onWater;
this.onLava = onLava;
}
public boolean onWater() {
return this.onWater;
}
public boolean onLava() {
return this.onLava;
}
public static class Factory implements BlockBehaviorFactory {
@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"));
}
}
@Override
protected boolean mayPlaceOn(Object belowState, Object world, Object belowPos) throws ReflectiveOperationException {
Object fluidState = Reflections.method$Level$getFluidState.invoke(world, belowPos);
Object fluidStateAbove = Reflections.method$Level$getFluidState.invoke(world, LocationUtils.above(belowPos));
if (Reflections.method$FluidState$getType.invoke(fluidStateAbove) != Reflections.instance$Fluids$EMPTY) {
return false;
}
if (this.onWater && (Reflections.method$FluidState$getType.invoke(fluidState) == Reflections.instance$Fluids$WATER || Reflections.field$StateHolder$owner.get(belowState) == Reflections.instance$Blocks$ICE)) {
return true;
}
if (this.onLava && Reflections.method$FluidState$getType.invoke(fluidState) == Reflections.instance$Fluids$LAVA) {
return true;
}
return false;
}
}

View File

@@ -1,7 +1,5 @@
package net.momirealms.craftengine.bukkit.entity.furniture;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.util.EntityUtils;
import net.momirealms.craftengine.core.entity.furniture.*;
@@ -37,11 +35,6 @@ public class BukkitFurnitureManager implements FurnitureManager {
private final Map<Integer, LoadedFurniture> furnitureByBaseEntityId = new ConcurrentHashMap<>(256, 0.5f);
private final Map<Integer, LoadedFurniture> furnitureByInteractionEntityId = new ConcurrentHashMap<>(512, 0.5f);
private final Map<Integer, int[]> baseEntity2SubEntities = new ConcurrentHashMap<>(256, 0.5f);
// Delay furniture cache remove for about 4-5 ticks
private static final int DELAYED_TICK = 5;
private final IntSet[] delayedRemove = new IntSet[DELAYED_TICK];
// Event listeners
private final Listener dismountListener;
private final FurnitureEventListener furnitureEventListener;
@@ -56,9 +49,6 @@ public class BukkitFurnitureManager implements FurnitureManager {
this.plugin = plugin;
this.furnitureEventListener = new FurnitureEventListener(this);
this.dismountListener = VersionHelper.isVersionNewerThan1_20_3() ? new DismountListener1_20_3(this) : new DismountListener1_20(this::handleDismount);
for (int i = 0; i < DELAYED_TICK; i++) {
this.delayedRemove[i] = new IntOpenHashSet();
}
instance = this;
}
@@ -171,18 +161,6 @@ public class BukkitFurnitureManager implements FurnitureManager {
}
public void tick() {
IntSet first = this.delayedRemove[0];
for (int i : first) {
// unloaded furniture might be loaded again
LoadedFurniture furniture = getLoadedFurnitureByBaseEntityId(i);
if (furniture == null)
this.baseEntity2SubEntities.remove(i);
}
first.clear();
for (int i = 1; i < DELAYED_TICK; i++) {
this.delayedRemove[i - 1] = this.delayedRemove[i];
}
this.delayedRemove[DELAYED_TICK-1] = first;
}
@Override
@@ -224,12 +202,6 @@ public class BukkitFurnitureManager implements FurnitureManager {
return Optional.ofNullable(this.byId.get(id));
}
@Nullable
@Override
public int[] getSubEntityIdsByBaseEntityId(int entityId) {
return this.baseEntity2SubEntities.get(entityId);
}
@Override
public boolean isFurnitureBaseEntity(int entityId) {
return this.furnitureByBaseEntityId.containsKey(entityId);
@@ -253,7 +225,6 @@ public class BukkitFurnitureManager implements FurnitureManager {
for (int sub : furniture.interactionEntityIds()) {
this.furnitureByInteractionEntityId.remove(sub);
}
this.delayedRemove[DELAYED_TICK-1].add(id);
}
}
@@ -270,6 +241,7 @@ public class BukkitFurnitureManager implements FurnitureManager {
if (previous != null) return;
LoadedFurniture furniture = addNewFurniture(display, customFurniture, getAnchorType(entity, customFurniture));
for (Player player : display.getTrackedPlayers()) {
this.plugin.adapt(player).furnitureView().computeIfAbsent(furniture.baseEntityId(), k -> new ArrayList<>()).addAll(furniture.subEntityIds());
this.plugin.networkManager().sendPacket(player, furniture.spawnPacket());
}
}
@@ -316,7 +288,6 @@ public class BukkitFurnitureManager implements FurnitureManager {
private synchronized LoadedFurniture addNewFurniture(ItemDisplay display, CustomFurniture furniture, AnchorType anchorType) {
LoadedFurniture loadedFurniture = new LoadedFurniture(display, furniture, anchorType);
this.furnitureByBaseEntityId.put(loadedFurniture.baseEntityId(), loadedFurniture);
this.baseEntity2SubEntities.put(loadedFurniture.baseEntityId(), loadedFurniture.subEntityIds());
for (int entityId : loadedFurniture.interactionEntityIds()) {
this.furnitureByInteractionEntityId.put(entityId, loadedFurniture);
}

View File

@@ -74,7 +74,6 @@ public class FurnitureEventListener implements Listener {
this.manager.handleEntityUnload(event.getEntity());
}
@EventHandler(ignoreCancelled = true)
public void onPlayerQuit(PlayerQuitEvent event) {
Player player = event.getPlayer();

View File

@@ -41,9 +41,9 @@ public class LoadedFurniture {
private final WeakReference<Entity> baseEntity;
private final int baseEntityId;
// includes elements + interactions
private final int[] subEntityIds;
private final List<Integer> subEntityIds;
// interactions
private final int[] interactionEntityIds;
private final List<Integer> interactionEntityIds;
// seats
private final Set<Vector3f> occupiedSeats = Collections.synchronizedSet(new HashSet<>());
private final Vector<Entity> seats = new Vector<>();
@@ -73,14 +73,9 @@ public class LoadedFurniture {
interactionEntityIds.add(entityId);
this.hitBoxes.put(entityId, hitBox);
}
this.subEntityIds = new int[entityIds.size()];
for (int i = 0; i < entityIds.size(); ++i) {
this.subEntityIds[i] = entityIds.get(i);
}
this.interactionEntityIds = new int[interactionEntityIds.size()];
for (int i = 0; i < interactionEntityIds.size(); ++i) {
this.interactionEntityIds[i] = interactionEntityIds.get(i);
}
this.subEntityIds = entityIds;
this.interactionEntityIds = interactionEntityIds;
this.resetSpawnPackets();
}
private void resetSpawnPackets() {
@@ -148,13 +143,6 @@ public class LoadedFurniture {
this.location = location;
}
public Object spawnPacket() {
if (this.cachedSpawnPacket == null) {
this.resetSpawnPackets();
}
return this.cachedSpawnPacket;
}
@NotNull
public Entity baseEntity() {
Entity entity = baseEntity.get();
@@ -235,11 +223,11 @@ public class LoadedFurniture {
return baseEntityId;
}
public int[] interactionEntityIds() {
public List<Integer> interactionEntityIds() {
return interactionEntityIds;
}
public int[] subEntityIds() {
public List<Integer> subEntityIds() {
return this.subEntityIds;
}
@@ -286,4 +274,8 @@ public class LoadedFurniture {
this.addSeatEntity(seatEntity);
seatEntity.addPassenger(player);
}
public @NotNull Object spawnPacket() {
return cachedSpawnPacket;
}
}

View File

@@ -1,7 +1,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.BoneMealBehavior;
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,7 +50,6 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
registerVanillaItemExtraBehavior(AxeItemBehavior.INSTANCE, ItemKeys.AXES);
registerVanillaItemExtraBehavior(WaterBucketItemBehavior.INSTANCE, ItemKeys.WATER_BUCKETS);
registerVanillaItemExtraBehavior(BucketItemBehavior.INSTANCE, ItemKeys.BUCKET);
registerVanillaItemExtraBehavior(BoneMealBehavior.INSTANCE, ItemKeys.BONE_MEAL);
}
private static BukkitItemManager instance;
@@ -71,8 +70,15 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
@Override
public void delayedInit() {
Bukkit.getPluginManager().registerEvents(this.itemEventListener, plugin.bootstrap());
Bukkit.getPluginManager().registerEvents(this.debugStickListener, plugin.bootstrap());
Bukkit.getPluginManager().registerEvents(this.itemEventListener, this.plugin.bootstrap());
Bukkit.getPluginManager().registerEvents(this.debugStickListener, this.plugin.bootstrap());
this.hookExternalPlugins();
}
private void hookExternalPlugins() {
if (this.plugin.isPluginEnabled("NeigeItems")) {
registerExternalItemProvider(new NeigeItemsProvider());
}
}
@Override

View File

@@ -79,7 +79,7 @@ public class BlockItemBehavior extends ItemBehavior {
BlockPos pos = placeContext.getClickedPos();
BlockPos againstPos = placeContext.getAgainstPos();
World world = (World) placeContext.getLevel().getHandle();
World world = (World) placeContext.getLevel().platformWorld();
Location placeLocation = new Location(world, pos.x(), pos.y(), pos.z());
Block bukkitBlock = world.getBlockAt(placeLocation);
@@ -151,7 +151,7 @@ public class BlockItemBehavior extends ItemBehavior {
Object blockState = state.customBlockState().handle();
Object blockPos = LocationUtils.toBlockPos(context.getClickedPos());
Object voxelShape = Reflections.method$CollisionContext$of.invoke(null, player);
Object world = Reflections.field$CraftWorld$ServerLevel.get(context.getLevel().getHandle());
Object world = Reflections.field$CraftWorld$ServerLevel.get(context.getLevel().platformWorld());
boolean defaultReturn = ((!this.checkStatePlacement() || (boolean) Reflections.method$BlockStateBase$canSurvive.invoke(blockState, world, blockPos))
&& (boolean) Reflections.method$ServerLevel$checkEntityCollision.invoke(world, blockState, player, voxelShape, blockPos, true));
Block block = (Block) Reflections.method$CraftBlock$at.invoke(null, world, blockPos);

View File

@@ -1,27 +0,0 @@
package net.momirealms.craftengine.bukkit.item.behavior;
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.util.Key;
import java.nio.file.Path;
import java.util.Map;
public class BoneMealBehavior extends ItemBehavior {
public static final BoneMealBehavior INSTANCE = new BoneMealBehavior();
@Override
public InteractionResult useOnBlock(UseOnContext context) {
return super.useOnBlock(context);
}
public static class Factory implements ItemBehaviorFactory {
@Override
public ItemBehavior create(Pack pack, Path path, Key id, Map<String, Object> arguments) {
return INSTANCE;
}
}
}

View File

@@ -5,16 +5,17 @@ import net.momirealms.craftengine.core.item.behavior.ItemBehaviors;
import net.momirealms.craftengine.core.util.Key;
public class BukkitItemBehaviors extends ItemBehaviors {
public static final Key EMPTY = Key.from("craftengine:empty");
public static final Key BLOCK_ITEM = Key.from("craftengine:block_item");
public static final Key ON_LIQUID_BLOCK_ITEM = Key.from("craftengine:liquid_collision_block_item");
public static final Key FURNITURE_ITEM = Key.from("craftengine:furniture_item");
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 void init() {
register(EMPTY, (pack, path, args, id) -> EmptyItemBehavior.INSTANCE);
register(EMPTY, EmptyItemBehavior.FACTORY);
register(BLOCK_ITEM, BlockItemBehavior.FACTORY);
register(ON_LIQUID_BLOCK_ITEM, LiquidCollisionBlockItemBehavior.FACTORY);
register(FURNITURE_ITEM, FurnitureItemBehavior.FACTORY);
register(AXE_ITEM, AxeItemBehavior.FACTORY);
register(WATER_BUCKET_ITEM, WaterBucketItemBehavior.FACTORY);

View File

@@ -77,7 +77,7 @@ public class FurnitureItemBehavior extends ItemBehavior {
// trigger event
org.bukkit.entity.Player bukkitPlayer = (org.bukkit.entity.Player) player.platformPlayer();
World world = (World) context.getLevel().getHandle();
World world = (World) context.getLevel().platformWorld();
// get position and rotation for placement
Vec3d finalPlacePosition;

View File

@@ -0,0 +1,76 @@
package net.momirealms.craftengine.bukkit.item.behavior;
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
import net.momirealms.craftengine.bukkit.util.LocationUtils;
import net.momirealms.craftengine.bukkit.util.Reflections;
import net.momirealms.craftengine.core.entity.player.InteractionHand;
import net.momirealms.craftengine.core.entity.player.InteractionResult;
import net.momirealms.craftengine.core.entity.player.Player;
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.Direction;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.MiscUtils;
import net.momirealms.craftengine.core.world.BlockHitResult;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.Vec3d;
import net.momirealms.craftengine.core.world.World;
import java.nio.file.Path;
import java.util.Map;
public class LiquidCollisionBlockItemBehavior extends BlockItemBehavior {
public static final Factory FACTORY = new Factory();
private final int offsetY;
public LiquidCollisionBlockItemBehavior(Key blockId, int offsetY) {
super(blockId);
this.offsetY = offsetY;
}
@Override
public InteractionResult useOnBlock(UseOnContext context) {
return use(context.getLevel(), context.getPlayer(), context.getHand());
}
@Override
public InteractionResult use(World world, Player player, InteractionHand hand) {
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));
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));
if (miss) {
return super.useOnBlock(new UseOnContext(player, hand, BlockHitResult.miss(hitPos, direction, above)));
} else {
boolean inside = Reflections.field$BlockHitResul$inside.getBoolean(blockHitResult);
return super.useOnBlock(new UseOnContext(player, hand, new BlockHitResult(hitPos, direction, above, inside)));
}
} catch (Exception e) {
CraftEngine.instance().logger().warn("Error handling use", e);
return InteractionResult.FAIL;
}
}
public static class Factory implements ItemBehaviorFactory {
@Override
public ItemBehavior create(Pack pack, Path path, Key key, Map<String, Object> arguments) {
Object id = arguments.get("block");
if (id == null) {
throw new IllegalArgumentException("Missing required parameter 'block' for on_liquid_block_item behavior");
}
int offset = MiscUtils.getAsInt(arguments.getOrDefault("y-offset", 1));
if (id instanceof Map<?, ?> map) {
BukkitBlockManager.instance().parseSection(pack, path, key, MiscUtils.castToMap(map, false));
return new LiquidCollisionBlockItemBehavior(key, offset);
} else {
return new LiquidCollisionBlockItemBehavior(Key.of(id.toString()), offset);
}
}
}
}

View File

@@ -299,7 +299,7 @@ public class ComponentItemFactory extends BukkitItemFactory {
}
@Override
protected ItemWrapper<ItemStack> merge(ItemWrapper<ItemStack> item1, ItemWrapper<ItemStack> item2) {
protected ItemWrapper<ItemStack> mergeCopy(ItemWrapper<ItemStack> item1, ItemWrapper<ItemStack> item2) {
Object itemStack1 = item1.getLiteralObject();
Object itemStack2 = item2.getLiteralObject();
try {
@@ -311,4 +311,17 @@ public class ComponentItemFactory extends BukkitItemFactory {
}
return null;
}
@Override
protected void merge(ItemWrapper<ItemStack> item1, ItemWrapper<ItemStack> item2) {
// load previous changes on nms items
item1.load();
Object itemStack1 = item1.getLiteralObject();
Object itemStack2 = item2.getLiteralObject();
try {
Reflections.method$ItemStack$applyComponents.invoke(itemStack1, Reflections.method$ItemStack$getComponentsPatch.invoke(itemStack2));
} catch (Exception e) {
plugin.logger().warn("Failed to merge item", e);
}
}
}

View File

@@ -234,11 +234,18 @@ public class UniversalItemFactory extends BukkitItemFactory {
}
@Override
protected ItemWrapper<ItemStack> merge(ItemWrapper<ItemStack> item1, ItemWrapper<ItemStack> item2) {
protected ItemWrapper<ItemStack> mergeCopy(ItemWrapper<ItemStack> item1, ItemWrapper<ItemStack> item2) {
Object itemStack = ItemObject.copy(item2.getLiteralObject());
ItemObject.setCustomDataTag(itemStack, TagCompound.clone(ItemObject.getCustomDataTag(item1.getLiteralObject())));
// one more step than vanilla
TagCompound.merge(ItemObject.getCustomDataTag(itemStack), ItemObject.getCustomDataTag(item2.getLiteralObject()), true, true);
return new RTagItemWrapper(new RtagItem(ItemObject.asCraftMirror(itemStack)), item2.count());
}
@Override
protected void merge(ItemWrapper<ItemStack> item1, ItemWrapper<ItemStack> item2) {
// load previous changes on nms items
item1.load();
TagCompound.merge(ItemObject.getCustomDataTag(item1.getLiteralObject()), ItemObject.getCustomDataTag(item2.getLiteralObject()), true, true);
}
}

View File

@@ -44,8 +44,10 @@ public class DebugAppearanceStateUsageCommand extends BukkitCommandFeature<Comma
List<Integer> appearances = blockManager.blockAppearanceArranger().get(baseBlockId);
if (appearances == null) return;
int i = 0;
Component component = Component.text(baseBlockId + ": ");
List<Component> children = new ArrayList<>();
Component block = Component.text(baseBlockId + ": ");
plugin().senderFactory().wrap(context.sender()).sendMessage(block);
List<Component> batch = new ArrayList<>();
for (int appearance : appearances) {
Component text = Component.text("|");
List<Integer> reals = blockManager.appearanceToRealStates(appearance);
@@ -64,11 +66,19 @@ public class DebugAppearanceStateUsageCommand extends BukkitCommandFeature<Comma
}
text = text.color(NamedTextColor.RED).hoverEvent(HoverEvent.showText(hover.children(hoverChildren)));
}
children.add(text);
batch.add(text);
i++;
if (batch.size() == 100) {
plugin().senderFactory().wrap(context.sender())
.sendMessage(Component.text("").children(batch));
batch.clear();
}
}
if (!batch.isEmpty()) {
plugin().senderFactory().wrap(context.sender())
.sendMessage(Component.text("").children(batch));
batch.clear();
}
plugin().senderFactory().wrap(context.sender())
.sendMessage(component.children(children));
});
}

View File

@@ -44,22 +44,32 @@ public class DebugRealStateUsageCommand extends BukkitCommandFeature<CommandSend
List<Integer> reals = blockManager.realBlockArranger().get(baseBlockId);
if (reals == null) return;
int i = 0;
Component component = Component.text(baseBlockId + ": ");
List<Component> children = new ArrayList<>();
Component block = Component.text(baseBlockId + ": ");
plugin().senderFactory().wrap(context.sender()).sendMessage(block);
List<Component> batch = new ArrayList<>(100);
for (int real : reals) {
ImmutableBlockState state = blockManager.getImmutableBlockStateUnsafe(real);
if (state.isEmpty()) {
Component hover = Component.text("craftengine:" + baseBlockId.value() + "_" + i).color(NamedTextColor.GREEN);
children.add(Component.text("|").color(NamedTextColor.GREEN).hoverEvent(HoverEvent.showText(hover)));
batch.add(Component.text("|").color(NamedTextColor.GREEN).hoverEvent(HoverEvent.showText(hover)));
} else {
Component hover = Component.text("craftengine:" + baseBlockId.value() + "_" + i).color(NamedTextColor.RED);
hover = hover.append(Component.newline()).append(Component.text(state.toString()).color(NamedTextColor.GRAY));
children.add(Component.text("|").color(NamedTextColor.RED).hoverEvent(HoverEvent.showText(hover)));
batch.add(Component.text("|").color(NamedTextColor.RED).hoverEvent(HoverEvent.showText(hover)));
}
i++;
if (batch.size() == 100) {
plugin().senderFactory().wrap(context.sender())
.sendMessage(Component.text("").children(batch));
batch.clear();
}
}
if (!batch.isEmpty()) {
plugin().senderFactory().wrap(context.sender())
.sendMessage(Component.text("").children(batch));
batch.clear();
}
plugin().senderFactory().wrap(context.sender())
.sendMessage(component.children(children));
});
}

View File

@@ -4,6 +4,7 @@ import net.kyori.adventure.text.Component;
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature;
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.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.plugin.CraftEngine;
@@ -50,6 +51,8 @@ public class DebugTargetBlockCommand extends BukkitCommandFeature<CommandSender>
if (immutableBlockState != null) {
sender.sendMessage(Component.text(immutableBlockState.toString()));
}
ImmutableBlockState dataInCache = plugin().worldManager().getWorld(block.getWorld().getUID()).getBlockStateAtIfLoaded(LocationUtils.toBlockPos(block.getLocation()));
sender.sendMessage(Component.text("cache-state: " + !dataInCache.isEmpty()));
try {
@SuppressWarnings("unchecked")
Set<Object> tags = (Set<Object>) Reflections.field$Holder$Reference$tags.get(holder);

View File

@@ -125,6 +125,9 @@ public class BukkitNetworkManager implements NetworkManager, Listener {
registerNMSPacketConsumer(PacketConsumers.MOVE_ENTITY, Reflections.clazz$ClientboundMoveEntityPacket$Pos);
registerNMSPacketConsumer(PacketConsumers.PICK_ITEM_FROM_ENTITY, Reflections.clazz$ServerboundPickItemFromEntityPacket);
registerNMSPacketConsumer(PacketConsumers.SOUND, Reflections.clazz$ClientboundSoundPacket);
registerNMSPacketConsumer(PacketConsumers.CHAT, Reflections.clazz$ServerboundChatPacket);
registerNMSPacketConsumer(PacketConsumers.RENAME_ITEM, Reflections.clazz$ServerboundRenameItemPacket);
registerNMSPacketConsumer(PacketConsumers.SIGN_UPDATE, Reflections.clazz$ServerboundSignUpdatePacket);
registerByteBufPacketConsumer(PacketConsumers.SECTION_BLOCK_UPDATE, this.packetIds.clientboundSectionBlocksUpdatePacket());
registerByteBufPacketConsumer(PacketConsumers.BLOCK_UPDATE, this.packetIds.clientboundBlockUpdatePacket());
registerByteBufPacketConsumer(PacketConsumers.LEVEL_PARTICLE, this.packetIds.clientboundLevelParticlesPacket());
@@ -265,6 +268,28 @@ public class BukkitNetworkManager implements NetworkManager, Listener {
this.packetsConsumer.accept(player.serverPlayer(), packet);
}
public void receivePacket(@NotNull NetWorkUser player, Object packet) {
Channel channel = player.nettyChannel();
if (channel.isOpen()) {
List<String> handlerNames = channel.pipeline().names();
if (handlerNames.contains("via-encoder")) {
channel.pipeline().context("via-decoder").fireChannelRead(packet);
} else if (handlerNames.contains("ps_decoder_transformer")) {
channel.pipeline().context("ps_decoder_transformer").fireChannelRead(packet);
} else if (handlerNames.contains("decompress")) {
channel.pipeline().context("decompress").fireChannelRead(packet);
} else {
if (handlerNames.contains("decrypt")) {
channel.pipeline().context("decrypt").fireChannelRead(packet);
} else {
channel.pipeline().context("splitter").fireChannelRead(packet);
}
}
} else {
((ByteBuf) packet).release();
}
}
private void injectServerChannel(Channel serverChannel) {
ChannelPipeline pipeline = serverChannel.pipeline();
ChannelHandler connectionHandler = pipeline.get(CONNECTION_HANDLER_NAME);

View File

@@ -14,6 +14,7 @@ import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
import net.momirealms.craftengine.bukkit.util.*;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.entity.player.InteractionHand;
import net.momirealms.craftengine.core.font.ImageManager;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.config.ConfigManager;
import net.momirealms.craftengine.core.plugin.network.ConnectionState;
@@ -32,10 +33,9 @@ import org.bukkit.util.RayTraceResult;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Map;
import java.util.Objects;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
public class PacketConsumers {
private static int[] mappings;
@@ -344,7 +344,7 @@ public class PacketConsumers {
} catch (Exception e) {
CraftEngine.instance().logger().warn("Failed to handle ServerboundSetCreativeModeSlotPacket", e);
}
}, (World) player.level().getHandle(), (MCUtils.fastFloor(player.x())) >> 4, (MCUtils.fastFloor(player.z())) >> 4);
}, (World) player.level().platformWorld(), (MCUtils.fastFloor(player.x())) >> 4, (MCUtils.fastFloor(player.z())) >> 4);
} else {
handleSetCreativeSlotPacketOnMainThread(player, packet);
}
@@ -532,6 +532,7 @@ public class PacketConsumers {
int entityId = (int) Reflections.field$ClientboundAddEntityPacket$entityId.get(packet);
LoadedFurniture furniture = BukkitFurnitureManager.instance().getLoadedFurnitureByBaseEntityId(entityId);
if (furniture != null) {
user.furnitureView().computeIfAbsent(furniture.baseEntityId(), k -> new ArrayList<>()).addAll(furniture.subEntityIds());
user.sendPacket(furniture.spawnPacket(), false);
}
}
@@ -566,7 +567,7 @@ public class PacketConsumers {
try {
IntList intList = (IntList) Reflections.field$ClientboundRemoveEntitiesPacket$entityIds.get(packet);
for (int i = 0, size = intList.size(); i < size; i++) {
int[] entities = BukkitFurnitureManager.instance().getSubEntityIdsByBaseEntityId(intList.getInt(i));
List<Integer> entities = user.furnitureView().remove(intList.getInt(i));
if (entities == null) continue;
for (int entityId : entities) {
intList.add(entityId);
@@ -661,4 +662,92 @@ public class PacketConsumers {
CraftEngine.instance().logger().warn("Failed to handle ClientboundSoundPacket", e);
}
};
// we handle it on packet level to prevent it from being captured by plugins (most are chat plugins)
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> CHAT = (user, event, packet) -> {
try {
String message = (String) Reflections.field$ServerboundChatPacket$message.get(packet);
if (message != null && !message.isEmpty()) {
ImageManager manager = CraftEngine.instance().imageManager();
if (!manager.isDefaultFontInUse()) return;
runIfContainsIllegalCharacter(message, manager, (s) -> {
event.setCancelled(true);
try {
Object newPacket = Reflections.constructor$ServerboundChatPacket.newInstance(
s,
Reflections.field$ServerboundChatPacket$timeStamp.get(packet),
Reflections.field$ServerboundChatPacket$salt.get(packet),
Reflections.field$ServerboundChatPacket$signature.get(packet),
Reflections.field$ServerboundChatPacket$lastSeenMessages.get(packet)
);
user.receivePacket(newPacket);
} catch (Exception e) {
CraftEngine.instance().logger().warn("Failed to create replaced chat packet", e);
}
});
}
} catch (Exception e) {
CraftEngine.instance().logger().warn("Failed to handle ServerboundChatPacket", e);
}
};
// we handle it on packet level to prevent it from being captured by plugins
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> RENAME_ITEM = (user, event, packet) -> {
try {
String message = (String) Reflections.field$ServerboundRenameItemPacket$name.get(packet);
if (message != null && !message.isEmpty()) {
ImageManager manager = CraftEngine.instance().imageManager();
if (!manager.isDefaultFontInUse()) return;
runIfContainsIllegalCharacter(message, manager, (s) -> {
try {
Reflections.field$ServerboundRenameItemPacket$name.set(packet, s);
} catch (ReflectiveOperationException e) {
CraftEngine.instance().logger().warn("Failed to replace chat", e);
}
});
}
} catch (Exception e) {
CraftEngine.instance().logger().warn("Failed to handle ServerboundRenameItemPacket", e);
}
};
// we handle it on packet level to prevent it from being captured by plugins
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> SIGN_UPDATE = (user, event, packet) -> {
try {
String[] lines = (String[]) Reflections.field$ServerboundSignUpdatePacket$lines.get(packet);
ImageManager manager = CraftEngine.instance().imageManager();
if (!manager.isDefaultFontInUse()) return;
for (int i = 0; i < lines.length; i++) {
String line = lines[i];
if (line != null && !line.isEmpty()) {
try {
int lineIndex = i;
runIfContainsIllegalCharacter(line, manager, (s) -> lines[lineIndex] = s);
} catch (Exception ignore) {
}
}
}
} catch (Exception e) {
CraftEngine.instance().logger().warn("Failed to handle ServerboundSignUpdatePacket", e);
}
};
private static void runIfContainsIllegalCharacter(String string, ImageManager manager, Consumer<String> callback) {
char[] chars = string.toCharArray();
int[] codepoints = CharacterUtils.charsToCodePoints(chars);
int[] newCodepoints = new int[codepoints.length];
boolean hasIllegal = false;
for (int i = 0; i < codepoints.length; i++) {
int codepoint = codepoints[i];
if (!manager.isIllegalCharacter(codepoint)) {
newCodepoints[i] = codepoint;
} else {
newCodepoints[i] = '*';
hasIllegal = true;
}
}
if (hasIllegal) {
callback.accept(new String(newCodepoints, 0, newCodepoints.length));
}
}
}

View File

@@ -16,20 +16,23 @@ import net.momirealms.craftengine.core.plugin.network.ConnectionState;
import net.momirealms.craftengine.core.util.Direction;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.VersionHelper;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.Vec3d;
import net.momirealms.craftengine.core.world.World;
import net.momirealms.craftengine.core.world.*;
import org.bukkit.*;
import org.bukkit.block.Block;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.bukkit.util.RayTraceResult;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.Nullable;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public class BukkitServerPlayer extends Player {
private final Channel channel;
@@ -60,6 +63,8 @@ public class BukkitServerPlayer extends Player {
private Key lastUsedRecipe = null;
private Map<Integer, List<Integer>> furnitureView = new ConcurrentHashMap<>();
public BukkitServerPlayer(BukkitCraftEngine plugin, Channel channel) {
this.channel = channel;
this.plugin = plugin;
@@ -201,11 +206,41 @@ public class BukkitServerPlayer extends Player {
platformPlayer().closeInventory();
}
// TODO DO NOT USE BUKKIT API
@Override
public BlockHitResult rayTrace(double distance, FluidCollisionRule collisionRule) {
RayTraceResult result = platformPlayer().rayTraceBlocks(distance, FluidUtils.toCollisionRule(collisionRule));
if (result == null) {
Location eyeLocation = platformPlayer().getEyeLocation();
Location targetLocation = eyeLocation.clone();
targetLocation.add(eyeLocation.getDirection().multiply(distance));
return BlockHitResult.miss(new Vec3d(eyeLocation.getX(), eyeLocation.getY(), eyeLocation.getZ()),
Direction.getApproximateNearest(eyeLocation.getX() - targetLocation.getX(), eyeLocation.getY() - targetLocation.getY(), eyeLocation.getZ() - targetLocation.getZ()),
new BlockPos(targetLocation.getBlockX(), targetLocation.getBlockY(), targetLocation.getBlockZ())
);
} else {
Vector hitPos = result.getHitPosition();
Block hitBlock = result.getHitBlock();
Location hitBlockLocation = hitBlock.getLocation();
return new BlockHitResult(
new Vec3d(hitPos.getX(), hitPos.getY(), hitPos.getZ()),
DirectionUtils.toDirection(result.getHitBlockFace()),
new BlockPos(hitBlockLocation.getBlockX(), hitBlockLocation.getBlockY(), hitBlockLocation.getBlockZ()),
false
);
}
}
@Override
public void sendPacket(Object packet, boolean immediately) {
this.plugin.networkManager().sendPacket(this, packet, immediately);
}
@Override
public void receivePacket(Object packet) {
this.plugin.networkManager().receivePacket(this, packet);
}
@Override
public ConnectionState decoderState() {
return decoderState;
@@ -553,6 +588,11 @@ public class BukkitServerPlayer extends Player {
return playerRef.get();
}
@Override
public Map<Integer, List<Integer>> furnitureView() {
return this.furnitureView;
}
public void setResendSound() {
resentSoundTick = gameTicks();
}

View File

@@ -0,0 +1,24 @@
package net.momirealms.craftengine.bukkit.util;
import net.momirealms.craftengine.core.world.FluidCollisionRule;
import org.bukkit.FluidCollisionMode;
public class FluidUtils {
private FluidUtils() {}
public static FluidCollisionMode toCollisionRule(FluidCollisionRule rule) {
switch (rule) {
case NONE -> {
return FluidCollisionMode.NEVER;
}
case ALWAYS -> {
return FluidCollisionMode.ALWAYS;
}
case SOURCE_ONLY -> {
return FluidCollisionMode.SOURCE_ONLY;
}
}
return FluidCollisionMode.NEVER;
}
}

View File

@@ -13,6 +13,14 @@ public class LocationUtils {
return new Vec3d(loc.getX(), loc.getY(), loc.getZ());
}
public static Vec3d fromVec(Object vec) throws ReflectiveOperationException {
return new Vec3d(
Reflections.field$Vec3$x.getDouble(vec),
Reflections.field$Vec3$y.getDouble(vec),
Reflections.field$Vec3$z.getDouble(vec)
);
}
public static Object toBlockPos(BlockPos pos) {
try {
return Reflections.constructor$BlockPos.newInstance(pos.x(), pos.y(), pos.z());
@@ -21,6 +29,14 @@ public class LocationUtils {
}
}
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)
);
}
public static Object toBlockPos(int x, int y, int z) {
try {
return Reflections.constructor$BlockPos.newInstance(x, y, z);

View File

@@ -7,6 +7,7 @@ import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.MessageToByteEncoder;
import net.kyori.adventure.text.Component;
import net.momirealms.craftengine.core.util.ReflectionUtils;
import net.momirealms.craftengine.core.util.VersionHelper;
import org.bukkit.Location;
@@ -26,6 +27,7 @@ import sun.misc.Unsafe;
import java.io.BufferedReader;
import java.lang.reflect.*;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicInteger;
@@ -134,9 +136,7 @@ public class Reflections {
);
public static final Constructor<?> constructor$ClientboundSystemChatPacket = requireNonNull(
ReflectionUtils.getConstructor(
clazz$ClientboundSystemChatPacket, clazz$Component, boolean.class
)
ReflectionUtils.getConstructor(clazz$ClientboundSystemChatPacket, clazz$Component, boolean.class)
);
public static final Field field$ClientboundSystemChatPacket$overlay = requireNonNull(
@@ -189,6 +189,11 @@ public class Reflections {
clazz$ClientboundSystemChatPacket, clazz$Component, 0
);
public static final Field field$ClientboundSystemChatPacket$adventure$content =
ReflectionUtils.getDeclaredField(
clazz$ClientboundSystemChatPacket, Component.class, 0
);
public static final Field field$ClientboundSystemChatPacket$text =
ReflectionUtils.getDeclaredField(
clazz$ClientboundSystemChatPacket, String.class, 0
@@ -1455,6 +1460,12 @@ public class Reflections {
)
);
public static final Method method$Direction$ordinal = requireNonNull(
ReflectionUtils.getMethod(
clazz$Direction, new String[]{"ordinal"}
)
);
public static final Method method$Direction$values = requireNonNull(
ReflectionUtils.getStaticMethod(
clazz$Direction, clazz$Direction.arrayType()
@@ -3326,6 +3337,7 @@ public class Reflections {
public static final Object instance$Blocks$STONE;
public static final Object instance$Blocks$STONE$defaultState;
public static final Object instance$Blocks$FIRE;
public static final Object instance$Blocks$ICE;
static {
try {
@@ -3337,6 +3349,8 @@ public class Reflections {
Object stone = method$ResourceLocation$fromNamespaceAndPath.invoke(null, "minecraft", "stone");
instance$Blocks$STONE = method$Registry$get.invoke(instance$BuiltInRegistries$BLOCK, stone);
instance$Blocks$STONE$defaultState = method$Block$defaultBlockState.invoke(instance$Blocks$STONE);
Object ice = method$ResourceLocation$fromNamespaceAndPath.invoke(null, "minecraft", "ice");
instance$Blocks$ICE = method$Registry$get.invoke(instance$BuiltInRegistries$BLOCK, ice);
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
@@ -3862,11 +3876,17 @@ public class Reflections {
);
public static final Object instance$Fluids$WATER;
public static final Object instance$Fluids$LAVA;
public static final Object instance$Fluids$EMPTY;
static {
try {
Object waterId = method$ResourceLocation$fromNamespaceAndPath.invoke(null, "minecraft", "water");
instance$Fluids$WATER = method$Registry$get.invoke(instance$BuiltInRegistries$FLUID, waterId);
Object lavaId = method$ResourceLocation$fromNamespaceAndPath.invoke(null, "minecraft", "lava");
instance$Fluids$LAVA = method$Registry$get.invoke(instance$BuiltInRegistries$FLUID, lavaId);
Object emptyId = method$ResourceLocation$fromNamespaceAndPath.invoke(null, "minecraft", "empty");
instance$Fluids$EMPTY = method$Registry$get.invoke(instance$BuiltInRegistries$FLUID, emptyId);
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
@@ -5015,4 +5035,170 @@ public class Reflections {
clazz$ItemStack, clazz$Item
)
);
public static final Class<?> clazz$BlockHitResult = requireNonNull(
ReflectionUtils.getClazz(
BukkitReflectionUtils.assembleMCClass("world.phys.BlockHitResult"),
BukkitReflectionUtils.assembleMCClass("world.phys.MovingObjectPositionBlock")
)
);
public static final Class<?> clazz$ClipContext$Fluid = requireNonNull(
ReflectionUtils.getClazz(
BukkitReflectionUtils.assembleMCClass("world.level.ClipContext$Fluid"),
BukkitReflectionUtils.assembleMCClass("world.level.RayTrace$FluidCollisionOption")
)
);
public static final Method method$ClipContext$Fluid$values = requireNonNull(
ReflectionUtils.getStaticMethod(
clazz$ClipContext$Fluid, clazz$ClipContext$Fluid.arrayType()
)
);
public static final Object instance$ClipContext$Fluid$NONE;
public static final Object instance$ClipContext$Fluid$SOURCE_ONLY;
public static final Object instance$ClipContext$Fluid$ANY;
static {
try {
Object[] values = (Object[]) method$ClipContext$Fluid$values.invoke(null);
instance$ClipContext$Fluid$NONE = values[0];
instance$ClipContext$Fluid$SOURCE_ONLY = values[1];
instance$ClipContext$Fluid$ANY = values[2];
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
public static final Method method$Item$getPlayerPOVHitResult = requireNonNull(
ReflectionUtils.getDeclaredMethod(
clazz$Item, clazz$BlockHitResult, clazz$Level, clazz$Player, clazz$ClipContext$Fluid
)
);
public static final Method method$BlockHitResult$withPosition = requireNonNull(
ReflectionUtils.getMethod(
clazz$BlockHitResult, clazz$BlockHitResult, clazz$BlockPos
)
);
public static final Field field$BlockHitResul$blockPos = requireNonNull(
ReflectionUtils.getDeclaredField(
clazz$BlockHitResult, clazz$BlockPos, 0
)
);
public static final Field field$BlockHitResul$direction = requireNonNull(
ReflectionUtils.getDeclaredField(
clazz$BlockHitResult, clazz$Direction, 0
)
);
public static final Field field$BlockHitResul$miss = requireNonNull(
ReflectionUtils.getDeclaredField(
clazz$BlockHitResult, boolean.class, 0
)
);
public static final Field field$BlockHitResul$inside = requireNonNull(
ReflectionUtils.getDeclaredField(
clazz$BlockHitResult, boolean.class, 1
)
);
public static final Class<?> clazz$HitResult = requireNonNull(
ReflectionUtils.getClazz(
BukkitReflectionUtils.assembleMCClass("world.phys.HitResult"),
BukkitReflectionUtils.assembleMCClass("world.phys.MovingObjectPosition")
)
);
public static final Field field$HitResult$location = requireNonNull(
ReflectionUtils.getDeclaredField(
clazz$HitResult, clazz$Vec3, 0
)
);
public static final Class<?> clazz$MessageSignature = requireNonNull(
ReflectionUtils.getClazz(
BukkitReflectionUtils.assembleMCClass("network.chat.MessageSignature")
)
);
public static final Class<?> clazz$LastSeenMessages$Update = requireNonNull(
ReflectionUtils.getClazz(
BukkitReflectionUtils.assembleMCClass("network.chat.LastSeenMessages$Update"),
BukkitReflectionUtils.assembleMCClass("network.chat.LastSeenMessages$b")
)
);
public static final Class<?> clazz$ServerboundChatPacket = requireNonNull(
ReflectionUtils.getClazz(
BukkitReflectionUtils.assembleMCClass("network.protocol.game.ServerboundChatPacket"),
BukkitReflectionUtils.assembleMCClass("network.protocol.game.PacketPlayInChat")
)
);
public static final Constructor<?> constructor$ServerboundChatPacket = requireNonNull(
ReflectionUtils.getConstructor(
clazz$ServerboundChatPacket, String.class, Instant.class, long.class, clazz$MessageSignature, clazz$LastSeenMessages$Update
)
);
public static final Field field$ServerboundChatPacket$message = requireNonNull(
ReflectionUtils.getDeclaredField(
clazz$ServerboundChatPacket, String.class, 0
)
);
public static final Field field$ServerboundChatPacket$timeStamp = requireNonNull(
ReflectionUtils.getDeclaredField(
clazz$ServerboundChatPacket, Instant.class, 0
)
);
public static final Field field$ServerboundChatPacket$salt = requireNonNull(
ReflectionUtils.getDeclaredField(
clazz$ServerboundChatPacket, long.class, 0
)
);
public static final Field field$ServerboundChatPacket$signature = requireNonNull(
ReflectionUtils.getDeclaredField(
clazz$ServerboundChatPacket, clazz$MessageSignature, 0
)
);
public static final Field field$ServerboundChatPacket$lastSeenMessages = requireNonNull(
ReflectionUtils.getDeclaredField(
clazz$ServerboundChatPacket, clazz$LastSeenMessages$Update, 0
)
);
public static final Class<?> clazz$ServerboundRenameItemPacket = requireNonNull(
ReflectionUtils.getClazz(
BukkitReflectionUtils.assembleMCClass("network.protocol.game.ServerboundRenameItemPacket"),
BukkitReflectionUtils.assembleMCClass("network.protocol.game.PacketPlayInItemName")
)
);
public static final Field field$ServerboundRenameItemPacket$name = requireNonNull(
ReflectionUtils.getDeclaredField(
clazz$ServerboundRenameItemPacket, String.class, 0
)
);
public static final Class<?> clazz$ServerboundSignUpdatePacket = requireNonNull(
ReflectionUtils.getClazz(
BukkitReflectionUtils.assembleMCClass("network.protocol.game.ServerboundSignUpdatePacket"),
BukkitReflectionUtils.assembleMCClass("network.protocol.game.PacketPlayInUpdateSign")
)
);
public static final Field field$ServerboundSignUpdatePacket$lines = requireNonNull(
ReflectionUtils.getDeclaredField(
clazz$ServerboundSignUpdatePacket, String[].class, 0
)
);
}

View File

@@ -15,7 +15,7 @@ public class BukkitCEWorld extends CEWorld {
@Override
public void tick() {
if (ConfigManager.enableLightSystem()) {
LightUtils.updateChunkLight((org.bukkit.World) world.getHandle(), SectionPosUtils.toMap(super.updatedSectionPositions, world.worldHeight().getMinSection() - 1, world.worldHeight().getMaxSection() + 1));
LightUtils.updateChunkLight((org.bukkit.World) world.platformWorld(), SectionPosUtils.toMap(super.updatedSectionPositions, world.worldHeight().getMinSection() - 1, world.worldHeight().getMaxSection() + 1));
super.updatedSectionPositions.clear();
}
}

View File

@@ -2,6 +2,7 @@ package net.momirealms.craftengine.bukkit.world;
import net.momirealms.craftengine.bukkit.util.EntityUtils;
import net.momirealms.craftengine.bukkit.util.ItemUtils;
import net.momirealms.craftengine.bukkit.util.Reflections;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.VersionHelper;
@@ -28,36 +29,45 @@ public class BukkitWorld implements World {
}
@Override
public org.bukkit.World getHandle() {
public org.bukkit.World platformWorld() {
return world.get();
}
@Override
public Object serverWorld() {
try {
return Reflections.field$CraftWorld$ServerLevel.get(platformWorld());
} catch (Exception e) {
throw new RuntimeException("Failed to get server world", e);
}
}
@Override
public WorldHeight worldHeight() {
if (this.worldHeight == null) {
this.worldHeight = WorldHeight.create(getHandle().getMinHeight(), getHandle().getMaxHeight() - getHandle().getMinHeight());
this.worldHeight = WorldHeight.create(platformWorld().getMinHeight(), platformWorld().getMaxHeight() - platformWorld().getMinHeight());
}
return this.worldHeight;
}
@Override
public WorldBlock getBlockAt(int x, int y, int z) {
return new BukkitWorldBlock(getHandle().getBlockAt(x, y, z));
return new BukkitWorldBlock(platformWorld().getBlockAt(x, y, z));
}
@Override
public String name() {
return getHandle().getName();
return platformWorld().getName();
}
@Override
public Path directory() {
return getHandle().getWorldFolder().toPath();
return platformWorld().getWorldFolder().toPath();
}
@Override
public UUID uuid() {
return getHandle().getUID();
return platformWorld().getUID();
}
@Override
@@ -65,16 +75,16 @@ public class BukkitWorld implements World {
ItemStack itemStack = (ItemStack) item.load();
if (ItemUtils.isEmpty(itemStack)) return;
if (VersionHelper.isVersionNewerThan1_21_2()) {
getHandle().dropItemNaturally(new Location(null, location.x(), location.y(), location.z()), (ItemStack) item.getItem());
platformWorld().dropItemNaturally(new Location(null, location.x(), location.y(), location.z()), (ItemStack) item.getItem());
} else {
getHandle().dropItemNaturally(new Location(null, location.x() - 0.5, location.y() - 0.5, location.z() - 0.5), (ItemStack) item.getItem());
platformWorld().dropItemNaturally(new Location(null, location.x() - 0.5, location.y() - 0.5, location.z() - 0.5), (ItemStack) item.getItem());
}
}
@Override
public void dropExp(Vec3d location, int amount) {
if (amount <= 0) return;
EntityUtils.spawnEntity(getHandle(), new Location(getHandle(), location.x(), location.y(), location.z()), EntityType.EXPERIENCE_ORB, (e) -> {
EntityUtils.spawnEntity(platformWorld(), new Location(platformWorld(), location.x(), location.y(), location.z()), EntityType.EXPERIENCE_ORB, (e) -> {
ExperienceOrb orb = (ExperienceOrb) e;
orb.setExperience(amount);
});
@@ -82,6 +92,6 @@ public class BukkitWorld implements World {
@Override
public void playBlockSound(Vec3d location, Key sound, float volume, float pitch) {
getHandle().playSound(new Location(null, location.x(), location.y(), location.z()), sound.toString(), SoundCategory.BLOCKS, volume, pitch);
platformWorld().playSound(new Location(null, location.x(), location.y(), location.z()), sound.toString(), SoundCategory.BLOCKS, volume, pitch);
}
}

View File

@@ -14,6 +14,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.Map;
public class BlockBehaviors {
public static final Key EMPTY = Key.from("craftengine:empty");
public static void register(Key key, BlockBehaviorFactory factory) {
Holder.Reference<BlockBehaviorFactory> holder = ((WritableRegistry<BlockBehaviorFactory>) BuiltInRegistries.BLOCK_BEHAVIOR_FACTORY)

View File

@@ -5,7 +5,6 @@ import net.momirealms.craftengine.core.plugin.Reloadable;
import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser;
import net.momirealms.craftengine.core.util.Key;
import javax.annotation.Nullable;
import java.util.Optional;
public interface FurnitureManager extends Reloadable, ConfigSectionParser {
@@ -25,8 +24,5 @@ public interface FurnitureManager extends Reloadable, ConfigSectionParser {
Optional<CustomFurniture> getFurniture(Key id);
@Nullable
int[] getSubEntityIdsByBaseEntityId(int entityId);
boolean isFurnitureBaseEntity(int entityId);
}

View File

@@ -5,7 +5,9 @@ import net.momirealms.craftengine.core.entity.Entity;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.plugin.network.NetWorkUser;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.world.BlockHitResult;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.FluidCollisionRule;
import org.jetbrains.annotations.Nullable;
public abstract class Player extends Entity implements NetWorkUser {
@@ -68,4 +70,6 @@ public abstract class Player extends Entity implements NetWorkUser {
public abstract void giveItem(Item<?> item);
public abstract void closeInventory();
public abstract BlockHitResult rayTrace(double distance, FluidCollisionRule collisionRule);
}

View File

@@ -3,6 +3,7 @@ package net.momirealms.craftengine.core.font;
import net.momirealms.craftengine.core.util.Key;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
@@ -19,6 +20,10 @@ public class Font {
return this.idToCodepoint.containsKey(codepoint);
}
public Collection<Integer> codepointsInUse() {
return Collections.unmodifiableCollection(this.idToCodepoint.keySet());
}
public BitmapImage getImageByCodepoint(int codepoint) {
return this.idToCodepoint.get(codepoint);
}

View File

@@ -13,11 +13,18 @@ import java.util.function.BiFunction;
public interface ImageManager extends Reloadable, ConfigSectionParser {
String CONFIG_SECTION_NAME = "images";
Key DEFAULT_FONT = Key.of("minecraft:default");
default String sectionId() {
return CONFIG_SECTION_NAME;
}
void delayedLoad();
boolean isDefaultFontInUse();
boolean isIllegalCharacter(int codepoint);
Collection<Font> fontsInUse();
Optional<BitmapImage> bitmapImageByCodepoint(Key font, int codepoint);

View File

@@ -18,6 +18,7 @@ public class ImageManagerImpl implements ImageManager {
private final HashMap<Key, Font> fonts = new HashMap<>();
// namespace:id image
private final HashMap<Key, BitmapImage> images = new HashMap<>();
private final Set<Integer> illegalChars = new HashSet<>();
private OffsetFont offsetFont;
@@ -36,6 +37,24 @@ public class ImageManagerImpl implements ImageManager {
public void unload() {
this.fonts.clear();
this.images.clear();
this.illegalChars.clear();
}
@Override
public void delayedLoad() {
Optional.ofNullable(this.fonts.get(DEFAULT_FONT)).ifPresent(font -> {
this.illegalChars.addAll(font.codepointsInUse());
});
}
@Override
public boolean isDefaultFontInUse() {
return !this.illegalChars.isEmpty();
}
@Override
public boolean isIllegalCharacter(int codepoint) {
return this.illegalChars.contains(codepoint);
}
@Override

View File

@@ -280,7 +280,13 @@ public class AbstractItem<W extends ItemWrapper<I>, I> implements Item<I> {
@SuppressWarnings({"unchecked", "rawtypes"})
@Override
public Item<I> merge(Item<?> another) {
return new AbstractItem<>(this.factory, this.factory.merge(this.item, ((AbstractItem) another).item));
public Item<I> mergeCopy(Item<?> another) {
return new AbstractItem<>(this.factory, this.factory.mergeCopy(this.item, ((AbstractItem) another).item));
}
@SuppressWarnings({"unchecked", "rawtypes"})
@Override
public void merge(Item<I> another) {
this.factory.merge(this.item, ((AbstractItem) another).item);
}
}

View File

@@ -23,6 +23,7 @@ public abstract class AbstractItemManager<I> extends AbstractModelGenerator impl
protected static final List<Key> VANILLA_ITEMS = new ArrayList<>();
protected static final Map<Key, List<Holder<Key>>> VANILLA_ITEM_TAGS = new HashMap<>();
protected final Map<String, ExternalItemProvider<I>> externalItemProviders = new HashMap<>();
protected final Map<String, Function<Object, ItemModifier<I>>> dataFunctions = new HashMap<>();
protected final Map<Key, CustomItem<I>> customItems = new HashMap<>();
protected final Map<Key, List<Holder<Key>>> customItemTags;
@@ -47,6 +48,18 @@ public abstract class AbstractItemManager<I> extends AbstractModelGenerator impl
}
}
@Override
public ExternalItemProvider<I> getExternalItemProvider(String name) {
return this.externalItemProviders.get(name);
}
@Override
public boolean registerExternalItemProvider(ExternalItemProvider<I> externalItemProvider) {
if (this.externalItemProviders.containsKey(externalItemProvider.plugin())) return false;
this.externalItemProviders.put(externalItemProvider.plugin(), externalItemProvider);
return true;
}
@Override
public void unload() {
super.clearModelsToGenerate();
@@ -165,6 +178,13 @@ public abstract class AbstractItemManager<I> extends AbstractModelGenerator impl
}
private void registerFunctions() {
registerDataFunction((obj) -> {
Map<String, Object> data = MiscUtils.castToMap(obj, false);
String plugin = data.get("plugin").toString();
String id = data.get("id").toString();
ExternalItemProvider<I> provider = AbstractItemManager.this.getExternalItemProvider(plugin);
return new ExternalModifier<>(id, Objects.requireNonNull(provider, "Item provider " + plugin + " not found"));
}, "external");
registerDataFunction((obj) -> {
String name = obj.toString();
return new DisplayNameModifier<>(name);

View File

@@ -0,0 +1,11 @@
package net.momirealms.craftengine.core.item;
import org.jetbrains.annotations.Nullable;
public interface ExternalItemProvider<I> {
String plugin();
@Nullable
I build(String id, ItemBuildContext context);
}

View File

@@ -117,5 +117,7 @@ public interface Item<I> {
Object getLiteralObject();
Item<I> merge(Item<?> another);
Item<I> mergeCopy(Item<?> another);
void merge(Item<I> another);
}

View File

@@ -112,5 +112,7 @@ public abstract class ItemFactory<P extends Plugin, W extends ItemWrapper<I>, I>
protected abstract Optional<Integer> repairCost(ItemWrapper<I> item);
protected abstract ItemWrapper<I> merge(ItemWrapper<I> item1, ItemWrapper<I> item2);
protected abstract ItemWrapper<I> mergeCopy(ItemWrapper<I> item1, ItemWrapper<I> item2);
protected abstract void merge(ItemWrapper<I> item1, ItemWrapper<I> item2);
}

View File

@@ -51,6 +51,10 @@ public interface ItemManager<T> extends Reloadable, ModelGenerator, ConfigSectio
Key customItemId(T itemStack);
ExternalItemProvider<T> getExternalItemProvider(String name);
boolean registerExternalItemProvider(ExternalItemProvider<T> externalItemProvider);
Optional<CustomItem<T>> getCustomItem(Key key);
Optional<List<ItemBehavior>> getItemBehavior(Key key);

View File

@@ -1,5 +1,20 @@
package net.momirealms.craftengine.core.item.behavior;
import net.momirealms.craftengine.core.pack.Pack;
import net.momirealms.craftengine.core.util.Key;
import java.nio.file.Path;
import java.util.Map;
public class EmptyItemBehavior extends ItemBehavior {
public static final Factory FACTORY = new Factory();
public static final EmptyItemBehavior INSTANCE = new EmptyItemBehavior();
public static class Factory implements ItemBehaviorFactory {
@Override
public ItemBehavior create(Pack pack, Path path, Key id, Map<String, Object> arguments) {
return INSTANCE;
}
}
}

View File

@@ -12,6 +12,7 @@ import java.nio.file.Path;
import java.util.Map;
public class ItemBehaviors {
public static final Key EMPTY = Key.from("craftengine:empty");
public static void register(Key key, ItemBehaviorFactory factory) {
Holder.Reference<ItemBehaviorFactory> holder = ((WritableRegistry<ItemBehaviorFactory>) BuiltInRegistries.ITEM_BEHAVIOR_FACTORY)

View File

@@ -0,0 +1,33 @@
package net.momirealms.craftengine.core.item.modifier;
import net.momirealms.craftengine.core.item.ExternalItemProvider;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.item.ItemBuildContext;
import net.momirealms.craftengine.core.plugin.CraftEngine;
public class ExternalModifier<I> implements ItemModifier<I> {
private final String id;
private final ExternalItemProvider<I> provider;
public ExternalModifier(String id, ExternalItemProvider<I> provider) {
this.id = id;
this.provider = provider;
}
@Override
public String name() {
return "external";
}
@SuppressWarnings("unchecked")
@Override
public void apply(Item<I> item, ItemBuildContext context) {
I another = this.provider.build(id, context);
if (another == null) {
CraftEngine.instance().logger().warn("'" + id + "' could not be found in " + provider.plugin());
return;
}
Item<I> anotherWrapped = (Item<I>) CraftEngine.instance().itemManager().wrap(another);
item.merge(anotherWrapped);
}
}

View File

@@ -96,7 +96,7 @@ public class CustomSmithingTransformRecipe<T> implements Recipe<T> {
Item<T> wrappedResult = (Item<T>) CraftEngine.instance().itemManager().wrap(result);
Item<T> finalResult = wrappedResult;
if (this.mergeComponents) {
finalResult = base.merge(wrappedResult);
finalResult = base.mergeCopy(wrappedResult);
}
for (ItemDataProcessor processor : this.processors) {
processor.accept(base, wrappedResult, finalResult);

View File

@@ -320,14 +320,17 @@ public abstract class AbstractPackManager implements PackManager {
plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/stripped_palm_log.png");
plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/stripped_palm_log_top.png");
plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/palm_leaves.png");
// fairy flower
plugin.saveResource("resources/default/configuration/fairy_flower.yml");
// plants
plugin.saveResource("resources/default/configuration/plants.yml");
plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/fairy_flower_1.png");
plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/fairy_flower_2.png");
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/item/custom/fairy_flower.png");
plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/item/custom/reed.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
plugin.saveResource("resources/default/configuration/furniture.yml");
plugin.saveResource("resources/default/resourcepack/assets/minecraft/models/item/custom/table_lamp.json");

View File

@@ -0,0 +1,58 @@
package net.momirealms.craftengine.core.pack.conflict.resolution;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.util.GsonHelper;
import net.momirealms.craftengine.core.util.Key;
import java.io.IOException;
import java.nio.file.Path;
import java.util.HashSet;
import java.util.Map;
public class MergeAltasResolution implements Resolution {
public static final Factory FACTORY = new Factory();
public static final MergeAltasResolution INSTANCE = new MergeAltasResolution();
@Override
public void run(Path existing, Path conflict) {
try {
JsonObject j1 = GsonHelper.readJsonFile(existing).getAsJsonObject();
JsonObject j2 = GsonHelper.readJsonFile(conflict).getAsJsonObject();
JsonObject j3 = new JsonObject();
JsonArray ja1 = j1.getAsJsonArray("sources");
JsonArray ja2 = j2.getAsJsonArray("sources");
JsonArray ja3 = new JsonArray();
HashSet<String> elements = new HashSet<>();
for (JsonElement je : ja1) {
if (elements.add(je.getAsString())) {
ja3.add(je);
}
}
for (JsonElement je : ja2) {
if (elements.add(je.getAsString())) {
ja3.add(je);
}
}
j3.add("sources", ja3);
GsonHelper.writeJsonFile(j3, existing);
} catch (IOException e) {
CraftEngine.instance().logger().severe("Failed to merge json when resolving file conflicts", e);
}
}
@Override
public Key type() {
return Resolutions.MERGE_ATLAS;
}
public static class Factory implements ResolutionFactory {
@Override
public Resolution create(Map<String, Object> arguments) {
return INSTANCE;
}
}
}

View File

@@ -12,6 +12,7 @@ import java.util.Map;
public class Resolutions {
public static final Key RETAIN_MATCHING = Key.of("craftengine:retain_matching");
public static final Key MERGE_JSON = Key.of("craftengine:merge_json");
public static final Key MERGE_ATLAS = Key.of("craftengine:merge_atlas");
public static final Key CONDITIONAL = Key.of("craftengine:conditional");
public static final Key MERGE_PACK_MCMETA = Key.of("craftengine:merge_pack_mcmeta");
@@ -20,6 +21,7 @@ public class Resolutions {
register(MERGE_JSON, MergeJsonResolution.FACTORY);
register(CONDITIONAL, ConditionalResolution.FACTORY);
register(MERGE_PACK_MCMETA, MergePackMcMetaResolution.FACTORY);
register(MERGE_ATLAS, MergeAltasResolution.FACTORY);
}
public static void register(Key key, ResolutionFactory factory) {

View File

@@ -106,6 +106,7 @@ public abstract class CraftEngine implements Plugin {
this.blockManager.delayedLoad();
this.itemBrowserManager.delayedLoad();
this.soundManager.delayedLoad();
this.imageManager.delayedLoad();
if (ConfigManager.debug()) {
this.debugger = (s) -> logger.info("[Debug] " + s.get());
} else {
@@ -126,11 +127,11 @@ public abstract class CraftEngine implements Plugin {
// delay the reload so other plugins can register some parsers
this.scheduler.sync().runDelayed(() -> {
this.registerParsers();
this.itemManager.delayedInit();
this.reload();
this.guiManager.delayedInit();
this.recipeManager.delayedInit();
this.blockManager.delayedInit();
this.itemManager.delayedInit();
this.worldManager.delayedInit();
this.packManager.delayedInit();
this.furnitureManager.delayedInit();

View File

@@ -5,6 +5,9 @@ import net.momirealms.craftengine.core.plugin.Plugin;
import net.momirealms.craftengine.core.util.Key;
import org.jetbrains.annotations.ApiStatus;
import java.util.List;
import java.util.Map;
public interface NetWorkUser {
boolean isOnline();
@@ -16,6 +19,8 @@ public interface NetWorkUser {
void sendPacket(Object packet, boolean immediately);
void receivePacket(Object packet);
@ApiStatus.Internal
ConnectionState decoderState();
@@ -29,4 +34,6 @@ public interface NetWorkUser {
Object serverPlayer();
Object platformPlayer();
Map<Integer, List<Integer>> furnitureView();
}

View File

@@ -182,6 +182,21 @@ public enum Direction {
};
}
public static Direction getApproximateNearest(double x, double y, double z) {
Direction nearestDirection = null;
double maxDotProduct = -Double.MAX_VALUE;
for (Direction direction : Direction.values()) {
double dotProduct = x * direction.vec.x() +
y * direction.vec.y() +
z * direction.vec.z();
if (dotProduct > maxDotProduct) {
maxDotProduct = dotProduct;
nearestDirection = direction;
}
}
return nearestDirection;
}
public static Direction from3DDataValue(int id) {
return BY_3D_DATA[Math.abs(id % BY_3D_DATA.length)];
}

View File

@@ -0,0 +1,7 @@
package net.momirealms.craftengine.core.world;
public enum FluidCollisionRule {
NONE,
SOURCE_ONLY,
ALWAYS
}

View File

@@ -8,7 +8,9 @@ import java.util.UUID;
public interface World {
Object getHandle();
Object platformWorld();
Object serverWorld();
WorldHeight worldHeight();

View File

@@ -24,7 +24,7 @@ import net.momirealms.craftengine.shared.block.*;
import org.jetbrains.annotations.NotNull;
public class CraftEngineBlock extends Block implements BehaviorHolder, ShapeHolder, NoteBlockIndicator, Fallable, BonemealableBlock {
private static final PaperWeightStoneBlockShape STONE = new PaperWeightStoneBlockShape(Blocks.STONE.defaultBlockState());
private static final StoneBlockShape STONE = new StoneBlockShape(Blocks.STONE.defaultBlockState());
private boolean isNoteBlock;
public ObjectHolder<BlockBehavior> behaviorHolder;
public ObjectHolder<BlockShape> shapeHolder;

View File

@@ -6,10 +6,10 @@ import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.momirealms.craftengine.shared.block.BlockShape;
public class PaperWeightStoneBlockShape implements BlockShape {
public class StoneBlockShape implements BlockShape {
private final BlockState rawBlockState;
public PaperWeightStoneBlockShape(BlockState rawBlockState) {
public StoneBlockShape(BlockState rawBlockState) {
this.rawBlockState = rawBlockState;
}

View File

@@ -3,6 +3,7 @@ include(":shared")
include(":core")
include(":bukkit")
include(":bukkit:legacy")
include(":bukkit:compatibility")
include(":bukkit-loader")
include(":server-mod")
pluginManagement {