9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-23 08:59:27 +00:00

Merge pull request #380 from Xiao-MoMi/main

更新上游
This commit is contained in:
XiaoMoMi
2025-09-14 23:21:20 +08:00
committed by GitHub
410 changed files with 12673 additions and 3914 deletions

View File

@@ -75,7 +75,7 @@ repositories {
```
```kotlin
dependencies {
compileOnly("net.momirealms:craft-engine-core:0.0.61")
compileOnly("net.momirealms:craft-engine-bukkit:0.0.61")
compileOnly("net.momirealms:craft-engine-core:0.0.63")
compileOnly("net.momirealms:craft-engine-bukkit:0.0.63")
}
```

View File

@@ -4,11 +4,11 @@ plugins {
}
repositories {
mavenCentral()
maven("https://jitpack.io/")
maven("https://repo.momirealms.net/releases/")
maven("https://libraries.minecraft.net/")
maven("https://repo.papermc.io/repository/maven-public/")
mavenCentral()
}
dependencies {
@@ -60,6 +60,8 @@ dependencies {
compileOnly("org.ahocorasick:ahocorasick:${rootProject.properties["ahocorasick_version"]}")
// authlib
compileOnly("com.mojang:authlib:${rootProject.properties["authlib_version"]}")
// concurrentutil
compileOnly("ca.spottedleaf:concurrentutil:${rootProject.properties["concurrent_util_version"]}")
}
java {
@@ -99,6 +101,10 @@ tasks {
relocate("com.google.common.jimfs", "net.momirealms.craftengine.libraries.jimfs")
relocate("org.apache.commons", "net.momirealms.craftengine.libraries.commons")
relocate("io.leangen.geantyref", "net.momirealms.craftengine.libraries.geantyref")
relocate("io.netty.handler.codec.http", "net.momirealms.craftengine.libraries.netty.handler.codec.http")
relocate("io.netty.handler.codec.rtsp", "net.momirealms.craftengine.libraries.netty.handler.codec.rtsp")
relocate("io.netty.handler.codec.spdy", "net.momirealms.craftengine.libraries.netty.handler.codec.spdy")
relocate("io.netty.handler.codec.http2", "net.momirealms.craftengine.libraries.netty.handler.codec.http2")
}
}

View File

@@ -14,6 +14,8 @@ repositories {
maven("https://nexus.neetgames.com/repository/maven-releases/") // mcmmo
maven("https://repo.dmulloy2.net/repository/public/") // mcmmo required
maven("https://repo.auxilor.io/repository/maven-public/") // eco
maven("https://repo.hiusers.com/releases") // zaphkiel
maven("https://jitpack.io") // sxitem slimefun
}
dependencies {
@@ -67,6 +69,16 @@ dependencies {
compileOnly("com.willfp:libreforge:4.58.1")
// AureliumSkills
compileOnly("com.github.Archy-X:AureliumSkills:Beta1.3.21")
// Zaphkiel
compileOnly("ink.ptms:ZaphkielAPI:2.1.0")
// WorldGuard
compileOnly(files("${rootProject.rootDir}/libs/worldguard-bukkit-7.0.14-dist.jar"))
// HeadDatabase
compileOnly("com.arcaniax:HeadDatabase-API:1.3.2")
// SXItem
compileOnly("com.github.Saukiya:SX-Item:4.4.6")
// Slimefun
compileOnly("io.github.Slimefun:Slimefun4:RC-32")
}
java {

View File

@@ -1,19 +1,18 @@
package net.momirealms.craftengine.bukkit.compatibility;
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
import net.momirealms.craftengine.bukkit.compatibility.item.CustomFishingSource;
import net.momirealms.craftengine.bukkit.compatibility.item.MMOItemsSource;
import net.momirealms.craftengine.bukkit.compatibility.item.MythicMobsSource;
import net.momirealms.craftengine.bukkit.compatibility.item.NeigeItemsSource;
import net.momirealms.craftengine.bukkit.compatibility.item.*;
import net.momirealms.craftengine.bukkit.compatibility.legacy.slimeworld.LegacySlimeFormatStorageAdaptor;
import net.momirealms.craftengine.bukkit.compatibility.leveler.*;
import net.momirealms.craftengine.bukkit.compatibility.model.bettermodel.BetterModelModel;
import net.momirealms.craftengine.bukkit.compatibility.model.bettermodel.BetterModelUtils;
import net.momirealms.craftengine.bukkit.compatibility.model.modelengine.ModelEngineModel;
import net.momirealms.craftengine.bukkit.compatibility.model.modelengine.ModelEngineUtils;
import net.momirealms.craftengine.bukkit.compatibility.mythicmobs.MythicItemDropListener;
import net.momirealms.craftengine.bukkit.compatibility.mythicmobs.MythicSkillHelper;
import net.momirealms.craftengine.bukkit.compatibility.papi.PlaceholderAPIUtils;
import net.momirealms.craftengine.bukkit.compatibility.permission.LuckPermsEventListeners;
import net.momirealms.craftengine.bukkit.compatibility.region.WorldGuardRegionCondition;
import net.momirealms.craftengine.bukkit.compatibility.skript.SkriptHook;
import net.momirealms.craftengine.bukkit.compatibility.slimeworld.SlimeFormatStorageAdaptor;
import net.momirealms.craftengine.bukkit.compatibility.viaversion.ViaVersionUtils;
@@ -23,9 +22,12 @@ import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.core.entity.furniture.ExternalModel;
import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.loot.LootConditions;
import net.momirealms.craftengine.core.plugin.compatibility.CompatibilityManager;
import net.momirealms.craftengine.core.plugin.compatibility.LevelerProvider;
import net.momirealms.craftengine.core.plugin.compatibility.ModelProvider;
import net.momirealms.craftengine.core.plugin.context.condition.AlwaysFalseCondition;
import net.momirealms.craftengine.core.plugin.context.event.EventConditions;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.VersionHelper;
import net.momirealms.craftengine.core.world.WorldManager;
@@ -120,6 +122,23 @@ public class BukkitCompatibilityManager implements CompatibilityManager {
new MythicItemDropListener(this.plugin);
logHook("MythicMobs");
}
Key worldGuardRegion = Key.of("worldguard:region");
if (this.isPluginEnabled("WorldGuard")) {
EventConditions.register(worldGuardRegion, new WorldGuardRegionCondition.FactoryImpl<>());
LootConditions.register(worldGuardRegion, new WorldGuardRegionCondition.FactoryImpl<>());
logHook("WorldGuard");
} else {
EventConditions.register(worldGuardRegion, new AlwaysFalseCondition.FactoryImpl<>());
LootConditions.register(worldGuardRegion, new AlwaysFalseCondition.FactoryImpl<>());
}
if (this.isPluginEnabled("BetterModel")) {
BetterModelUtils.registerConstantBlockEntityRender();
logHook("BetterModel");
}
if (this.isPluginEnabled("ModelEngine")) {
ModelEngineUtils.registerConstantBlockEntityRender();
logHook("ModelEngine");
}
}
@Override
@@ -249,6 +268,22 @@ public class BukkitCompatibilityManager implements CompatibilityManager {
itemManager.registerExternalItemSource(new CustomFishingSource());
logHook("CustomFishing");
}
if (this.isPluginEnabled("Zaphkiel")) {
itemManager.registerExternalItemSource(new ZaphkielSource());
logHook("Zaphkiel");
}
if (this.isPluginEnabled("HeadDatabase")) {
itemManager.registerExternalItemSource(new HeadDatabaseSource());
logHook("HeadDatabase");
}
if (this.isPluginEnabled("SX-Item")) {
itemManager.registerExternalItemSource(new SXItemSource());
logHook("SX-Item");
}
if (this.isPluginEnabled("Slimefun")) {
itemManager.registerExternalItemSource(new SlimefunSource());
logHook("Slimefun");
}
}
private Plugin getPlugin(String name) {

View File

@@ -0,0 +1,33 @@
package net.momirealms.craftengine.bukkit.compatibility.item;
import me.arcaniax.hdb.api.HeadDatabaseAPI;
import net.momirealms.craftengine.core.item.ExternalItemSource;
import net.momirealms.craftengine.core.item.ItemBuildContext;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;
public class HeadDatabaseSource implements ExternalItemSource<ItemStack> {
private HeadDatabaseAPI api;
@Override
public String plugin() {
return "headdatabase";
}
@Nullable
@Override
public ItemStack build(String id, ItemBuildContext context) {
if (api == null) {
api = new HeadDatabaseAPI();
}
return api.getItemHead(id);
}
@Override
public String id(ItemStack item) {
if (api == null) {
api = new HeadDatabaseAPI();
}
return api.getItemID(item);
}
}

View File

@@ -1,11 +1,18 @@
package net.momirealms.craftengine.bukkit.compatibility.item;
import io.lumine.mythic.api.adapters.AbstractPlayer;
import io.lumine.mythic.api.skills.SkillCaster;
import io.lumine.mythic.bukkit.BukkitAdapter;
import io.lumine.mythic.bukkit.MythicBukkit;
import io.lumine.mythic.core.drops.DropMetadataImpl;
import net.momirealms.craftengine.core.item.ExternalItemSource;
import net.momirealms.craftengine.core.item.ItemBuildContext;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;
import java.util.Optional;
public class MythicMobsSource implements ExternalItemSource<ItemStack> {
private MythicBukkit mythicBukkit;
@@ -20,7 +27,18 @@ public class MythicMobsSource implements ExternalItemSource<ItemStack> {
if (mythicBukkit == null || mythicBukkit.isClosed()) {
this.mythicBukkit = MythicBukkit.inst();
}
return mythicBukkit.getItemManager().getItemStack(id);
return Optional.ofNullable(context.player())
.map(p -> (Player) p.platformPlayer())
.map(p -> {
AbstractPlayer target = BukkitAdapter.adapt(p);
SkillCaster caster = mythicBukkit.getSkillManager().getCaster(target);
DropMetadataImpl meta = new DropMetadataImpl(caster, target);
return mythicBukkit.getItemManager().getItem(id)
.map(i -> i.generateItemStack(meta, 1))
.map(BukkitAdapter::adapt)
.orElse(null);
})
.orElseGet(() -> mythicBukkit.getItemManager().getItemStack(id));
}
@Override

View File

@@ -0,0 +1,29 @@
package net.momirealms.craftengine.bukkit.compatibility.item;
import github.saukiya.sxitem.SXItem;
import net.momirealms.craftengine.core.item.ExternalItemSource;
import net.momirealms.craftengine.core.item.ItemBuildContext;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;
import java.util.Optional;
public class SXItemSource implements ExternalItemSource<ItemStack> {
@Override
public String plugin() {
return "sx-item";
}
@Nullable
@Override
public ItemStack build(String id, ItemBuildContext context) {
return SXItem.getItemManager().getItem(id, Optional.ofNullable(context.player()).map(p -> (Player) p.platformPlayer()).orElse(null));
}
@Override
public String id(ItemStack item) {
return SXItem.getItemManager().getItemKey(item);
}
}

View File

@@ -0,0 +1,28 @@
package net.momirealms.craftengine.bukkit.compatibility.item;
import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem;
import net.momirealms.craftengine.core.item.ExternalItemSource;
import net.momirealms.craftengine.core.item.ItemBuildContext;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;
import java.util.Optional;
public class SlimefunSource implements ExternalItemSource<ItemStack> {
@Override
public String plugin() {
return "slimefun";
}
@Nullable
@Override
public ItemStack build(String id, ItemBuildContext context) {
return Optional.ofNullable(SlimefunItem.getById(id)).map(SlimefunItem::getItem).orElse(null);
}
@Override
public String id(ItemStack item) {
return Optional.ofNullable(SlimefunItem.getByItem(item)).map(SlimefunItem::getId).orElse(null);
}
}

View File

@@ -0,0 +1,33 @@
package net.momirealms.craftengine.bukkit.compatibility.item;
import ink.ptms.zaphkiel.Zaphkiel;
import net.momirealms.craftengine.core.item.ExternalItemSource;
import net.momirealms.craftengine.core.item.ItemBuildContext;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import javax.annotation.Nullable;
import java.util.Optional;
/**
* @author iiabc
* @since 2025/8/30 09:39
*/
public class ZaphkielSource implements ExternalItemSource<ItemStack> {
@Override
public String plugin() {
return "zaphkiel";
}
@Override
public @Nullable ItemStack build(String id, ItemBuildContext context) {
Player player = Optional.ofNullable(context.player()).map(it -> (Player) it.platformPlayer()).orElse(null);
return Zaphkiel.INSTANCE.api().getItemManager().generateItemStack(id, player);
}
@Override
public String id(ItemStack item) {
return Zaphkiel.INSTANCE.api().getItemHandler().getItemId(item);
}
}

View File

@@ -0,0 +1,62 @@
package net.momirealms.craftengine.bukkit.compatibility.model.bettermodel;
import kr.toxicity.model.api.BetterModel;
import kr.toxicity.model.api.data.renderer.ModelRenderer;
import kr.toxicity.model.api.tracker.DummyTracker;
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElement;
import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.World;
import org.bukkit.Location;
import org.joml.Vector3f;
public class BetterModelBlockEntityElement implements BlockEntityElement {
private DummyTracker dummyTracker;
private final Location location;
private final BetterModelBlockEntityElementConfig config;
public BetterModelBlockEntityElement(World world, BlockPos pos, BetterModelBlockEntityElementConfig config) {
this.config = config;
Vector3f position = config.position();
this.location = new Location((org.bukkit.World) world.platformWorld(), pos.x() + position.x, pos.y() + position.y, pos.z() + position.z, config.yaw(), config.pitch());
this.dummyTracker = createDummyTracker();
}
private DummyTracker createDummyTracker() {
ModelRenderer modelRenderer = BetterModel.plugin().modelManager().renderer(this.config.model());
if (modelRenderer == null) {
return null;
} else {
return modelRenderer.create(this.location);
}
}
@Override
public void hide(Player player) {
if (this.dummyTracker != null) {
this.dummyTracker.remove((org.bukkit.entity.Player) player.platformPlayer());
}
}
@Override
public void show(Player player) {
if (this.dummyTracker != null) {
this.dummyTracker.spawn((org.bukkit.entity.Player) player.platformPlayer());
}
}
@Override
public void deactivate() {
if (this.dummyTracker != null) {
this.dummyTracker.close();
this.dummyTracker = null;
}
}
@Override
public void activate() {
if (this.dummyTracker == null) {
this.dummyTracker = createDummyTracker();
}
}
}

View File

@@ -0,0 +1,61 @@
package net.momirealms.craftengine.bukkit.compatibility.model.bettermodel;
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElement;
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfig;
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfigFactory;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.World;
import org.joml.Vector3f;
import java.util.Map;
public class BetterModelBlockEntityElementConfig implements BlockEntityElementConfig<BetterModelBlockEntityElement> {
private final Vector3f position;
private final float yaw;
private final float pitch;
private final String model;
public BetterModelBlockEntityElementConfig(String model, Vector3f position, float yaw, float pitch) {
this.pitch = pitch;
this.position = position;
this.yaw = yaw;
this.model = model;
}
public String model() {
return model;
}
public float pitch() {
return pitch;
}
public Vector3f position() {
return position;
}
public float yaw() {
return yaw;
}
@Override
public BetterModelBlockEntityElement create(World world, BlockPos pos) {
return new BetterModelBlockEntityElement(world, pos, this);
}
public static class Factory implements BlockEntityElementConfigFactory {
@SuppressWarnings("unchecked")
@Override
public <E extends BlockEntityElement> BlockEntityElementConfig<E> create(Map<String, Object> arguments) {
String model = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("model"), "warning.config.block.state.entity_renderer.better_model.missing_model");
return (BlockEntityElementConfig<E>) new BetterModelBlockEntityElementConfig(
model,
ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0.5f), "position"),
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("yaw", 0f), "yaw"),
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("pitch", 0f), "pitch")
);
}
}
}

View File

@@ -2,6 +2,8 @@ package net.momirealms.craftengine.bukkit.compatibility.model.bettermodel;
import kr.toxicity.model.api.BetterModel;
import kr.toxicity.model.api.data.renderer.ModelRenderer;
import net.momirealms.craftengine.bukkit.block.entity.renderer.element.BukkitBlockEntityElementConfigs;
import net.momirealms.craftengine.core.util.Key;
import org.bukkit.entity.Entity;
public class BetterModelUtils {
@@ -13,4 +15,8 @@ public class BetterModelUtils {
}
renderer.create(base);
}
public static void registerConstantBlockEntityRender() {
BukkitBlockEntityElementConfigs.register(Key.of("craftengine:better_model"), new BetterModelBlockEntityElementConfig.Factory());
}
}

View File

@@ -0,0 +1,69 @@
package net.momirealms.craftengine.bukkit.compatibility.model.modelengine;
import com.ticxo.modelengine.api.ModelEngineAPI;
import com.ticxo.modelengine.api.entity.Dummy;
import com.ticxo.modelengine.api.model.ActiveModel;
import com.ticxo.modelengine.api.model.ModeledEntity;
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElement;
import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.World;
import org.bukkit.Location;
import org.joml.Vector3f;
// TODO not tested yet
public class ModelEngineBlockEntityElement implements BlockEntityElement {
private Dummy<?> dummy;
private final Location location;
private final ModelEngineBlockEntityElementConfig config;
public ModelEngineBlockEntityElement(World world, BlockPos pos, ModelEngineBlockEntityElementConfig config) {
this.config = config;
Vector3f position = config.position();
this.location = new Location((org.bukkit.World) world.platformWorld(), pos.x() + position.x, pos.y() + position.y, pos.z() + position.z, config.yaw(), config.pitch());
this.dummy = createDummy();
}
private Dummy<?> createDummy() {
ActiveModel activeModel = ModelEngineAPI.createActiveModel(config.model());
if (activeModel == null) {
return null;
} else {
Dummy<?> dummy = new Dummy<>();
dummy.setLocation(this.location);
dummy.setDetectingPlayers(false);
ModeledEntity modeledEntity = ModelEngineAPI.createModeledEntity(dummy);
modeledEntity.addModel(activeModel, false);
return dummy;
}
}
@Override
public void hide(Player player) {
if (this.dummy != null) {
this.dummy.setForceViewing((org.bukkit.entity.Player) player.platformPlayer(), true);
}
}
@Override
public void show(Player player) {
if (this.dummy != null) {
this.dummy.setForceHidden((org.bukkit.entity.Player) player.platformPlayer(), true);
}
}
@Override
public void deactivate() {
if (this.dummy != null) {
this.dummy.setRemoved(true);
this.dummy = null;
}
}
@Override
public void activate() {
if (this.dummy == null) {
this.dummy = createDummy();
}
}
}

View File

@@ -0,0 +1,61 @@
package net.momirealms.craftengine.bukkit.compatibility.model.modelengine;
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElement;
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfig;
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfigFactory;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.World;
import org.joml.Vector3f;
import java.util.Map;
public class ModelEngineBlockEntityElementConfig implements BlockEntityElementConfig<ModelEngineBlockEntityElement> {
private final Vector3f position;
private final float yaw;
private final float pitch;
private final String model;
public ModelEngineBlockEntityElementConfig(String model, Vector3f position, float yaw, float pitch) {
this.pitch = pitch;
this.position = position;
this.yaw = yaw;
this.model = model;
}
public String model() {
return model;
}
public float pitch() {
return pitch;
}
public Vector3f position() {
return position;
}
public float yaw() {
return yaw;
}
@Override
public ModelEngineBlockEntityElement create(World world, BlockPos pos) {
return new ModelEngineBlockEntityElement(world, pos, this);
}
public static class Factory implements BlockEntityElementConfigFactory {
@SuppressWarnings("unchecked")
@Override
public <E extends BlockEntityElement> BlockEntityElementConfig<E> create(Map<String, Object> arguments) {
String model = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("model"), "warning.config.block.state.entity_renderer.model_engine.missing_model");
return (BlockEntityElementConfig<E>) new ModelEngineBlockEntityElementConfig(
model,
ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0.5f), "position"),
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("yaw", 0f), "yaw"),
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("pitch", 0f), "pitch")
);
}
}
}

View File

@@ -3,6 +3,8 @@ package net.momirealms.craftengine.bukkit.compatibility.model.modelengine;
import com.ticxo.modelengine.api.ModelEngineAPI;
import com.ticxo.modelengine.api.model.ActiveModel;
import com.ticxo.modelengine.api.model.ModeledEntity;
import net.momirealms.craftengine.bukkit.block.entity.renderer.element.BukkitBlockEntityElementConfigs;
import net.momirealms.craftengine.core.util.Key;
import org.bukkit.entity.Entity;
public class ModelEngineUtils {
@@ -24,4 +26,8 @@ public class ModelEngineUtils {
}
return entityId;
}
public static void registerConstantBlockEntityRender() {
BukkitBlockEntityElementConfigs.register(Key.of("craftengine:model_engine"), new ModelEngineBlockEntityElementConfig.Factory());
}
}

View File

@@ -33,7 +33,7 @@ public class MythicItemDrop extends ItemDrop implements IItemDrop {
@Override
public AbstractItemStack getDrop(DropMetadata dropMetadata, double amount) {
ItemBuildContext context = ItemBuildContext.EMPTY;
ItemBuildContext context = ItemBuildContext.empty();
SkillCaster caster = dropMetadata.getCaster();
if (caster != null && caster.getEntity() instanceof AbstractPlayer abstractPlayer) {
Entity bukkitEntity = abstractPlayer.getBukkitEntity();

View File

@@ -0,0 +1,98 @@
package net.momirealms.craftengine.bukkit.compatibility.region;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldguard.WorldGuard;
import com.sk89q.worldguard.protection.ApplicableRegionSet;
import com.sk89q.worldguard.protection.managers.RegionManager;
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
import net.momirealms.craftengine.core.plugin.context.Condition;
import net.momirealms.craftengine.core.plugin.context.Context;
import net.momirealms.craftengine.core.plugin.context.condition.ConditionFactory;
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.MiscUtils;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.world.WorldPosition;
import org.bukkit.World;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.BiFunction;
import java.util.function.Predicate;
public class WorldGuardRegionCondition<CTX extends Context> implements Condition<CTX> {
private static final Key TYPE = Key.of("worldguard:region");
private final MatchMode mode;
private final List<String> regions;
public WorldGuardRegionCondition(MatchMode mode, List<String> regions) {
this.mode = mode;
this.regions = regions;
}
@Override
public boolean test(CTX ctx) {
if (this.regions.isEmpty()) return false;
Optional<WorldPosition> optionalPos = ctx.getOptionalParameter(DirectContextParameters.POSITION);
if (optionalPos.isEmpty()) {
return false;
}
WorldPosition position = optionalPos.get();
RegionManager regionManager = WorldGuard.getInstance().getPlatform().getRegionContainer().get(BukkitAdapter.adapt((World) position.world().platformWorld()));
if (regionManager != null) {
ApplicableRegionSet set = regionManager.getApplicableRegions(BlockVector3.at(position.x(), position.y(), position.z()));
List<String> regionsAtThisPos = new ArrayList<>(set.size());
for (ProtectedRegion region : set) {
String id = region.getId();
regionsAtThisPos.add(id);
}
Predicate<String> predicate = regionsAtThisPos::contains;
return this.mode.matcher.apply(predicate, this.regions);
}
return false;
}
@Override
public Key type() {
return TYPE;
}
public enum MatchMode {
ANY((p, regions) -> {
for (String region : regions) {
if (p.test(region)) {
return true;
}
}
return false;
}),
ALL((p, regions) -> {
for (String region : regions) {
if (!p.test(region)) {
return false;
}
}
return true;
});
private final BiFunction<Predicate<String>, List<String>, Boolean> matcher;
MatchMode(BiFunction<Predicate<String>, List<String>, Boolean> matcher) {
this.matcher = matcher;
}
}
public static class FactoryImpl<CTX extends Context> implements ConditionFactory<CTX> {
@Override
public Condition<CTX> create(Map<String, Object> arguments) {
int mode = ResourceConfigUtils.getAsInt(arguments.getOrDefault("mode", 1), "mode") - 1;
MatchMode matchMode = MatchMode.values()[mode];
List<String> regions = MiscUtils.getAsStringList(arguments.get("regions"));
return new WorldGuardRegionCondition<>(matchMode, regions);
}
}
}

View File

@@ -33,7 +33,6 @@ public class ExprCustomItem extends SimpleExpression<ItemType> {
private Expression<?> itemIds;
@Override
@SuppressWarnings("unchecked")
public boolean init(Expression<?>[] exprs, int matchedPattern, Kleenean isDelayed, SkriptParser.ParseResult parseResult) {
itemIds = exprs[0];
return true;
@@ -49,7 +48,7 @@ public class ExprCustomItem extends SimpleExpression<ItemType> {
if (object instanceof String string) {
CustomItem<ItemStack> customItem = CraftEngineItems.byId(Key.of(string));
if (customItem != null) {
ItemType itemType = new ItemType(customItem.buildItemStack(ItemBuildContext.EMPTY));
ItemType itemType = new ItemType(customItem.buildItemStack(ItemBuildContext.empty()));
items.add(itemType);
}
}

View File

@@ -90,7 +90,7 @@ public class WorldEditBlockRegister {
if (state == null) return null;
try {
String id = state.customBlockState().handle().toString();
String id = state.customBlockState().literalObject().toString();
int first = id.indexOf('{');
int last = id.indexOf('}');
if (first != -1 && last != -1 && last > first) {

View File

@@ -21,6 +21,9 @@ dependencies {
implementation(project(":bukkit:compatibility:legacy"))
implementation(project(":common-files"))
// concurrentutil
implementation(files("${rootProject.rootDir}/libs/concurrentutil-${rootProject.properties["concurrent_util_version"]}.jar"))
implementation("net.momirealms:sparrow-util:${rootProject.properties["sparrow_util_version"]}")
implementation("net.momirealms:antigrieflib:${rootProject.properties["anti_grief_version"]}")
implementation("net.momirealms:craft-engine-nms-helper:${rootProject.properties["nms_helper_version"]}")
@@ -47,7 +50,7 @@ bukkit {
name = "CraftEngine"
apiVersion = "1.20"
authors = listOf("XiaoMoMi")
contributors = listOf("jhqwqmc", "iqtesterrr", "WhiteProject1", "Catnies", "xiaozhangup", "TamashiiMon")
contributors = listOf("https://github.com/Xiao-MoMi/craft-engine/graphs/contributors")
softDepend = listOf("PlaceholderAPI", "WorldEdit", "FastAsyncWorldEdit", "Skript")
foliaSupported = true
}
@@ -77,5 +80,10 @@ tasks {
relocate("com.google.common.jimfs", "net.momirealms.craftengine.libraries.jimfs")
relocate("org.apache.commons", "net.momirealms.craftengine.libraries.commons")
relocate("io.leangen.geantyref", "net.momirealms.craftengine.libraries.geantyref")
relocate("ca.spottedleaf.concurrentutil", "net.momirealms.craftengine.libraries.concurrentutil")
relocate("io.netty.handler.codec.http", "net.momirealms.craftengine.libraries.netty.handler.codec.http")
relocate("io.netty.handler.codec.rtsp", "net.momirealms.craftengine.libraries.netty.handler.codec.rtsp")
relocate("io.netty.handler.codec.spdy", "net.momirealms.craftengine.libraries.netty.handler.codec.spdy")
relocate("io.netty.handler.codec.http2", "net.momirealms.craftengine.libraries.netty.handler.codec.http2")
}
}

View File

@@ -23,6 +23,9 @@ dependencies {
implementation(project(":bukkit:compatibility:legacy"))
implementation(project(":common-files"))
// concurrentutil
implementation(files("${rootProject.rootDir}/libs/concurrentutil-${rootProject.properties["concurrent_util_version"]}.jar"))
implementation("net.momirealms:sparrow-util:${rootProject.properties["sparrow_util_version"]}")
implementation("net.momirealms:antigrieflib:${rootProject.properties["anti_grief_version"]}")
implementation("net.momirealms:craft-engine-nms-helper-mojmap:${rootProject.properties["nms_helper_version"]}")
@@ -50,7 +53,7 @@ paper {
name = "CraftEngine"
apiVersion = "1.20"
authors = listOf("XiaoMoMi")
contributors = listOf("jhqwqmc", "iqtesterrr", "WhiteProject1", "Catnies", "xiaozhangup", "TamashiiMon")
contributors = listOf("https://github.com/Xiao-MoMi/craft-engine/graphs/contributors")
foliaSupported = true
serverDependencies {
register("PlaceholderAPI") {
@@ -82,6 +85,10 @@ paper {
register("MMOItems") { required = false }
register("MythicMobs") { required = false }
register("CustomFishing") { required = false }
register("Zaphkiel") { required = false }
register("HeadDatabase") { required = false }
register("SX-Item") { required = false }
register("Slimefun") { required = false }
// leveler
register("AuraSkills") { required = false }
@@ -149,5 +156,10 @@ tasks {
relocate("com.google.common.jimfs", "net.momirealms.craftengine.libraries.jimfs")
relocate("org.apache.commons", "net.momirealms.craftengine.libraries.commons")
relocate("io.leangen.geantyref", "net.momirealms.craftengine.libraries.geantyref")
relocate("ca.spottedleaf.concurrentutil", "net.momirealms.craftengine.libraries.concurrentutil")
relocate("io.netty.handler.codec.http", "net.momirealms.craftengine.libraries.netty.handler.codec.http")
relocate("io.netty.handler.codec.rtsp", "net.momirealms.craftengine.libraries.netty.handler.codec.rtsp")
relocate("io.netty.handler.codec.spdy", "net.momirealms.craftengine.libraries.netty.handler.codec.spdy")
relocate("io.netty.handler.codec.http2", "net.momirealms.craftengine.libraries.netty.handler.codec.http2")
}
}

View File

@@ -5,11 +5,11 @@ import io.papermc.paper.plugin.bootstrap.PluginBootstrap;
import io.papermc.paper.plugin.bootstrap.PluginProviderContext;
import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents;
import net.momirealms.craftengine.bukkit.plugin.agent.RuntimePatcher;
import net.momirealms.craftengine.bukkit.plugin.classpath.PaperClassPathAppender;
import net.momirealms.craftengine.bukkit.plugin.classpath.BukkitClassPathAppender;
import net.momirealms.craftengine.bukkit.plugin.classpath.PaperPluginClassPathAppender;
import net.momirealms.craftengine.core.plugin.logger.PluginLogger;
import net.momirealms.craftengine.core.plugin.logger.Slf4jPluginLogger;
import net.momirealms.craftengine.core.util.ReflectionUtils;
import net.momirealms.craftengine.core.util.VersionHelper;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
@@ -39,14 +39,24 @@ public class PaperCraftEngineBootstrap implements PluginBootstrap {
} catch (ReflectiveOperationException e) {
throw new RuntimeException("Failed to getLogger", e);
}
this.plugin = new BukkitCraftEngine(
logger,
context.getDataDirectory(),
new PaperClassPathAppender(this.getClass().getClassLoader())
);
try {
this.plugin = new BukkitCraftEngine(
logger,
context.getDataDirectory(),
new BukkitClassPathAppender(),
new PaperPluginClassPathAppender(this.getClass().getClassLoader())
);
} catch (UnsupportedOperationException e) {
this.plugin = new BukkitCraftEngine(
logger,
context.getDataDirectory(),
new PaperPluginClassPathAppender(this.getClass().getClassLoader()),
new PaperPluginClassPathAppender(this.getClass().getClassLoader())
);
}
this.plugin.applyDependencies();
this.plugin.setUpConfig();
if (VersionHelper.isOrAbove1_21_4()) {
if (isDatapackDiscoveryAvailable()) {
new ModernEventHandler(context, this.plugin).register();
} else {
try {
@@ -58,6 +68,16 @@ public class PaperCraftEngineBootstrap implements PluginBootstrap {
}
}
private static boolean isDatapackDiscoveryAvailable() {
try {
Class<?> eventsClass = Class.forName("io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents");
eventsClass.getField("DATAPACK_DISCOVERY");
return true;
} catch (ClassNotFoundException | NoSuchFieldException e) {
return false;
}
}
@Override
public @NotNull JavaPlugin createPlugin(@NotNull PluginProviderContext context) {
return new PaperCraftEnginePlugin(this);

View File

@@ -1,17 +1,25 @@
package net.momirealms.craftengine.bukkit.advancement;
import com.google.gson.JsonElement;
import net.kyori.adventure.text.Component;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections;
import net.momirealms.craftengine.bukkit.util.ComponentUtils;
import net.momirealms.craftengine.bukkit.util.KeyUtils;
import net.momirealms.craftengine.core.advancement.AbstractAdvancementManager;
import net.momirealms.craftengine.core.advancement.AdvancementType;
import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.pack.LoadingSequence;
import net.momirealms.craftengine.core.pack.Pack;
import net.momirealms.craftengine.core.plugin.config.ConfigParser;
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.VersionHelper;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import java.util.*;
public class BukkitAdvancementManager extends AbstractAdvancementManager {
private final BukkitCraftEngine plugin;
@@ -33,6 +41,70 @@ public class BukkitAdvancementManager extends AbstractAdvancementManager {
return this.advancementParser;
}
@Override
public void sendToast(Player player, Item<?> icon, Component message, AdvancementType type) {
try {
Object displayInfo = CoreReflections.constructor$DisplayInfo.newInstance(
icon.getLiteralObject(),
ComponentUtils.adventureToMinecraft(message), // title
CoreReflections.instance$Component$empty, // description
VersionHelper.isOrAbove1_20_3() ? Optional.empty() : null, // background
CoreReflections.instance$AdvancementType$values[type.ordinal()],
true, // show toast
false, // announce to chat
true // hidden
);
if (VersionHelper.isOrAbove1_20_2()) {
displayInfo = Optional.of(displayInfo);
}
Object resourceLocation = KeyUtils.toResourceLocation(Key.of("craftengine", "toast"));
Object criterion = VersionHelper.isOrAbove1_20_2() ?
CoreReflections.constructor$Criterion.newInstance(CoreReflections.constructor$ImpossibleTrigger.newInstance(), CoreReflections.constructor$ImpossibleTrigger$TriggerInstance.newInstance()) :
CoreReflections.constructor$Criterion.newInstance(CoreReflections.constructor$ImpossibleTrigger$TriggerInstance.newInstance());
Map<String, Object> criteria = Map.of("impossible", criterion);
Object advancementProgress = CoreReflections.constructor$AdvancementProgress.newInstance();
Object advancement;
if (VersionHelper.isOrAbove1_20_2()) {
Object advancementRequirements = VersionHelper.isOrAbove1_20_3() ?
CoreReflections.constructor$AdvancementRequirements.newInstance(List.of(List.of("impossible"))) :
CoreReflections.constructor$AdvancementRequirements.newInstance((Object) new String[][] {{"impossible"}});
advancement = CoreReflections.constructor$Advancement.newInstance(
Optional.empty(),
displayInfo,
CoreReflections.instance$AdvancementRewards$EMPTY,
criteria,
advancementRequirements,
false
);
CoreReflections.method$AdvancementProgress$update.invoke(advancementProgress, advancementRequirements);
advancement = CoreReflections.constructor$AdvancementHolder.newInstance(resourceLocation, advancement);
} else {
advancement = CoreReflections.constructor$Advancement.newInstance(
resourceLocation,
null, // parent
displayInfo,
CoreReflections.instance$AdvancementRewards$EMPTY,
criteria,
new String[][] {{"impossible"}},
false
);
CoreReflections.method$AdvancementProgress$update.invoke(advancementProgress, criteria, new String[][] {{"impossible"}});
}
CoreReflections.method$AdvancementProgress$grantProgress.invoke(advancementProgress, "impossible");
Map<Object, Object> advancementsToGrant = new HashMap<>();
advancementsToGrant.put(resourceLocation, advancementProgress);
Object grantPacket = VersionHelper.isOrAbove1_21_5() ?
NetworkReflections.constructor$ClientboundUpdateAdvancementsPacket.newInstance(false, Arrays.asList(advancement), new HashSet<>(), advancementsToGrant, true) :
NetworkReflections.constructor$ClientboundUpdateAdvancementsPacket.newInstance(false, Arrays.asList(advancement), new HashSet<>(), advancementsToGrant);
Object removePacket = VersionHelper.isOrAbove1_21_5() ?
NetworkReflections.constructor$ClientboundUpdateAdvancementsPacket.newInstance(false, new ArrayList<>(), new HashSet<>() {{add(resourceLocation);}}, new HashMap<>(), true) :
NetworkReflections.constructor$ClientboundUpdateAdvancementsPacket.newInstance(false, new ArrayList<>(), new HashSet<>() {{add(resourceLocation);}}, new HashMap<>());
player.sendPackets(List.of(grantPacket, removePacket), false);
} catch (ReflectiveOperationException e) {
this.plugin.logger().warn("Failed to send toast for player " + player.name(), e);
}
}
public class AdvancementParser implements ConfigParser {
public static final String[] CONFIG_SECTION_NAME = new String[] {"advancements", "advancement"};

View File

@@ -4,7 +4,7 @@ import net.momirealms.craftengine.bukkit.entity.BukkitEntity;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
import net.momirealms.craftengine.bukkit.util.LocationUtils;
import net.momirealms.craftengine.bukkit.world.BukkitBlockInWorld;
import net.momirealms.craftengine.bukkit.world.BukkitExistingBlock;
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
import net.momirealms.craftengine.core.world.WorldPosition;
import org.bukkit.Location;
@@ -29,8 +29,8 @@ public final class BukkitAdaptors {
return new BukkitEntity(entity);
}
public static BukkitBlockInWorld adapt(final Block block) {
return new BukkitBlockInWorld(block);
public static BukkitExistingBlock adapt(final Block block) {
return new BukkitExistingBlock(block);
}
public static Location toLocation(WorldPosition position) {

View File

@@ -27,10 +27,28 @@ import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
public final class CraftEngineBlocks {
private CraftEngineBlocks() {}
/**
*
* Returns an unmodifiable map of all currently loaded custom blocks.
* The map keys represent unique identifiers, and the values are the corresponding CustomBlock instances.
*
* <p><strong>Important:</strong> Do not attempt to access this method during the onEnable phase
* as it will be empty. Instead, listen for the {@code CraftEngineReloadEvent} and use this method
* after the event is fired to obtain the complete block list.
*
* @return a non-null map containing all loaded custom blocks
*/
@NotNull
public static Map<Key, CustomBlock> loadedBlocks() {
return BukkitBlockManager.instance().loadedBlocks();
}
/**
* Gets a custom block by ID
*
@@ -126,7 +144,7 @@ public final class CraftEngineBlocks {
boolean success;
Object worldServer = FastNMS.INSTANCE.field$CraftWorld$ServerLevel(location.getWorld());
Object blockPos = FastNMS.INSTANCE.constructor$BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ());
Object blockState = block.customBlockState().handle();
Object blockState = block.customBlockState().literalObject();
Object oldBlockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(worldServer, blockPos);
success = FastNMS.INSTANCE.method$LevelWriter$setBlock(worldServer, blockPos, blockState, option.flags());
if (success) {
@@ -188,9 +206,10 @@ public final class CraftEngineBlocks {
if (dropLoot) {
ContextHolder.Builder builder = new ContextHolder.Builder()
.withParameter(DirectContextParameters.POSITION, position);
BukkitServerPlayer serverPlayer = BukkitCraftEngine.instance().adapt(player);
BukkitServerPlayer serverPlayer = null;
if (player != null) {
builder.withParameter(DirectContextParameters.PLAYER, serverPlayer);
serverPlayer = BukkitCraftEngine.instance().adapt(player);
builder.withOptionalParameter(DirectContextParameters.PLAYER, serverPlayer);
}
for (Item<?> item : state.getDrops(builder, world, serverPlayer)) {
world.dropItemNaturally(position, item);
@@ -249,6 +268,6 @@ public final class CraftEngineBlocks {
*/
@NotNull
public static BlockData getBukkitBlockData(@NotNull ImmutableBlockState blockState) {
return BlockStateUtils.fromBlockData(blockState.customBlockState().handle());
return BlockStateUtils.fromBlockData(blockState.customBlockState().literalObject());
}
}

View File

@@ -27,11 +27,27 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Map;
public final class CraftEngineFurniture {
private CraftEngineFurniture() {}
/**
* Returns an unmodifiable map of all currently loaded custom furniture.
* The map keys represent unique identifiers, and the values are the corresponding CustomFurniture instances.
*
* <p><strong>Important:</strong> Do not attempt to access this method during the onEnable phase
* as it will be empty. Instead, listen for the {@code CraftEngineReloadEvent} and use this method
* after the event is fired to obtain the complete furniture list.
*
* @return a non-null map containing all loaded custom furniture
*/
@NotNull
public static Map<Key, CustomFurniture> loadedFurniture() {
return BukkitFurnitureManager.instance().loadedFurniture();
}
/**
* Gets custom furniture by ID
*

View File

@@ -8,10 +8,28 @@ import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
public final class CraftEngineItems {
private CraftEngineItems() {}
/**
* Returns an unmodifiable map of all currently loaded custom items.
* The map keys represent unique identifiers, and the values are the corresponding CustomItem instances.
*
* <p><strong>Important:</strong> Do not attempt to access this method during the onEnable phase
* as it will be empty. Instead, listen for the {@code CraftEngineReloadEvent} and use this method
* after the event is fired to obtain the complete item list.
*
* @return a non-null map containing all loaded custom items
* @throws IllegalStateException if the BukkitItemManager instance is not available
*/
@NotNull
public static Map<Key, CustomItem<ItemStack>> loadedItems() {
return BukkitItemManager.instance().loadedItems();
}
/**
* Gets a custom item by ID
*

View File

@@ -0,0 +1,67 @@
package net.momirealms.craftengine.bukkit.api.event;
import net.momirealms.craftengine.core.pack.PackCacheData;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
import java.nio.file.Files;
import java.nio.file.Path;
/**
* This event is triggered when a user executes the "/ce reload pack" command.
* <p>
* The event initiates a process that caches all resource content into a virtual file system
* to ensure optimal build performance. To add your resource pack through this event,
* you must use the {@link #registerExternalResourcePack(Path)} method every time this event is called.
* </p>
* <p>
* Important: The caching system will not update your resource pack if its file size or
* last modification time remains unchanged between reloads. Ensure these attributes change
* if you need the cache to recognize updates.
* </p>
*/
public class AsyncResourcePackCacheEvent extends Event {
private static final HandlerList HANDLER_LIST = new HandlerList();
private final PackCacheData cacheData;
public AsyncResourcePackCacheEvent(@NotNull PackCacheData cacheData) {
super(true);
this.cacheData = cacheData;
}
@NotNull
public PackCacheData cacheData() {
return this.cacheData;
}
@NotNull
public static HandlerList getHandlerList() {
return HANDLER_LIST;
}
@NotNull
public HandlerList getHandlers() {
return getHandlerList();
}
/**
* Adds an external resource pack to the cache.
* <p>
* This method accepts either a .zip file or a directory path representing a resource pack.
* The resource pack will be added to the appropriate cache collection based on its type.
* </p>
*
* @param path the file system path to the resource pack. Must be either a .zip file or a directory.
* @throws IllegalArgumentException if the provided path is neither a .zip file nor a directory.
*/
public void registerExternalResourcePack(@NotNull final Path path) {
if (Files.isRegularFile(path) && path.getFileName().endsWith(".zip")) {
this.cacheData.externalZips().add(path);
} else if (Files.isDirectory(path)) {
this.cacheData.externalFolders().add(path);
} else {
throw new IllegalArgumentException("Illegal resource pack path: " + path);
}
}
}

View File

@@ -8,9 +8,17 @@ import org.jetbrains.annotations.NotNull;
public class CraftEngineReloadEvent extends Event {
private static final HandlerList HANDLER_LIST = new HandlerList();
private final BukkitCraftEngine plugin;
private static boolean firstFlag = true;
private final boolean isFirstReload;
public CraftEngineReloadEvent(BukkitCraftEngine plugin) {
this.plugin = plugin;
this.isFirstReload = firstFlag;
firstFlag = false;
}
public boolean isFirstReload() {
return this.isFirstReload;
}
public BukkitCraftEngine plugin() {

View File

@@ -9,7 +9,7 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflect
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBlocks;
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
import net.momirealms.craftengine.bukkit.util.*;
import net.momirealms.craftengine.bukkit.world.BukkitBlockInWorld;
import net.momirealms.craftengine.bukkit.world.BukkitExistingBlock;
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.entity.player.InteractionHand;
@@ -82,7 +82,7 @@ public final class BlockEventListener implements Listener {
try {
Object soundType = CoreReflections.field$BlockBehaviour$soundType.get(ownerBlock);
Object placeSound = CoreReflections.field$SoundType$placeSound.get(soundType);
player.playSound(block.getLocation(), FastNMS.INSTANCE.field$SoundEvent$location(placeSound).toString(), SoundCategory.BLOCKS, 1f, 0.8f);
player.playSound(block.getLocation().add(0.5, 0.5, 0.5), FastNMS.INSTANCE.field$SoundEvent$location(placeSound).toString(), SoundCategory.BLOCKS, 1f, 0.8f);
} catch (ReflectiveOperationException e) {
this.plugin.logger().warn("Failed to get sound type", e);
}
@@ -99,7 +99,7 @@ public final class BlockEventListener implements Listener {
Object ownerBlock = BlockStateUtils.getBlockOwner(blockState);
Object soundType = CoreReflections.field$BlockBehaviour$soundType.get(ownerBlock);
Object placeSound = CoreReflections.field$SoundType$placeSound.get(soundType);
player.playSound(block.getLocation(), FastNMS.INSTANCE.field$SoundEvent$location(placeSound).toString(), SoundCategory.BLOCKS, 1f, 0.8f);
player.playSound(block.getLocation().add(0.5, 0.5, 0.5), FastNMS.INSTANCE.field$SoundEvent$location(placeSound).toString(), SoundCategory.BLOCKS, 1f, 0.8f);
} catch (ReflectiveOperationException e) {
this.plugin.logger().warn("Failed to get sound type", e);
}
@@ -124,7 +124,7 @@ public final class BlockEventListener implements Listener {
Cancellable cancellable = Cancellable.of(event::isCancelled, event::setCancelled);
optionalCustomItem.get().execute(
PlayerOptionalContext.of(serverPlayer, ContextHolder.builder()
.withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block))
.withParameter(DirectContextParameters.BLOCK, new BukkitExistingBlock(block))
.withParameter(DirectContextParameters.POSITION, position)
.withParameter(DirectContextParameters.PLAYER, serverPlayer)
.withParameter(DirectContextParameters.EVENT, cancellable)
@@ -156,7 +156,7 @@ public final class BlockEventListener implements Listener {
// execute functions
Cancellable cancellable = Cancellable.of(event::isCancelled, event::setCancelled);
PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder()
.withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block))
.withParameter(DirectContextParameters.BLOCK, new BukkitExistingBlock(block))
.withParameter(DirectContextParameters.CUSTOM_BLOCK_STATE, state)
.withParameter(DirectContextParameters.EVENT, cancellable)
.withParameter(DirectContextParameters.POSITION, position)
@@ -174,12 +174,15 @@ public final class BlockEventListener implements Listener {
// override vanilla block loots
if (player.getGameMode() != GameMode.CREATIVE) {
this.plugin.vanillaLootManager().getBlockLoot(stateId).ifPresent(it -> {
if (!event.isDropItems()) {
return;
}
if (it.override()) {
event.setDropItems(false);
event.setExpToDrop(0);
}
ContextHolder lootContext = ContextHolder.builder()
.withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block))
.withParameter(DirectContextParameters.BLOCK, new BukkitExistingBlock(block))
.withParameter(DirectContextParameters.POSITION, position)
.withParameter(DirectContextParameters.PLAYER, serverPlayer)
.withOptionalParameter(DirectContextParameters.ITEM_IN_HAND, ItemUtils.isEmpty(itemInHand) ? null : itemInHand).build();
@@ -198,7 +201,7 @@ public final class BlockEventListener implements Listener {
try {
Object soundType = CoreReflections.field$BlockBehaviour$soundType.get(ownerBlock);
Object breakSound = CoreReflections.field$SoundType$breakSound.get(soundType);
block.getWorld().playSound(block.getLocation(), FastNMS.INSTANCE.field$SoundEvent$location(breakSound).toString(), SoundCategory.BLOCKS, 1f, 0.8f);
block.getWorld().playSound(block.getLocation().add(0.5, 0.5, 0.5), FastNMS.INSTANCE.field$SoundEvent$location(breakSound).toString(), SoundCategory.BLOCKS, 1f, 0.8f);
} catch (ReflectiveOperationException e) {
this.plugin.logger().warn("Failed to get sound type", e);
}
@@ -224,7 +227,7 @@ public final class BlockEventListener implements Listener {
WorldPosition position = new WorldPosition(world, location.getBlockX() + 0.5, location.getBlockY() + 0.5, location.getBlockZ() + 0.5);
ContextHolder.Builder builder = ContextHolder.builder()
.withParameter(DirectContextParameters.POSITION, position)
.withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block));
.withParameter(DirectContextParameters.BLOCK, new BukkitExistingBlock(block));
for (LootTable<?> lootTable : it.lootTables()) {
for (Item<?> item : lootTable.getRandomItems(builder.build(), world, null)) {
world.dropItemNaturally(position, item);
@@ -250,7 +253,7 @@ public final class BlockEventListener implements Listener {
state.owner().value().execute(PlayerOptionalContext.of(BukkitAdaptors.adapt(player), ContextHolder.builder()
.withParameter(DirectContextParameters.EVENT, cancellable)
.withParameter(DirectContextParameters.POSITION, new WorldPosition(new BukkitWorld(event.getWorld()), LocationUtils.toVec3d(location)))
.withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block))
.withParameter(DirectContextParameters.BLOCK, new BukkitExistingBlock(block))
.withParameter(DirectContextParameters.CUSTOM_BLOCK_STATE, state)
), EventTrigger.STEP);
if (cancellable.isCancelled()) {

View File

@@ -76,7 +76,6 @@ public final class BukkitBlockManager extends AbstractBlockManager {
private List<Key> blockRegisterOrder = new ObjectArrayList<>();
// Event listeners
private BlockEventListener blockEventListener;
private FallingBlockRemoveListener fallingBlockRemoveListener;
// cached tag packet
private Object cachedUpdateTagsPacket;
@@ -101,7 +100,6 @@ public final class BukkitBlockManager extends AbstractBlockManager {
if (enableNoteBlocks) {
this.recordVanillaNoteBlocks();
}
this.fallingBlockRemoveListener = VersionHelper.isOrAbove1_20_3() ? new FallingBlockRemoveListener() : null;
this.stateId2ImmutableBlockStates = new ImmutableBlockState[this.customBlockCount];
Arrays.fill(this.stateId2ImmutableBlockStates, EmptyBlock.INSTANCE.defaultState());
this.resetPacketConsumers();
@@ -123,9 +121,6 @@ public final class BukkitBlockManager extends AbstractBlockManager {
@Override
public void delayedInit() {
Bukkit.getPluginManager().registerEvents(this.blockEventListener, this.plugin.javaPlugin());
if (this.fallingBlockRemoveListener != null) {
Bukkit.getPluginManager().registerEvents(this.fallingBlockRemoveListener, this.plugin.javaPlugin());
}
}
@Override
@@ -145,7 +140,6 @@ public final class BukkitBlockManager extends AbstractBlockManager {
public void disable() {
this.unload();
HandlerList.unregisterAll(this.blockEventListener);
if (this.fallingBlockRemoveListener != null) HandlerList.unregisterAll(this.fallingBlockRemoveListener);
}
@Override
@@ -181,14 +175,14 @@ public final class BukkitBlockManager extends AbstractBlockManager {
@Nullable
@Override
public BlockStateWrapper createPackedBlockState(String blockState) {
public BlockStateWrapper createBlockState(String blockState) {
ImmutableBlockState state = BlockStateParser.deserialize(blockState);
if (state != null) {
return state.customBlockState();
}
try {
BlockData blockData = Bukkit.createBlockData(blockState);
return BlockStateUtils.toPackedBlockState(blockData);
return BlockStateUtils.toBlockStateWrapper(blockData);
} catch (IllegalArgumentException e) {
return null;
}
@@ -220,7 +214,7 @@ public final class BukkitBlockManager extends AbstractBlockManager {
for (ImmutableBlockState state : customBlock.variantProvider().states()) {
ImmutableBlockState previous = this.stateId2ImmutableBlockStates[state.customBlockState().registryId() - BlockStateUtils.vanillaStateSize()];
if (previous != null && !previous.isEmpty()) {
throw new LocalizedResourceConfigException("warning.config.block.state.bind_failed", state.toString(), previous.toString());
throw new LocalizedResourceConfigException("warning.config.block.state.bind_failed", state.toString(), previous.toString(), BlockStateUtils.getBlockOwnerIdFromState(previous.customBlockState().literalObject()).toString());
}
this.stateId2ImmutableBlockStates[state.customBlockState().registryId() - BlockStateUtils.vanillaStateSize()] = state;
this.tempBlockAppearanceConvertor.put(state.customBlockState().registryId(), state.vanillaBlockState().registryId());
@@ -231,7 +225,7 @@ public final class BukkitBlockManager extends AbstractBlockManager {
@Override
public Key getBlockOwnerId(BlockStateWrapper state) {
return BlockStateUtils.getBlockOwnerIdFromState(state.handle());
return BlockStateUtils.getBlockOwnerIdFromState(state.literalObject());
}
@Override
@@ -258,9 +252,9 @@ public final class BukkitBlockManager extends AbstractBlockManager {
int size = RegistryUtils.currentBlockRegistrySize();
BlockStateWrapper[] states = new BlockStateWrapper[size];
for (int i = 0; i < size; i++) {
states[i] = BlockStateWrapper.create(BlockStateUtils.idToBlockState(i), i, BlockStateUtils.isVanillaBlock(i));
states[i] = new BukkitBlockStateWrapper(BlockStateUtils.idToBlockState(i), i);
}
BlockRegistryMirror.init(states, BlockStateWrapper.vanilla(MBlocks.STONE$defaultState, BlockStateUtils.blockStateToId(MBlocks.STONE$defaultState)));
BlockRegistryMirror.init(states, new BukkitBlockStateWrapper(MBlocks.STONE$defaultState, BlockStateUtils.blockStateToId(MBlocks.STONE$defaultState)));
}
private void registerEmptyBlock() {

View File

@@ -0,0 +1,23 @@
package net.momirealms.craftengine.bukkit.block;
import net.momirealms.craftengine.core.block.BlockStateWrapper;
public class BukkitBlockStateWrapper implements BlockStateWrapper {
private final Object blockState;
private final int registryId;
public BukkitBlockStateWrapper(Object blockState, int registryId) {
this.blockState = blockState;
this.registryId = registryId;
}
@Override
public Object literalObject() {
return this.blockState;
}
@Override
public int registryId() {
return this.registryId;
}
}

View File

@@ -39,7 +39,7 @@ public final class BukkitCustomBlock extends AbstractCustomBlock {
@NotNull Key id,
@NotNull Holder.Reference<CustomBlock> holder,
@NotNull Map<String, Property<?>> properties,
@NotNull Map<String, Integer> appearances,
@NotNull Map<String, BlockStateAppearance> appearances,
@NotNull Map<String, BlockStateVariant> variantMapper,
@NotNull BlockSettings settings,
@NotNull Map<EventTrigger, List<Function<PlayerOptionalContext>>> events,
@@ -76,13 +76,13 @@ public final class BukkitCustomBlock extends AbstractCustomBlock {
try {
for (ImmutableBlockState immutableBlockState : variantProvider().states()) {
if (immutableBlockState.vanillaBlockState() == null) {
CraftEngine.instance().logger().warn("Could not find vanilla block immutableBlockState for " + immutableBlockState + ". This might cause errors!");
CraftEngine.instance().logger().warn("Could not find vanilla visual block state for " + immutableBlockState + ". This might cause errors!");
continue;
} else if (immutableBlockState.customBlockState() == null) {
CraftEngine.instance().logger().warn("Could not find custom block immutableBlockState for " + immutableBlockState + ". This might cause errors!");
CraftEngine.instance().logger().warn("Could not find real block state for " + immutableBlockState + ". This might cause errors!");
continue;
}
DelegatingBlockState nmsState = (DelegatingBlockState) immutableBlockState.customBlockState().handle();
DelegatingBlockState nmsState = (DelegatingBlockState) immutableBlockState.customBlockState().literalObject();
nmsState.setBlockState(immutableBlockState);
BlockSettings settings = immutableBlockState.settings();
@@ -98,10 +98,10 @@ public final class BukkitCustomBlock extends AbstractCustomBlock {
Object pushReaction = ((Object[]) CoreReflections.method$PushReaction$values.invoke(null))[settings.pushReaction().ordinal()];
CoreReflections.field$BlockStateBase$pushReaction.set(nmsState, pushReaction);
boolean canOcclude = settings.canOcclude() == Tristate.UNDEFINED ? BlockStateUtils.isOcclude(immutableBlockState.vanillaBlockState().handle()) : settings.canOcclude().asBoolean();
boolean canOcclude = settings.canOcclude() == Tristate.UNDEFINED ? BlockStateUtils.isOcclude(immutableBlockState.vanillaBlockState().literalObject()) : settings.canOcclude().asBoolean();
CoreReflections.field$BlockStateBase$canOcclude.set(nmsState, canOcclude);
boolean useShapeForLightOcclusion = settings.useShapeForLightOcclusion() == Tristate.UNDEFINED ? CoreReflections.field$BlockStateBase$useShapeForLightOcclusion.getBoolean(immutableBlockState.vanillaBlockState().handle()) : settings.useShapeForLightOcclusion().asBoolean();
boolean useShapeForLightOcclusion = settings.useShapeForLightOcclusion() == Tristate.UNDEFINED ? CoreReflections.field$BlockStateBase$useShapeForLightOcclusion.getBoolean(immutableBlockState.vanillaBlockState().literalObject()) : settings.useShapeForLightOcclusion().asBoolean();
CoreReflections.field$BlockStateBase$useShapeForLightOcclusion.set(nmsState, useShapeForLightOcclusion);
CoreReflections.field$BlockStateBase$isRedstoneConductor.set(nmsState, settings.isRedstoneConductor().asBoolean() ? ALWAYS_TRUE : ALWAYS_FALSE);
@@ -111,7 +111,7 @@ public final class BukkitCustomBlock extends AbstractCustomBlock {
// set parent block properties
DelegatingBlock nmsBlock = (DelegatingBlock) BlockStateUtils.getBlockOwner(nmsState);
ObjectHolder<BlockShape> shapeHolder = nmsBlock.shapeDelegate();
shapeHolder.bindValue(new BukkitBlockShape(immutableBlockState.vanillaBlockState().handle(), Optional.ofNullable(immutableBlockState.settings().supportShapeBlockState()).map(it -> {
shapeHolder.bindValue(new BukkitBlockShape(immutableBlockState.vanillaBlockState().literalObject(), Optional.ofNullable(immutableBlockState.settings().supportShapeBlockState()).map(it -> {
try {
Object blockState = BlockStateUtils.blockDataToBlockState(Bukkit.createBlockData(it));
if (!BlockStateUtils.isVanillaBlock(blockState)) {
@@ -128,6 +128,9 @@ public final class BukkitCustomBlock extends AbstractCustomBlock {
behaviorHolder.bindValue(super.behavior);
// set block side properties
CoreReflections.field$BlockBehaviour$explosionResistance.set(nmsBlock, settings.resistance());
CoreReflections.field$BlockBehaviour$friction.set(nmsBlock, settings.friction());
CoreReflections.field$BlockBehaviour$speedFactor.set(nmsBlock, settings.speedFactor());
CoreReflections.field$BlockBehaviour$jumpFactor.set(nmsBlock, settings.jumpFactor());
CoreReflections.field$BlockBehaviour$soundType.set(nmsBlock, SoundUtils.toSoundType(settings.sounds()));
// init cache
CoreReflections.method$BlockStateBase$initCache.invoke(nmsState);
@@ -137,7 +140,7 @@ public final class BukkitCustomBlock extends AbstractCustomBlock {
}
// modify cache
if (VersionHelper.isOrAbove1_21_2()) {
int blockLight = settings.blockLight() != -1 ? settings.blockLight() : CoreReflections.field$BlockStateBase$lightBlock.getInt(immutableBlockState.vanillaBlockState().handle());
int blockLight = settings.blockLight() != -1 ? settings.blockLight() : CoreReflections.field$BlockStateBase$lightBlock.getInt(immutableBlockState.vanillaBlockState().literalObject());
// set block light
CoreReflections.field$BlockStateBase$lightBlock.set(nmsState, blockLight);
// set propagates skylight
@@ -146,11 +149,11 @@ public final class BukkitCustomBlock extends AbstractCustomBlock {
} else if (settings.propagatesSkylightDown() == Tristate.FALSE) {
CoreReflections.field$BlockStateBase$propagatesSkylightDown.set(nmsState, false);
} else {
CoreReflections.field$BlockStateBase$propagatesSkylightDown.set(nmsState, CoreReflections.field$BlockStateBase$propagatesSkylightDown.getBoolean(immutableBlockState.vanillaBlockState().handle()));
CoreReflections.field$BlockStateBase$propagatesSkylightDown.set(nmsState, CoreReflections.field$BlockStateBase$propagatesSkylightDown.getBoolean(immutableBlockState.vanillaBlockState().literalObject()));
}
} else {
Object cache = CoreReflections.field$BlockStateBase$cache.get(nmsState);
int blockLight = settings.blockLight() != -1 ? settings.blockLight() : CoreReflections.field$BlockStateBase$Cache$lightBlock.getInt(CoreReflections.field$BlockStateBase$cache.get(immutableBlockState.vanillaBlockState().handle()));
int blockLight = settings.blockLight() != -1 ? settings.blockLight() : CoreReflections.field$BlockStateBase$Cache$lightBlock.getInt(CoreReflections.field$BlockStateBase$cache.get(immutableBlockState.vanillaBlockState().literalObject()));
// set block light
CoreReflections.field$BlockStateBase$Cache$lightBlock.set(cache, blockLight);
// set propagates skylight
@@ -159,7 +162,7 @@ public final class BukkitCustomBlock extends AbstractCustomBlock {
} else if (settings.propagatesSkylightDown() == Tristate.FALSE) {
CoreReflections.field$BlockStateBase$Cache$propagatesSkylightDown.set(cache, false);
} else {
CoreReflections.field$BlockStateBase$Cache$propagatesSkylightDown.set(cache, CoreReflections.field$BlockStateBase$Cache$propagatesSkylightDown.getBoolean(CoreReflections.field$BlockStateBase$cache.get(immutableBlockState.vanillaBlockState().handle())));
CoreReflections.field$BlockStateBase$Cache$propagatesSkylightDown.set(cache, CoreReflections.field$BlockStateBase$Cache$propagatesSkylightDown.getBoolean(CoreReflections.field$BlockStateBase$cache.get(immutableBlockState.vanillaBlockState().literalObject())));
}
if (!isConditionallyFullOpaque) {
CoreReflections.field$BlockStateBase$opacityIfCached.set(nmsState, blockLight);
@@ -197,7 +200,7 @@ public final class BukkitCustomBlock extends AbstractCustomBlock {
public static class BuilderImpl implements Builder {
protected final Key id;
protected Map<String, Property<?>> properties;
protected Map<String, Integer> appearances;
protected Map<String, BlockStateAppearance> appearances;
protected Map<String, BlockStateVariant> variantMapper;
protected BlockSettings settings;
protected List<Map<String, Object>> behavior;
@@ -215,7 +218,7 @@ public final class BukkitCustomBlock extends AbstractCustomBlock {
}
@Override
public Builder appearances(Map<String, Integer> appearances) {
public Builder appearances(Map<String, BlockStateAppearance> appearances) {
this.appearances = appearances;
return this;
}

View File

@@ -1,51 +0,0 @@
package net.momirealms.craftengine.bukkit.block;
import net.momirealms.craftengine.bukkit.plugin.reflection.bukkit.CraftBukkitReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.context.ContextHolder;
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
import net.momirealms.craftengine.core.world.WorldPosition;
import org.bukkit.entity.FallingBlock;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import java.util.Optional;
@SuppressWarnings("DuplicatedCode")
public final class FallingBlockRemoveListener implements Listener {
@EventHandler
public void onFallingBlockBreak(org.bukkit.event.entity.EntityRemoveEvent event) {
if (event.getCause() == org.bukkit.event.entity.EntityRemoveEvent.Cause.DROP && event.getEntity() instanceof FallingBlock fallingBlock) {
try {
Object fallingBlockEntity = CraftBukkitReflections.field$CraftEntity$entity.get(fallingBlock);
boolean cancelDrop = (boolean) CoreReflections.field$FallingBlockEntity$cancelDrop.get(fallingBlockEntity);
if (cancelDrop) return;
Object blockState = CoreReflections.field$FallingBlockEntity$blockState.get(fallingBlockEntity);
Optional<ImmutableBlockState> optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(blockState);
if (optionalCustomState.isEmpty()) return;
ImmutableBlockState customState = optionalCustomState.get();
net.momirealms.craftengine.core.world.World world = new BukkitWorld(fallingBlock.getWorld());
WorldPosition position = new WorldPosition(world, CoreReflections.field$Entity$xo.getDouble(fallingBlockEntity), CoreReflections.field$Entity$yo.getDouble(fallingBlockEntity), CoreReflections.field$Entity$zo.getDouble(fallingBlockEntity));
ContextHolder.Builder builder = ContextHolder.builder()
.withParameter(DirectContextParameters.FALLING_BLOCK, true)
.withParameter(DirectContextParameters.POSITION, position);
for (Item<Object> item : customState.getDrops(builder, world, null)) {
world.dropItemNaturally(position, item);
}
Object entityData = CoreReflections.field$Entity$entityData.get(fallingBlockEntity);
boolean isSilent = (boolean) CoreReflections.method$SynchedEntityData$get.invoke(entityData, CoreReflections.instance$Entity$DATA_SILENT);
if (!isSilent) {
world.playBlockSound(position, customState.settings().sounds().destroySound());
}
} catch (ReflectiveOperationException e) {
CraftEngine.instance().logger().warn("Failed to handle EntityRemoveEvent", e);
}
}
}
}

View File

@@ -0,0 +1,90 @@
package net.momirealms.craftengine.bukkit.block.behavior;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.util.LocationUtils;
import net.momirealms.craftengine.core.block.BlockBehavior;
import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
import net.momirealms.craftengine.core.block.behavior.special.FallOnBlockBehavior;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.util.VersionHelper;
import net.momirealms.craftengine.core.world.Vec3d;
import org.bukkit.entity.Entity;
import java.util.Map;
import java.util.concurrent.Callable;
public class BouncingBlockBehavior extends BukkitBlockBehavior implements FallOnBlockBehavior {
public static final Factory FACTORY = new Factory();
private final double bounceHeight;
private final boolean syncPlayerPosition;
private final double fallDamageMultiplier;
public BouncingBlockBehavior(CustomBlock customBlock, double bounceHeight, boolean syncPlayerPosition, double fallDamageMultiplier) {
super(customBlock);
this.bounceHeight = bounceHeight;
this.syncPlayerPosition = syncPlayerPosition;
this.fallDamageMultiplier = fallDamageMultiplier;
}
@Override
public void fallOn(Object thisBlock, Object[] args, Callable<Object> superMethod) {
if (this.fallDamageMultiplier <= 0.0) return;
Object entity = args[3];
Number fallDistance = (Number) args[4];
FastNMS.INSTANCE.method$Entity$causeFallDamage(
entity, fallDistance.doubleValue() * this.fallDamageMultiplier, 1.0F,
FastNMS.INSTANCE.method$DamageSources$fall(FastNMS.INSTANCE.method$Entity$damageSources(entity))
);
}
@Override
public void updateEntityMovementAfterFallOn(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
Object entity = args[1];
if (FastNMS.INSTANCE.method$Entity$getSharedFlag(entity, 1)) {
superMethod.call();
} else {
bounceUp(entity);
}
}
private void bounceUp(Object entity) {
Vec3d deltaMovement = LocationUtils.fromVec(FastNMS.INSTANCE.method$Entity$getDeltaMovement(entity));
if (deltaMovement.y < 0.0) {
double d = CoreReflections.clazz$LivingEntity.isInstance(entity) ? 1.0 : 0.8;
double y = -deltaMovement.y * this.bounceHeight * d;
FastNMS.INSTANCE.method$Entity$setDeltaMovement(entity, deltaMovement.x, y, deltaMovement.z);
if (CoreReflections.clazz$Player.isInstance(entity) && this.syncPlayerPosition
&& /* 防抖 -> */ y > 0.035 /* <- 防抖 */
) {
// 这里一定要延迟 1t 不然就会出问题
if (VersionHelper.isFolia()) {
Entity bukkitEntity = FastNMS.INSTANCE.method$Entity$getBukkitEntity(entity);
bukkitEntity.getScheduler().runDelayed(BukkitCraftEngine.instance().javaPlugin(),
r -> FastNMS.INSTANCE.field$Entity$hurtMarked(entity, true),
null, 1L
);
} else {
CraftEngine.instance().scheduler().sync().runLater(
() -> FastNMS.INSTANCE.field$Entity$hurtMarked(entity, true),
1L
);
}
}
}
}
public static class Factory implements BlockBehaviorFactory {
@Override
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
double bounceHeight = ResourceConfigUtils.getAsDouble(arguments.getOrDefault("bounce-height", 0.66), "bounce-height");
boolean syncPlayerPosition = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("sync-player-position", true), "sync-player-position");
double fallDamageMultiplier = ResourceConfigUtils.getAsDouble(arguments.getOrDefault("fall-damage-multiplier", 0.5), "fall-damage-multiplier");
return new BouncingBlockBehavior(block, bounceHeight, syncPlayerPosition, fallDamageMultiplier);
}
}
}

View File

@@ -31,11 +31,11 @@ public class BukkitBlockBehavior extends AbstractBlockBehavior {
Direction.Axis axis = blockState.get(axisProperty);
return switch (rotation) {
case COUNTERCLOCKWISE_90, CLOCKWISE_90 -> switch (axis) {
case X -> blockState.with(axisProperty, Direction.Axis.Z).customBlockState().handle();
case Z -> blockState.with(axisProperty, Direction.Axis.X).customBlockState().handle();
default -> blockState.customBlockState().handle();
case X -> blockState.with(axisProperty, Direction.Axis.Z).customBlockState().literalObject();
case Z -> blockState.with(axisProperty, Direction.Axis.X).customBlockState().literalObject();
default -> blockState.customBlockState().literalObject();
};
default -> blockState.customBlockState().handle();
default -> blockState.customBlockState().literalObject();
};
};
});
@@ -45,7 +45,7 @@ public class BukkitBlockBehavior extends AbstractBlockBehavior {
Property<HorizontalDirection> directionProperty = (Property<HorizontalDirection>) property;
behavior.rotateFunction = (thisBlock, blockState, rotation) ->
blockState.with(directionProperty, rotation.rotate(blockState.get(directionProperty).toDirection()).toHorizontalDirection())
.customBlockState().handle();
.customBlockState().literalObject();
behavior.mirrorFunction = (thisBlock, blockState, mirror) -> {
Rotation rotation = mirror.getRotation(blockState.get(directionProperty).toDirection());
return behavior.rotateFunction.rotate(thisBlock, blockState, rotation);
@@ -55,7 +55,7 @@ public class BukkitBlockBehavior extends AbstractBlockBehavior {
Property<Direction> directionProperty = (Property<Direction>) property;
behavior.rotateFunction = (thisBlock, blockState, rotation) ->
blockState.with(directionProperty, rotation.rotate(blockState.get(directionProperty)))
.customBlockState().handle();
.customBlockState().literalObject();
behavior.mirrorFunction = (thisBlock, blockState, mirror) -> {
Rotation rotation = mirror.getRotation(blockState.get(directionProperty));
return behavior.rotateFunction.rotate(thisBlock, blockState, rotation);
@@ -68,7 +68,7 @@ public class BukkitBlockBehavior extends AbstractBlockBehavior {
Property<HorizontalDirection> directionProperty = (Property<HorizontalDirection>) property;
behavior.rotateFunction = (thisBlock, blockState, rotation) ->
blockState.with(directionProperty, rotation.rotate(blockState.get(directionProperty).toDirection()).toHorizontalDirection())
.customBlockState().handle();
.customBlockState().literalObject();
behavior.mirrorFunction = (thisBlock, blockState, mirror) -> {
Rotation rotation = mirror.getRotation(blockState.get(directionProperty).toDirection());
return behavior.rotateFunction.rotate(thisBlock, blockState, rotation);
@@ -139,7 +139,7 @@ public class BukkitBlockBehavior extends AbstractBlockBehavior {
if (optionalCustomState.isEmpty()) return CoreReflections.instance$ItemStack$EMPTY;
ImmutableBlockState immutableBlockState = optionalCustomState.get();
if (immutableBlockState.get(this.waterloggedProperty)) {
FastNMS.INSTANCE.method$LevelWriter$setBlock(world, pos, immutableBlockState.with(this.waterloggedProperty, false).customBlockState().handle(), 3);
FastNMS.INSTANCE.method$LevelWriter$setBlock(world, pos, immutableBlockState.with(this.waterloggedProperty, false).customBlockState().literalObject(), 3);
return FastNMS.INSTANCE.constructor$ItemStack(MItems.WATER_BUCKET, 1);
}
return CoreReflections.instance$ItemStack$EMPTY;
@@ -154,7 +154,7 @@ public class BukkitBlockBehavior extends AbstractBlockBehavior {
ImmutableBlockState immutableBlockState = optionalCustomState.get();
Object fluidType = FastNMS.INSTANCE.method$FluidState$getType(args[3]);
if (!immutableBlockState.get(this.waterloggedProperty) && fluidType == MFluids.WATER) {
FastNMS.INSTANCE.method$LevelWriter$setBlock(args[0], args[1], immutableBlockState.with(this.waterloggedProperty, true).customBlockState().handle(), 3);
FastNMS.INSTANCE.method$LevelWriter$setBlock(args[0], args[1], immutableBlockState.with(this.waterloggedProperty, true).customBlockState().literalObject(), 3);
FastNMS.INSTANCE.method$ScheduledTickAccess$scheduleFluidTick(args[0], args[1], fluidType, 5);
return true;
}
@@ -182,6 +182,6 @@ public class BukkitBlockBehavior extends AbstractBlockBehavior {
if (optionalCustomState.isEmpty()) return false;
BlockStateWrapper vanillaState = optionalCustomState.get().vanillaBlockState();
if (vanillaState == null) return false;
return FastNMS.INSTANCE.method$BlockStateBase$isPathFindable(vanillaState.handle(), VersionHelper.isOrAbove1_20_5() ? null : args[1], VersionHelper.isOrAbove1_20_5() ? null : args[2], args[isPathFindable$type]);
return FastNMS.INSTANCE.method$BlockStateBase$isPathFindable(vanillaState.literalObject(), VersionHelper.isOrAbove1_20_5() ? null : args[1], VersionHelper.isOrAbove1_20_5() ? null : args[2], args[isPathFindable$type]);
}
}

View File

@@ -27,6 +27,15 @@ public class BukkitBlockBehaviors extends BlockBehaviors {
public static final Key STAIRS_BLOCK = Key.from("craftengine:stairs_block");
public static final Key PRESSURE_PLATE_BLOCK = Key.from("craftengine:pressure_plate_block");
public static final Key DOUBLE_HIGH_BLOCK = Key.from("craftengine:double_high_block");
public static final Key CHANGE_OVER_TIME_BLOCK = Key.from("craftengine:change_over_time_block");
public static final Key SIMPLE_STORAGE_BLOCK = Key.from("craftengine:simple_storage_block");
public static final Key TOGGLEABLE_LAMP_BLOCK = Key.from("craftengine:toggleable_lamp_block");
public static final Key SOFA_BLOCK = Key.from("craftengine:sofa_block");
public static final Key BOUNCING_BLOCK = Key.from("craftengine:bouncing_block");
public static final Key DIRECTIONAL_ATTACHED_BLOCK = Key.from("craftengine:directional_attached_block");
public static final Key LIQUID_FLOWABLE_BLOCK = Key.from("craftengine:liquid_flowable_block");
public static final Key SIMPLE_PARTICLE_BLOCK = Key.from("craftengine:simple_particle_block");
public static final Key WALL_TORCH_PARTICLE_BLOCK = Key.from("craftengine:wall_torch_particle_block");
public static void init() {
register(EMPTY, (block, args) -> EmptyBlockBehavior.INSTANCE);
@@ -52,5 +61,14 @@ public class BukkitBlockBehaviors extends BlockBehaviors {
register(STAIRS_BLOCK, StairsBlockBehavior.FACTORY);
register(PRESSURE_PLATE_BLOCK, PressurePlateBlockBehavior.FACTORY);
register(DOUBLE_HIGH_BLOCK, DoubleHighBlockBehavior.FACTORY);
register(CHANGE_OVER_TIME_BLOCK, ChangeOverTimeBlockBehavior.FACTORY);
register(SIMPLE_STORAGE_BLOCK, SimpleStorageBlockBehavior.FACTORY);
register(TOGGLEABLE_LAMP_BLOCK, ToggleableLampBlockBehavior.FACTORY);
register(SOFA_BLOCK, SofaBlockBehavior.FACTORY);
register(BOUNCING_BLOCK, BouncingBlockBehavior.FACTORY);
register(DIRECTIONAL_ATTACHED_BLOCK, DirectionalAttachedBlockBehavior.FACTORY);
register(LIQUID_FLOWABLE_BLOCK, LiquidFlowableBlockBehavior.FACTORY);
register(SIMPLE_PARTICLE_BLOCK, SimpleParticleBlockBehavior.FACTORY);
register(WALL_TORCH_PARTICLE_BLOCK, WallTorchParticleBlockBehavior.FACTORY);
}
}

View File

@@ -3,6 +3,7 @@ package net.momirealms.craftengine.bukkit.block.behavior;
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;
import net.momirealms.craftengine.core.block.BlockBehavior;
import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
@@ -76,10 +77,7 @@ public class BushBlockBehavior extends AbstractCanSurviveBlockBehavior {
@SuppressWarnings("DuplicatedCode")
@Override
protected boolean canSurvive(Object thisBlock, Object state, Object world, Object blockPos) throws Exception {
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 belowPos = LocationUtils.below(blockPos);
Object belowState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(world, belowPos);
return mayPlaceOn(belowState, world, belowPos);
}

View File

@@ -0,0 +1,46 @@
package net.momirealms.craftengine.bukkit.block.behavior;
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
import net.momirealms.craftengine.bukkit.plugin.reflection.bukkit.CraftBukkitReflections;
import net.momirealms.craftengine.core.block.*;
import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.RandomUtils;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.Callable;
public class ChangeOverTimeBlockBehavior extends BukkitBlockBehavior {
public static final Factory FACTORY = new Factory();
private final float changeSpeed;
private final Key nextBlock;
public ChangeOverTimeBlockBehavior(CustomBlock customBlock, float changeSpeed, Key nextBlock) {
super(customBlock);
this.changeSpeed = changeSpeed;
this.nextBlock = nextBlock;
}
@Override
public void randomTick(Object thisBlock, Object[] args, Callable<Object> superMethod) throws ReflectiveOperationException {
if (RandomUtils.generateRandomFloat(0F, 1F) >= this.changeSpeed) return;
Optional<Object> nextState = BukkitBlockManager.instance().blockById(this.nextBlock)
.map(CustomBlock::defaultState)
.map(ImmutableBlockState::customBlockState)
.map(BlockStateWrapper::literalObject);
if (nextState.isEmpty()) return;
CraftBukkitReflections.method$CraftEventFactory$handleBlockFormEvent.invoke(null, args[1], args[2], nextState.get(), UpdateOption.UPDATE_ALL.flags());
}
public static class Factory implements BlockBehaviorFactory {
@Override
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
float changeSpeed = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("change-speed", 0.05688889F), "change-speed");
Key nextBlock = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.getOrDefault("next-block", "minecraft:air"), "warning.config.block.behavior.change_over_time.missing_next_block"));
return new ChangeOverTimeBlockBehavior(block, changeSpeed, nextBlock);
}
}
}

View File

@@ -48,7 +48,7 @@ public class ConcretePowderBlockBehavior extends BukkitBlockBehavior {
Optional<CustomBlock> optionalCustomBlock = BukkitBlockManager.instance().blockById(this.targetBlock);
if (optionalCustomBlock.isPresent()) {
CustomBlock customBlock = optionalCustomBlock.get();
this.defaultBlockState = customBlock.defaultState().customBlockState().handle();
this.defaultBlockState = customBlock.defaultState().customBlockState().literalObject();
this.defaultImmutableBlockState = customBlock.defaultState();
} else {
CraftEngine.instance().logger().warn("Failed to create solid block " + this.targetBlock + " in ConcretePowderBlockBehavior");
@@ -129,7 +129,7 @@ public class ConcretePowderBlockBehavior extends BukkitBlockBehavior {
Object mutablePos = CoreReflections.method$BlockPos$mutable.invoke(pos);
int j = Direction.values().length;
for (int k = 0; k < j; k++) {
Object direction = CoreReflections.instance$Directions[k];
Object direction = CoreReflections.instance$Direction$values[k];
Object blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, mutablePos);
if (direction != CoreReflections.instance$Direction$DOWN || canSolidify(blockState)) {
CoreReflections.method$MutableBlockPos$setWithOffset.invoke(mutablePos, pos, direction);

View File

@@ -94,7 +94,7 @@ public class CropBlockBehavior extends BukkitBlockBehavior {
BlockStateUtils.getOptionalCustomBlockState(state).ifPresent(customState -> {
int age = this.getAge(customState);
if (age < this.ageProperty.max && RandomUtils.generateRandomFloat(0, 1) < this.growSpeed) {
FastNMS.INSTANCE.method$LevelWriter$setBlock(level, pos, customState.with(this.ageProperty, age + 1).customBlockState().handle(), UpdateOption.UPDATE_ALL.flags());
FastNMS.INSTANCE.method$LevelWriter$setBlock(level, pos, customState.with(this.ageProperty, age + 1).customBlockState().literalObject(), UpdateOption.UPDATE_ALL.flags());
}
});
}
@@ -133,7 +133,7 @@ public class CropBlockBehavior extends BukkitBlockBehavior {
if (isMaxAge(state))
return InteractionResult.PASS;
boolean sendSwing = false;
Object visualState = state.vanillaBlockState().handle();
Object visualState = state.vanillaBlockState().literalObject();
Object visualStateBlock = BlockStateUtils.getBlockOwner(visualState);
if (CoreReflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) {
boolean is = FastNMS.INSTANCE.method$BonemealableBlock$isValidBonemealTarget(visualStateBlock, context.getLevel().serverWorld(), LocationUtils.toBlockPos(context.getClickedPos()), visualState);
@@ -156,7 +156,7 @@ public class CropBlockBehavior extends BukkitBlockBehavior {
}
ImmutableBlockState customState = optionalCustomState.get();
boolean sendParticles = false;
Object visualState = customState.vanillaBlockState().handle();
Object visualState = customState.vanillaBlockState().literalObject();
Object visualStateBlock = BlockStateUtils.getBlockOwner(visualState);
if (CoreReflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) {
boolean is = FastNMS.INSTANCE.method$BonemealableBlock$isValidBonemealTarget(visualStateBlock, level, pos, visualState);
@@ -180,7 +180,7 @@ public class CropBlockBehavior extends BukkitBlockBehavior {
if (i > maxAge) {
i = maxAge;
}
FastNMS.INSTANCE.method$LevelWriter$setBlock(level, pos, customState.with(this.ageProperty, i).customBlockState().handle(), UpdateOption.UPDATE_ALL.flags());
FastNMS.INSTANCE.method$LevelWriter$setBlock(level, pos, customState.with(this.ageProperty, i).customBlockState().literalObject(), UpdateOption.UPDATE_ALL.flags());
if (sendParticles) {
world.spawnParticle(ParticleUtils.HAPPY_VILLAGER, x + 0.5, y + 0.5, z + 0.5, 15, 0.25, 0.25, 0.25);
}

View File

@@ -0,0 +1,107 @@
package net.momirealms.craftengine.bukkit.block.behavior;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBlocks;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.DirectionUtils;
import net.momirealms.craftengine.bukkit.util.LocationUtils;
import net.momirealms.craftengine.core.block.BlockBehavior;
import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
import net.momirealms.craftengine.core.block.properties.Property;
import net.momirealms.craftengine.core.item.context.BlockPlaceContext;
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
import net.momirealms.craftengine.core.util.Direction;
import net.momirealms.craftengine.core.util.HorizontalDirection;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.World;
import java.util.Map;
import java.util.concurrent.Callable;
public class DirectionalAttachedBlockBehavior extends BukkitBlockBehavior {
public static final Factory FACTORY = new Factory();
private final Property<?> facingProperty;
private final boolean isSixDirection;
public DirectionalAttachedBlockBehavior(CustomBlock customBlock, Property<?> facingProperty, boolean isSixDirection) {
super(customBlock);
this.facingProperty = facingProperty;
this.isSixDirection = isSixDirection;
}
@Override
public Object updateShape(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
ImmutableBlockState state = BlockStateUtils.getOptionalCustomBlockState(args[0]).orElse(null);
if (state == null) return args[0];
DirectionalAttachedBlockBehavior behavior = state.behavior().getAs(DirectionalAttachedBlockBehavior.class).orElse(null);
if (behavior == null) return state;
boolean flag;
if (isSixDirection) {
Direction direction = DirectionUtils.fromNMSDirection(args[updateShape$direction]).opposite();
flag = direction == state.get(behavior.facingProperty);
} else {
HorizontalDirection direction = DirectionUtils.fromNMSDirection(args[updateShape$direction]).opposite().toHorizontalDirection();
flag = direction == state.get(behavior.facingProperty);
}
return flag && !FastNMS.INSTANCE.method$BlockStateBase$canSurvive(args[0], args[updateShape$level], args[updateShape$blockPos]) ? MBlocks.AIR$defaultState : args[0];
}
@Override
public boolean canSurvive(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
ImmutableBlockState state = BlockStateUtils.getOptionalCustomBlockState(args[0]).orElse(null);
if (state == null) return false;
DirectionalAttachedBlockBehavior behavior = state.behavior().getAs(DirectionalAttachedBlockBehavior.class).orElse(null);
if (behavior == null) return false;
Direction direction;
if (isSixDirection) {
direction = ((Direction) state.get(behavior.facingProperty)).opposite();
} else {
direction = ((HorizontalDirection) state.get(behavior.facingProperty)).opposite().toDirection();
}
BlockPos blockPos = LocationUtils.fromBlockPos(args[2]).relative(direction);
Object nmsPos = LocationUtils.toBlockPos(blockPos);
Object nmsState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(args[1], nmsPos);
return FastNMS.INSTANCE.method$BlockStateBase$isFaceSturdy(nmsState, args[1], nmsPos, DirectionUtils.toNMSDirection(direction), CoreReflections.instance$SupportType$FULL);
}
@SuppressWarnings("unchecked")
@Override
public ImmutableBlockState updateStateForPlacement(BlockPlaceContext context, ImmutableBlockState state) {
DirectionalAttachedBlockBehavior behavior = state.behavior().getAs(DirectionalAttachedBlockBehavior.class).orElse(null);
if (behavior == null) return null;
World level = context.getLevel();
BlockPos clickedPos = context.getClickedPos();
for (Direction direction : context.getNearestLookingDirections()) {
if (isSixDirection) {
state = state.with((Property<Direction>) behavior.facingProperty, direction.opposite());
if (FastNMS.INSTANCE.method$BlockStateBase$canSurvive(state.customBlockState().literalObject(), level.serverWorld(), LocationUtils.toBlockPos(clickedPos))) {
return state;
}
} else if (direction.axis().isHorizontal()) {
state = state.with((Property<HorizontalDirection>) behavior.facingProperty, direction.opposite().toHorizontalDirection());
if (FastNMS.INSTANCE.method$BlockStateBase$canSurvive(state.customBlockState().literalObject(), level.serverWorld(), LocationUtils.toBlockPos(clickedPos))) {
return state;
}
}
}
return null;
}
public static class Factory implements BlockBehaviorFactory {
@Override
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
Property<?> facing = ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("facing"), "warning.config.block.behavior.surface_attached.missing_facing");
boolean isHorizontalDirection = facing.valueClass() == HorizontalDirection.class;
boolean isDirection = facing.valueClass() == Direction.class;
if (!(isHorizontalDirection || isDirection)) {
throw new LocalizedResourceConfigException("warning.config.block.behavior.surface_attached.missing_facing");
}
return new DirectionalAttachedBlockBehavior(block, facing, isDirection);
}
}
}

View File

@@ -107,7 +107,7 @@ public class DoorBlockBehavior extends AbstractCanSurviveBlockBehavior {
return MBlocks.AIR$defaultState;
}
if (neighborState.get(anotherDoorBehavior.get().halfProperty) != half) {
return neighborState.with(anotherDoorBehavior.get().halfProperty, half).customBlockState().handle();
return neighborState.with(anotherDoorBehavior.get().halfProperty, half).customBlockState().literalObject();
}
return MBlocks.AIR$defaultState;
} else {
@@ -245,7 +245,7 @@ public class DoorBlockBehavior extends AbstractCanSurviveBlockBehavior {
public void setOpen(@Nullable Player player, Object serverLevel, ImmutableBlockState state, BlockPos pos, boolean isOpen) {
if (isOpen(state) != isOpen) {
org.bukkit.World world = FastNMS.INSTANCE.method$Level$getCraftWorld(serverLevel);
FastNMS.INSTANCE.method$LevelWriter$setBlock(serverLevel, LocationUtils.toBlockPos(pos), state.with(this.openProperty, isOpen).customBlockState().handle(), UpdateOption.builder().updateImmediate().updateClients().build().flags());
FastNMS.INSTANCE.method$LevelWriter$setBlock(serverLevel, LocationUtils.toBlockPos(pos), state.with(this.openProperty, isOpen).customBlockState().literalObject(), UpdateOption.builder().updateImmediate().updateClients().build().flags());
world.sendGameEvent(player == null ? null : (org.bukkit.entity.Player) player.platformPlayer(), isOpen ? GameEvent.BLOCK_OPEN : GameEvent.BLOCK_CLOSE, new Vector(pos.x(), pos.y(), pos.z()));
SoundData soundData = isOpen ? this.openSound : this.closeSound;
if (soundData != null) {
@@ -307,7 +307,7 @@ public class DoorBlockBehavior extends AbstractCanSurviveBlockBehavior {
);
}
}
FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, customState.with(this.poweredProperty, flag).with(this.openProperty, flag).customBlockState().handle(), UpdateOption.Flags.UPDATE_CLIENTS);
FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, customState.with(this.poweredProperty, flag).with(this.openProperty, flag).customBlockState().literalObject(), UpdateOption.Flags.UPDATE_CLIENTS);
}
}

View File

@@ -63,7 +63,7 @@ public class DoubleHighBlockBehavior extends BukkitBlockBehavior {
World level = context.getLevel();
BlockPos anotherHalfPos = context.getClickedPos().relative(Direction.UP);
BlockStateWrapper blockStateWrapper = state.with(this.halfProperty, DoubleBlockHalf.UPPER).customBlockState();
FastNMS.INSTANCE.method$LevelWriter$setBlock(level.serverWorld(), LocationUtils.toBlockPos(anotherHalfPos), blockStateWrapper.handle(), UpdateOption.Flags.UPDATE_CLIENTS);
FastNMS.INSTANCE.method$LevelWriter$setBlock(level.serverWorld(), LocationUtils.toBlockPos(anotherHalfPos), blockStateWrapper.literalObject(), UpdateOption.Flags.UPDATE_CLIENTS);
}
@Override

View File

@@ -10,11 +10,7 @@ import net.momirealms.craftengine.core.block.BlockBehavior;
import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.plugin.context.ContextHolder;
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.util.VersionHelper;
import net.momirealms.craftengine.core.world.Vec3d;
import net.momirealms.craftengine.core.world.WorldPosition;
@@ -67,7 +63,7 @@ public class FallingBlockBehavior extends BukkitBlockBehavior {
return;
}
Object blockState = args[0];
Object fallingBlockEntity = CoreReflections.method$FallingBlockEntity$fall.invoke(null, world, blockPos, blockState);
Object fallingBlockEntity = FastNMS.INSTANCE.createInjectedFallingBlockEntity(world, blockPos, blockState);
if (this.hurtAmount > 0 && this.maxHurt > 0) {
CoreReflections.method$FallingBlockEntity$setHurtsEntities.invoke(fallingBlockEntity, this.hurtAmount, this.maxHurt);
}
@@ -76,27 +72,17 @@ public class FallingBlockBehavior extends BukkitBlockBehavior {
@SuppressWarnings("DuplicatedCode")
@Override
public void onBrokenAfterFall(Object thisBlock, Object[] args) throws Exception {
// Use EntityRemoveEvent for 1.20.3+
if (VersionHelper.isOrAbove1_20_3()) return;
Object level = args[0];
Object fallingBlockEntity = args[2];
boolean cancelDrop = (boolean) CoreReflections.field$FallingBlockEntity$cancelDrop.get(fallingBlockEntity);
if (cancelDrop) return;
Object blockState = CoreReflections.field$FallingBlockEntity$blockState.get(fallingBlockEntity);
Optional<ImmutableBlockState> optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(blockState);
if (optionalCustomState.isEmpty()) return;
ImmutableBlockState customState = optionalCustomState.get();
net.momirealms.craftengine.core.world.World world = new BukkitWorld(FastNMS.INSTANCE.method$Level$getCraftWorld(level));
WorldPosition position = new WorldPosition(world, CoreReflections.field$Entity$xo.getDouble(fallingBlockEntity), CoreReflections.field$Entity$yo.getDouble(fallingBlockEntity), CoreReflections.field$Entity$zo.getDouble(fallingBlockEntity));
ContextHolder.Builder builder = ContextHolder.builder()
.withParameter(DirectContextParameters.FALLING_BLOCK, true)
.withParameter(DirectContextParameters.POSITION, position);
for (Item<Object> item : customState.getDrops(builder, world, null)) {
world.dropItemNaturally(position, item);
}
Object entityData = CoreReflections.field$Entity$entityData.get(fallingBlockEntity);
boolean isSilent = (boolean) CoreReflections.method$SynchedEntityData$get.invoke(entityData, CoreReflections.instance$Entity$DATA_SILENT);
if (!isSilent) {
Object blockState = CoreReflections.field$FallingBlockEntity$blockState.get(fallingBlockEntity);
Optional<ImmutableBlockState> optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(blockState);
if (optionalCustomState.isEmpty()) return;
ImmutableBlockState customState = optionalCustomState.get();
net.momirealms.craftengine.core.world.World world = new BukkitWorld(FastNMS.INSTANCE.method$Level$getCraftWorld(level));
WorldPosition position = new WorldPosition(world, CoreReflections.field$Entity$xo.getDouble(fallingBlockEntity), CoreReflections.field$Entity$yo.getDouble(fallingBlockEntity), CoreReflections.field$Entity$zo.getDouble(fallingBlockEntity));
world.playBlockSound(position, customState.settings().sounds().destroySound());
}
}

View File

@@ -113,7 +113,7 @@ public class FenceGateBlockBehavior extends BukkitBlockBehavior {
if (relativeStateIsWall) {
// TODO: 连接原版方块
}
return customState.with(this.inWallProperty, flag).customBlockState().handle();
return customState.with(this.inWallProperty, flag).customBlockState().literalObject();
}
@Override
@@ -148,7 +148,7 @@ public class FenceGateBlockBehavior extends BukkitBlockBehavior {
private void playerToggle(UseOnContext context, ImmutableBlockState state) {
Player player = context.getPlayer();
this.toggle(state, context.getLevel(), context.getClickedPos(), player);
if (!InteractUtils.isInteractable((org.bukkit.entity.Player) player.platformPlayer(), BlockStateUtils.fromBlockData(state.vanillaBlockState().handle()), context.getHitResult(), (Item<ItemStack>) context.getItem())) {
if (!InteractUtils.isInteractable((org.bukkit.entity.Player) player.platformPlayer(), BlockStateUtils.fromBlockData(state.vanillaBlockState().literalObject()), context.getHitResult(), (Item<ItemStack>) context.getItem())) {
player.swingHand(context.getHand());
}
}
@@ -223,7 +223,7 @@ public class FenceGateBlockBehavior extends BukkitBlockBehavior {
this.playSound(LocationUtils.fromBlockPos(blockPos), world, hasSignal);
}
FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, customState.with(this.poweredProperty, hasSignal).customBlockState().handle(), UpdateOption.Flags.UPDATE_CLIENTS);
FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, customState.with(this.poweredProperty, hasSignal).customBlockState().literalObject(), UpdateOption.Flags.UPDATE_CLIENTS);
}
private void toggle(ImmutableBlockState state, World world, BlockPos pos, @Nullable Player player) {
@@ -240,7 +240,7 @@ public class FenceGateBlockBehavior extends BukkitBlockBehavior {
}
newState = blockState.with(this.openProperty, true);
}
FastNMS.INSTANCE.method$LevelWriter$setBlock(world.serverWorld(), LocationUtils.toBlockPos(pos), newState.customBlockState().handle(), UpdateOption.UPDATE_ALL.flags());
FastNMS.INSTANCE.method$LevelWriter$setBlock(world.serverWorld(), LocationUtils.toBlockPos(pos), newState.customBlockState().literalObject(), UpdateOption.UPDATE_ALL.flags());
boolean open = isOpen(newState);
((org.bukkit.World) world.platformWorld()).sendGameEvent(
player != null ? (org.bukkit.entity.Player) player.platformPlayer() : null,

View File

@@ -8,7 +8,7 @@ import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.FeatureUtils;
import net.momirealms.craftengine.bukkit.util.LocationUtils;
import net.momirealms.craftengine.bukkit.util.ParticleUtils;
import net.momirealms.craftengine.bukkit.world.BukkitBlockInWorld;
import net.momirealms.craftengine.bukkit.world.BukkitExistingBlock;
import net.momirealms.craftengine.core.block.BlockBehavior;
import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
@@ -59,7 +59,7 @@ public class GrassBlockBehavior extends BukkitBlockBehavior {
}
boolean sendParticles = false;
ImmutableBlockState customState = optionalCustomState.get();
Object visualState = customState.vanillaBlockState().handle();
Object visualState = customState.vanillaBlockState().literalObject();
Object visualStateBlock = BlockStateUtils.getBlockOwner(visualState);
if (CoreReflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) {
boolean is = FastNMS.INSTANCE.method$BonemealableBlock$isValidBonemealTarget(visualStateBlock, level, blockPos, visualState);
@@ -86,12 +86,12 @@ public class GrassBlockBehavior extends BukkitBlockBehavior {
if (ItemUtils.isEmpty(item) || !item.vanillaId().equals(ItemKeys.BONE_MEAL) || context.getPlayer().isAdventureMode())
return InteractionResult.PASS;
BlockPos pos = context.getClickedPos();
BukkitBlockInWorld upper = (BukkitBlockInWorld) context.getLevel().getBlockAt(pos.x(), pos.y() + 1, pos.z());
BukkitExistingBlock upper = (BukkitExistingBlock) context.getLevel().getBlockAt(pos.x(), pos.y() + 1, pos.z());
Block block = upper.block();
if (!block.isEmpty())
return InteractionResult.PASS;
boolean sendSwing = false;
Object visualState = state.vanillaBlockState().handle();
Object visualState = state.vanillaBlockState().literalObject();
Object visualStateBlock = BlockStateUtils.getBlockOwner(visualState);
if (CoreReflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) {
boolean is = FastNMS.INSTANCE.method$BonemealableBlock$isValidBonemealTarget(visualStateBlock, context.getLevel().serverWorld(), LocationUtils.toBlockPos(context.getClickedPos()), visualState);

View File

@@ -1,6 +1,7 @@
package net.momirealms.craftengine.bukkit.block.behavior;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.util.LocationUtils;
import net.momirealms.craftengine.core.block.BlockBehavior;
import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
@@ -20,10 +21,7 @@ public class HangingBlockBehavior extends BushBlockBehavior {
@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 belowPos = LocationUtils.above(blockPos);
Object belowState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(world, belowPos);
return mayPlaceOn(belowState, world, belowPos);
}

View File

@@ -44,7 +44,7 @@ public class LampBlockBehavior extends BukkitBlockBehavior {
if (FastNMS.INSTANCE.method$CraftEventFactory$callRedstoneChange(world, blockPos, 0, 15).getNewCurrent() != 15) {
return;
}
FastNMS.INSTANCE.method$LevelWriter$setBlock(world, blockPos, customState.cycle(this.litProperty).customBlockState().handle(), 2);
FastNMS.INSTANCE.method$LevelWriter$setBlock(world, blockPos, customState.cycle(this.litProperty).customBlockState().literalObject(), 2);
}
}
@@ -64,7 +64,7 @@ public class LampBlockBehavior extends BukkitBlockBehavior {
if (FastNMS.INSTANCE.method$CraftEventFactory$callRedstoneChange(world, blockPos, 0, 15).getNewCurrent() != 15) {
return;
}
FastNMS.INSTANCE.method$LevelWriter$setBlock(world, blockPos, customState.cycle(this.litProperty).customBlockState().handle(), 2);
FastNMS.INSTANCE.method$LevelWriter$setBlock(world, blockPos, customState.cycle(this.litProperty).customBlockState().literalObject(), 2);
}
}
}

View File

@@ -86,10 +86,10 @@ public class LeavesBlockBehavior extends BukkitBlockBehavior {
LeavesBlockBehavior behavior = optionalBehavior.get();
ImmutableBlockState newState = behavior.updateDistance(customState, level, blockPos);
if (newState != customState) {
if (blockState == newState.customBlockState().handle()) {
if (blockState == newState.customBlockState().literalObject()) {
CoreReflections.method$BlockStateBase$updateNeighbourShapes.invoke(blockState, level, blockPos, UpdateOption.UPDATE_ALL.flags(), 512);
} else {
FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, newState.customBlockState().handle(), UpdateOption.UPDATE_ALL.flags());
FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, newState.customBlockState().literalObject(), UpdateOption.UPDATE_ALL.flags());
}
}
}
@@ -132,7 +132,7 @@ public class LeavesBlockBehavior extends BukkitBlockBehavior {
Object mutablePos = CoreReflections.constructor$MutableBlockPos.newInstance();
int j = Direction.values().length;
for (int k = 0; k < j; ++k) {
Object direction = CoreReflections.instance$Directions[k];
Object direction = CoreReflections.instance$Direction$values[k];
CoreReflections.method$MutableBlockPos$setWithOffset.invoke(mutablePos, blockPos, direction);
Object blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(world, mutablePos);
i = Math.min(i, getDistanceAt(blockState) + 1);

View File

@@ -0,0 +1,49 @@
package net.momirealms.craftengine.bukkit.block.behavior;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MFluids;
import net.momirealms.craftengine.core.block.BlockBehavior;
import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.UpdateOption;
import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
import net.momirealms.craftengine.core.block.behavior.special.PlaceLiquidBlockBehavior;
import net.momirealms.craftengine.core.world.WorldEvents;
import java.util.Map;
import java.util.concurrent.Callable;
public class LiquidFlowableBlockBehavior extends BukkitBlockBehavior implements PlaceLiquidBlockBehavior {
public static final Factory FACTORY = new Factory();
public LiquidFlowableBlockBehavior(CustomBlock customBlock) {
super(customBlock);
}
@Override
public boolean canPlaceLiquid(Object thisBlock, Object[] args, Callable<Object> superMethod) {
return true;
}
@Override
public boolean placeLiquid(Object thisBlock, Object[] args, Callable<Object> superMethod) {
Object level = args[0];
Object pos = args[1];
Object blockState = args[2];
Object fluidState = args[3];
Object fluidType = FastNMS.INSTANCE.method$FluidState$getType(fluidState);
if (fluidType == MFluids.LAVA || fluidType == MFluids.FLOWING_LAVA) {
FastNMS.INSTANCE.method$LevelAccessor$levelEvent(level, WorldEvents.LAVA_CONVERTS_BLOCK, pos, 0);
} else {
FastNMS.INSTANCE.method$Block$dropResources(blockState, level, pos);
}
FastNMS.INSTANCE.method$LevelWriter$setBlock(level, pos, FastNMS.INSTANCE.method$FluidState$createLegacyBlock(fluidState), UpdateOption.UPDATE_ALL.flags());
return true;
}
public static class Factory implements BlockBehaviorFactory {
@Override
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
return new LiquidFlowableBlockBehavior(block);
}
}
}

View File

@@ -105,7 +105,6 @@ public class PressurePlateBlockBehavior extends BukkitBlockBehavior {
if (signalForState == 0) {
this.checkPressed(args[3], args[1], args[2], state, signalForState, thisBlock);
} else {
// todo 为什么
FastNMS.INSTANCE.method$ScheduledTickAccess$scheduleBlockTick(args[1], args[2], thisBlock, this.pressedTime);
}
}
@@ -122,7 +121,7 @@ public class PressurePlateBlockBehavior extends BukkitBlockBehavior {
private Object setSignalForState(Object state, int strength) {
Optional<ImmutableBlockState> optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(state);
if (optionalCustomState.isEmpty()) return state;
return optionalCustomState.get().with(this.poweredProperty, strength > 0).customBlockState().handle();
return optionalCustomState.get().with(this.poweredProperty, strength > 0).customBlockState().literalObject();
}
private void checkPressed(@Nullable Object entity, Object level, Object pos, Object state, int currentSignal, Object thisBlock) {
@@ -189,7 +188,7 @@ public class PressurePlateBlockBehavior extends BukkitBlockBehavior {
Object pos = args[2];
Object newState = args[3];
boolean movedByPiston = (boolean) args[4];
if (!movedByPiston && !FastNMS.INSTANCE.method$BlockStateBase$is(state, FastNMS.INSTANCE.method$BlockState$getBlock(newState))) {
if (!movedByPiston && !FastNMS.INSTANCE.method$BlockStateBase$isBlock(state, FastNMS.INSTANCE.method$BlockState$getBlock(newState))) {
if (this.getSignalForState(state) > 0) {
this.updateNeighbours(level, pos, thisBlock);
}

View File

@@ -114,7 +114,7 @@ public class SaplingBlockBehavior extends BukkitBlockBehavior {
}
ImmutableBlockState customState = optionalCustomState.get();
boolean sendParticles = false;
Object visualState = customState.vanillaBlockState().handle();
Object visualState = customState.vanillaBlockState().literalObject();
Object visualStateBlock = BlockStateUtils.getBlockOwner(visualState);
if (CoreReflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) {
boolean is = FastNMS.INSTANCE.method$BonemealableBlock$isValidBonemealTarget(visualStateBlock, level, blockPos, visualState);
@@ -151,7 +151,7 @@ public class SaplingBlockBehavior extends BukkitBlockBehavior {
if (ItemUtils.isEmpty(item) || !item.vanillaId().equals(ItemKeys.BONE_MEAL) || context.getPlayer().isAdventureMode())
return InteractionResult.PASS;
boolean sendSwing = false;
Object visualState = state.vanillaBlockState().handle();
Object visualState = state.vanillaBlockState().literalObject();
Object visualStateBlock = BlockStateUtils.getBlockOwner(visualState);
if (CoreReflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) {
boolean is = FastNMS.INSTANCE.method$BonemealableBlock$isValidBonemealTarget(visualStateBlock, context.getLevel().serverWorld(), LocationUtils.toBlockPos(context.getClickedPos()), visualState);

View File

@@ -0,0 +1,64 @@
package net.momirealms.craftengine.bukkit.block.behavior;
import net.momirealms.craftengine.bukkit.block.entity.BukkitBlockEntityTypes;
import net.momirealms.craftengine.bukkit.block.entity.SimpleParticleBlockEntity;
import net.momirealms.craftengine.core.block.BlockBehavior;
import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
import net.momirealms.craftengine.core.block.behavior.EntityBlockBehavior;
import net.momirealms.craftengine.core.block.entity.BlockEntity;
import net.momirealms.craftengine.core.block.entity.BlockEntityType;
import net.momirealms.craftengine.core.block.entity.tick.BlockEntityTicker;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.CEWorld;
import net.momirealms.craftengine.core.world.particle.ParticleConfig;
import java.util.List;
import java.util.Map;
public class SimpleParticleBlockBehavior extends BukkitBlockBehavior implements EntityBlockBehavior {
public static final Factory FACTORY = new Factory();
public final ParticleConfig[] particles;
public final int tickInterval;
public SimpleParticleBlockBehavior(CustomBlock customBlock, ParticleConfig[] particles, int tickInterval) {
super(customBlock);
this.particles = particles;
this.tickInterval = tickInterval;
}
public ParticleConfig[] particles() {
return this.particles;
}
public int tickInterval() {
return tickInterval;
}
@Override
public <T extends BlockEntity> BlockEntityType<T> blockEntityType() {
return EntityBlockBehavior.blockEntityTypeHelper(BukkitBlockEntityTypes.SIMPLE_PARTICLE);
}
@Override
public BlockEntity createBlockEntity(BlockPos pos, ImmutableBlockState state) {
return new SimpleParticleBlockEntity(pos, state);
}
@Override
public <T extends BlockEntity> BlockEntityTicker<T> createBlockEntityTicker(CEWorld level, ImmutableBlockState state, BlockEntityType<T> blockEntityType) {
if (this.particles.length == 0) return null;
return EntityBlockBehavior.createTickerHelper(SimpleParticleBlockEntity::tick);
}
public static class Factory implements BlockBehaviorFactory {
@Override
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
List<ParticleConfig> particles = ResourceConfigUtils.parseConfigAsList(ResourceConfigUtils.get(arguments, "particles", "particle"), ParticleConfig::fromMap$blockEntity);
int tickInterval = ResourceConfigUtils.getAsInt(arguments.getOrDefault("tick-interval", 10), "tick-interval");
return new SimpleParticleBlockBehavior(block, particles.toArray(new ParticleConfig[0]), tickInterval);
}
}
}

View File

@@ -0,0 +1,212 @@
package net.momirealms.craftengine.bukkit.block.behavior;
import net.momirealms.craftengine.bukkit.block.entity.BukkitBlockEntityTypes;
import net.momirealms.craftengine.bukkit.block.entity.SimpleStorageBlockEntity;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.gui.BukkitInventory;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.LocationUtils;
import net.momirealms.craftengine.bukkit.world.BukkitWorldManager;
import net.momirealms.craftengine.core.block.BlockBehavior;
import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
import net.momirealms.craftengine.core.block.behavior.EntityBlockBehavior;
import net.momirealms.craftengine.core.block.entity.BlockEntity;
import net.momirealms.craftengine.core.block.entity.BlockEntityType;
import net.momirealms.craftengine.core.block.properties.Property;
import net.momirealms.craftengine.core.entity.player.InteractionResult;
import net.momirealms.craftengine.core.item.context.UseOnContext;
import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext;
import net.momirealms.craftengine.core.sound.SoundData;
import net.momirealms.craftengine.core.util.AdventureHelper;
import net.momirealms.craftengine.core.util.MCUtils;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.CEWorld;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.Callable;
public class SimpleStorageBlockBehavior extends BukkitBlockBehavior implements EntityBlockBehavior {
public static final Factory FACTORY = new Factory();
private final String containerTitle;
private final int rows;
private final SoundData openSound;
private final SoundData closeSound;
private final boolean hasAnalogOutputSignal;
private final boolean canPlaceItem;
private final boolean canTakeItem;
@Nullable
private final Property<Boolean> openProperty;
public SimpleStorageBlockBehavior(CustomBlock customBlock,
String containerTitle,
int rows,
SoundData openSound,
SoundData closeSound,
boolean hasAnalogOutputSignal,
boolean canPlaceItem,
boolean canTakeItem,
@Nullable Property<Boolean> openProperty) {
super(customBlock);
this.containerTitle = containerTitle;
this.rows = rows;
this.openSound = openSound;
this.closeSound = closeSound;
this.hasAnalogOutputSignal = hasAnalogOutputSignal;
this.canPlaceItem = canPlaceItem;
this.canTakeItem = canTakeItem;
this.openProperty = openProperty;
}
@Override
public InteractionResult useWithoutItem(UseOnContext context, ImmutableBlockState state) {
CEWorld world = context.getLevel().storageWorld();
net.momirealms.craftengine.core.entity.player.Player player = context.getPlayer();
BlockEntity blockEntity = world.getBlockEntityAtIfLoaded(context.getClickedPos());
if (player != null && blockEntity instanceof SimpleStorageBlockEntity entity) {
Player bukkitPlayer = (Player) player.platformPlayer();
Optional.ofNullable(entity.inventory()).ifPresent(inventory -> {
entity.onPlayerOpen(player);
bukkitPlayer.openInventory(inventory);
new BukkitInventory(inventory).open(player, AdventureHelper.miniMessage().deserialize(this.containerTitle, PlayerOptionalContext.of(player).tagResolvers()));
});
}
return InteractionResult.SUCCESS_AND_CANCEL;
}
// 1.21.5+
@Override
public void affectNeighborsAfterRemoval(Object thisBlock, Object[] args, Callable<Object> superMethod) {
Object level = args[1];
Object pos = args[2];
Object blockState = args[0];
FastNMS.INSTANCE.method$Level$updateNeighbourForOutputSignal(level, pos, BlockStateUtils.getBlockOwner(blockState));
}
@Override
public void tick(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
Object world = args[1];
Object blockPos = args[2];
BlockPos pos = LocationUtils.fromBlockPos(blockPos);
World bukkitWorld = FastNMS.INSTANCE.method$Level$getCraftWorld(world);
CEWorld ceWorld = BukkitWorldManager.instance().getWorld(bukkitWorld.getUID());
BlockEntity blockEntity = ceWorld.getBlockEntityAtIfLoaded(pos);
if (blockEntity instanceof SimpleStorageBlockEntity entity) {
entity.checkOpeners(world, blockPos, args[0]);
}
}
@Override
public <T extends BlockEntity> BlockEntityType<T> blockEntityType() {
return EntityBlockBehavior.blockEntityTypeHelper(BukkitBlockEntityTypes.SIMPLE_STORAGE);
}
@Override
public BlockEntity createBlockEntity(BlockPos pos, ImmutableBlockState state) {
return new SimpleStorageBlockEntity(pos, state);
}
@NotNull
public String containerTitle() {
return this.containerTitle;
}
@Nullable
public SoundData closeSound() {
return this.closeSound;
}
@Nullable
public SoundData openSound() {
return this.openSound;
}
public int rows() {
return this.rows;
}
public boolean canPlaceItem() {
return this.canPlaceItem;
}
public boolean canTakeItem() {
return this.canTakeItem;
}
public @Nullable Property<Boolean> openProperty() {
return openProperty;
}
@Override
public int getAnalogOutputSignal(Object thisBlock, Object[] args) {
if (!this.hasAnalogOutputSignal) return 0;
Object world = args[1];
Object blockPos = args[2];
BlockPos pos = LocationUtils.fromBlockPos(blockPos);
World bukkitWorld = FastNMS.INSTANCE.method$Level$getCraftWorld(world);
CEWorld ceWorld = BukkitWorldManager.instance().getWorld(bukkitWorld.getUID());
BlockEntity blockEntity = ceWorld.getBlockEntityAtIfLoaded(pos);
if (blockEntity instanceof SimpleStorageBlockEntity entity) {
Inventory inventory = entity.inventory();
if (inventory != null) {
float signal = 0.0F;
for (int i = 0; i < inventory.getSize(); i++) {
ItemStack item = inventory.getItem(i);
if (item != null) {
signal += (float) item.getAmount() / (float) (Math.min(inventory.getMaxStackSize(), item.getMaxStackSize()));
}
}
signal /= (float) inventory.getSize();
return MCUtils.lerpDiscrete(signal, 0, 15);
}
}
return 0;
}
@Override
public boolean hasAnalogOutputSignal(Object thisBlock, Object[] args) {
return this.hasAnalogOutputSignal;
}
@Override
public Object getContainer(Object thisBlock, Object[] args) {
CEWorld ceWorld = BukkitWorldManager.instance().getWorld(FastNMS.INSTANCE.method$Level$getCraftWorld(args[1]));
BlockPos blockPos = LocationUtils.fromBlockPos(args[2]);
BlockEntity blockEntity = ceWorld.getBlockEntityAtIfLoaded(blockPos);
if (blockEntity instanceof SimpleStorageBlockEntity entity) {
return FastNMS.INSTANCE.method$CraftInventory$getInventory(entity.inventory());
}
return null;
}
public static class Factory implements BlockBehaviorFactory {
@SuppressWarnings("unchecked")
@Override
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
String title = arguments.getOrDefault("title", "").toString();
int rows = MCUtils.clamp(ResourceConfigUtils.getAsInt(arguments.getOrDefault("rows", 1), "rows"), 1, 6);
Map<String, Object> sounds = (Map<String, Object>) arguments.get("sounds");
boolean hasAnalogOutputSignal = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("has-signal", true), "has-signal");
SoundData openSound = null;
SoundData closeSound = null;
if (sounds != null) {
openSound = Optional.ofNullable(sounds.get("open")).map(obj -> SoundData.create(obj, SoundData.SoundValue.FIXED_0_5, SoundData.SoundValue.ranged(0.9f, 1f))).orElse(null);
closeSound = Optional.ofNullable(sounds.get("close")).map(obj -> SoundData.create(obj, SoundData.SoundValue.FIXED_0_5, SoundData.SoundValue.ranged(0.9f, 1f))).orElse(null);
}
boolean canPlaceItem = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("allow-input", true), "allow-input");
boolean canTakeItem = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("allow-output", true), "allow-output");
Property<Boolean> property = (Property<Boolean>) block.getProperty("open");
return new SimpleStorageBlockBehavior(block, title, rows, openSound, closeSound, hasAnalogOutputSignal, canPlaceItem, canTakeItem, property);
}
}
}

View File

@@ -0,0 +1,112 @@
package net.momirealms.craftengine.bukkit.block.behavior;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MFluids;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.DirectionUtils;
import net.momirealms.craftengine.bukkit.util.LocationUtils;
import net.momirealms.craftengine.core.block.BlockBehavior;
import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
import net.momirealms.craftengine.core.block.properties.Property;
import net.momirealms.craftengine.core.block.state.properties.SofaShape;
import net.momirealms.craftengine.core.item.context.BlockPlaceContext;
import net.momirealms.craftengine.core.util.Direction;
import net.momirealms.craftengine.core.util.HorizontalDirection;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.util.VersionHelper;
import net.momirealms.craftengine.core.world.BlockPos;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.Callable;
public class SofaBlockBehavior extends BukkitBlockBehavior {
public static final Factory FACTORY = new Factory();
private final Property<HorizontalDirection> facingProperty;
private final Property<SofaShape> shapeProperty;
public SofaBlockBehavior(CustomBlock block, Property<HorizontalDirection> facing, Property<SofaShape> shape) {
super(block);
this.facingProperty = facing;
this.shapeProperty = shape;
}
@Override
public ImmutableBlockState updateStateForPlacement(BlockPlaceContext context, ImmutableBlockState state) {
BlockPos clickedPos = context.getClickedPos();
ImmutableBlockState blockState = state.owner().value().defaultState()
.with(this.facingProperty, context.getHorizontalDirection().toHorizontalDirection());
if (super.waterloggedProperty != null) {
Object fluidState = FastNMS.INSTANCE.method$BlockGetter$getFluidState(context.getLevel().serverWorld(), LocationUtils.toBlockPos(clickedPos));
blockState = blockState.with(this.waterloggedProperty, FastNMS.INSTANCE.method$FluidState$getType(fluidState) == MFluids.WATER);
}
return blockState.with(this.shapeProperty, getSofaShape(blockState, context.getLevel().serverWorld(), clickedPos));
}
@Override
public Object updateShape(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
Object level = args[updateShape$level];
Object blockPos = args[updateShape$blockPos];
Object blockState = args[0];
Optional<ImmutableBlockState> optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(blockState);
if (optionalCustomState.isEmpty()) return blockState;
ImmutableBlockState customState = optionalCustomState.get();
if (super.waterloggedProperty != null && customState.get(this.waterloggedProperty)) {
FastNMS.INSTANCE.method$ScheduledTickAccess$scheduleFluidTick(args[updateShape$level], args[updateShape$blockPos], MFluids.WATER, 5);
}
Direction direction = DirectionUtils.fromNMSDirection(VersionHelper.isOrAbove1_21_2() ? args[4] : args[1]);
SofaShape sofaShape = getSofaShape(customState, level, LocationUtils.fromBlockPos(blockPos));
return direction.axis().isHorizontal()
? customState.with(this.shapeProperty, sofaShape).customBlockState().literalObject()
: superMethod.call();
}
private SofaShape getSofaShape(ImmutableBlockState state, Object level, BlockPos pos) {
Direction direction = state.get(this.facingProperty).toDirection();
Object relativeBlockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, LocationUtils.toBlockPos(pos.relative(direction.opposite())));
Optional<ImmutableBlockState> optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(relativeBlockState);
if (optionalCustomState.isPresent()) {
ImmutableBlockState customState = optionalCustomState.get();
Optional<SofaBlockBehavior> optionalStairsBlockBehavior = customState.behavior().getAs(SofaBlockBehavior.class);
if (optionalStairsBlockBehavior.isPresent()) {
SofaBlockBehavior stairsBlockBehavior = optionalStairsBlockBehavior.get();
Direction direction1 = customState.get(stairsBlockBehavior.facingProperty).toDirection();
if (direction1.axis() != state.get(this.facingProperty).toDirection().axis() && canTakeShape(state, level, pos, direction1)) {
if (direction1 == direction.counterClockWise()) {
return SofaShape.INNER_LEFT;
}
return SofaShape.INNER_RIGHT;
}
}
}
return SofaShape.STRAIGHT;
}
private boolean canTakeShape(ImmutableBlockState state, Object level, BlockPos pos, Direction face) {
Object blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, LocationUtils.toBlockPos(pos.relative(face)));
Optional<ImmutableBlockState> optionalAnotherState = BlockStateUtils.getOptionalCustomBlockState(blockState);
if (optionalAnotherState.isEmpty()) {
return true;
}
ImmutableBlockState anotherState = optionalAnotherState.get();
Optional<SofaBlockBehavior> optionalBehavior = anotherState.behavior().getAs(SofaBlockBehavior.class);
if (optionalBehavior.isEmpty()) {
return true;
}
SofaBlockBehavior anotherBehavior = optionalBehavior.get();
return anotherState.get(anotherBehavior.facingProperty) != state.get(this.facingProperty);
}
public static class Factory implements BlockBehaviorFactory {
@Override
@SuppressWarnings("unchecked")
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
Property<HorizontalDirection> facing = (Property<HorizontalDirection>) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("facing"), "warning.config.block.behavior.sofa.missing_facing");
Property<SofaShape> shape = (Property<SofaShape>) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("shape"), "warning.config.block.behavior.sofa.missing_shape");
return new SofaBlockBehavior(block, facing, shape);
}
}
}

View File

@@ -68,7 +68,7 @@ public class StackableBlockBehavior extends BukkitBlockBehavior {
private void updateStackableBlock(ImmutableBlockState state, BlockPos pos, World world, Item<ItemStack> item, Player player, InteractionHand hand) {
ImmutableBlockState nextStage = state.cycle(this.amountProperty);
Location location = new Location((org.bukkit.World) world.platformWorld(), pos.x(), pos.y(), pos.z());
FastNMS.INSTANCE.method$LevelWriter$setBlock(world.serverWorld(), LocationUtils.toBlockPos(pos), nextStage.customBlockState().handle(), UpdateOption.UPDATE_ALL.flags());
FastNMS.INSTANCE.method$LevelWriter$setBlock(world.serverWorld(), LocationUtils.toBlockPos(pos), nextStage.customBlockState().literalObject(), UpdateOption.UPDATE_ALL.flags());
if (this.stackSound != null) {
world.playBlockSound(new Vec3d(location.getX(), location.getY(), location.getZ()), this.stackSound);
}

View File

@@ -65,7 +65,7 @@ public class StairsBlockBehavior extends BukkitBlockBehavior {
Direction direction = DirectionUtils.fromNMSDirection(VersionHelper.isOrAbove1_21_2() ? args[4] : args[1]);
StairsShape stairsShape = getStairsShape(customState, level, LocationUtils.fromBlockPos(blockPos));
return direction.axis().isHorizontal()
? customState.with(this.shapeProperty, stairsShape).customBlockState().handle()
? customState.with(this.shapeProperty, stairsShape).customBlockState().literalObject()
: superMethod.call();
}

View File

@@ -0,0 +1,100 @@
package net.momirealms.craftengine.bukkit.block.behavior;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.LocationUtils;
import net.momirealms.craftengine.core.block.BlockBehavior;
import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
import net.momirealms.craftengine.core.block.properties.Property;
import net.momirealms.craftengine.core.entity.player.InteractionResult;
import net.momirealms.craftengine.core.item.context.UseOnContext;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.Callable;
public class ToggleableLampBlockBehavior extends BukkitBlockBehavior {
public static final Factory FACTORY = new Factory();
private final Property<Boolean> litProperty;
private final Property<Boolean> poweredProperty;
private final boolean canOpenWithHand;
public ToggleableLampBlockBehavior(CustomBlock block, Property<Boolean> litProperty, Property<Boolean> poweredProperty, boolean canOpenWithHand) {
super(block);
this.litProperty = litProperty;
this.poweredProperty = poweredProperty;
this.canOpenWithHand = canOpenWithHand;
}
@Override
public InteractionResult useWithoutItem(UseOnContext context, ImmutableBlockState state) {
if (!this.canOpenWithHand) {
return InteractionResult.PASS;
}
ToggleableLampBlockBehavior behavior = state.behavior().getAs(ToggleableLampBlockBehavior.class).orElse(null);
if (behavior == null) return InteractionResult.PASS;
FastNMS.INSTANCE.method$LevelWriter$setBlock(
context.getLevel().serverWorld(),
LocationUtils.toBlockPos(context.getClickedPos()),
state.cycle(behavior.litProperty).customBlockState().literalObject(),
2
);
Optional.ofNullable(context.getPlayer()).ifPresent(p -> p.swingHand(context.getHand()));
return InteractionResult.SUCCESS_AND_CANCEL;
}
@Override
public void onPlace(Object thisBlock, Object[] args, Callable<Object> superMethod) {
if (this.poweredProperty == null) return;
Object state = args[0];
Object level = args[1];
Object pos = args[2];
Object oldState = args[3];
if (FastNMS.INSTANCE.method$BlockState$getBlock(oldState) != FastNMS.INSTANCE.method$BlockState$getBlock(state) && CoreReflections.clazz$ServerLevel.isInstance(level)) {
Optional<ImmutableBlockState> optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(state);
if (optionalCustomState.isEmpty()) return;
checkAndFlip(optionalCustomState.get(), level, pos);
}
}
@Override
public void neighborChanged(Object thisBlock, Object[] args, Callable<Object> superMethod) {
if (this.poweredProperty == null) return;
Object blockState = args[0];
Object world = args[1];
if (!CoreReflections.clazz$ServerLevel.isInstance(world)) return;
Optional<ImmutableBlockState> optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(blockState);
if (optionalCustomState.isEmpty()) return;
Object blockPos = args[2];
ImmutableBlockState customState = optionalCustomState.get();
checkAndFlip(customState, world, blockPos);
}
private void checkAndFlip(ImmutableBlockState customState, Object level, Object pos) {
boolean hasNeighborSignal = FastNMS.INSTANCE.method$SignalGetter$hasNeighborSignal(level, pos);
boolean isPowered = customState.get(this.poweredProperty);
if (hasNeighborSignal != isPowered) {
ImmutableBlockState blockState = customState;
if (!isPowered) {
blockState = blockState.cycle(this.litProperty);
}
FastNMS.INSTANCE.method$LevelWriter$setBlock(level, pos, blockState.with(this.poweredProperty, hasNeighborSignal).customBlockState().literalObject(), 3);
}
}
@SuppressWarnings("unchecked")
public static class Factory implements BlockBehaviorFactory {
@Override
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
boolean canOpenWithHand = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-open-with-hand", false), "can-open-with-hand");
Property<Boolean> lit = (Property<Boolean>) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("lit"), "warning.config.block.behavior.toggleable_lamp.missing_lit");
Property<Boolean> powered = (Property<Boolean>) (canOpenWithHand ? block.getProperty("powered") : ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("powered"), "warning.config.block.behavior.toggleable_lamp.missing_powered"));
return new ToggleableLampBlockBehavior(block, lit, powered, canOpenWithHand);
}
}
}

View File

@@ -118,7 +118,7 @@ public class TrapDoorBlockBehavior extends BukkitBlockBehavior {
private void playerToggle(UseOnContext context, ImmutableBlockState state) {
Player player = context.getPlayer();
this.toggle(state, context.getLevel(), context.getClickedPos(), player);
if (!InteractUtils.isInteractable((org.bukkit.entity.Player) player.platformPlayer(), BlockStateUtils.fromBlockData(state.vanillaBlockState().handle()), context.getHitResult(), (Item<ItemStack>) context.getItem())) {
if (!InteractUtils.isInteractable((org.bukkit.entity.Player) player.platformPlayer(), BlockStateUtils.fromBlockData(state.vanillaBlockState().literalObject()), context.getHitResult(), (Item<ItemStack>) context.getItem())) {
player.swingHand(context.getHand());
}
}
@@ -195,7 +195,7 @@ public class TrapDoorBlockBehavior extends BukkitBlockBehavior {
this.playSound(LocationUtils.fromBlockPos(blockPos), world, hasSignal);
}
FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, customState.with(this.poweredProperty, hasSignal).customBlockState().handle(), UpdateOption.Flags.UPDATE_CLIENTS);
FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, customState.with(this.poweredProperty, hasSignal).customBlockState().literalObject(), UpdateOption.Flags.UPDATE_CLIENTS);
if (this.waterloggedProperty != null && customState.get(this.waterloggedProperty)) {
FastNMS.INSTANCE.method$ScheduledTickAccess$scheduleFluidTick(level, blockPos, MFluids.WATER, 5);
}
@@ -203,7 +203,7 @@ public class TrapDoorBlockBehavior extends BukkitBlockBehavior {
private void toggle(ImmutableBlockState state, World world, BlockPos pos, @Nullable Player player) {
ImmutableBlockState newState = state.cycle(this.openProperty);
FastNMS.INSTANCE.method$LevelWriter$setBlock(world.serverWorld(), LocationUtils.toBlockPos(pos), newState.customBlockState().handle(), UpdateOption.UPDATE_ALL.flags());
FastNMS.INSTANCE.method$LevelWriter$setBlock(world.serverWorld(), LocationUtils.toBlockPos(pos), newState.customBlockState().literalObject(), UpdateOption.UPDATE_ALL.flags());
boolean open = newState.get(this.openProperty);
((org.bukkit.World) world.platformWorld()).sendGameEvent(
player != null ? (org.bukkit.entity.Player) player.platformPlayer() : null,

View File

@@ -4,15 +4,20 @@ import net.momirealms.craftengine.core.block.BlockBehavior;
import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.behavior.AbstractBlockBehavior;
import net.momirealms.craftengine.core.block.behavior.EntityBlockBehavior;
import net.momirealms.craftengine.core.block.behavior.special.FallOnBlockBehavior;
import net.momirealms.craftengine.core.block.behavior.special.PlaceLiquidBlockBehavior;
import net.momirealms.craftengine.core.entity.player.InteractionResult;
import net.momirealms.craftengine.core.item.context.BlockPlaceContext;
import net.momirealms.craftengine.core.item.context.UseOnContext;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.Callable;
public class UnsafeCompositeBlockBehavior extends BukkitBlockBehavior {
public class UnsafeCompositeBlockBehavior extends BukkitBlockBehavior
implements FallOnBlockBehavior, PlaceLiquidBlockBehavior {
private final AbstractBlockBehavior[] behaviors;
public UnsafeCompositeBlockBehavior(CustomBlock customBlock, List<AbstractBlockBehavior> behaviors) {
@@ -20,6 +25,26 @@ public class UnsafeCompositeBlockBehavior extends BukkitBlockBehavior {
this.behaviors = behaviors.toArray(new AbstractBlockBehavior[0]);
}
@Override
public boolean canPlaceLiquid(Object thisBlock, Object[] args, Callable<Object> superMethod) {
for (AbstractBlockBehavior behavior : behaviors) {
if (behavior instanceof PlaceLiquidBlockBehavior) {
return behavior.canPlaceLiquid(thisBlock, args, superMethod);
}
}
return super.canPlaceLiquid(thisBlock, args, superMethod);
}
@Override
public boolean placeLiquid(Object thisBlock, Object[] args, Callable<Object> superMethod) {
for (AbstractBlockBehavior behavior : behaviors) {
if (behavior instanceof PlaceLiquidBlockBehavior) {
return behavior.placeLiquid(thisBlock, args, superMethod);
}
}
return super.placeLiquid(thisBlock, args, superMethod);
}
@SuppressWarnings("unchecked")
@Override
public <T extends BlockBehavior> Optional<T> getAs(Class<T> tClass) {
@@ -31,6 +56,22 @@ public class UnsafeCompositeBlockBehavior extends BukkitBlockBehavior {
return Optional.empty();
}
@Nullable
@Override
public EntityBlockBehavior getEntityBehavior() {
EntityBlockBehavior target = null;
for (AbstractBlockBehavior behavior : this.behaviors) {
if (behavior instanceof EntityBlockBehavior entityBehavior) {
if (target == null) {
target = entityBehavior;
} else {
throw new IllegalArgumentException("Multiple entity block behaviors are not allowed");
}
}
}
return target;
}
@Override
public InteractionResult useOnBlock(UseOnContext context, ImmutableBlockState state) {
for (AbstractBlockBehavior behavior : this.behaviors) {
@@ -74,6 +115,18 @@ public class UnsafeCompositeBlockBehavior extends BukkitBlockBehavior {
return previous;
}
@Override
public Object getContainer(Object thisBlock, Object[] args) throws Exception {
for (AbstractBlockBehavior behavior : this.behaviors) {
Object container = behavior.getContainer(thisBlock, args);
if (container != null) {
return container;
}
}
return null;
}
@Override
public void tick(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
for (AbstractBlockBehavior behavior : this.behaviors) {
@@ -264,6 +317,30 @@ public class UnsafeCompositeBlockBehavior extends BukkitBlockBehavior {
return false;
}
@Override
public boolean hasAnalogOutputSignal(Object thisBlock, Object[] args) throws Exception {
for (AbstractBlockBehavior behavior : this.behaviors) {
if (behavior.hasAnalogOutputSignal(thisBlock, args)) {
return true;
}
}
return false;
}
@Override
public int getAnalogOutputSignal(Object thisBlock, Object[] args) throws Exception {
int signal = 0;
int count = 0;
for (AbstractBlockBehavior behavior : this.behaviors) {
int s = behavior.getAnalogOutputSignal(thisBlock, args);
if (s != 0) {
signal += s;
count++;
}
}
return count == 0 ? 0 : signal / count;
}
@Override
public Object playerWillDestroy(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
Object previous = args[0];
@@ -282,4 +359,26 @@ public class UnsafeCompositeBlockBehavior extends BukkitBlockBehavior {
behavior.spawnAfterBreak(thisBlock, args, superMethod);
}
}
}
@Override
public void fallOn(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
for (AbstractBlockBehavior behavior : this.behaviors) {
if (behavior instanceof FallOnBlockBehavior f) {
f.fallOn(thisBlock, args, superMethod);
return;
}
}
FallOnBlockBehavior.super.fallOn(thisBlock, args, superMethod);
}
@Override
public void updateEntityMovementAfterFallOn(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
for (AbstractBlockBehavior behavior : this.behaviors) {
if (behavior instanceof FallOnBlockBehavior f) {
f.updateEntityMovementAfterFallOn(thisBlock, args, superMethod);
return;
}
}
FallOnBlockBehavior.super.updateEntityMovementAfterFallOn(thisBlock, args, superMethod);
}
}

View File

@@ -66,13 +66,13 @@ public class VerticalCropBlockBehavior extends BukkitBlockBehavior {
if (age >= this.ageProperty.max || RandomUtils.generateRandomFloat(0, 1) < this.growSpeed) {
Object nextPos = this.direction ? LocationUtils.above(blockPos) : LocationUtils.below(blockPos);
if (VersionHelper.isOrAbove1_21_5()) {
CraftBukkitReflections.method$CraftEventFactory$handleBlockGrowEvent.invoke(null, level, nextPos, super.customBlock.defaultState().customBlockState().handle(), UpdateOption.UPDATE_ALL.flags());
CraftBukkitReflections.method$CraftEventFactory$handleBlockGrowEvent.invoke(null, level, nextPos, super.customBlock.defaultState().customBlockState().literalObject(), UpdateOption.UPDATE_ALL.flags());
} else {
CraftBukkitReflections.method$CraftEventFactory$handleBlockGrowEvent.invoke(null, level, nextPos, super.customBlock.defaultState().customBlockState().handle());
CraftBukkitReflections.method$CraftEventFactory$handleBlockGrowEvent.invoke(null, level, nextPos, super.customBlock.defaultState().customBlockState().literalObject());
}
FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, currentState.with(this.ageProperty, this.ageProperty.min).customBlockState().handle(), UpdateOption.UPDATE_NONE.flags());
FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, currentState.with(this.ageProperty, this.ageProperty.min).customBlockState().literalObject(), UpdateOption.UPDATE_NONE.flags());
} else if (RandomUtils.generateRandomFloat(0, 1) < this.growSpeed) {
FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, currentState.with(this.ageProperty, age + 1).customBlockState().handle(), UpdateOption.UPDATE_NONE.flags());
FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, currentState.with(this.ageProperty, age + 1).customBlockState().literalObject(), UpdateOption.UPDATE_NONE.flags());
}
}
}
@@ -83,7 +83,7 @@ public class VerticalCropBlockBehavior extends BukkitBlockBehavior {
@SuppressWarnings("unchecked")
@Override
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
Property<Integer> ageProperty = (Property<Integer>) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("age"), "warning.config.block.behavior.sugar_cane.missing_age");
Property<Integer> ageProperty = (Property<Integer>) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("age"), "warning.config.block.behavior.vertical_crop.missing_age");
int maxHeight = ResourceConfigUtils.getAsInt(arguments.getOrDefault("max-height", 3), "max-height");
boolean direction = arguments.getOrDefault("direction", "up").toString().equalsIgnoreCase("up");
return new VerticalCropBlockBehavior(block, ageProperty, maxHeight,

View File

@@ -0,0 +1,78 @@
package net.momirealms.craftengine.bukkit.block.behavior;
import net.momirealms.craftengine.bukkit.block.entity.BukkitBlockEntityTypes;
import net.momirealms.craftengine.bukkit.block.entity.WallTorchParticleBlockEntity;
import net.momirealms.craftengine.core.block.BlockBehavior;
import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
import net.momirealms.craftengine.core.block.behavior.EntityBlockBehavior;
import net.momirealms.craftengine.core.block.entity.BlockEntity;
import net.momirealms.craftengine.core.block.entity.BlockEntityType;
import net.momirealms.craftengine.core.block.entity.tick.BlockEntityTicker;
import net.momirealms.craftengine.core.block.properties.Property;
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
import net.momirealms.craftengine.core.util.HorizontalDirection;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.CEWorld;
import net.momirealms.craftengine.core.world.particle.ParticleConfig;
import java.util.List;
import java.util.Map;
public class WallTorchParticleBlockBehavior extends BukkitBlockBehavior implements EntityBlockBehavior {
public static final Factory FACTORY = new Factory();
public final ParticleConfig[] particles;
public final int tickInterval;
public final Property<HorizontalDirection> facingProperty;
public WallTorchParticleBlockBehavior(CustomBlock customBlock, ParticleConfig[] particles, int tickInterval, Property<HorizontalDirection> facingProperty) {
super(customBlock);
this.particles = particles;
this.tickInterval = tickInterval;
this.facingProperty = facingProperty;
}
public ParticleConfig[] particles() {
return this.particles;
}
public int tickInterval() {
return tickInterval;
}
public Property<HorizontalDirection> facingProperty() {
return facingProperty;
}
@Override
public <T extends BlockEntity> BlockEntityType<T> blockEntityType() {
return EntityBlockBehavior.blockEntityTypeHelper(BukkitBlockEntityTypes.WALL_TORCH_PARTICLE);
}
@Override
public BlockEntity createBlockEntity(BlockPos pos, ImmutableBlockState state) {
return new WallTorchParticleBlockEntity(pos, state);
}
@Override
public <T extends BlockEntity> BlockEntityTicker<T> createBlockEntityTicker(CEWorld level, ImmutableBlockState state, BlockEntityType<T> blockEntityType) {
if (this.particles.length == 0) return null;
return EntityBlockBehavior.createTickerHelper(WallTorchParticleBlockEntity::tick);
}
public static class Factory implements BlockBehaviorFactory {
@SuppressWarnings("unchecked")
@Override
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
List<ParticleConfig> particles = ResourceConfigUtils.parseConfigAsList(ResourceConfigUtils.get(arguments, "particles", "particle"), ParticleConfig::fromMap$blockEntity);
int tickInterval = ResourceConfigUtils.getAsInt(arguments.getOrDefault("tick-interval", 10), "tick-interval");
Property<HorizontalDirection> directionProperty = (Property<HorizontalDirection>) block.getProperty("facing");
if (directionProperty == null) {
throw new LocalizedResourceConfigException("warning.config.block.behavior.wall_torch_particle.missing_facing");
}
return new WallTorchParticleBlockBehavior(block, particles.toArray(new ParticleConfig[0]), tickInterval, directionProperty);
}
}
}

View File

@@ -0,0 +1,14 @@
package net.momirealms.craftengine.bukkit.block.entity;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.entity.BlockEntity;
import net.momirealms.craftengine.core.block.entity.BlockEntityType;
import net.momirealms.craftengine.core.world.BlockPos;
public abstract class AbstractAnimateTickBlockEntity extends BlockEntity {
protected int tickCount;
public AbstractAnimateTickBlockEntity(BlockEntityType<? extends BlockEntity> type, BlockPos pos, ImmutableBlockState blockState) {
super(type, pos, blockState);
}
}

View File

@@ -0,0 +1,28 @@
package net.momirealms.craftengine.bukkit.block.entity;
import net.momirealms.craftengine.core.block.entity.BlockEntity;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.jetbrains.annotations.NotNull;
public class BlockEntityHolder implements InventoryHolder {
private final BlockEntity blockEntity;
private Inventory inventory;
public BlockEntityHolder(BlockEntity entity) {
this.blockEntity = entity;
}
public BlockEntity blockEntity() {
return blockEntity;
}
public void setInventory(Inventory inventory) {
this.inventory = inventory;
}
@Override
public @NotNull Inventory getInventory() {
return this.inventory;
}
}

View File

@@ -0,0 +1,11 @@
package net.momirealms.craftengine.bukkit.block.entity;
import net.momirealms.craftengine.core.block.entity.BlockEntityType;
import net.momirealms.craftengine.core.block.entity.BlockEntityTypeKeys;
import net.momirealms.craftengine.core.block.entity.BlockEntityTypes;
public class BukkitBlockEntityTypes extends BlockEntityTypes {
public static final BlockEntityType<SimpleStorageBlockEntity> SIMPLE_STORAGE = register(BlockEntityTypeKeys.SIMPLE_STORAGE, SimpleStorageBlockEntity::new);
public static final BlockEntityType<SimpleParticleBlockEntity> SIMPLE_PARTICLE = register(BlockEntityTypeKeys.SIMPLE_PARTICLE, SimpleParticleBlockEntity::new);
public static final BlockEntityType<WallTorchParticleBlockEntity> WALL_TORCH_PARTICLE = register(BlockEntityTypeKeys.WALL_TORCH_PARTICLE, WallTorchParticleBlockEntity::new);
}

View File

@@ -0,0 +1,45 @@
package net.momirealms.craftengine.bukkit.block.entity;
import net.momirealms.craftengine.bukkit.block.behavior.SimpleParticleBlockBehavior;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.plugin.context.Context;
import net.momirealms.craftengine.core.plugin.context.ContextHolder;
import net.momirealms.craftengine.core.plugin.context.SimpleContext;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.CEWorld;
import net.momirealms.craftengine.core.world.Vec3d;
import net.momirealms.craftengine.core.world.World;
import net.momirealms.craftengine.core.world.particle.ParticleConfig;
public class SimpleParticleBlockEntity extends AbstractAnimateTickBlockEntity {
private final SimpleParticleBlockBehavior behavior;
private final Context context = SimpleContext.of(ContextHolder.empty());
public SimpleParticleBlockEntity(BlockPos pos, ImmutableBlockState blockState) {
super(BukkitBlockEntityTypes.SIMPLE_PARTICLE, pos, blockState);
this.behavior = blockState.behavior().getAs(SimpleParticleBlockBehavior.class).orElseThrow();
}
public void animateTick(ImmutableBlockState state, World level, BlockPos pos) {
for (ParticleConfig particle : this.behavior.particles) {
Vec3d location = new Vec3d(super.pos.x() + particle.x.getDouble(context), super.pos.y() + particle.y.getDouble(context), super.pos.z() + particle.z.getDouble(context));
level.spawnParticle(
location,
particle.particleType,
particle.count.getInt(context),
particle.xOffset.getDouble(context),
particle.yOffset.getDouble(context),
particle.zOffset.getDouble(context),
particle.speed.getDouble(context),
particle.particleData,
context
);
}
}
public static void tick(CEWorld ceWorld, BlockPos blockPos, ImmutableBlockState state, SimpleParticleBlockEntity particle) {
particle.tickCount++;
if (particle.tickCount % particle.behavior.tickInterval != 0) return;
particle.animateTick(state, ceWorld.world(), blockPos);
}
}

View File

@@ -0,0 +1,218 @@
package net.momirealms.craftengine.bukkit.block.entity;
import net.momirealms.craftengine.bukkit.api.BukkitAdaptors;
import net.momirealms.craftengine.bukkit.block.behavior.SimpleStorageBlockBehavior;
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistryOps;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.LocationUtils;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.UpdateOption;
import net.momirealms.craftengine.core.block.entity.BlockEntity;
import net.momirealms.craftengine.core.block.properties.Property;
import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.sound.SoundData;
import net.momirealms.craftengine.core.util.VersionHelper;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.Vec3d;
import net.momirealms.sparrow.nbt.CompoundTag;
import net.momirealms.sparrow.nbt.ListTag;
import org.bukkit.GameEvent;
import org.bukkit.GameMode;
import org.bukkit.entity.HumanEntity;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Optional;
public class SimpleStorageBlockEntity extends BlockEntity {
private final SimpleStorageBlockBehavior behavior;
private final Inventory inventory;
private double maxInteractionDistance;
private boolean openState = false;
public SimpleStorageBlockEntity(BlockPos pos, ImmutableBlockState blockState) {
super(BukkitBlockEntityTypes.SIMPLE_STORAGE, pos, blockState);
this.behavior = super.blockState.behavior().getAs(SimpleStorageBlockBehavior.class).orElseThrow();
BlockEntityHolder holder = new BlockEntityHolder(this);
this.inventory = FastNMS.INSTANCE.createSimpleStorageContainer(holder, this.behavior.rows() * 9, this.behavior.canPlaceItem(), this.behavior.canTakeItem());
holder.setInventory(this.inventory);
}
@Override
protected void saveCustomData(CompoundTag tag) {
// 保存前先把所有打开此容器的玩家界面关闭
this.inventory.close();
ListTag itemsTag = new ListTag();
@Nullable ItemStack[] storageContents = this.inventory.getStorageContents();
for (int i = 0; i < storageContents.length; i++) {
if (storageContents[i] != null) {
if (VersionHelper.isOrAbove1_20_5()) {
int slot = i;
CoreReflections.instance$ItemStack$CODEC.encodeStart(MRegistryOps.SPARROW_NBT, FastNMS.INSTANCE.field$CraftItemStack$handle(storageContents[i]))
.ifSuccess(success -> {
CompoundTag itemTag = (CompoundTag) success;
itemTag.putInt("slot", slot);
itemsTag.add(itemTag);
})
.ifError(error -> CraftEngine.instance().logger().severe("Error while saving storage item: " + error));
} else {
Object nmsTag = FastNMS.INSTANCE.method$itemStack$save(FastNMS.INSTANCE.field$CraftItemStack$handle(storageContents[i]), FastNMS.INSTANCE.constructor$CompoundTag());
CompoundTag itemTag = (CompoundTag) MRegistryOps.NBT.convertTo(MRegistryOps.SPARROW_NBT, nmsTag);
itemTag.putInt("slot", i);
itemsTag.add(itemTag);
}
}
}
tag.put("items", itemsTag);
}
@Override
public void loadCustomData(CompoundTag tag) {
ListTag itemsTag = Optional.ofNullable(tag.getList("items")).orElseGet(ListTag::new);
ItemStack[] storageContents = new ItemStack[this.behavior.rows() * 9];
for (int i = 0; i < itemsTag.size(); i++) {
CompoundTag itemTag = itemsTag.getCompound(i);
int slot = itemTag.getInt("slot");
if (slot < 0 || slot >= storageContents.length) {
continue;
}
if (VersionHelper.isOrAbove1_20_5()) {
CoreReflections.instance$ItemStack$CODEC.parse(MRegistryOps.SPARROW_NBT, itemTag)
.resultOrPartial((s) -> CraftEngine.instance().logger().severe("Tried to load invalid item: '" + itemTag + "'. " + s))
.ifPresent(nmsStack -> storageContents[slot] = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(nmsStack));
} else {
Object nmsTag = MRegistryOps.SPARROW_NBT.convertTo(MRegistryOps.NBT, itemTag);
Object itemStack = FastNMS.INSTANCE.method$ItemStack$of(nmsTag);
storageContents[slot] = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(itemStack);
}
}
this.inventory.setStorageContents(storageContents);
}
public Inventory inventory() {
if (!isValid()) return null;
return this.inventory;
}
public void onPlayerOpen(Player player) {
if (!isValidContainer()) return;
if (!player.isSpectatorMode()) {
// 有非观察者的人,那么就不触发开启音效和事件
if (!hasNoViewer(this.inventory.getViewers())) return;
this.maxInteractionDistance = Math.max(player.getCachedInteractionRange(), this.maxInteractionDistance);
this.setOpen(player);
FastNMS.INSTANCE.method$ScheduledTickAccess$scheduleBlockTick(super.world.world().serverWorld(), LocationUtils.toBlockPos(this.pos), BlockStateUtils.getBlockOwner(this.blockState.customBlockState().literalObject()), 5);
}
}
public void onPlayerClose(Player player) {
if (!isValidContainer()) return;
if (!player.isSpectatorMode()) {
// 有非观察者的人,那么就不触发关闭音效和事件
for (HumanEntity viewer : this.inventory.getViewers()) {
if (viewer.getGameMode() == GameMode.SPECTATOR || viewer == player.platformPlayer()) {
continue;
}
return;
}
this.maxInteractionDistance = 0;
this.setClose(player);
}
}
private void setOpen(@Nullable Player player) {
this.updateOpenBlockState(true);
org.bukkit.World bukkitWorld = (org.bukkit.World) super.world.world().platformWorld();
if (player != null) {
bukkitWorld.sendGameEvent((org.bukkit.entity.Player) player.platformPlayer(), GameEvent.CONTAINER_OPEN, new Vector(this.pos.x(), this.pos.y(), this.pos.z()));
} else {
bukkitWorld.sendGameEvent(null, GameEvent.CONTAINER_OPEN, new Vector(this.pos.x(), this.pos.y(), this.pos.z()));
}
this.openState = true;
SoundData soundData = this.behavior.openSound();
if (soundData != null) {
super.world.world().playBlockSound(Vec3d.atCenterOf(this.pos), soundData);
}
}
private void setClose(@Nullable Player player) {
this.updateOpenBlockState(false);
org.bukkit.World bukkitWorld = (org.bukkit.World) super.world.world().platformWorld();
if (player != null) {
bukkitWorld.sendGameEvent((org.bukkit.entity.Player) player.platformPlayer(), GameEvent.CONTAINER_CLOSE, new Vector(this.pos.x(), this.pos.y(), this.pos.z()));
} else {
bukkitWorld.sendGameEvent(null, GameEvent.CONTAINER_CLOSE, new Vector(this.pos.x(), this.pos.y(), this.pos.z()));
}
this.openState = false;
SoundData soundData = this.behavior.closeSound();
if (soundData != null) {
super.world.world().playBlockSound(Vec3d.atCenterOf(this.pos), soundData);
}
}
private boolean hasNoViewer(List<HumanEntity> viewers) {
for (HumanEntity viewer : viewers) {
if (viewer.getGameMode() != GameMode.SPECTATOR) {
return false;
}
}
return true;
}
private boolean isValidContainer() {
return this.isValid() && this.inventory != null && this.behavior != null;
}
public void updateOpenBlockState(boolean open) {
ImmutableBlockState state = super.world.getBlockStateAtIfLoaded(this.pos);
if (state == null || state.behavior() != this.behavior) return;
Property<Boolean> property = this.behavior.openProperty();
if (property == null) return;
super.world.world().setBlockAt(this.pos.x(), this.pos.y(), this.pos.z(), state.with(property, open), UpdateOption.UPDATE_ALL.flags());
}
public void checkOpeners(Object level, Object pos, Object blockState) {
if (!this.isValidContainer()) return;
double maxInteractionDistance = 0d;
List<HumanEntity> viewers = this.inventory.getViewers();
int validViewers = 0;
for (HumanEntity viewer : viewers) {
if (viewer instanceof org.bukkit.entity.Player player) {
maxInteractionDistance = Math.max(BukkitAdaptors.adapt(player).getCachedInteractionRange(), maxInteractionDistance);
if (player.getGameMode() != GameMode.SPECTATOR) {
validViewers++;
}
}
}
boolean shouldOpen = validViewers != 0;
if (shouldOpen && !this.openState) {
this.setOpen(null);
} else if (!shouldOpen && this.openState) {
this.setClose(null);
}
this.maxInteractionDistance = maxInteractionDistance;
if (!viewers.isEmpty()) {
FastNMS.INSTANCE.method$ScheduledTickAccess$scheduleBlockTick(level, pos, BlockStateUtils.getBlockOwner(blockState), 5);
}
}
@Override
public void preRemove() {
this.inventory.close();
Vec3d pos = Vec3d.atCenterOf(this.pos);
for (ItemStack stack : this.inventory.getContents()) {
if (stack != null) {
super.world.world().dropItemNaturally(pos, BukkitItemManager.instance().wrap(stack));
}
}
this.inventory.clear();
}
}

View File

@@ -0,0 +1,55 @@
package net.momirealms.craftengine.bukkit.block.entity;
import net.momirealms.craftengine.bukkit.block.behavior.WallTorchParticleBlockBehavior;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.plugin.context.Context;
import net.momirealms.craftengine.core.plugin.context.ContextHolder;
import net.momirealms.craftengine.core.plugin.context.SimpleContext;
import net.momirealms.craftengine.core.util.Direction;
import net.momirealms.craftengine.core.util.HorizontalDirection;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.CEWorld;
import net.momirealms.craftengine.core.world.Vec3d;
import net.momirealms.craftengine.core.world.World;
import net.momirealms.craftengine.core.world.particle.ParticleConfig;
public class WallTorchParticleBlockEntity extends AbstractAnimateTickBlockEntity {
private final WallTorchParticleBlockBehavior behavior;
private final Context context = SimpleContext.of(ContextHolder.empty());
public WallTorchParticleBlockEntity(BlockPos pos, ImmutableBlockState blockState) {
super(BukkitBlockEntityTypes.WALL_TORCH_PARTICLE, pos, blockState);
this.behavior = blockState.behavior().getAs(WallTorchParticleBlockBehavior.class).orElseThrow();
}
public void animateTick(ImmutableBlockState state, World level, BlockPos pos) {
HorizontalDirection direction = state.get(this.behavior.facingProperty);
if (direction == null) return;
Vec3d center = Vec3d.atCenterOf(pos);
HorizontalDirection opposite = direction.opposite();
for (ParticleConfig particle : this.behavior.particles) {
Vec3d location = new Vec3d(
center.x() + particle.x.getDouble(context) * opposite.stepX(),
center.y() + particle.y.getDouble(context),
center.z() + particle.z.getDouble(context) * opposite.stepZ()
);
level.spawnParticle(
location,
particle.particleType,
particle.count.getInt(context),
particle.xOffset.getDouble(context),
particle.yOffset.getDouble(context),
particle.zOffset.getDouble(context),
particle.speed.getDouble(context),
particle.particleData,
context
);
}
}
public static void tick(CEWorld ceWorld, BlockPos blockPos, ImmutableBlockState state, WallTorchParticleBlockEntity particle) {
particle.tickCount++;
if (particle.tickCount % particle.behavior.tickInterval != 0) return;
particle.animateTick(state, ceWorld.world(), blockPos);
}
}

View File

@@ -0,0 +1,14 @@
package net.momirealms.craftengine.bukkit.block.entity.renderer.element;
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfigs;
public class BukkitBlockEntityElementConfigs extends BlockEntityElementConfigs {
static {
register(ITEM_DISPLAY, ItemDisplayBlockEntityElementConfig.FACTORY);
register(TEXT_DISPLAY, TextDisplayBlockEntityElementConfig.FACTORY);
}
public static void init() {
}
}

View File

@@ -0,0 +1,42 @@
package net.momirealms.craftengine.bukkit.block.entity.renderer.element;
import it.unimi.dsi.fastutil.ints.IntList;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes;
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElement;
import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.world.BlockPos;
import org.joml.Vector3f;
import java.util.List;
import java.util.UUID;
public class ItemDisplayBlockEntityElement implements BlockEntityElement {
private final ItemDisplayBlockEntityElementConfig config;
private final Object cachedSpawnPacket;
private final Object cachedDespawnPacket;
private final int entityId;
public ItemDisplayBlockEntityElement(ItemDisplayBlockEntityElementConfig config, BlockPos pos) {
int entityId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet();
Vector3f position = config.position();
this.cachedSpawnPacket = FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
entityId, UUID.randomUUID(), pos.x() + position.x, pos.y() + position.y, pos.z() + position.z,
config.xRot(), config.yRot(), MEntityTypes.ITEM_DISPLAY, 0, CoreReflections.instance$Vec3$Zero, 0
);
this.config = config;
this.cachedDespawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(IntList.of(entityId));
this.entityId = entityId;
}
@Override
public void hide(Player player) {
player.sendPacket(this.cachedDespawnPacket, false);
}
@Override
public void show(Player player) {
player.sendPackets(List.of(this.cachedSpawnPacket, FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId, this.config.metadataValues(player))), true);
}
}

View File

@@ -0,0 +1,132 @@
package net.momirealms.craftengine.bukkit.block.entity.renderer.element;
import net.momirealms.craftengine.bukkit.entity.data.ItemDisplayEntityData;
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElement;
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfig;
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfigFactory;
import net.momirealms.craftengine.core.entity.Billboard;
import net.momirealms.craftengine.core.entity.ItemDisplayContext;
import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.World;
import org.joml.Quaternionf;
import org.joml.Vector3f;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.function.Function;
public class ItemDisplayBlockEntityElementConfig implements BlockEntityElementConfig<ItemDisplayBlockEntityElement> {
public static final Factory FACTORY = new Factory();
private final Function<Player, List<Object>> lazyMetadataPacket;
private final Function<Player, Item<?>> item;
private final Vector3f scale;
private final Vector3f position;
private final Vector3f translation;
private final float xRot;
private final float yRot;
private final Quaternionf rotation;
private final ItemDisplayContext displayContext;
private final Billboard billboard;
public ItemDisplayBlockEntityElementConfig(Function<Player, Item<?>> item,
Vector3f scale,
Vector3f position,
Vector3f translation,
float xRot,
float yRot,
Quaternionf rotation,
ItemDisplayContext displayContext,
Billboard billboard) {
this.item = item;
this.scale = scale;
this.position = position;
this.translation = translation;
this.xRot = xRot;
this.yRot = yRot;
this.rotation = rotation;
this.displayContext = displayContext;
this.billboard = billboard;
this.lazyMetadataPacket = player -> {
List<Object> dataValues = new ArrayList<>();
ItemDisplayEntityData.DisplayedItem.addEntityDataIfNotDefaultValue(item.apply(player).getLiteralObject(), dataValues);
ItemDisplayEntityData.Scale.addEntityDataIfNotDefaultValue(this.scale, dataValues);
ItemDisplayEntityData.RotationLeft.addEntityDataIfNotDefaultValue(this.rotation, dataValues);
ItemDisplayEntityData.BillboardConstraints.addEntityDataIfNotDefaultValue(this.billboard.id(), dataValues);
ItemDisplayEntityData.Translation.addEntityDataIfNotDefaultValue(this.translation, dataValues);
ItemDisplayEntityData.DisplayType.addEntityDataIfNotDefaultValue(this.displayContext.id(), dataValues);
return dataValues;
};
}
@Override
public ItemDisplayBlockEntityElement create(World world, BlockPos pos) {
return new ItemDisplayBlockEntityElement(this, pos);
}
public Item<?> item(Player player) {
return this.item.apply(player);
}
public Vector3f scale() {
return this.scale;
}
public Vector3f translation() {
return this.translation;
}
public Vector3f position() {
return this.position;
}
public float yRot() {
return this.yRot;
}
public float xRot() {
return this.xRot;
}
public Billboard billboard() {
return billboard;
}
public ItemDisplayContext displayContext() {
return displayContext;
}
public Quaternionf rotation() {
return rotation;
}
public List<Object> metadataValues(Player player) {
return this.lazyMetadataPacket.apply(player);
}
public static class Factory implements BlockEntityElementConfigFactory {
@SuppressWarnings("unchecked")
@Override
public <E extends BlockEntityElement> BlockEntityElementConfig<E> create(Map<String, Object> arguments) {
Key itemId = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("item"), "warning.config.block.state.entity_renderer.item_display.missing_item"));
return (BlockEntityElementConfig<E>) new ItemDisplayBlockEntityElementConfig(
player -> BukkitItemManager.instance().createWrappedItem(itemId, player),
ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("scale", 1f), "scale"),
ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0.5f), "position"),
ResourceConfigUtils.getAsVector3f(arguments.get("translation"), "translation"),
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("pitch", 0f), "pitch"),
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("yaw", 0f), "yaw"),
ResourceConfigUtils.getAsQuaternionf(arguments.getOrDefault("rotation", 0f), "rotation"),
ItemDisplayContext.valueOf(arguments.getOrDefault("display-context", "none").toString().toUpperCase(Locale.ROOT)),
Billboard.valueOf(arguments.getOrDefault("billboard", "fixed").toString().toUpperCase(Locale.ROOT))
);
}
}
}

View File

@@ -0,0 +1,42 @@
package net.momirealms.craftengine.bukkit.block.entity.renderer.element;
import it.unimi.dsi.fastutil.ints.IntList;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes;
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElement;
import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.world.BlockPos;
import org.joml.Vector3f;
import java.util.List;
import java.util.UUID;
public class TextDisplayBlockEntityElement implements BlockEntityElement {
private final TextDisplayBlockEntityElementConfig config;
private final Object cachedSpawnPacket;
private final Object cachedDespawnPacket;
private final int entityId;
public TextDisplayBlockEntityElement(TextDisplayBlockEntityElementConfig config, BlockPos pos) {
int entityId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet();
Vector3f position = config.position();
this.cachedSpawnPacket = FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
entityId, UUID.randomUUID(), pos.x() + position.x, pos.y() + position.y, pos.z() + position.z,
config.xRot(), config.yRot(), MEntityTypes.TEXT_DISPLAY, 0, CoreReflections.instance$Vec3$Zero, 0
);
this.config = config;
this.cachedDespawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(IntList.of(entityId));
this.entityId = entityId;
}
@Override
public void hide(Player player) {
player.sendPacket(this.cachedDespawnPacket, false);
}
@Override
public void show(Player player) {
player.sendPackets(List.of(this.cachedSpawnPacket, FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId, this.config.metadataValues(player))), true);
}
}

View File

@@ -0,0 +1,123 @@
package net.momirealms.craftengine.bukkit.block.entity.renderer.element;
import net.kyori.adventure.text.Component;
import net.momirealms.craftengine.bukkit.entity.data.TextDisplayEntityData;
import net.momirealms.craftengine.bukkit.util.ComponentUtils;
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElement;
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfig;
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfigFactory;
import net.momirealms.craftengine.core.entity.Billboard;
import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext;
import net.momirealms.craftengine.core.util.AdventureHelper;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.World;
import org.joml.Quaternionf;
import org.joml.Vector3f;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.function.Function;
public class TextDisplayBlockEntityElementConfig implements BlockEntityElementConfig<TextDisplayBlockEntityElement> {
public static final Factory FACTORY = new Factory();
private final Function<Player, List<Object>> lazyMetadataPacket;
private final String text;
private final Vector3f scale;
private final Vector3f position;
private final Vector3f translation;
private final float xRot;
private final float yRot;
private final Quaternionf rotation;
private final Billboard billboard;
public TextDisplayBlockEntityElementConfig(String text,
Vector3f scale,
Vector3f position,
Vector3f translation,
float xRot,
float yRot,
Quaternionf rotation,
Billboard billboard) {
this.text = text;
this.scale = scale;
this.position = position;
this.translation = translation;
this.xRot = xRot;
this.yRot = yRot;
this.rotation = rotation;
this.billboard = billboard;
this.lazyMetadataPacket = player -> {
List<Object> dataValues = new ArrayList<>();
TextDisplayEntityData.Text.addEntityDataIfNotDefaultValue(ComponentUtils.adventureToMinecraft(text(player)), dataValues);
TextDisplayEntityData.Scale.addEntityDataIfNotDefaultValue(this.scale, dataValues);
TextDisplayEntityData.RotationLeft.addEntityDataIfNotDefaultValue(this.rotation, dataValues);
TextDisplayEntityData.BillboardConstraints.addEntityDataIfNotDefaultValue(this.billboard.id(), dataValues);
TextDisplayEntityData.Translation.addEntityDataIfNotDefaultValue(this.translation, dataValues);
return dataValues;
};
}
@Override
public TextDisplayBlockEntityElement create(World world, BlockPos pos) {
return new TextDisplayBlockEntityElement(this, pos);
}
public Component text(Player player) {
return AdventureHelper.miniMessage().deserialize(this.text, PlayerOptionalContext.of(player).tagResolvers());
}
public Vector3f scale() {
return this.scale;
}
public Vector3f translation() {
return this.translation;
}
public Vector3f position() {
return this.position;
}
public float yRot() {
return this.yRot;
}
public float xRot() {
return this.xRot;
}
public Billboard billboard() {
return billboard;
}
public Quaternionf rotation() {
return rotation;
}
public List<Object> metadataValues(Player player) {
return this.lazyMetadataPacket.apply(player);
}
public static class Factory implements BlockEntityElementConfigFactory {
@SuppressWarnings("unchecked")
@Override
public <E extends BlockEntityElement> BlockEntityElementConfig<E> create(Map<String, Object> arguments) {
String text = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("text"), "warning.config.block.state.entity_renderer.text_display.missing_text");
return (BlockEntityElementConfig<E>) new TextDisplayBlockEntityElementConfig(
text,
ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("scale", 1f), "scale"),
ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0.5f), "position"),
ResourceConfigUtils.getAsVector3f(arguments.get("translation"), "translation"),
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("pitch", 0f), "pitch"),
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("yaw", 0f), "yaw"),
ResourceConfigUtils.getAsQuaternionf(arguments.getOrDefault("rotation", 0f), "rotation"),
Billboard.valueOf(arguments.getOrDefault("billboard", "fixed").toString().toUpperCase(Locale.ROOT))
);
}
}
}

View File

@@ -1,6 +1,6 @@
package net.momirealms.craftengine.bukkit.entity;
import net.momirealms.craftengine.bukkit.util.KeyUtils;
import net.momirealms.craftengine.bukkit.util.EntityUtils;
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
import net.momirealms.craftengine.core.entity.AbstractEntity;
import net.momirealms.craftengine.core.util.Direction;
@@ -68,7 +68,7 @@ public class BukkitEntity extends AbstractEntity {
@Override
public Key type() {
return KeyUtils.namespacedKey2Key(literalObject().getType().getKey());
return EntityUtils.getEntityType(literalObject());
}
@Override

View File

@@ -15,10 +15,10 @@ public class DisplayEntityData<T> extends BaseEntityData<T> {
public static final DisplayEntityData<Integer> TransformationInterpolationDuration = of(DisplayEntityData.class, EntityDataValue.Serializers$INT, 0, VersionHelper.isOrAbove1_20_2());
public static final DisplayEntityData<Integer> PositionRotationInterpolationDuration = of(DisplayEntityData.class, EntityDataValue.Serializers$INT, 0, VersionHelper.isOrAbove1_20_2());
public static final DisplayEntityData<Object> Translation = of(DisplayEntityData.class, EntityDataValue.Serializers$VECTOR3, new Vector3f(0f), true);
public static final DisplayEntityData<Object> Scale = of(DisplayEntityData.class, EntityDataValue.Serializers$VECTOR3, new Vector3f(1f), true);
public static final DisplayEntityData<Object> RotationLeft = of(DisplayEntityData.class, EntityDataValue.Serializers$QUATERNION, new Quaternionf(0f, 0f, 0f, 1f), true);
public static final DisplayEntityData<Object> RotationRight = of(DisplayEntityData.class, EntityDataValue.Serializers$QUATERNION, new Quaternionf(0f, 0f, 0f, 1f), true);
public static final DisplayEntityData<Vector3f> Translation = of(DisplayEntityData.class, EntityDataValue.Serializers$VECTOR3, new Vector3f(0f), true);
public static final DisplayEntityData<Vector3f> Scale = of(DisplayEntityData.class, EntityDataValue.Serializers$VECTOR3, new Vector3f(1f), true);
public static final DisplayEntityData<Quaternionf> RotationLeft = of(DisplayEntityData.class, EntityDataValue.Serializers$QUATERNION, new Quaternionf(0f, 0f, 0f, 1f), true);
public static final DisplayEntityData<Quaternionf> RotationRight = of(DisplayEntityData.class, EntityDataValue.Serializers$QUATERNION, new Quaternionf(0f, 0f, 0f, 1f), true);
/**
* Billboard Constraints (0 = FIXED, 1 = VERTICAL, 2 = HORIZONTAL, 3 = CENTER)
*/

View File

@@ -15,7 +15,7 @@ import net.momirealms.craftengine.core.entity.furniture.*;
import net.momirealms.craftengine.core.plugin.config.Config;
import net.momirealms.craftengine.core.sound.SoundData;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.MiscUtils;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.util.VersionHelper;
import net.momirealms.craftengine.core.world.WorldPosition;
import org.bukkit.*;
@@ -337,6 +337,7 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
plugin.scheduler().sync().runDelayed(() -> tryLeavingSeat(player, entity), player.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4);
}
@SuppressWarnings("DuplicatedCode")
protected void tryLeavingSeat(@NotNull Player player, @NotNull Entity vehicle) {
Integer baseFurniture = vehicle.getPersistentDataContainer().get(FURNITURE_SEAT_BASE_ENTITY_KEY, PersistentDataType.INTEGER);
if (baseFurniture == null) return;
@@ -350,7 +351,7 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
plugin.logger().warn("Failed to get vector3f for player " + player.getName() + "'s seat");
return;
}
Vector3f seatPos = MiscUtils.getAsVector3f(vector3f, "seat");
Vector3f seatPos = ResourceConfigUtils.getAsVector3f(vector3f, "seat");
furniture.removeOccupiedSeat(seatPos);
if (player.getVehicle() != null) return;
@@ -375,6 +376,7 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
return (entity instanceof ArmorStand || entity instanceof ItemDisplay);
}
@SuppressWarnings("DuplicatedCode")
private boolean isSafeLocation(Location location) {
World world = location.getWorld();
if (world == null) return false;
@@ -386,6 +388,7 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
return world.getBlockAt(x, y + 1, z).isPassable();
}
@SuppressWarnings("DuplicatedCode")
@Nullable
private Location findSafeLocationNearby(Location center) {
World world = center.getWorld();

View File

@@ -8,7 +8,6 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkRefl
import net.momirealms.craftengine.core.entity.furniture.*;
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.MiscUtils;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.util.VersionHelper;
import net.momirealms.craftengine.core.world.WorldPosition;
@@ -84,7 +83,7 @@ public class CustomHitBox extends AbstractHitBox {
@Override
public HitBox create(Map<String, Object> arguments) {
Vector3f position = MiscUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position");
Vector3f position = ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position");
float scale = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("scale", 1), "scale");
String type = (String) arguments.getOrDefault("entity-type", "slime");
EntityType entityType = Registry.ENTITY_TYPE.get(new NamespacedKey("minecraft", type));

View File

@@ -9,7 +9,6 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityType
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections;
import net.momirealms.craftengine.core.entity.furniture.*;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.MiscUtils;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.util.VersionHelper;
import net.momirealms.craftengine.core.world.World;
@@ -124,7 +123,7 @@ public class HappyGhastHitBox extends AbstractHitBox {
boolean blocksBuilding = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("blocks-building", true), "blocks-building");
return new HappyGhastHitBox(
HitBoxFactory.getSeats(arguments),
MiscUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position"),
ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position"),
scale, canUseOn, blocksBuilding, canBeHitByProjectile, hardCollision
);
}

View File

@@ -7,7 +7,6 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflect
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes;
import net.momirealms.craftengine.core.entity.furniture.*;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.MiscUtils;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.world.Vec3d;
import net.momirealms.craftengine.core.world.WorldPosition;
@@ -93,7 +92,7 @@ public class InteractionHitBox extends AbstractHitBox {
@Override
public HitBox create(Map<String, Object> arguments) {
Vector3f position = MiscUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position");
Vector3f position = ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position");
float width;
float height;
if (arguments.containsKey("scale")) {

View File

@@ -280,7 +280,7 @@ public class ShulkerHitBox extends AbstractHitBox {
@Override
public HitBox create(Map<String, Object> arguments) {
Vector3f position = MiscUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position");
Vector3f position = ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position");
float scale = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("scale", "1"), "scale");
byte peek = (byte) ResourceConfigUtils.getAsInt(arguments.getOrDefault("peek", 0), "peek");
Direction directionEnum = Optional.ofNullable(arguments.get("direction")).map(it -> Direction.valueOf(it.toString().toUpperCase(Locale.ENGLISH))).orElse(Direction.UP);

View File

@@ -21,7 +21,6 @@ import net.momirealms.craftengine.core.item.recipe.DatapackRecipeResult;
import net.momirealms.craftengine.core.item.recipe.UniqueIdItem;
import net.momirealms.craftengine.core.pack.AbstractPackManager;
import net.momirealms.craftengine.core.plugin.config.Config;
import net.momirealms.craftengine.core.plugin.context.ContextHolder;
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
import net.momirealms.craftengine.core.plugin.logger.Debugger;
import net.momirealms.craftengine.core.util.*;
@@ -335,7 +334,7 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
@Override
public ItemStack buildCustomItemStack(Key id, Player player) {
return Optional.ofNullable(this.customItemsById.get(id)).map(it -> it.buildItemStack(new ItemBuildContext(player, ContextHolder.EMPTY), 1)).orElse(null);
return Optional.ofNullable(this.customItemsById.get(id)).map(it -> it.buildItemStack(ItemBuildContext.of(player), 1)).orElse(null);
}
@Override

View File

@@ -1,6 +1,5 @@
package net.momirealms.craftengine.bukkit.item;
import net.kyori.adventure.text.Component;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.item.CustomItem;
@@ -11,8 +10,11 @@ import net.momirealms.craftengine.core.item.modifier.ArgumentsModifier;
import net.momirealms.craftengine.core.item.modifier.ItemDataModifier;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.config.Config;
import net.momirealms.craftengine.core.plugin.context.Context;
import net.momirealms.craftengine.core.plugin.context.ContextHolder;
import net.momirealms.craftengine.core.plugin.context.ContextKey;
import net.momirealms.craftengine.core.plugin.context.NetworkTextReplaceContext;
import net.momirealms.craftengine.core.plugin.text.component.ComponentProvider;
import net.momirealms.craftengine.core.util.AdventureHelper;
import net.momirealms.sparrow.nbt.CompoundTag;
import net.momirealms.sparrow.nbt.ListTag;
@@ -61,7 +63,7 @@ public final class LegacyNetworkItemHandler implements NetworkItemHandler<ItemSt
Optional<CustomItem<ItemStack>> optionalCustomItem = wrapped.getCustomItem();
if (optionalCustomItem.isEmpty()) {
if (!Config.interceptItem()) return Optional.empty();
return new OtherItem(wrapped, false).process();
return new OtherItem(wrapped, false).process(NetworkTextReplaceContext.of(player));
} else {
BukkitCustomItem customItem = (BukkitCustomItem) optionalCustomItem.get();
Object serverItem = FastNMS.INSTANCE.method$ItemStack$getItem(wrapped.getLiteralObject());
@@ -71,7 +73,7 @@ public final class LegacyNetworkItemHandler implements NetworkItemHandler<ItemSt
}
if (!customItem.hasClientBoundDataModifier()) {
if (!Config.interceptItem() && !hasDifferentMaterial) return Optional.empty();
return new OtherItem(wrapped, hasDifferentMaterial).process();
return new OtherItem(wrapped, hasDifferentMaterial).process(NetworkTextReplaceContext.of(player));
} else {
CompoundTag tag = new CompoundTag();
Tag argumentTag = wrapped.getTag(ArgumentsModifier.ARGUMENTS_TAG);
@@ -93,10 +95,10 @@ public final class LegacyNetworkItemHandler implements NetworkItemHandler<ItemSt
}
if (Config.interceptItem()) {
if (!tag.containsKey("display.Name")) {
processCustomName(wrapped, tag::put);
processCustomName(wrapped, tag::put, context);
}
if (!tag.containsKey("display.Lore")) {
processLore(wrapped, tag::put);
processLore(wrapped, tag::put, context);
}
}
if (tag.isEmpty()) {
@@ -111,13 +113,13 @@ public final class LegacyNetworkItemHandler implements NetworkItemHandler<ItemSt
}
}
public static boolean processCustomName(Item<ItemStack> item, BiConsumer<String, CompoundTag> callback) {
public static boolean processCustomName(Item<ItemStack> item, BiConsumer<String, CompoundTag> callback, Context context) {
Optional<String> optionalCustomName = item.customNameJson();
if (optionalCustomName.isPresent()) {
String line = optionalCustomName.get();
Map<String, Component> tokens = CraftEngine.instance().fontManager().matchTags(line);
Map<String, ComponentProvider> tokens = CraftEngine.instance().fontManager().matchTags(line);
if (!tokens.isEmpty()) {
item.customNameJson(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(line), tokens)));
item.customNameJson(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(line), tokens, context)));
callback.accept("display.Name", NetworkItemHandler.pack(Operation.ADD, new StringTag(line)));
return true;
}
@@ -125,18 +127,18 @@ public final class LegacyNetworkItemHandler implements NetworkItemHandler<ItemSt
return false;
}
private static boolean processLore(Item<ItemStack> item, BiConsumer<String, CompoundTag> callback) {
private static boolean processLore(Item<ItemStack> item, BiConsumer<String, CompoundTag> callback, Context context) {
Optional<List<String>> optionalLore = item.loreJson();
if (optionalLore.isPresent()) {
boolean changed = false;
List<String> lore = optionalLore.get();
List<String> newLore = new ArrayList<>(lore.size());
for (String line : lore) {
Map<String, Component> tokens = CraftEngine.instance().fontManager().matchTags(line);
Map<String, ComponentProvider> tokens = CraftEngine.instance().fontManager().matchTags(line);
if (tokens.isEmpty()) {
newLore.add(line);
} else {
newLore.add(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(line), tokens)));
newLore.add(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(line), tokens, context)));
changed = true;
}
}
@@ -164,11 +166,11 @@ public final class LegacyNetworkItemHandler implements NetworkItemHandler<ItemSt
this.forceReturn = forceReturn;
}
public Optional<Item<ItemStack>> process() {
if (processLore(this.item, (s, c) -> networkTag().put(s, c))) {
public Optional<Item<ItemStack>> process(Context context) {
if (processLore(this.item, (s, c) -> networkTag().put(s, c), context)) {
this.globalChanged = true;
}
if (processCustomName(this.item, (s, c) -> networkTag().put(s, c))) {
if (processCustomName(this.item, (s, c) -> networkTag().put(s, c), context)) {
this.globalChanged = true;
}
if (this.globalChanged) {

View File

@@ -1,6 +1,5 @@
package net.momirealms.craftengine.bukkit.item;
import net.kyori.adventure.text.Component;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.item.*;
@@ -8,8 +7,11 @@ import net.momirealms.craftengine.core.item.modifier.ArgumentsModifier;
import net.momirealms.craftengine.core.item.modifier.ItemDataModifier;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.config.Config;
import net.momirealms.craftengine.core.plugin.context.Context;
import net.momirealms.craftengine.core.plugin.context.ContextHolder;
import net.momirealms.craftengine.core.plugin.context.ContextKey;
import net.momirealms.craftengine.core.plugin.context.NetworkTextReplaceContext;
import net.momirealms.craftengine.core.plugin.text.component.ComponentProvider;
import net.momirealms.craftengine.core.util.AdventureHelper;
import net.momirealms.craftengine.core.util.VersionHelper;
import net.momirealms.sparrow.nbt.CompoundTag;
@@ -64,7 +66,7 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler<ItemSt
Optional<CustomItem<ItemStack>> optionalCustomItem = wrapped.getCustomItem();
if (optionalCustomItem.isEmpty()) {
if (!Config.interceptItem()) return Optional.empty();
return new OtherItem(wrapped, false).process();
return new OtherItem(wrapped, false).process(NetworkTextReplaceContext.of(player));
} else {
BukkitCustomItem customItem = (BukkitCustomItem) optionalCustomItem.get();
Object serverItem = FastNMS.INSTANCE.method$ItemStack$getItem(wrapped.getLiteralObject());
@@ -74,7 +76,7 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler<ItemSt
}
if (!customItem.hasClientBoundDataModifier()) {
if (!Config.interceptItem() && !hasDifferentMaterial) return Optional.empty();
return new OtherItem(wrapped, hasDifferentMaterial).process();
return new OtherItem(wrapped, hasDifferentMaterial).process(NetworkTextReplaceContext.of(player));
} else {
CompoundTag customData = Optional.ofNullable(wrapped.getSparrowNBTComponent(ComponentTypes.CUSTOM_DATA)).map(CompoundTag.class::cast).orElse(new CompoundTag());
CompoundTag arguments = customData.getCompound(ArgumentsModifier.ARGUMENTS_TAG);
@@ -97,16 +99,16 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler<ItemSt
}
if (Config.interceptItem()) {
if (!tag.containsKey(ComponentIds.ITEM_NAME)) {
if (VersionHelper.isOrAbove1_21_5()) processModernItemName(wrapped, () -> tag);
else processLegacyItemName(wrapped, () -> tag);
if (VersionHelper.isOrAbove1_21_5()) processModernItemName(wrapped, () -> tag, context);
else processLegacyItemName(wrapped, () -> tag, context);
}
if (!tag.containsKey(ComponentIds.CUSTOM_NAME)) {
if (VersionHelper.isOrAbove1_21_5()) processModernCustomName(wrapped, () -> tag);
else processLegacyCustomName(wrapped, () -> tag);
if (VersionHelper.isOrAbove1_21_5()) processModernCustomName(wrapped, () -> tag, context);
else processLegacyCustomName(wrapped, () -> tag, context);
}
if (!tag.containsKey(ComponentIds.LORE)) {
if (VersionHelper.isOrAbove1_21_5()) processModernLore(wrapped, () -> tag);
else processLegacyLore(wrapped, () -> tag);
if (VersionHelper.isOrAbove1_21_5()) processModernLore(wrapped, () -> tag, context);
else processLegacyLore(wrapped, () -> tag, context);
}
}
if (tag.isEmpty()) {
@@ -120,18 +122,18 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler<ItemSt
}
}
public static boolean processLegacyLore(Item<ItemStack> item, Supplier<CompoundTag> tag) {
public static boolean processLegacyLore(Item<ItemStack> item, Supplier<CompoundTag> tag, Context context) {
Optional<List<String>> optionalLore = item.loreJson();
if (optionalLore.isPresent()) {
boolean changed = false;
List<String> lore = optionalLore.get();
List<String> newLore = new ArrayList<>(lore.size());
for (String line : lore) {
Map<String, Component> tokens = CraftEngine.instance().fontManager().matchTags(line);
Map<String, ComponentProvider> tokens = CraftEngine.instance().fontManager().matchTags(line);
if (tokens.isEmpty()) {
newLore.add(line);
} else {
newLore.add(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(line), tokens)));
newLore.add(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(line), tokens, context)));
changed = true;
}
}
@@ -148,13 +150,13 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler<ItemSt
return false;
}
public static boolean processLegacyCustomName(Item<ItemStack> item, Supplier<CompoundTag> tag) {
public static boolean processLegacyCustomName(Item<ItemStack> item, Supplier<CompoundTag> tag, Context context) {
Optional<String> optionalCustomName = item.customNameJson();
if (optionalCustomName.isPresent()) {
String line = optionalCustomName.get();
Map<String, Component> tokens = CraftEngine.instance().fontManager().matchTags(line);
Map<String, ComponentProvider> tokens = CraftEngine.instance().fontManager().matchTags(line);
if (!tokens.isEmpty()) {
item.customNameJson(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(line), tokens)));
item.customNameJson(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(line), tokens, context)));
tag.get().put(ComponentIds.CUSTOM_NAME, NetworkItemHandler.pack(Operation.ADD, new StringTag(line)));
return true;
}
@@ -162,13 +164,13 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler<ItemSt
return false;
}
public static boolean processLegacyItemName(Item<ItemStack> item, Supplier<CompoundTag> tag) {
public static boolean processLegacyItemName(Item<ItemStack> item, Supplier<CompoundTag> tag, Context context) {
Optional<String> optionalItemName = item.itemNameJson();
if (optionalItemName.isPresent()) {
String line = optionalItemName.get();
Map<String, Component> tokens = CraftEngine.instance().fontManager().matchTags(line);
Map<String, ComponentProvider> tokens = CraftEngine.instance().fontManager().matchTags(line);
if (!tokens.isEmpty()) {
item.itemNameJson(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(line), tokens)));
item.itemNameJson(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(line), tokens, context)));
tag.get().put(ComponentIds.ITEM_NAME, NetworkItemHandler.pack(Operation.ADD, new StringTag(line)));
return true;
}
@@ -176,33 +178,33 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler<ItemSt
return false;
}
public static boolean processModernItemName(Item<ItemStack> item, Supplier<CompoundTag> tag) {
public static boolean processModernItemName(Item<ItemStack> item, Supplier<CompoundTag> tag, Context context) {
Tag nameTag = item.getSparrowNBTComponent(ComponentTypes.ITEM_NAME);
if (nameTag == null) return false;
String tagStr = nameTag.getAsString();
Map<String, Component> tokens = CraftEngine.instance().fontManager().matchTags(tagStr);
Map<String, ComponentProvider> tokens = CraftEngine.instance().fontManager().matchTags(tagStr);
if (!tokens.isEmpty()) {
item.setNBTComponent(ComponentKeys.ITEM_NAME, AdventureHelper.componentToNbt(AdventureHelper.replaceText(AdventureHelper.nbtToComponent(nameTag), tokens)));
item.setNBTComponent(ComponentKeys.ITEM_NAME, AdventureHelper.componentToNbt(AdventureHelper.replaceText(AdventureHelper.nbtToComponent(nameTag), tokens, context)));
tag.get().put(ComponentIds.ITEM_NAME, NetworkItemHandler.pack(Operation.ADD, nameTag));
return true;
}
return false;
}
public static boolean processModernCustomName(Item<ItemStack> item, Supplier<CompoundTag> tag) {
public static boolean processModernCustomName(Item<ItemStack> item, Supplier<CompoundTag> tag, Context context) {
Tag nameTag = item.getSparrowNBTComponent(ComponentTypes.CUSTOM_NAME);
if (nameTag == null) return false;
String tagStr = nameTag.getAsString();
Map<String, Component> tokens = CraftEngine.instance().fontManager().matchTags(tagStr);
Map<String, ComponentProvider> tokens = CraftEngine.instance().fontManager().matchTags(tagStr);
if (!tokens.isEmpty()) {
item.setNBTComponent(ComponentKeys.CUSTOM_NAME, AdventureHelper.componentToNbt(AdventureHelper.replaceText(AdventureHelper.nbtToComponent(nameTag), tokens)));
item.setNBTComponent(ComponentKeys.CUSTOM_NAME, AdventureHelper.componentToNbt(AdventureHelper.replaceText(AdventureHelper.nbtToComponent(nameTag), tokens, context)));
tag.get().put(ComponentIds.CUSTOM_NAME, NetworkItemHandler.pack(Operation.ADD, nameTag));
return true;
}
return false;
}
public static boolean processModernLore(Item<ItemStack> item, Supplier<CompoundTag> tagSupplier) {
public static boolean processModernLore(Item<ItemStack> item, Supplier<CompoundTag> tagSupplier, Context context) {
Tag loreTag = item.getSparrowNBTComponent(ComponentTypes.LORE);
boolean changed = false;
if (!(loreTag instanceof ListTag listTag)) {
@@ -211,11 +213,11 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler<ItemSt
ListTag newLore = new ListTag();
for (Tag tag : listTag) {
String tagStr = tag.getAsString();
Map<String, Component> tokens = CraftEngine.instance().fontManager().matchTags(tagStr);
Map<String, ComponentProvider> tokens = CraftEngine.instance().fontManager().matchTags(tagStr);
if (tokens.isEmpty()) {
newLore.add(tag);
} else {
newLore.add(AdventureHelper.componentToNbt(AdventureHelper.replaceText(AdventureHelper.nbtToComponent(tag), tokens)));
newLore.add(AdventureHelper.componentToNbt(AdventureHelper.replaceText(AdventureHelper.nbtToComponent(tag), tokens, context)));
changed = true;
}
}
@@ -238,20 +240,20 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler<ItemSt
this.forceReturn = forceReturn;
}
public Optional<Item<ItemStack>> process() {
public Optional<Item<ItemStack>> process(Context context) {
if (VersionHelper.isOrAbove1_21_5()) {
if (processModernLore(this.item, this::getOrCreateTag))
if (processModernLore(this.item, this::getOrCreateTag, context))
this.globalChanged = true;
if (processModernCustomName(this.item, this::getOrCreateTag))
if (processModernCustomName(this.item, this::getOrCreateTag, context))
this.globalChanged = true;
if (processModernItemName(this.item, this::getOrCreateTag))
if (processModernItemName(this.item, this::getOrCreateTag, context))
this.globalChanged = true;
} else {
if (processLegacyLore(this.item, this::getOrCreateTag))
if (processLegacyLore(this.item, this::getOrCreateTag, context))
this.globalChanged = true;
if (processLegacyCustomName(this.item, this::getOrCreateTag))
if (processLegacyCustomName(this.item, this::getOrCreateTag, context))
this.globalChanged = true;
if (processLegacyItemName(this.item, this::getOrCreateTag))
if (processLegacyItemName(this.item, this::getOrCreateTag, context))
this.globalChanged = true;
}
if (this.globalChanged) {

View File

@@ -6,7 +6,7 @@ import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.util.*;
import net.momirealms.craftengine.bukkit.world.BukkitBlockInWorld;
import net.momirealms.craftengine.bukkit.world.BukkitExistingBlock;
import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.UpdateOption;
@@ -82,12 +82,12 @@ public class AxeItemBehavior extends ItemBehavior {
CompoundTag compoundTag = customState.propertiesNbt();
ImmutableBlockState newState = newCustomBlock.getBlockState(compoundTag);
BukkitBlockInWorld clicked = (BukkitBlockInWorld) context.getLevel().getBlockAt(context.getClickedPos());
BukkitExistingBlock clicked = (BukkitExistingBlock) context.getLevel().getBlockAt(context.getClickedPos());
org.bukkit.entity.Player bukkitPlayer = null;
if (player != null) {
bukkitPlayer = ((org.bukkit.entity.Player) player.platformPlayer());
// Call bukkit event
EntityChangeBlockEvent event = new EntityChangeBlockEvent(bukkitPlayer, clicked.block(), BlockStateUtils.fromBlockData(newState.customBlockState().handle()));
EntityChangeBlockEvent event = new EntityChangeBlockEvent(bukkitPlayer, clicked.block(), BlockStateUtils.fromBlockData(newState.customBlockState().literalObject()));
if (EventUtils.fireAndCheckCancel(event)) {
return InteractionResult.FAIL;
}
@@ -98,7 +98,7 @@ public class AxeItemBehavior extends ItemBehavior {
if (ItemUtils.isEmpty(item)) return InteractionResult.FAIL;
BlockPos pos = context.getClickedPos();
context.getLevel().playBlockSound(Vec3d.atCenterOf(pos), AXE_STRIP_SOUND, 1, 1);
FastNMS.INSTANCE.method$LevelWriter$setBlock(context.getLevel().serverWorld(), LocationUtils.toBlockPos(pos), newState.customBlockState().handle(), UpdateOption.UPDATE_ALL_IMMEDIATE.flags());
FastNMS.INSTANCE.method$LevelWriter$setBlock(context.getLevel().serverWorld(), LocationUtils.toBlockPos(pos), newState.customBlockState().literalObject(), UpdateOption.UPDATE_ALL_IMMEDIATE.flags());
clicked.block().getWorld().sendGameEvent(bukkitPlayer, GameEvent.BLOCK_CHANGE, new Vector(pos.x(), pos.y(), pos.z()));
Material material = MaterialUtils.getMaterial(item.vanillaId());
if (bukkitPlayer != null) {
@@ -106,7 +106,7 @@ public class AxeItemBehavior extends ItemBehavior {
// resend swing if it's not interactable on client side
if (!InteractUtils.isInteractable(
bukkitPlayer, BlockStateUtils.fromBlockData(customState.vanillaBlockState().handle()),
bukkitPlayer, BlockStateUtils.fromBlockData(customState.vanillaBlockState().literalObject()),
context.getHitResult(), item
) || player.isSecondaryUseActive()) {
player.swingHand(context.getHand());

View File

@@ -9,7 +9,7 @@ import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.util.*;
import net.momirealms.craftengine.bukkit.world.BukkitBlockInWorld;
import net.momirealms.craftengine.bukkit.world.BukkitExistingBlock;
import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.UpdateOption;
@@ -73,7 +73,7 @@ public class BlockItemBehavior extends BlockBoundItemBehavior {
return InteractionResult.FAIL;
}
if (!context.canPlace()) {
return InteractionResult.FAIL;
return InteractionResult.PASS;
}
Player player = context.getPlayer();
@@ -89,7 +89,7 @@ public class BlockItemBehavior extends BlockBoundItemBehavior {
ImmutableBlockState blockStateToPlace = getPlacementState(context, block);
if (blockStateToPlace == null) {
return InteractionResult.FAIL;
return InteractionResult.PASS;
}
BlockPos againstPos = context.getAgainstPos();
@@ -111,7 +111,7 @@ public class BlockItemBehavior extends BlockBoundItemBehavior {
} else {
ImmutableBlockState customState = optionalCustomState.get();
// custom block
if (!AdventureModeUtils.canPlace(context.getItem(), context.getLevel(), againstPos, Config.simplifyAdventurePlaceCheck() ? customState.vanillaBlockState().handle() : againstBlockState)) {
if (!AdventureModeUtils.canPlace(context.getItem(), context.getLevel(), againstPos, Config.simplifyAdventurePlaceCheck() ? customState.vanillaBlockState().literalObject() : againstBlockState)) {
return InteractionResult.FAIL;
}
}
@@ -157,7 +157,7 @@ public class BlockItemBehavior extends BlockBoundItemBehavior {
WorldPosition position = new WorldPosition(context.getLevel(), pos.x() + 0.5, pos.y() + 0.5, pos.z() + 0.5);
Cancellable dummy = Cancellable.dummy();
PlayerOptionalContext functionContext = PlayerOptionalContext.of(player, ContextHolder.builder()
.withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(bukkitBlock))
.withParameter(DirectContextParameters.BLOCK, new BukkitExistingBlock(bukkitBlock))
.withParameter(DirectContextParameters.POSITION, position)
.withParameter(DirectContextParameters.EVENT, dummy)
.withParameter(DirectContextParameters.HAND, context.getHand())
@@ -196,7 +196,7 @@ public class BlockItemBehavior extends BlockBoundItemBehavior {
try {
Player cePlayer = context.getPlayer();
Object player = cePlayer != null ? cePlayer.serverPlayer() : null;
Object blockState = state.customBlockState().handle();
Object blockState = state.customBlockState().literalObject();
Object blockPos = LocationUtils.toBlockPos(context.getClickedPos());
Object voxelShape;
if (VersionHelper.isOrAbove1_21_6()) {

View File

@@ -12,6 +12,7 @@ public class BukkitItemBehaviors extends ItemBehaviors {
public static final Key COMPOSTABLE_ITEM = Key.from("craftengine:compostable_item");
public static final Key AXE_ITEM = Key.from("craftengine:axe_item");
public static final Key DOUBLE_HIGH_BLOCK_ITEM = Key.from("craftengine:double_high_block_item");
public static final Key WALL_BLOCK_ITEM = Key.from("craftengine:wall_block_item");
public static void init() {
register(EMPTY, EmptyItemBehavior.FACTORY);
@@ -22,5 +23,6 @@ public class BukkitItemBehaviors extends ItemBehaviors {
register(COMPOSTABLE_ITEM, CompostableItemBehavior.FACTORY);
register(AXE_ITEM, AxeItemBehavior.FACTORY);
register(DOUBLE_HIGH_BLOCK_ITEM, DoubleHighBlockItemBehavior.FACTORY);
register(WALL_BLOCK_ITEM, WallBlockItemBehavior.FACTORY);
}
}

View File

@@ -5,7 +5,7 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBlocks;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.EventUtils;
import net.momirealms.craftengine.bukkit.util.LocationUtils;
import net.momirealms.craftengine.bukkit.world.BukkitBlockInWorld;
import net.momirealms.craftengine.bukkit.world.BukkitExistingBlock;
import net.momirealms.craftengine.core.entity.player.InteractionResult;
import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
@@ -38,7 +38,7 @@ public class CompostableItemBehavior extends ItemBehavior {
@SuppressWarnings("UnstableApiUsage")
@Override
public InteractionResult useOnBlock(UseOnContext context) {
BukkitBlockInWorld block = (BukkitBlockInWorld) context.getLevel().getBlockAt(context.getClickedPos());
BukkitExistingBlock block = (BukkitExistingBlock) context.getLevel().getBlockAt(context.getClickedPos());
BlockData blockData = block.block().getBlockData();
Object blockOwner = BlockStateUtils.getBlockOwner(BlockStateUtils.blockDataToBlockState(blockData));
if (blockOwner != MBlocks.COMPOSTER) return InteractionResult.PASS;

View File

@@ -6,7 +6,7 @@ import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.DirectionUtils;
import net.momirealms.craftengine.bukkit.util.InteractUtils;
import net.momirealms.craftengine.bukkit.util.LocationUtils;
import net.momirealms.craftengine.bukkit.world.BukkitBlockInWorld;
import net.momirealms.craftengine.bukkit.world.BukkitExistingBlock;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.entity.player.InteractionResult;
import net.momirealms.craftengine.core.item.Item;
@@ -40,7 +40,7 @@ public class FlintAndSteelItemBehavior extends ItemBehavior {
if (player == null) return InteractionResult.PASS;
BlockPos clickedPos = context.getClickedPos();
BukkitBlockInWorld clicked = (BukkitBlockInWorld) context.getLevel().getBlockAt(clickedPos);
BukkitExistingBlock clicked = (BukkitExistingBlock) context.getLevel().getBlockAt(clickedPos);
Block block = clicked.block();
BlockPos firePos = clickedPos.relative(context.getClickedFace());
Direction direction = context.getHorizontalDirection();
@@ -77,10 +77,10 @@ public class FlintAndSteelItemBehavior extends ItemBehavior {
// 点击对象为自定义方块
ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockStateUnsafe(stateId);
// 原版外观也可燃
if (BlockStateUtils.isBurnable(immutableBlockState.vanillaBlockState().handle())) {
if (BlockStateUtils.isBurnable(immutableBlockState.vanillaBlockState().literalObject())) {
return InteractionResult.PASS;
}
BlockData vanillaBlockState = BlockStateUtils.fromBlockData(immutableBlockState.vanillaBlockState().handle());
BlockData vanillaBlockState = BlockStateUtils.fromBlockData(immutableBlockState.vanillaBlockState().literalObject());
// 点击的是方块上面则只需要判断shift和可交互
if (direction == Direction.UP) {
// 客户端层面必须可交互
@@ -95,7 +95,7 @@ public class FlintAndSteelItemBehavior extends ItemBehavior {
} else {
// 玩家觉得自定义方块不可燃,且点击了侧面,那么就要判断火源下方的方块是否可燃,如果不可燃,则补发声音
BlockPos belowFirePos = firePos.relative(Direction.DOWN);
BukkitBlockInWorld belowFireBlock = (BukkitBlockInWorld) context.getLevel().getBlockAt(belowFirePos);
BukkitExistingBlock belowFireBlock = (BukkitExistingBlock) context.getLevel().getBlockAt(belowFirePos);
boolean belowCanBurn;
try {
Block belowBlock = belowFireBlock.block();
@@ -134,7 +134,7 @@ public class FlintAndSteelItemBehavior extends ItemBehavior {
for (Direction dir : Direction.values()) {
if (dir == relativeDirection) continue;
BlockPos relPos = firePos.relative(dir);
BukkitBlockInWorld nearByBlock = (BukkitBlockInWorld) context.getLevel().getBlockAt(relPos);
BukkitExistingBlock nearByBlock = (BukkitExistingBlock) context.getLevel().getBlockAt(relPos);
BlockData nearbyBlockData = nearByBlock.block().getBlockData();
Object nearbyBlockState = BlockStateUtils.blockDataToBlockState(nearbyBlockData);
int stateID = BlockStateUtils.blockStateToId(nearbyBlockState);

View File

@@ -3,6 +3,8 @@ 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.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MFluids;
import net.momirealms.craftengine.bukkit.util.DirectionUtils;
import net.momirealms.craftengine.bukkit.util.LocationUtils;
import net.momirealms.craftengine.core.entity.player.InteractionHand;
import net.momirealms.craftengine.core.entity.player.InteractionResult;
@@ -45,11 +47,15 @@ public class LiquidCollisionBlockItemBehavior extends BlockItemBehavior {
try {
if (player == null) return InteractionResult.FAIL;
Object blockHitResult = CoreReflections.method$Item$getPlayerPOVHitResult.invoke(null, world.serverWorld(), player.serverPlayer(), CoreReflections.instance$ClipContext$Fluid$SOURCE_ONLY);
Object blockPos = CoreReflections.field$BlockHitResul$blockPos.get(blockHitResult);
Object blockPos = FastNMS.INSTANCE.field$BlockHitResul$blockPos(blockHitResult);
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) CoreReflections.method$Direction$ordinal.invoke(CoreReflections.field$BlockHitResul$direction.get(blockHitResult))];
boolean miss = CoreReflections.field$BlockHitResul$miss.getBoolean(blockHitResult);
Direction direction = DirectionUtils.fromNMSDirection(FastNMS.INSTANCE.field$BlockHitResul$direction(blockHitResult));
boolean miss = FastNMS.INSTANCE.field$BlockHitResul$miss(blockHitResult);
Vec3d hitPos = LocationUtils.fromVec(CoreReflections.field$HitResult$location.get(blockHitResult));
Object fluidType = FastNMS.INSTANCE.method$FluidState$getType(FastNMS.INSTANCE.method$BlockGetter$getFluidState(world.serverWorld(), blockPos));
if (fluidType != MFluids.WATER && fluidType != MFluids.LAVA) {
return InteractionResult.PASS;
}
if (miss) {
return super.useOnBlock(new UseOnContext(player, hand, BlockHitResult.miss(hitPos, direction, above)));
} else {

View File

@@ -0,0 +1,56 @@
package net.momirealms.craftengine.bukkit.item.behavior;
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
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.BlockPlaceContext;
import net.momirealms.craftengine.core.item.context.UseOnContext;
import net.momirealms.craftengine.core.pack.Pack;
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.MiscUtils;
import java.nio.file.Path;
import java.util.Map;
public class WallBlockItemBehavior extends BlockItemBehavior {
public static final Factory FACTORY = new Factory();
public WallBlockItemBehavior(Key wallBlockId) {
super(wallBlockId);
}
@Override
public InteractionResult useOnBlock(UseOnContext context) {
return this.place(new BlockPlaceContext(context));
}
public InteractionResult place(BlockPlaceContext context) {
if (context.getClickedFace().stepY() != 0) {
return InteractionResult.PASS;
}
return super.place(context);
}
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 LocalizedResourceConfigException("warning.config.item.behavior.wall_block.missing_block", new IllegalArgumentException("Missing required parameter 'block' for wall_block_item behavior"));
}
if (id instanceof Map<?, ?> map) {
if (map.containsKey(key.toString())) {
// 防呆
BukkitBlockManager.instance().parser().parseSection(pack, path, key, MiscUtils.castToMap(map.get(key.toString()), false));
} else {
BukkitBlockManager.instance().parser().parseSection(pack, path, key, MiscUtils.castToMap(map, false));
}
return new WallBlockItemBehavior(key);
} else {
return new WallBlockItemBehavior(Key.of(id.toString()));
}
}
}
}

View File

@@ -424,6 +424,7 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory<ComponentItemW
@Override
protected Optional<Enchantment> getEnchantment(ComponentItemWrapper item, Key key) {
Object enchant = item.getComponentExact(ComponentTypes.ENCHANTMENTS);
if (enchant == null) return Optional.empty();
try {
Map<String, Integer> map = EnchantmentUtils.toMap(enchant);
Integer level = map.get(key.toString());

View File

@@ -190,7 +190,6 @@ public class UniversalItemFactory extends BukkitItemFactory<LegacyItemWrapper> {
@Override
protected void maxDamage(LegacyItemWrapper item, Integer damage) {
throw new UnsupportedOperationException("This feature is only available on 1.20.5+");
}
@Override

View File

@@ -3,6 +3,7 @@ package net.momirealms.craftengine.bukkit.item.listener;
import io.papermc.paper.event.block.CompostItemEvent;
import net.momirealms.craftengine.bukkit.api.BukkitAdaptors;
import net.momirealms.craftengine.bukkit.api.event.CustomBlockInteractEvent;
import net.momirealms.craftengine.bukkit.entity.BukkitEntity;
import net.momirealms.craftengine.bukkit.item.BukkitCustomItem;
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
@@ -10,7 +11,7 @@ import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
import net.momirealms.craftengine.bukkit.util.*;
import net.momirealms.craftengine.bukkit.world.BukkitBlockInWorld;
import net.momirealms.craftengine.bukkit.world.BukkitExistingBlock;
import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.behavior.AbstractBlockBehavior;
@@ -96,9 +97,10 @@ public class ItemEventListener implements Listener {
Cancellable cancellable = Cancellable.of(event::isCancelled, event::setCancelled);
PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder()
.withOptionalParameter(DirectContextParameters.ITEM_IN_HAND, itemInHand)
.withParameter(DirectContextParameters.EVENT, cancellable)
.withParameter(DirectContextParameters.POSITION, LocationUtils.toWorldPosition(event.getRightClicked().getLocation()))
.withParameter(DirectContextParameters.HAND, hand)
.withParameter(DirectContextParameters.EVENT, cancellable)
.withParameter(DirectContextParameters.ENTITY, new BukkitEntity(entity))
.withParameter(DirectContextParameters.POSITION, LocationUtils.toWorldPosition(event.getRightClicked().getLocation()))
);
CustomItem<ItemStack> customItem = optionalCustomItem.get();
customItem.execute(context, EventTrigger.RIGHT_CLICK);
@@ -162,7 +164,7 @@ public class ItemEventListener implements Listener {
// fix client side issues
if (action.isRightClick() && hitResult != null &&
InteractUtils.willConsume(player, BlockStateUtils.fromBlockData(immutableBlockState.vanillaBlockState().handle()), hitResult, itemInHand)) {
InteractUtils.willConsume(player, BlockStateUtils.fromBlockData(immutableBlockState.vanillaBlockState().literalObject()), hitResult, itemInHand)) {
player.updateInventory();
//PlayerUtils.resendItemInHand(player);
}
@@ -171,7 +173,7 @@ public class ItemEventListener implements Listener {
// run custom functions
CustomBlock customBlock = immutableBlockState.owner().value();
PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder()
.withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block))
.withParameter(DirectContextParameters.BLOCK, new BukkitExistingBlock(block))
.withParameter(DirectContextParameters.CUSTOM_BLOCK_STATE, immutableBlockState)
.withParameter(DirectContextParameters.HAND, hand)
.withParameter(DirectContextParameters.EVENT, dummy)
@@ -253,13 +255,13 @@ public class ItemEventListener implements Listener {
if (immutableBlockState != null) {
// client won't have sounds if the clientside block is interactable
// so we should check and resend sounds on BlockPlaceEvent
BlockData craftBlockData = BlockStateUtils.fromBlockData(immutableBlockState.vanillaBlockState().handle());
BlockData craftBlockData = BlockStateUtils.fromBlockData(immutableBlockState.vanillaBlockState().literalObject());
if (InteractUtils.isInteractable(player, craftBlockData, hitResult, itemInHand)) {
if (!serverPlayer.isSecondaryUseActive()) {
serverPlayer.setResendSound();
}
} else {
if (BlockStateUtils.isReplaceable(immutableBlockState.customBlockState().handle()) && !BlockStateUtils.isReplaceable(immutableBlockState.vanillaBlockState().handle())) {
if (BlockStateUtils.isReplaceable(immutableBlockState.customBlockState().literalObject()) && !BlockStateUtils.isReplaceable(immutableBlockState.vanillaBlockState().literalObject())) {
serverPlayer.setResendSwing();
}
}
@@ -316,7 +318,7 @@ public class ItemEventListener implements Listener {
if (serverPlayer.isSecondaryUseActive() || !InteractUtils.isInteractable(player, blockData, hitResult, itemInHand)) {
Cancellable dummy = Cancellable.dummy();
PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder()
.withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block))
.withParameter(DirectContextParameters.BLOCK, new BukkitExistingBlock(block))
.withOptionalParameter(DirectContextParameters.CUSTOM_BLOCK_STATE, immutableBlockState)
.withOptionalParameter(DirectContextParameters.ITEM_IN_HAND, itemInHand)
.withParameter(DirectContextParameters.POSITION, LocationUtils.toWorldPosition(block.getLocation()))
@@ -337,7 +339,7 @@ public class ItemEventListener implements Listener {
if (hasCustomItem && action == Action.LEFT_CLICK_BLOCK) {
Cancellable dummy = Cancellable.dummy();
PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder()
.withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block))
.withParameter(DirectContextParameters.BLOCK, new BukkitExistingBlock(block))
.withOptionalParameter(DirectContextParameters.CUSTOM_BLOCK_STATE, immutableBlockState)
.withOptionalParameter(DirectContextParameters.ITEM_IN_HAND, itemInHand)
.withParameter(DirectContextParameters.POSITION, LocationUtils.toWorldPosition(block.getLocation()))

View File

@@ -3,7 +3,6 @@ package net.momirealms.craftengine.bukkit.item.recipe;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import io.papermc.paper.potion.PotionMix;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
@@ -69,58 +68,35 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
return recipe;
};
static {
try {
Key dyeRecipeId = Key.from("armor_dye");
MINECRAFT_RECIPE_REMOVER.accept(dyeRecipeId);
MINECRAFT_RECIPE_ADDER.apply(dyeRecipeId, RecipeInjector.createCustomDyeRecipe(dyeRecipeId));
Key repairRecipeId = Key.from("repair_item");
MINECRAFT_RECIPE_REMOVER.accept(repairRecipeId);
MINECRAFT_RECIPE_ADDER.apply(repairRecipeId, RecipeInjector.createRepairItemRecipe(repairRecipeId));
Key fireworkStarFadeRecipeId = Key.from("firework_star_fade");
MINECRAFT_RECIPE_REMOVER.accept(fireworkStarFadeRecipeId);
MINECRAFT_RECIPE_ADDER.apply(fireworkStarFadeRecipeId, RecipeInjector.createFireworkStarFadeRecipe(fireworkStarFadeRecipeId));
} catch (ReflectiveOperationException e) {
throw new ReflectionInitException("Failed to inject special recipes", e);
}
}
private static final List<Object> MODIFIED_INGREDIENTS = new ArrayList<>();
private static final Map<Key, Function<Recipe<ItemStack>, Object>> ADD_RECIPE_FOR_MINECRAFT_RECIPE_HOLDER = Map.of(
RecipeSerializers.SHAPED, recipe -> {
CustomShapedRecipe<ItemStack> shapedRecipe = (CustomShapedRecipe<ItemStack>) recipe;
Object mcRecipe = FastNMS.INSTANCE.createShapedRecipe(shapedRecipe);
modifyShapedRecipeIngredients(shapedRecipe, mcRecipe);
return MINECRAFT_RECIPE_ADDER.apply(recipe.id(), mcRecipe);
},
RecipeSerializers.SHAPELESS, recipe -> {
CustomShapelessRecipe<ItemStack> shapelessRecipe = (CustomShapelessRecipe<ItemStack>) recipe;
Object mcRecipe = FastNMS.INSTANCE.createShapelessRecipe(shapelessRecipe);
modifyShapelessRecipeIngredients(shapelessRecipe, mcRecipe);
return MINECRAFT_RECIPE_ADDER.apply(recipe.id(), mcRecipe);
},
RecipeSerializers.SMELTING, recipe -> {
CustomSmeltingRecipe<ItemStack> smeltingRecipe = (CustomSmeltingRecipe<ItemStack>) recipe;
Object mcRecipe = FastNMS.INSTANCE.createSmeltingRecipe(smeltingRecipe);
modifyCookingRecipeIngredient(smeltingRecipe, mcRecipe);
return MINECRAFT_RECIPE_ADDER.apply(recipe.id(), mcRecipe);
},
RecipeSerializers.BLASTING, recipe -> {
CustomBlastingRecipe<ItemStack> blastingRecipe = (CustomBlastingRecipe<ItemStack>) recipe;
Object mcRecipe = FastNMS.INSTANCE.createBlastingRecipe(blastingRecipe);
modifyCookingRecipeIngredient(blastingRecipe, mcRecipe);
return MINECRAFT_RECIPE_ADDER.apply(recipe.id(), mcRecipe);
},
RecipeSerializers.SMOKING, recipe -> {
CustomSmokingRecipe<ItemStack> smokingRecipe = (CustomSmokingRecipe<ItemStack>) recipe;
Object mcRecipe = FastNMS.INSTANCE.createSmokingRecipe(smokingRecipe);
modifyCookingRecipeIngredient(smokingRecipe, mcRecipe);
return MINECRAFT_RECIPE_ADDER.apply(recipe.id(), mcRecipe);
},
RecipeSerializers.CAMPFIRE_COOKING, recipe -> {
CustomCampfireRecipe<ItemStack> campfireRecipe = (CustomCampfireRecipe<ItemStack>) recipe;
Object mcRecipe = FastNMS.INSTANCE.createCampfireRecipe(campfireRecipe);
modifyCookingRecipeIngredient(campfireRecipe, mcRecipe);
return MINECRAFT_RECIPE_ADDER.apply(recipe.id(), mcRecipe);
},
RecipeSerializers.STONECUTTING, recipe -> {
@@ -219,18 +195,19 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
}
}
private static List<Object> getIngredientLooks(List<UniqueKey> holders) {
public static List<Object> getIngredientLooks(List<UniqueKey> holders) {
List<Object> itemStacks = new ArrayList<>();
for (UniqueKey holder : holders) {
Optional<? extends BuildableItem<ItemStack>> buildableItem = BukkitItemManager.instance().getBuildableItem(holder.key());
if (buildableItem.isPresent()) {
ItemStack itemStack = buildableItem.get().buildItemStack(ItemBuildContext.EMPTY, 1);
ItemStack itemStack = buildableItem.get().buildItemStack(ItemBuildContext.empty(), 1);
Object nmsStack = FastNMS.INSTANCE.method$CraftItemStack$asNMSCopy(itemStack);
itemStacks.add(nmsStack);
} else {
Item<ItemStack> barrier = BukkitItemManager.instance().createWrappedItem(ItemKeys.BARRIER, null);
assert barrier != null;
barrier.customNameJson(AdventureHelper.componentToJson(Component.text(holder.key().asString()).color(NamedTextColor.RED)));
itemStacks.add(barrier.getLiteralObject());
}
}
return itemStacks;
@@ -245,9 +222,9 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
Ingredient<ItemStack> actualIngredient = actualIngredients.get(i);
List<Object> items = getIngredientLooks(actualIngredient.items());
if (VersionHelper.isOrAbove1_21_4()) {
CoreReflections.methodHandle$Ingredient$itemStacksSetter.invokeExact(ingredient, (Set<Object>) new ObjectOpenHashSet<>(items));
CoreReflections.methodHandle$Ingredient$itemStacksSetter.invokeExact(ingredient, (Set<Object>) new CustomIngredientSet(items, actualIngredient));
} else if (VersionHelper.isOrAbove1_21_2()) {
CoreReflections.methodHandle$Ingredient$itemStacksSetter.invokeExact(ingredient, (List<Object>) items);
CoreReflections.methodHandle$Ingredient$itemStacksSetter.invokeExact(ingredient, (List<Object>) new CustomIngredientList(items, actualIngredient));
} else {
Object itemStackArray = Array.newInstance(CoreReflections.clazz$ItemStack, items.size());
for (int j = 0; j < items.size(); j++) {
@@ -255,7 +232,6 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
}
CoreReflections.methodHandle$Ingredient$itemStacksSetter.invokeExact(ingredient, (Object) itemStackArray);
}
MODIFIED_INGREDIENTS.add(ingredient);
}
}
@@ -360,7 +336,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
if (recipe instanceof CustomBrewingRecipe<ItemStack> brewingRecipe) {
if (!VersionHelper.isOrAbove1_20_2()) return;
PotionMix potionMix = new PotionMix(new NamespacedKey(id.namespace(), id.value()),
brewingRecipe.result(ItemBuildContext.EMPTY),
brewingRecipe.result(ItemBuildContext.empty()),
PotionMix.createPredicateChoice(container -> {
Item<ItemStack> wrapped = this.plugin.itemManager().wrap(container);
return brewingRecipe.container().test(UniqueIdItem.of(wrapped));
@@ -484,6 +460,22 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
this.plugin.logger().warn("Failed to register recipe " + recipe.id().toString(), e);
}
}
// 重新注入特殊配方
try {
Key dyeRecipeId = Key.from("armor_dye");
MINECRAFT_RECIPE_REMOVER.accept(dyeRecipeId);
MINECRAFT_RECIPE_ADDER.apply(dyeRecipeId, RecipeInjector.createCustomDyeRecipe(dyeRecipeId));
Key repairRecipeId = Key.from("repair_item");
MINECRAFT_RECIPE_REMOVER.accept(repairRecipeId);
MINECRAFT_RECIPE_ADDER.apply(repairRecipeId, RecipeInjector.createRepairItemRecipe(repairRecipeId));
Key fireworkStarFadeRecipeId = Key.from("firework_star_fade");
MINECRAFT_RECIPE_REMOVER.accept(fireworkStarFadeRecipeId);
MINECRAFT_RECIPE_ADDER.apply(fireworkStarFadeRecipeId, RecipeInjector.createFireworkStarFadeRecipe(fireworkStarFadeRecipeId));
} catch (ReflectiveOperationException e) {
throw new ReflectionInitException("Failed to inject special recipes", e);
}
try {
// give flags back on 1.21.2+
if (VersionHelper.isOrAbove1_21_2() && this.stolenFeatureFlagSet != null) {
@@ -498,20 +490,6 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
// send to players
CoreReflections.methodHandle$DedicatedPlayerList$reloadRecipes.invokeExact(CraftBukkitReflections.methodHandle$CraftServer$playerListGetter.invokeExact(Bukkit.getServer()));
// now we need to remove the fake `exact` choices
if (VersionHelper.isOrAbove1_21_4()) {
for (Object ingredient : MODIFIED_INGREDIENTS) {
CoreReflections.methodHandle$Ingredient$itemStacksSetter.invokeExact(ingredient, (Set<Object>) null);
}
} else if (VersionHelper.isOrAbove1_21_2()) {
for (Object ingredient : MODIFIED_INGREDIENTS) {
CoreReflections.methodHandle$Ingredient$itemStacksSetter.invokeExact(ingredient, (List<Object>) null);
}
}
// clear cache
MODIFIED_INGREDIENTS.clear();
} catch (Throwable e) {
this.plugin.logger().warn("Failed to run delayed recipe tasks", e);
}

View File

@@ -0,0 +1,28 @@
package net.momirealms.craftengine.bukkit.item.recipe;
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.core.item.recipe.Ingredient;
import net.momirealms.craftengine.core.item.recipe.UniqueIdItem;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Collection;
public class CustomIngredientList extends ArrayList<Object> {
private final Ingredient<ItemStack> ingredient;
public CustomIngredientList(@NotNull Collection<?> c, Ingredient<ItemStack> ingredient) {
super(c);
this.ingredient = ingredient;
}
@Override
public boolean contains(Object o) {
if (o == null || FastNMS.INSTANCE.method$ItemStack$isEmpty(o)) {
return false;
}
return this.ingredient.test(UniqueIdItem.of(BukkitItemManager.instance().wrap(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(o))));
}
}

View File

@@ -0,0 +1,28 @@
package net.momirealms.craftengine.bukkit.item.recipe;
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.core.item.recipe.Ingredient;
import net.momirealms.craftengine.core.item.recipe.UniqueIdItem;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.HashSet;
public class CustomIngredientSet extends HashSet<Object> {
private final Ingredient<ItemStack> ingredient;
public CustomIngredientSet(@NotNull Collection<?> c, Ingredient<ItemStack> ingredient) {
super(c);
this.ingredient = ingredient;
}
@Override
public boolean contains(Object o) {
if (o == null || FastNMS.INSTANCE.method$ItemStack$isEmpty(o)) {
return false;
}
return this.ingredient.test(UniqueIdItem.of(BukkitItemManager.instance().wrap(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(o))));
}
}

View File

@@ -23,7 +23,6 @@ import net.momirealms.craftengine.core.item.recipe.input.SmithingInput;
import net.momirealms.craftengine.core.item.setting.AnvilRepairItem;
import net.momirealms.craftengine.core.item.setting.ItemEquipment;
import net.momirealms.craftengine.core.plugin.config.Config;
import net.momirealms.craftengine.core.plugin.context.ContextHolder;
import net.momirealms.craftengine.core.util.*;
import org.bukkit.Material;
import org.bukkit.entity.Player;
@@ -270,6 +269,7 @@ public class RecipeEventListener implements Listener {
预处理会阻止一些不合理的原版材质造成的合并问题
*/
private void preProcess(PrepareAnvilEvent event) {
if (event.getResult() == null) return;
AnvilInventory inventory = event.getInventory();
ItemStack first = inventory.getFirstItem();
ItemStack second = inventory.getSecondItem();
@@ -284,6 +284,10 @@ public class RecipeEventListener implements Listener {
}
// 如果第二个物品是附魔书,那么忽略
if (wrappedSecond.vanillaId().equals(ItemKeys.ENCHANTED_BOOK)) {
// 禁止不可附魔的物品被附魔书附魔
if (firstCustom.isPresent() && !firstCustom.get().settings().canEnchant()) {
event.setResult(null);
}
return;
}
@@ -313,12 +317,23 @@ public class RecipeEventListener implements Listener {
return;
}
// 如果禁止在铁砧使用两个相同物品修复
firstCustom.ifPresent(it -> {
if (it.settings().canRepair() == Tristate.FALSE) {
if (firstCustom.isPresent()) {
CustomItem<ItemStack> firstCustomItem = firstCustom.get();
if (firstCustomItem.settings().repairable().anvilCombine() == Tristate.FALSE) {
event.setResult(null);
return;
}
});
Item<ItemStack> wrappedResult = BukkitItemManager.instance().wrap(event.getResult());
if (!firstCustomItem.settings().canEnchant()) {
Object previousEnchantment = wrappedFirst.getExactComponent(ComponentTypes.ENCHANTMENTS);
if (previousEnchantment != null) {
wrappedResult.setExactComponent(ComponentTypes.ENCHANTMENTS, previousEnchantment);
} else {
wrappedResult.resetComponent(ComponentTypes.ENCHANTMENTS);
}
}
}
}
/*
@@ -356,7 +371,7 @@ public class RecipeEventListener implements Listener {
Key firstId = wrappedFirst.id();
Optional<CustomItem<ItemStack>> optionalCustomTool = wrappedFirst.getCustomItem();
// 物品无法被修复
if (optionalCustomTool.isPresent() && optionalCustomTool.get().settings().canRepair() == Tristate.FALSE) {
if (optionalCustomTool.isPresent() && optionalCustomTool.get().settings().repairable().anvilRepair() == Tristate.FALSE) {
return;
}
@@ -477,14 +492,12 @@ public class RecipeEventListener implements Listener {
*/
@SuppressWarnings("UnstableApiUsage")
private void processRename(PrepareAnvilEvent event) {
if (event.getResult() == null) return;
AnvilInventory inventory = event.getInventory();
ItemStack first = inventory.getFirstItem();
if (ItemStackUtils.isEmpty(first)) {
return;
}
if (event.getResult() == null) {
return;
}
Item<ItemStack> wrappedFirst = BukkitItemManager.instance().wrap(first);
wrappedFirst.getCustomItem().ifPresent(item -> {
if (!item.settings().renameable()) {
@@ -599,7 +612,36 @@ public class RecipeEventListener implements Listener {
if (input == null) return;
Player player = InventoryUtils.getPlayerFromInventoryEvent(event);
BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player);
inventory.setResult(craftingTableRecipe.assemble(input, new ItemBuildContext(serverPlayer, ContextHolder.EMPTY)));
if (craftingTableRecipe.hasVisualResult()) {
inventory.setResult(craftingTableRecipe.assembleVisual(input, ItemBuildContext.of(serverPlayer)));
} else {
inventory.setResult(craftingTableRecipe.assemble(input, ItemBuildContext.of(serverPlayer)));
}
}
@EventHandler(ignoreCancelled = true)
public void onCraftingFinish(CraftItemEvent event) {
if (!Config.enableRecipeSystem()) return;
org.bukkit.inventory.Recipe recipe = event.getRecipe();
if (!(recipe instanceof CraftingRecipe craftingRecipe)) return;
Key recipeId = Key.of(craftingRecipe.getKey().namespace(), craftingRecipe.getKey().value());
Optional<Recipe<ItemStack>> optionalRecipe = this.recipeManager.recipeById(recipeId);
// 也许是其他插件注册的配方,直接无视
if (optionalRecipe.isEmpty()) {
return;
}
CraftingInventory inventory = event.getInventory();
if (!(optionalRecipe.get() instanceof CustomCraftingTableRecipe<ItemStack> craftingTableRecipe)) {
return;
}
if (!craftingTableRecipe.hasVisualResult()) {
return;
}
CraftingInput<ItemStack> input = getCraftingInput(inventory);
if (input == null) return;
Player player = InventoryUtils.getPlayerFromInventoryEvent(event);
BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player);
inventory.setResult(craftingTableRecipe.assemble(input, ItemBuildContext.of(serverPlayer)));
}
private CraftingInput<ItemStack> getCraftingInput(CraftingInventory inventory) {
@@ -651,7 +693,7 @@ public class RecipeEventListener implements Listener {
SmithingInput<ItemStack> input = getSmithingInput(inventory);
if (smithingTrimRecipe.matches(input)) {
Player player = InventoryUtils.getPlayerFromInventoryEvent(event);
ItemStack result = smithingTrimRecipe.assemble(getSmithingInput(inventory), new ItemBuildContext(BukkitAdaptors.adapt(player), ContextHolder.EMPTY));
ItemStack result = smithingTrimRecipe.assemble(getSmithingInput(inventory), ItemBuildContext.of(BukkitAdaptors.adapt(player)));
event.setResult(result);
} else {
event.setResult(null);
@@ -675,7 +717,7 @@ public class RecipeEventListener implements Listener {
SmithingInput<ItemStack> input = getSmithingInput(inventory);
if (smithingTransformRecipe.matches(input)) {
Player player = InventoryUtils.getPlayerFromInventoryEvent(event);
ItemStack processed = smithingTransformRecipe.assemble(input, new ItemBuildContext(BukkitAdaptors.adapt(player), ContextHolder.EMPTY));
ItemStack processed = smithingTransformRecipe.assemble(input, ItemBuildContext.of(BukkitAdaptors.adapt(player)));
event.setResult(processed);
} else {
event.setResult(null);

View File

@@ -55,7 +55,6 @@ public class BukkitVanillaLootManager extends AbstractVanillaLootManager impleme
HandlerList.unregisterAll(this);
}
@SuppressWarnings("UnstableApiUsage")
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
public void onEntityDeath(EntityDeathEvent event) {
Entity entity = event.getEntity();

View File

@@ -1,6 +1,7 @@
package net.momirealms.craftengine.bukkit.pack;
import net.momirealms.craftengine.bukkit.api.BukkitAdaptors;
import net.momirealms.craftengine.bukkit.api.event.AsyncResourcePackCacheEvent;
import net.momirealms.craftengine.bukkit.api.event.AsyncResourcePackGenerateEvent;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.plugin.command.feature.ReloadCommand;
@@ -28,10 +29,17 @@ public class BukkitPackManager extends AbstractPackManager implements Listener {
private final BukkitCraftEngine plugin;
public BukkitPackManager(BukkitCraftEngine plugin) {
super(plugin, (rf, zp) -> {
AsyncResourcePackGenerateEvent endEvent = new AsyncResourcePackGenerateEvent(rf, zp);
EventUtils.fireAndForget(endEvent);
});
super(
plugin,
(cd) -> {
AsyncResourcePackCacheEvent cacheEvent = new AsyncResourcePackCacheEvent(cd);
EventUtils.fireAndForget(cacheEvent);
},
(rf, zp) -> {
AsyncResourcePackGenerateEvent endEvent = new AsyncResourcePackGenerateEvent(rf, zp);
EventUtils.fireAndForget(endEvent);
}
);
this.plugin = plugin;
}
@@ -45,6 +53,7 @@ public class BukkitPackManager extends AbstractPackManager implements Listener {
public void onPlayerJoin(PlayerJoinEvent event) {
if (Config.sendPackOnJoin() && !VersionHelper.isOrAbove1_20_2()) {
Player player = BukkitAdaptors.adapt(event.getPlayer());
if (player == null) return;
this.sendResourcePack(player);
}
}
@@ -81,7 +90,7 @@ public class BukkitPackManager extends AbstractPackManager implements Listener {
return;
}
if (!Config.sendPackOnUpload()) return;
CraftEngine.instance().logger().info("Complete uploading resource pack");
CraftEngine.instance().logger().info("Completed uploading resource pack");
for (BukkitServerPlayer player : this.plugin.networkManager().onlineUsers()) {
sendResourcePack(player);
}
@@ -98,7 +107,7 @@ public class BukkitPackManager extends AbstractPackManager implements Listener {
return;
}
if (dataList.size() == 1) {
ResourcePackDownloadData data = dataList.get(0);
ResourcePackDownloadData data = dataList.getFirst();
player.sendPacket(ResourcePackUtils.createPacket(data.uuid(), data.url(), data.sha1()), true);
player.addResourcePackUUID(data.uuid());
} else {

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