mirror of
https://github.com/Xiao-MoMi/craft-engine.git
synced 2025-12-28 19:39:11 +00:00
Merge branch 'dev' into dev
This commit is contained in:
12
README.md
12
README.md
@@ -54,10 +54,10 @@ The code you contribute will be open-sourced under the GPLv3 license. If you pre
|
||||
3. Once done, submit a **pull request** to **dev** branch for review. We appreciate your contributions!
|
||||
|
||||
## Differences Between Versions
|
||||
| Version | Official Support | Max Players | Dev Builds |
|
||||
|-------------------|------------------|-------------|------------|
|
||||
| Community Edition | ❌ No | 30 | ❌ No |
|
||||
| Premium Edition | ✔️ Yes | Unlimited | ✔️ Yes |
|
||||
| Version | Official Support | Exclusive Features | Dev Builds |
|
||||
|-------------------|------------------|--------------------|------------|
|
||||
| Community Edition | ❌ No | ❌ No | ❌ No |
|
||||
| Premium Edition | ✔️ Yes | ✔️ Yes | ✔️ Yes |
|
||||
|
||||
### 💖 Support the Developer
|
||||
Help sustain CraftEngine's development by going Premium!
|
||||
@@ -75,7 +75,7 @@ repositories {
|
||||
```
|
||||
```kotlin
|
||||
dependencies {
|
||||
compileOnly("net.momirealms:craft-engine-core:0.0.63")
|
||||
compileOnly("net.momirealms:craft-engine-bukkit:0.0.63")
|
||||
compileOnly("net.momirealms:craft-engine-core:0.0.64")
|
||||
compileOnly("net.momirealms:craft-engine-bukkit:0.0.64")
|
||||
}
|
||||
```
|
||||
@@ -101,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")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ repositories {
|
||||
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 {
|
||||
@@ -70,6 +71,14 @@ dependencies {
|
||||
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 {
|
||||
|
||||
@@ -5,12 +5,14 @@ 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;
|
||||
@@ -18,11 +20,16 @@ import net.momirealms.craftengine.bukkit.compatibility.worldedit.WorldEditBlockR
|
||||
import net.momirealms.craftengine.bukkit.font.BukkitFontManager;
|
||||
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
|
||||
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
|
||||
import net.momirealms.craftengine.core.block.BlockManager;
|
||||
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.config.Config;
|
||||
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;
|
||||
@@ -117,6 +124,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
|
||||
@@ -224,8 +248,8 @@ public class BukkitCompatibilityManager implements CompatibilityManager {
|
||||
private void initWorldEditHook() {
|
||||
WorldEditBlockRegister weBlockRegister = new WorldEditBlockRegister(BukkitBlockManager.instance(), false);
|
||||
try {
|
||||
for (Key newBlockId : BukkitBlockManager.instance().blockRegisterOrder()) {
|
||||
weBlockRegister.register(newBlockId);
|
||||
for (int i = 0; i < Config.serverSideBlocks(); i++) {
|
||||
weBlockRegister.register(BlockManager.createCustomBlockKey(i));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
this.plugin.logger().warn("Failed to initialize world edit hook", e);
|
||||
@@ -250,6 +274,18 @@ public class BukkitCompatibilityManager implements CompatibilityManager {
|
||||
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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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")
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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")
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import net.momirealms.craftengine.bukkit.api.BukkitAdaptors;
|
||||
import net.momirealms.craftengine.core.item.CustomItem;
|
||||
import net.momirealms.craftengine.core.item.ItemBuildContext;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.util.MCUtils;
|
||||
import net.momirealms.craftengine.core.util.MiscUtils;
|
||||
import net.momirealms.craftengine.core.util.ReflectionUtils;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
@@ -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();
|
||||
@@ -42,7 +42,7 @@ public class MythicItemDrop extends ItemDrop implements IItemDrop {
|
||||
context = ItemBuildContext.of(player);
|
||||
}
|
||||
}
|
||||
int amountInt = MCUtils.fastFloor(amount + 0.5F);
|
||||
int amountInt = MiscUtils.fastFloor(amount + 0.5F);
|
||||
ItemStack itemStack = this.customItem.buildItemStack(context, amountInt);
|
||||
return adapt(itemStack).amount(amountInt);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ repositories {
|
||||
dependencies {
|
||||
// Platform
|
||||
compileOnly("io.papermc.paper:paper-api:1.20.1-R0.1-SNAPSHOT")
|
||||
// authlib
|
||||
compileOnly("com.mojang:authlib:6.0.58")
|
||||
}
|
||||
|
||||
java {
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package net.momirealms.craftengine.bukkit.util;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class LegacyAuthLibUtils {
|
||||
|
||||
public static String getName(GameProfile profile) {
|
||||
return profile.getName();
|
||||
}
|
||||
|
||||
public static UUID getId(GameProfile profile) {
|
||||
return profile.getId();
|
||||
}
|
||||
}
|
||||
@@ -50,7 +50,7 @@ bukkit {
|
||||
name = "CraftEngine"
|
||||
apiVersion = "1.20"
|
||||
authors = listOf("XiaoMoMi")
|
||||
contributors = listOf("jhqwqmc", "iqtesterr", "WhiteProject1", "Catnies", "xiaozhangup", "TamashiiMon", "Halogly", "ArubikU", "Maxsh001", "Sasha2294", "MrPanda8")
|
||||
contributors = listOf("https://github.com/Xiao-MoMi/craft-engine/graphs/contributors")
|
||||
softDepend = listOf("PlaceholderAPI", "WorldEdit", "FastAsyncWorldEdit", "Skript")
|
||||
foliaSupported = true
|
||||
}
|
||||
@@ -81,5 +81,9 @@ tasks {
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ public class BukkitCraftEnginePlugin extends JavaPlugin {
|
||||
public BukkitCraftEnginePlugin() {
|
||||
this.plugin = new BukkitCraftEngine(this);
|
||||
this.plugin.applyDependencies();
|
||||
this.plugin.setUpConfig();
|
||||
this.plugin.setUpConfigAndLocale();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -53,7 +53,7 @@ paper {
|
||||
name = "CraftEngine"
|
||||
apiVersion = "1.20"
|
||||
authors = listOf("XiaoMoMi")
|
||||
contributors = listOf("jhqwqmc", "iqtesterrr", "WhiteProject1", "Catnies", "xiaozhangup", "TamashiiMon", "Halogly", "ArubikU", "Maxsh001", "Sasha2294", "MrPanda8")
|
||||
contributors = listOf("https://github.com/Xiao-MoMi/craft-engine/graphs/contributors")
|
||||
foliaSupported = true
|
||||
serverDependencies {
|
||||
register("PlaceholderAPI") {
|
||||
@@ -86,6 +86,9 @@ paper {
|
||||
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 }
|
||||
@@ -154,5 +157,9 @@ tasks {
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()) {
|
||||
this.plugin.setUpConfigAndLocale();
|
||||
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);
|
||||
|
||||
@@ -1,17 +1,26 @@
|
||||
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.config.IdSectionConfigParser;
|
||||
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,7 +42,71 @@ public class BukkitAdvancementManager extends AbstractAdvancementManager {
|
||||
return this.advancementParser;
|
||||
}
|
||||
|
||||
public class AdvancementParser implements ConfigParser {
|
||||
@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 extends IdSectionConfigParser {
|
||||
public static final String[] CONFIG_SECTION_NAME = new String[] {"advancements", "advancement"};
|
||||
|
||||
@Override
|
||||
@@ -47,7 +120,7 @@ public class BukkitAdvancementManager extends AbstractAdvancementManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parseSection(Pack pack, Path path, Key id, Map<String, Object> section) {
|
||||
public void parseSection(Pack pack, Path path, String node, Key id, Map<String, Object> section) {
|
||||
if (advancements.containsKey(id)) {
|
||||
throw new LocalizedResourceConfigException("warning.config.advancement.duplicate", path, id);
|
||||
}
|
||||
|
||||
@@ -3,37 +3,63 @@ package net.momirealms.craftengine.bukkit.api;
|
||||
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.BukkitExistingBlock;
|
||||
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
|
||||
import net.momirealms.craftengine.core.world.WorldPosition;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public final class BukkitAdaptors {
|
||||
|
||||
private BukkitAdaptors() {}
|
||||
|
||||
public static BukkitServerPlayer adapt(final Player player) {
|
||||
/**
|
||||
* Adapts a Bukkit Player to a CraftEngine BukkitServerPlayer.
|
||||
* This provides access to CraftEngine-specific player functionality and data.
|
||||
*
|
||||
* @param player the Bukkit Player to adapt, must not be null
|
||||
* @return a non-null BukkitServerPlayer instance wrapping the provided player
|
||||
*/
|
||||
@NotNull
|
||||
public static BukkitServerPlayer adapt(@NotNull final Player player) {
|
||||
return BukkitCraftEngine.instance().adapt(player);
|
||||
}
|
||||
|
||||
public static BukkitWorld adapt(final World world) {
|
||||
/**
|
||||
* Adapts a Bukkit World to a CraftEngine BukkitWorld.
|
||||
* This enables CraftEngine world operations on Bukkit world instances.
|
||||
*
|
||||
* @param world the Bukkit World to adapt, must not be null
|
||||
* @return a non-null BukkitWorld instance wrapping the provided world
|
||||
*/
|
||||
@NotNull
|
||||
public static BukkitWorld adapt(@NotNull final World world) {
|
||||
return new BukkitWorld(world);
|
||||
}
|
||||
|
||||
public static BukkitEntity adapt(final Entity entity) {
|
||||
/**
|
||||
* Adapts a Bukkit Entity to a CraftEngine BukkitEntity.
|
||||
* This provides CraftEngine entity functionality for Bukkit entities.
|
||||
*
|
||||
* @param entity the Bukkit Entity to adapt, must not be null
|
||||
* @return a non-null BukkitEntity instance wrapping the provided entity
|
||||
*/
|
||||
@NotNull
|
||||
public static BukkitEntity adapt(@NotNull final Entity entity) {
|
||||
return new BukkitEntity(entity);
|
||||
}
|
||||
|
||||
public static BukkitExistingBlock adapt(final Block block) {
|
||||
/**
|
||||
* Adapts a Bukkit Block to a CraftEngine BukkitExistingBlock.
|
||||
* This enables CraftEngine block operations on Bukkit block instances.
|
||||
*
|
||||
* @param block the Bukkit Block to adapt, must not be null
|
||||
* @return a non-null BukkitExistingBlock instance wrapping the provided block
|
||||
*/
|
||||
@NotNull
|
||||
public static BukkitExistingBlock adapt(@NotNull final Block block) {
|
||||
return new BukkitExistingBlock(block);
|
||||
}
|
||||
|
||||
public static Location toLocation(WorldPosition position) {
|
||||
return LocationUtils.toLocation(position);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
*
|
||||
@@ -170,16 +188,14 @@ public final class CraftEngineBlocks {
|
||||
* @param player player who breaks the block
|
||||
* @param dropLoot whether to drop block loots
|
||||
* @param isMoving is moving
|
||||
* @param playSound whether to play break sounds
|
||||
* @param sendParticles whether to send break particles
|
||||
* @param sendLevelEvent whether to send break particles and sounds
|
||||
* @return success or not
|
||||
*/
|
||||
public static boolean remove(@NotNull Block block,
|
||||
@Nullable Player player,
|
||||
boolean isMoving,
|
||||
boolean dropLoot,
|
||||
boolean playSound,
|
||||
boolean sendParticles) {
|
||||
boolean sendLevelEvent) {
|
||||
ImmutableBlockState state = getCustomBlockState(block);
|
||||
if (state == null || state.isEmpty()) return false;
|
||||
World world = new BukkitWorld(block.getWorld());
|
||||
@@ -188,24 +204,43 @@ 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);
|
||||
}
|
||||
}
|
||||
if (playSound) {
|
||||
world.playBlockSound(position, state.settings().sounds().breakSound());
|
||||
}
|
||||
if (sendParticles) {
|
||||
if (sendLevelEvent) {
|
||||
FastNMS.INSTANCE.method$LevelAccessor$levelEvent(world.serverWorld(), WorldEvents.BLOCK_BREAK_EFFECT, LocationUtils.toBlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()), state.customBlockState().registryId());
|
||||
}
|
||||
FastNMS.INSTANCE.method$Level$removeBlock(world.serverWorld(), LocationUtils.toBlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()), isMoving);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a block from the world if it's custom
|
||||
*
|
||||
* @param block block to remove
|
||||
* @param player player who breaks the block
|
||||
* @param dropLoot whether to drop block loots
|
||||
* @param isMoving is moving
|
||||
* @param playSound whether to play break sounds
|
||||
* @param sendParticles whether to send break particles
|
||||
* @return success or not
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
public static boolean remove(@NotNull Block block,
|
||||
@Nullable Player player,
|
||||
boolean isMoving,
|
||||
boolean dropLoot,
|
||||
boolean playSound,
|
||||
boolean sendParticles) {
|
||||
return remove(block, player, dropLoot, isMoving, playSound || sendParticles);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a block is custom
|
||||
*
|
||||
@@ -251,4 +286,14 @@ public final class CraftEngineBlocks {
|
||||
public static BlockData getBukkitBlockData(@NotNull ImmutableBlockState blockState) {
|
||||
return BlockStateUtils.fromBlockData(blockState.customBlockState().literalObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the block state is a vanilla block state
|
||||
*
|
||||
* @param id state id
|
||||
* @return is vanilla block or not
|
||||
*/
|
||||
public static boolean isVanillaBlockState(int id) {
|
||||
return BukkitBlockManager.instance().isVanillaBlockState(id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package net.momirealms.craftengine.bukkit.api;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.font.BukkitFontManager;
|
||||
import net.momirealms.craftengine.core.font.BitmapImage;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public final class CraftEngineImages {
|
||||
|
||||
private CraftEngineImages() {}
|
||||
|
||||
/**
|
||||
* Returns an unmodifiable map of all currently loaded custom images.
|
||||
* The map keys represent unique identifiers, and the values are the corresponding BitmapImage 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 image list.
|
||||
*
|
||||
* @return a non-null map containing all loaded custom images
|
||||
*/
|
||||
@NotNull
|
||||
public static Map<Key, BitmapImage> loadedImages() {
|
||||
return BukkitFontManager.instance().loadedImages();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a custom image by ID
|
||||
*
|
||||
* @param id id
|
||||
* @return the custom image
|
||||
*/
|
||||
@Nullable
|
||||
public static BitmapImage byId(@NotNull Key id) {
|
||||
return BukkitFontManager.instance().loadedImages().get(id);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import net.momirealms.craftengine.core.plugin.context.ContextHolder;
|
||||
import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext;
|
||||
import net.momirealms.craftengine.core.plugin.context.event.EventTrigger;
|
||||
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
|
||||
import net.momirealms.craftengine.core.sound.SoundSource;
|
||||
import net.momirealms.craftengine.core.util.Cancellable;
|
||||
import net.momirealms.craftengine.core.util.ItemUtils;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
@@ -44,13 +45,11 @@ import java.util.Optional;
|
||||
|
||||
public final class BlockEventListener implements Listener {
|
||||
private final BukkitCraftEngine plugin;
|
||||
private final boolean enableNoteBlockCheck;
|
||||
private final BukkitBlockManager manager;
|
||||
|
||||
public BlockEventListener(BukkitCraftEngine plugin, BukkitBlockManager manager, boolean enableNoteBlockCheck) {
|
||||
public BlockEventListener(BukkitCraftEngine plugin, BukkitBlockManager manager) {
|
||||
this.plugin = plugin;
|
||||
this.manager = manager;
|
||||
this.enableNoteBlockCheck = enableNoteBlockCheck;
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
@@ -74,18 +73,14 @@ public final class BlockEventListener implements Listener {
|
||||
// send sound if the placed block's sounds are removed
|
||||
if (Config.enableSoundSystem()) {
|
||||
Block block = event.getBlock();
|
||||
Object blockState = BlockStateUtils.blockDataToBlockState(block.getBlockData());
|
||||
if (blockState != MBlocks.AIR$defaultState) {
|
||||
Object ownerBlock = BlockStateUtils.getBlockOwner(blockState);
|
||||
if (this.manager.isBlockSoundRemoved(ownerBlock)) {
|
||||
Object blockState = BlockStateUtils.getBlockState(block);
|
||||
if (blockState != MBlocks.AIR$defaultState && BlockStateUtils.isVanillaBlock(blockState)) {
|
||||
Object soundType = FastNMS.INSTANCE.method$BlockBehaviour$BlockStateBase$getSoundType(blockState);
|
||||
Object soundEvent = FastNMS.INSTANCE.field$SoundType$placeSound(soundType);
|
||||
Object soundId = FastNMS.INSTANCE.field$SoundEvent$location(soundEvent);
|
||||
if (this.manager.isPlaceSoundMissing(soundId)) {
|
||||
if (player.getInventory().getItemInMainHand().getType() != Material.DEBUG_STICK) {
|
||||
try {
|
||||
Object soundType = CoreReflections.field$BlockBehaviour$soundType.get(ownerBlock);
|
||||
Object placeSound = CoreReflections.field$SoundType$placeSound.get(soundType);
|
||||
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);
|
||||
}
|
||||
player.playSound(block.getLocation().add(0.5, 0.5, 0.5), soundId.toString(), SoundCategory.BLOCKS, 1f, 0.8f);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -93,23 +88,19 @@ public final class BlockEventListener implements Listener {
|
||||
}
|
||||
// resend sound if the clicked block is interactable on client side
|
||||
if (serverPlayer.shouldResendSound()) {
|
||||
try {
|
||||
Block block = event.getBlock();
|
||||
Object blockState = BlockStateUtils.blockDataToBlockState(block.getBlockData());
|
||||
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().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);
|
||||
}
|
||||
Block block = event.getBlock();
|
||||
Object blockState = BlockStateUtils.getBlockState(block);
|
||||
Object soundType = FastNMS.INSTANCE.method$BlockBehaviour$BlockStateBase$getSoundType(blockState);
|
||||
Object soundEvent = FastNMS.INSTANCE.field$SoundType$placeSound(soundType);
|
||||
Object soundId = FastNMS.INSTANCE.field$SoundEvent$location(soundEvent);
|
||||
player.playSound(block.getLocation().add(0.5, 0.5, 0.5), soundId.toString(), SoundCategory.BLOCKS, 1f, 0.8f);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
|
||||
public void onPlayerBreak(BlockBreakEvent event) {
|
||||
org.bukkit.block.Block block = event.getBlock();
|
||||
Object blockState = BlockStateUtils.blockDataToBlockState(block.getBlockData());
|
||||
Object blockState = BlockStateUtils.getBlockState(block);
|
||||
int stateId = BlockStateUtils.blockStateToId(blockState);
|
||||
Player player = event.getPlayer();
|
||||
Location location = block.getLocation();
|
||||
@@ -168,12 +159,15 @@ public final class BlockEventListener implements Listener {
|
||||
}
|
||||
|
||||
// play sound
|
||||
world.playBlockSound(position, state.settings().sounds().breakSound());
|
||||
serverPlayer.playSound(position, state.settings().sounds().breakSound(), SoundSource.BLOCK);
|
||||
}
|
||||
} else {
|
||||
// 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);
|
||||
@@ -190,18 +184,13 @@ public final class BlockEventListener implements Listener {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// sound system
|
||||
if (Config.enableSoundSystem()) {
|
||||
Object ownerBlock = BlockStateUtils.getBlockOwner(blockState);
|
||||
if (this.manager.isBlockSoundRemoved(ownerBlock)) {
|
||||
try {
|
||||
Object soundType = CoreReflections.field$BlockBehaviour$soundType.get(ownerBlock);
|
||||
Object breakSound = CoreReflections.field$SoundType$breakSound.get(soundType);
|
||||
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);
|
||||
}
|
||||
Object soundType = FastNMS.INSTANCE.method$BlockBehaviour$BlockStateBase$getSoundType(blockState);
|
||||
Object soundEvent = FastNMS.INSTANCE.field$SoundType$breakSound(soundType);
|
||||
Object soundId = FastNMS.INSTANCE.field$SoundEvent$location(soundEvent);
|
||||
if (this.manager.isBreakSoundMissing(soundId)) {
|
||||
player.playSound(block.getLocation().add(0.5, 0.5, 0.5), soundId.toString(), SoundCategory.BLOCKS, 1f, 0.8f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -258,22 +247,17 @@ public final class BlockEventListener implements Listener {
|
||||
}
|
||||
player.playSound(location, state.settings().sounds().stepSound().id().toString(), SoundCategory.BLOCKS, state.settings().sounds().stepSound().volume().get(), state.settings().sounds().stepSound().pitch().get());
|
||||
} else if (Config.enableSoundSystem()) {
|
||||
Object ownerBlock = BlockStateUtils.getBlockOwner(blockState);
|
||||
if (this.manager.isBlockSoundRemoved(ownerBlock)) {
|
||||
try {
|
||||
Object soundType = CoreReflections.field$BlockBehaviour$soundType.get(ownerBlock);
|
||||
Object stepSound = CoreReflections.field$SoundType$stepSound.get(soundType);
|
||||
player.playSound(player.getLocation(), FastNMS.INSTANCE.field$SoundEvent$location(stepSound).toString(), SoundCategory.BLOCKS, 0.15f, 1f);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
this.plugin.logger().warn("Failed to get sound type", e);
|
||||
}
|
||||
Object soundType = FastNMS.INSTANCE.method$BlockBehaviour$BlockStateBase$getSoundType(blockState);
|
||||
Object soundEvent = FastNMS.INSTANCE.field$SoundType$stepSound(soundType);
|
||||
Object soundId = FastNMS.INSTANCE.field$SoundEvent$location(soundEvent);
|
||||
if (this.manager.isStepSoundMissing(soundId)) {
|
||||
player.playSound(player.getLocation(), soundId.toString(), SoundCategory.BLOCKS, 0.15f, 1f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
|
||||
public void onBlockPhysics(BlockPhysicsEvent event) {
|
||||
if (!this.enableNoteBlockCheck) return;
|
||||
// for vanilla blocks
|
||||
if (event.getChangedType() == Material.NOTE_BLOCK) {
|
||||
Block block = event.getBlock();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,23 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,263 +1,27 @@
|
||||
package net.momirealms.craftengine.bukkit.block;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.block.behavior.UnsafeCompositeBlockBehavior;
|
||||
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.plugin.reflection.minecraft.MBlocks;
|
||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MFluids;
|
||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistries;
|
||||
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.KeyUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.SoundUtils;
|
||||
import net.momirealms.craftengine.core.block.*;
|
||||
import net.momirealms.craftengine.core.block.behavior.AbstractBlockBehavior;
|
||||
import net.momirealms.craftengine.core.block.behavior.BlockBehaviors;
|
||||
import net.momirealms.craftengine.core.block.behavior.EmptyBlockBehavior;
|
||||
import net.momirealms.craftengine.core.block.properties.Property;
|
||||
import net.momirealms.craftengine.core.block.AbstractCustomBlock;
|
||||
import net.momirealms.craftengine.core.block.BlockStateVariantProvider;
|
||||
import net.momirealms.craftengine.core.block.CustomBlock;
|
||||
import net.momirealms.craftengine.core.loot.LootTable;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext;
|
||||
import net.momirealms.craftengine.core.plugin.context.event.EventTrigger;
|
||||
import net.momirealms.craftengine.core.plugin.context.function.Function;
|
||||
import net.momirealms.craftengine.core.registry.BuiltInRegistries;
|
||||
import net.momirealms.craftengine.core.registry.Holder;
|
||||
import net.momirealms.craftengine.core.registry.WritableRegistry;
|
||||
import net.momirealms.craftengine.core.util.*;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public final class BukkitCustomBlock extends AbstractCustomBlock {
|
||||
private static final Object ALWAYS_FALSE = FastNMS.INSTANCE.method$StatePredicate$always(false);
|
||||
private static final Object ALWAYS_TRUE = FastNMS.INSTANCE.method$StatePredicate$always(true);
|
||||
|
||||
private BukkitCustomBlock(
|
||||
@NotNull Key id,
|
||||
public BukkitCustomBlock(
|
||||
@NotNull Holder.Reference<CustomBlock> holder,
|
||||
@NotNull Map<String, Property<?>> properties,
|
||||
@NotNull Map<String, Integer> appearances,
|
||||
@NotNull Map<String, BlockStateVariant> variantMapper,
|
||||
@NotNull BlockSettings settings,
|
||||
@NotNull BlockStateVariantProvider variantProvider,
|
||||
@NotNull Map<EventTrigger, List<Function<PlayerOptionalContext>>> events,
|
||||
@Nullable List<Map<String, Object>> behavior,
|
||||
@Nullable LootTable<?> lootTable
|
||||
) {
|
||||
super(id, holder, properties, appearances, variantMapper, settings, events, behavior, lootTable);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BlockBehavior setupBehavior(List<Map<String, Object>> behaviorConfig) {
|
||||
if (behaviorConfig == null || behaviorConfig.isEmpty()) {
|
||||
return new EmptyBlockBehavior();
|
||||
} else if (behaviorConfig.size() == 1) {
|
||||
return BlockBehaviors.fromMap(this, behaviorConfig.getFirst());
|
||||
} else {
|
||||
List<AbstractBlockBehavior> behaviors = new ArrayList<>();
|
||||
for (Map<String, Object> config : behaviorConfig) {
|
||||
behaviors.add((AbstractBlockBehavior) BlockBehaviors.fromMap(this, config));
|
||||
}
|
||||
return new UnsafeCompositeBlockBehavior(this, behaviors);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Nullable
|
||||
@Override
|
||||
public LootTable<ItemStack> lootTable() {
|
||||
return (LootTable<ItemStack>) super.lootTable();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyPlatformSettings() {
|
||||
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!");
|
||||
continue;
|
||||
} else if (immutableBlockState.customBlockState() == null) {
|
||||
CraftEngine.instance().logger().warn("Could not find custom block immutableBlockState for " + immutableBlockState + ". This might cause errors!");
|
||||
continue;
|
||||
}
|
||||
DelegatingBlockState nmsState = (DelegatingBlockState) immutableBlockState.customBlockState().literalObject();
|
||||
nmsState.setBlockState(immutableBlockState);
|
||||
BlockSettings settings = immutableBlockState.settings();
|
||||
|
||||
// set block properties
|
||||
CoreReflections.field$BlockStateBase$lightEmission.set(nmsState, settings.luminance());
|
||||
CoreReflections.field$BlockStateBase$burnable.set(nmsState, settings.burnable());
|
||||
CoreReflections.field$BlockStateBase$hardness.set(nmsState, settings.hardness());
|
||||
CoreReflections.field$BlockStateBase$replaceable.set(nmsState, settings.replaceable());
|
||||
Object mcMapColor = CoreReflections.method$MapColor$byId.invoke(null, settings.mapColor().id);
|
||||
CoreReflections.field$BlockStateBase$mapColor.set(nmsState, mcMapColor);
|
||||
Object mcInstrument = ((Object[]) CoreReflections.method$NoteBlockInstrument$values.invoke(null))[settings.instrument().ordinal()];
|
||||
CoreReflections.field$BlockStateBase$instrument.set(nmsState, mcInstrument);
|
||||
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().literalObject()) : settings.canOcclude().asBoolean();
|
||||
CoreReflections.field$BlockStateBase$canOcclude.set(nmsState, canOcclude);
|
||||
|
||||
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);
|
||||
CoreReflections.field$BlockStateBase$isSuffocating.set(nmsState, settings.isSuffocating().asBoolean() ? ALWAYS_TRUE : ALWAYS_FALSE);
|
||||
CoreReflections.field$BlockStateBase$isViewBlocking.set(nmsState, settings.isViewBlocking() == Tristate.UNDEFINED ? settings.isSuffocating().asBoolean() ? ALWAYS_TRUE : ALWAYS_FALSE : (settings.isViewBlocking().asBoolean() ? ALWAYS_TRUE : ALWAYS_FALSE));
|
||||
|
||||
// set parent block properties
|
||||
DelegatingBlock nmsBlock = (DelegatingBlock) BlockStateUtils.getBlockOwner(nmsState);
|
||||
ObjectHolder<BlockShape> shapeHolder = nmsBlock.shapeDelegate();
|
||||
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)) {
|
||||
throw new IllegalArgumentException("BlockState is not a Vanilla block");
|
||||
}
|
||||
return blockState;
|
||||
} catch (IllegalArgumentException e) {
|
||||
CraftEngine.instance().logger().warn("Illegal shape block state: " + it, e);
|
||||
return null;
|
||||
}
|
||||
}).orElse(null)));
|
||||
// bind behavior
|
||||
ObjectHolder<BlockBehavior> behaviorHolder = nmsBlock.behaviorDelegate();
|
||||
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);
|
||||
boolean isConditionallyFullOpaque = canOcclude & useShapeForLightOcclusion;
|
||||
if (!VersionHelper.isOrAbove1_21_2()) {
|
||||
CoreReflections.field$BlockStateBase$isConditionallyFullOpaque.set(nmsState, isConditionallyFullOpaque);
|
||||
}
|
||||
// modify cache
|
||||
if (VersionHelper.isOrAbove1_21_2()) {
|
||||
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
|
||||
if (settings.propagatesSkylightDown() == Tristate.TRUE) {
|
||||
CoreReflections.field$BlockStateBase$propagatesSkylightDown.set(nmsState, true);
|
||||
} 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().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().literalObject()));
|
||||
// set block light
|
||||
CoreReflections.field$BlockStateBase$Cache$lightBlock.set(cache, blockLight);
|
||||
// set propagates skylight
|
||||
if (settings.propagatesSkylightDown() == Tristate.TRUE) {
|
||||
CoreReflections.field$BlockStateBase$Cache$propagatesSkylightDown.set(cache, true);
|
||||
} 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().literalObject())));
|
||||
}
|
||||
if (!isConditionallyFullOpaque) {
|
||||
CoreReflections.field$BlockStateBase$opacityIfCached.set(nmsState, blockLight);
|
||||
}
|
||||
}
|
||||
// set fluid later
|
||||
if (settings.fluidState()) {
|
||||
CoreReflections.field$BlockStateBase$fluidState.set(nmsState, CoreReflections.method$FlowingFluid$getSource.invoke(MFluids.WATER, false));
|
||||
} else {
|
||||
CoreReflections.field$BlockStateBase$fluidState.set(nmsState, MFluids.EMPTY$defaultState);
|
||||
}
|
||||
// set random tick later
|
||||
CoreReflections.field$BlockStateBase$isRandomlyTicking.set(nmsState, settings.isRandomlyTicking());
|
||||
// bind tags
|
||||
Object holder = BukkitCraftEngine.instance().blockManager().getMinecraftBlockHolder(immutableBlockState.customBlockState().registryId());
|
||||
Set<Object> tags = new HashSet<>();
|
||||
for (Key tag : settings.tags()) {
|
||||
tags.add(CoreReflections.method$TagKey$create.invoke(null, MRegistries.BLOCK, KeyUtils.toResourceLocation(tag)));
|
||||
}
|
||||
CoreReflections.field$Holder$Reference$tags.set(holder, tags);
|
||||
// set burning properties
|
||||
if (settings.burnable()) {
|
||||
CoreReflections.method$FireBlock$setFlammable.invoke(MBlocks.FIRE, nmsBlock, settings.burnChance(), settings.fireSpreadChance());
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
CraftEngine.instance().logger().warn("Failed to init block settings", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static Builder builder(Key id) {
|
||||
return new BuilderImpl(id);
|
||||
}
|
||||
|
||||
public static class BuilderImpl implements Builder {
|
||||
protected final Key id;
|
||||
protected Map<String, Property<?>> properties;
|
||||
protected Map<String, Integer> appearances;
|
||||
protected Map<String, BlockStateVariant> variantMapper;
|
||||
protected BlockSettings settings;
|
||||
protected List<Map<String, Object>> behavior;
|
||||
protected LootTable<?> lootTable;
|
||||
protected Map<EventTrigger, List<Function<PlayerOptionalContext>>> events;
|
||||
|
||||
public BuilderImpl(Key id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder events(Map<EventTrigger, List<Function<PlayerOptionalContext>>> events) {
|
||||
this.events = events;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder appearances(Map<String, Integer> appearances) {
|
||||
this.appearances = appearances;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder behavior(List<Map<String, Object>> behavior) {
|
||||
this.behavior = behavior;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder lootTable(LootTable<?> lootTable) {
|
||||
this.lootTable = lootTable;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder properties(Map<String, Property<?>> properties) {
|
||||
this.properties = properties;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder settings(BlockSettings settings) {
|
||||
this.settings = settings;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder variantMapper(Map<String, BlockStateVariant> variantMapper) {
|
||||
this.variantMapper = variantMapper;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull CustomBlock build() {
|
||||
// create or get block holder
|
||||
Holder.Reference<CustomBlock> holder = ((WritableRegistry<CustomBlock>) BuiltInRegistries.BLOCK).getOrRegisterForHolder(ResourceKey.create(BuiltInRegistries.BLOCK.key().location(), this.id));
|
||||
return new BukkitCustomBlock(this.id, holder, this.properties, this.appearances, this.variantMapper, this.settings, this.events, this.behavior, this.lootTable);
|
||||
}
|
||||
super(holder, variantProvider, events, lootTable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package net.momirealms.craftengine.bukkit.block;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
|
||||
import net.momirealms.craftengine.core.block.AbstractBlockStateWrapper;
|
||||
import net.momirealms.craftengine.core.block.ImmutableBlockState;
|
||||
import net.momirealms.craftengine.core.block.properties.Property;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class BukkitCustomBlockStateWrapper extends AbstractBlockStateWrapper {
|
||||
|
||||
public BukkitCustomBlockStateWrapper(Object blockState, int registryId) {
|
||||
super(blockState, registryId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key ownerId() {
|
||||
return getImmutableBlockState().map(state -> state.owner().value().id()).orElseGet(() -> BlockStateUtils.getBlockOwnerIdFromState(super.blockState));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> T getProperty(String propertyName) {
|
||||
return (T) getImmutableBlockState().map(state -> {
|
||||
Property<?> property = state.owner().value().getProperty(propertyName);
|
||||
if (property == null)
|
||||
return null;
|
||||
return state.getNullable(property);
|
||||
}).orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasProperty(String propertyName) {
|
||||
return getImmutableBlockState().map(state -> state.owner().value().getProperty(propertyName) != null).orElse(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAsString() {
|
||||
return getImmutableBlockState().map(ImmutableBlockState::toString).orElseGet(() -> BlockStateUtils.fromBlockData(super.blockState).getAsString());
|
||||
}
|
||||
|
||||
public Optional<ImmutableBlockState> getImmutableBlockState() {
|
||||
return BlockStateUtils.getOptionalCustomBlockState(super.blockState);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package net.momirealms.craftengine.bukkit.block;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
|
||||
import net.momirealms.craftengine.core.block.AbstractBlockStateWrapper;
|
||||
import net.momirealms.craftengine.core.block.StatePropertyAccessor;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
|
||||
public class BukkitVanillaBlockStateWrapper extends AbstractBlockStateWrapper {
|
||||
private final StatePropertyAccessor accessor;
|
||||
|
||||
public BukkitVanillaBlockStateWrapper(Object blockState, int registryId) {
|
||||
super(blockState, registryId);
|
||||
this.accessor = FastNMS.INSTANCE.createStatePropertyAccessor(blockState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key ownerId() {
|
||||
return BlockStateUtils.getBlockOwnerIdFromState(super.blockState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getProperty(String propertyName) {
|
||||
return this.accessor.getPropertyValue(propertyName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasProperty(String propertyName) {
|
||||
return this.accessor.hasProperty(propertyName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAsString() {
|
||||
return BlockStateUtils.fromBlockData(super.blockState).getAsString();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,6 @@ import net.momirealms.craftengine.bukkit.util.LocationUtils;
|
||||
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
|
||||
import net.momirealms.craftengine.core.block.CustomBlock;
|
||||
import net.momirealms.craftengine.core.block.ImmutableBlockState;
|
||||
import net.momirealms.craftengine.core.world.BlockPos;
|
||||
import net.momirealms.craftengine.core.world.Vec3d;
|
||||
import net.momirealms.craftengine.core.world.WorldEvents;
|
||||
import net.momirealms.craftengine.core.world.WorldPosition;
|
||||
@@ -69,13 +68,8 @@ public abstract class AbstractCanSurviveBlockBehavior extends BukkitBlockBehavio
|
||||
FastNMS.INSTANCE.method$ScheduledTickAccess$scheduleBlockTick(level, blockPos, thisBlock, this.delay);
|
||||
return state;
|
||||
}
|
||||
if (!canSurvive(thisBlock, new Object[] {state, level, blockPos}, () -> true)) {
|
||||
BlockPos pos = LocationUtils.fromBlockPos(blockPos);
|
||||
ImmutableBlockState customState = optionalCustomState.get();
|
||||
net.momirealms.craftengine.core.world.World world = new BukkitWorld(FastNMS.INSTANCE.method$Level$getCraftWorld(level));
|
||||
WorldPosition position = new WorldPosition(world, Vec3d.atCenterOf(pos));
|
||||
world.playBlockSound(position, customState.settings().sounds().breakSound());
|
||||
FastNMS.INSTANCE.method$LevelAccessor$levelEvent(level, WorldEvents.BLOCK_BREAK_EFFECT, blockPos, customState.customBlockState().registryId());
|
||||
if (!FastNMS.INSTANCE.method$BlockStateBase$canSurvive(state, level, blockPos)) {
|
||||
FastNMS.INSTANCE.method$LevelAccessor$levelEvent(level, WorldEvents.BLOCK_BREAK_EFFECT, blockPos, optionalCustomState.get().customBlockState().registryId());
|
||||
return MBlocks.AIR$defaultState;
|
||||
}
|
||||
return state;
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
package net.momirealms.craftengine.bukkit.block.behavior;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
|
||||
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.DirectionUtils;
|
||||
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.IntegerProperty;
|
||||
import net.momirealms.craftengine.core.block.properties.Property;
|
||||
import net.momirealms.craftengine.core.util.HorizontalDirection;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
public class AttachedStemBlockBehavior extends BukkitBlockBehavior {
|
||||
public static final Factory FACTORY = new Factory();
|
||||
private final Property<HorizontalDirection> facingProperty;
|
||||
private final Key fruit;
|
||||
private final Key stem;
|
||||
|
||||
public AttachedStemBlockBehavior(CustomBlock customBlock,
|
||||
Property<HorizontalDirection> facingProperty,
|
||||
Key fruit,
|
||||
Key stem) {
|
||||
super(customBlock);
|
||||
this.facingProperty = facingProperty;
|
||||
this.fruit = fruit;
|
||||
this.stem = stem;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPathFindable(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
|
||||
return (VersionHelper.isOrAbove1_20_5() ? args[1] : args[3]).equals(CoreReflections.instance$PathComputationType$AIR)
|
||||
&& !FastNMS.INSTANCE.field$BlockBehavior$hasCollision(thisBlock) || (boolean) superMethod.call();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object updateShape(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
|
||||
Object state = args[0];
|
||||
HorizontalDirection direction = DirectionUtils.fromNMSDirection(args[updateShape$direction]).toHorizontalDirection();
|
||||
Object neighborState = args[updateShape$neighborState];
|
||||
Optional<ImmutableBlockState> optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(state);
|
||||
if (optionalCustomState.isEmpty() || direction != optionalCustomState.get().get(this.facingProperty)) {
|
||||
return super.updateShape(thisBlock, args, superMethod);
|
||||
}
|
||||
Optional<ImmutableBlockState> optionalCustomNeighborState = BlockStateUtils.getOptionalCustomBlockState(neighborState);
|
||||
if (optionalCustomNeighborState.isPresent()) {
|
||||
ImmutableBlockState customNeighborState = optionalCustomNeighborState.get();
|
||||
if (!customNeighborState.owner().value().id().equals(this.fruit)) {
|
||||
Object stemBlock = resetStemBlock();
|
||||
if (stemBlock != null) return stemBlock;
|
||||
}
|
||||
} else {
|
||||
if (this.stem.namespace().equals("minecraft")) {
|
||||
Key neighborBlockId = BlockStateUtils.getBlockOwnerIdFromState(neighborState);
|
||||
if (!neighborBlockId.equals(this.fruit)) {
|
||||
Object stemBlock = resetStemBlock();
|
||||
if (stemBlock != null) return stemBlock;
|
||||
}
|
||||
} else {
|
||||
Object stemBlock = resetStemBlock();
|
||||
if (stemBlock != null) return stemBlock;
|
||||
}
|
||||
}
|
||||
return super.updateShape(thisBlock, args, superMethod);
|
||||
}
|
||||
|
||||
private Object resetStemBlock() {
|
||||
Optional<CustomBlock> optionalStemBlock = BukkitBlockManager.instance().blockById(this.stem);
|
||||
if (optionalStemBlock.isPresent()) {
|
||||
CustomBlock stemBlock = optionalStemBlock.get();
|
||||
IntegerProperty ageProperty = (IntegerProperty) stemBlock.getProperty("age");
|
||||
if (ageProperty == null) return stemBlock.defaultState().customBlockState().literalObject();
|
||||
return stemBlock.defaultState().with(ageProperty, ageProperty.max).customBlockState().literalObject();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static class Factory implements BlockBehaviorFactory {
|
||||
|
||||
@Override
|
||||
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Property<HorizontalDirection> facingProperty = (Property<HorizontalDirection>) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("facing"), "warning.config.block.behavior.attached_stem.missing_facing");
|
||||
Key fruit = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("fruit"), "warning.config.block.behavior.attached_stem.missing_fruit"));
|
||||
Key stem = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("stem"), "warning.config.block.behavior.attached_stem.missing_stem"));
|
||||
return new AttachedStemBlockBehavior(block, facingProperty, fruit, stem);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package net.momirealms.craftengine.bukkit.block.behavior;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.*;
|
||||
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.DirectionUtils;
|
||||
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.BooleanProperty;
|
||||
import net.momirealms.craftengine.core.block.properties.Property;
|
||||
import net.momirealms.craftengine.core.util.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
public class BuddingBlockBehavior extends BukkitBlockBehavior {
|
||||
public static final Factory FACTORY = new Factory();
|
||||
private final float growthChance;
|
||||
private final List<Key> blocks;
|
||||
|
||||
public BuddingBlockBehavior(CustomBlock customBlock, float growthChance, List<Key> blocks) {
|
||||
super(customBlock);
|
||||
this.growthChance = growthChance;
|
||||
this.blocks = blocks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void randomTick(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
|
||||
if (RandomUtils.generateRandomFloat(0, 1) >= growthChance) return;
|
||||
Object nmsDirection = CoreReflections.instance$Direction$values[RandomUtils.generateRandomInt(0, 6)];
|
||||
Direction direction = DirectionUtils.fromNMSDirection(nmsDirection);
|
||||
Object blockPos = FastNMS.INSTANCE.method$BlockPos$relative(args[2], nmsDirection);
|
||||
Object blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(args[1], blockPos);
|
||||
if (canClusterGrowAtState(blockState)) {
|
||||
Key blockId = blocks.getFirst();
|
||||
CustomBlock firstBlock = BukkitBlockManager.instance().blockById(blockId).orElse(null);
|
||||
placeWithPropertyBlock(firstBlock, blockId, direction, nmsDirection, args[1], blockPos, blockState);
|
||||
} else {
|
||||
Key blockId = BlockStateUtils.getOptionalCustomBlockState(blockState)
|
||||
.map(it -> it.owner().value().id())
|
||||
.orElseGet(() -> BlockStateUtils.getBlockOwnerIdFromState(blockState));
|
||||
int blockIdIndex = blocks.indexOf(blockId);
|
||||
if (blockIdIndex < 0 || blockIdIndex == blocks.size() - 1) return;
|
||||
Key nextBlockId = blocks.get(blockIdIndex + 1);
|
||||
CustomBlock nextBlock = BukkitBlockManager.instance().blockById(nextBlockId).orElse(null);
|
||||
placeWithPropertyBlock(nextBlock, nextBlockId, direction, nmsDirection, args[1], blockPos, blockState);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void placeWithPropertyBlock(CustomBlock customBlock, Key blockId, Direction direction, Object nmsDirection, Object level, Object blockPos, Object blockState) {
|
||||
if (customBlock != null) {
|
||||
ImmutableBlockState newState = customBlock.defaultState();
|
||||
Property<?> facing = customBlock.getProperty("facing");
|
||||
if (facing != null) {
|
||||
if (facing.valueClass() == Direction.class) {
|
||||
newState = newState.with((Property<Direction>) facing, direction);
|
||||
} else if (facing.valueClass() == HorizontalDirection.class) {
|
||||
if (!direction.axis().isHorizontal()) return;
|
||||
newState = newState.with((Property<HorizontalDirection>) facing, direction.toHorizontalDirection());
|
||||
}
|
||||
}
|
||||
BooleanProperty waterlogged = (BooleanProperty) customBlock.getProperty("waterlogged");
|
||||
if (waterlogged != null) {
|
||||
newState = newState.with(waterlogged, FastNMS.INSTANCE.method$FluidState$getType(FastNMS.INSTANCE.field$BlockBehaviour$BlockStateBase$fluidState(blockState)) == MFluids.WATER);
|
||||
}
|
||||
FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, newState.customBlockState().literalObject(), 3);
|
||||
} else if (blockId.namespace().equals("minecraft")) {
|
||||
Object block = FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.BLOCK, FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", blockId.value()));
|
||||
if (block == null) return;
|
||||
Object newState = FastNMS.INSTANCE.method$Block$defaultState(block);
|
||||
newState = FastNMS.INSTANCE.method$StateHolder$trySetValue(newState, MBlockStateProperties.WATERLOGGED, FastNMS.INSTANCE.method$FluidState$getType(FastNMS.INSTANCE.field$BlockBehaviour$BlockStateBase$fluidState(blockState)) == MFluids.WATER);
|
||||
newState = FastNMS.INSTANCE.method$StateHolder$trySetValue(newState, MBlockStateProperties.FACING, (Comparable<?>) nmsDirection);
|
||||
FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, newState, 3);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean canClusterGrowAtState(Object state) {
|
||||
return FastNMS.INSTANCE.method$BlockStateBase$isAir(state)
|
||||
|| FastNMS.INSTANCE.method$BlockStateBase$isBlock(state, MBlocks.WATER)
|
||||
&& FastNMS.INSTANCE.field$FluidState$amount(FastNMS.INSTANCE.field$BlockBehaviour$BlockStateBase$fluidState(state)) == 8;
|
||||
}
|
||||
|
||||
public static class Factory implements BlockBehaviorFactory {
|
||||
|
||||
@Override
|
||||
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
|
||||
float growthChance = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("growth-chance", 0.2), "growth-chance");
|
||||
List<Key> blocks = new ObjectArrayList<>();
|
||||
MiscUtils.getAsStringList(arguments.get("blocks")).forEach(s -> blocks.add(Key.of(s)));
|
||||
return new BuddingBlockBehavior(block, growthChance, blocks);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,21 @@ public class BukkitBlockBehaviors extends BlockBehaviors {
|
||||
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 final Key FENCE_BLOCK = Key.from("craftengine:fence_block");
|
||||
public static final Key BUTTON_BLOCK = Key.from("craftengine:button_block");
|
||||
public static final Key FACE_ATTACHED_HORIZONTAL_DIRECTIONAL_BLOCK = Key.from("craftengine:face_attached_horizontal_directional_block");
|
||||
public static final Key STEM_BLOCK = Key.from("craftengine:stem_block");
|
||||
public static final Key ATTACHED_STEM_BLOCK = Key.from("craftengine:attached_stem_block");
|
||||
public static final Key CHIME_BLOCK = Key.from("craftengine:chime_block");
|
||||
public static final Key BUDDING_BLOCK = Key.from("craftengine:budding_block");
|
||||
|
||||
public static void init() {
|
||||
register(EMPTY, (block, args) -> EmptyBlockBehavior.INSTANCE);
|
||||
@@ -54,5 +69,20 @@ public class BukkitBlockBehaviors extends BlockBehaviors {
|
||||
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);
|
||||
register(FENCE_BLOCK, FenceBlockBehavior.FACTORY);
|
||||
register(BUTTON_BLOCK, ButtonBlockBehavior.FACTORY);
|
||||
register(FACE_ATTACHED_HORIZONTAL_DIRECTIONAL_BLOCK, FaceAttachedHorizontalDirectionalBlockBehavior.FACTORY);
|
||||
register(STEM_BLOCK, StemBlockBehavior.FACTORY);
|
||||
register(ATTACHED_STEM_BLOCK, AttachedStemBlockBehavior.FACTORY);
|
||||
register(CHIME_BLOCK, ChimeBlockBehavior.FACTORY);
|
||||
register(BUDDING_BLOCK, BuddingBlockBehavior.FACTORY);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,11 +26,13 @@ public class BushBlockBehavior extends AbstractCanSurviveBlockBehavior {
|
||||
protected final Set<String> customBlocksCansSurviveOn;
|
||||
protected final boolean blacklistMode;
|
||||
protected final boolean stackable;
|
||||
protected final int maxHeight;
|
||||
|
||||
public BushBlockBehavior(CustomBlock block, int delay, boolean blacklist, boolean stackable, List<Object> tagsCanSurviveOn, Set<Object> blockStatesCanSurviveOn, Set<String> customBlocksCansSurviveOn) {
|
||||
public BushBlockBehavior(CustomBlock block, int delay, boolean blacklist, boolean stackable, int maxHeight, List<Object> tagsCanSurviveOn, Set<Object> blockStatesCanSurviveOn, Set<String> customBlocksCansSurviveOn) {
|
||||
super(block, delay);
|
||||
this.blacklistMode = blacklist;
|
||||
this.stackable = stackable;
|
||||
this.maxHeight = maxHeight;
|
||||
this.tagsCanSurviveOn = tagsCanSurviveOn;
|
||||
this.blockStatesCanSurviveOn = blockStatesCanSurviveOn;
|
||||
this.customBlocksCansSurviveOn = customBlocksCansSurviveOn;
|
||||
@@ -42,9 +44,10 @@ public class BushBlockBehavior extends AbstractCanSurviveBlockBehavior {
|
||||
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
|
||||
Tuple<List<Object>, Set<Object>, Set<String>> tuple = readTagsAndState(arguments, false);
|
||||
boolean stackable = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("stackable", false), "stackable");
|
||||
int maxHeight = ResourceConfigUtils.getAsInt(arguments.getOrDefault("max-height", 0), "max-height");
|
||||
int delay = ResourceConfigUtils.getAsInt(arguments.getOrDefault("delay", 0), "delay");
|
||||
boolean blacklistMode = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("blacklist", false), "blacklist");
|
||||
return new BushBlockBehavior(block, delay, blacklistMode, stackable, tuple.left(), tuple.mid(), tuple.right());
|
||||
return new BushBlockBehavior(block, delay, blacklistMode, stackable, maxHeight,tuple.left(), tuple.mid(), tuple.right());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +65,7 @@ public class BushBlockBehavior extends AbstractCanSurviveBlockBehavior {
|
||||
if (material != null) {
|
||||
if (index == -1) {
|
||||
// vanilla
|
||||
mcBlocks.addAll(BlockStateUtils.getAllVanillaBlockStates(blockType));
|
||||
mcBlocks.addAll(BlockStateUtils.getPossibleBlockStates(blockType));
|
||||
} else {
|
||||
mcBlocks.add(BlockStateUtils.blockDataToBlockState(Bukkit.createBlockData(blockStateStr)));
|
||||
}
|
||||
@@ -96,7 +99,11 @@ public class BushBlockBehavior extends AbstractCanSurviveBlockBehavior {
|
||||
} else {
|
||||
ImmutableBlockState belowCustomState = optionalCustomState.get();
|
||||
if (belowCustomState.owner().value() == super.customBlock) {
|
||||
return this.stackable;
|
||||
if (!this.stackable || this.maxHeight == 1) return false;
|
||||
if (this.maxHeight > 1) {
|
||||
return mayStackOn(world, belowPos);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (this.customBlocksCansSurviveOn.contains(belowCustomState.owner().value().id().toString())) {
|
||||
return !this.blacklistMode;
|
||||
@@ -107,4 +114,21 @@ public class BushBlockBehavior extends AbstractCanSurviveBlockBehavior {
|
||||
}
|
||||
return this.blacklistMode;
|
||||
}
|
||||
|
||||
protected boolean mayStackOn(Object world, Object belowPos) {
|
||||
int count = 1;
|
||||
Object cursorPos = LocationUtils.below(belowPos);
|
||||
|
||||
while (count < this.maxHeight) {
|
||||
Object belowState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(world, cursorPos);
|
||||
Optional<ImmutableBlockState> belowCustomState = BlockStateUtils.getOptionalCustomBlockState(belowState);
|
||||
if (belowCustomState.isPresent() && belowCustomState.get().owner().value() == super.customBlock) {
|
||||
count++;
|
||||
cursorPos = LocationUtils.below(cursorPos);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return count < this.maxHeight;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,217 @@
|
||||
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.MEntitySelectors;
|
||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MGameEvents;
|
||||
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.DirectionUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.KeyUtils;
|
||||
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.UpdateOption;
|
||||
import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
|
||||
import net.momirealms.craftengine.core.block.properties.BooleanProperty;
|
||||
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.sound.SoundData;
|
||||
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 javax.annotation.Nullable;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
public class ButtonBlockBehavior extends BukkitBlockBehavior {
|
||||
public static final Factory FACTORY = new Factory();
|
||||
private final BooleanProperty poweredProperty;
|
||||
private final int ticksToStayPressed;
|
||||
private final boolean canButtonBeActivatedByArrows;
|
||||
private final SoundData buttonClickOnSound;
|
||||
private final SoundData buttonClickOffSound;
|
||||
|
||||
public ButtonBlockBehavior(CustomBlock customBlock,
|
||||
BooleanProperty powered,
|
||||
int ticksToStayPressed,
|
||||
boolean canButtonBeActivatedByArrows,
|
||||
SoundData buttonClickOnSound,
|
||||
SoundData buttonClickOffSound) {
|
||||
super(customBlock);
|
||||
this.poweredProperty = powered;
|
||||
this.ticksToStayPressed = ticksToStayPressed;
|
||||
this.canButtonBeActivatedByArrows = canButtonBeActivatedByArrows;
|
||||
this.buttonClickOnSound = buttonClickOnSound;
|
||||
this.buttonClickOffSound = buttonClickOffSound;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InteractionResult useWithoutItem(UseOnContext context, ImmutableBlockState state) {
|
||||
if (!state.get(this.poweredProperty)) {
|
||||
press(BlockStateUtils.getBlockOwner(state.customBlockState().literalObject()),
|
||||
state, context.getLevel().serverWorld(), LocationUtils.toBlockPos(context.getClickedPos()),
|
||||
context.getPlayer() != null ? context.getPlayer().serverPlayer() : null);
|
||||
return InteractionResult.SUCCESS_AND_CANCEL;
|
||||
}
|
||||
return InteractionResult.PASS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onExplosionHit(Object thisBlock, Object[] args, Callable<Object> superMethod) {
|
||||
ImmutableBlockState blockState = BlockStateUtils.getOptionalCustomBlockState(args[0]).orElse(null);
|
||||
if (blockState == null) return;
|
||||
if (FastNMS.INSTANCE.method$Explosion$canTriggerBlocks(args[3]) && !blockState.get(this.poweredProperty)) {
|
||||
press(thisBlock, blockState, args[1], args[2], null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void affectNeighborsAfterRemoval(Object thisBlock, Object[] args, Callable<Object> superMethod) {
|
||||
ImmutableBlockState blockState = BlockStateUtils.getOptionalCustomBlockState(args[0]).orElse(null);
|
||||
if (blockState == null) return;
|
||||
if (!(boolean) args[3] && blockState.get(this.poweredProperty)) {
|
||||
updateNeighbours(thisBlock, blockState, args[1], args[2]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemove(Object thisBlock, Object[] args, Callable<Object> superMethod) {
|
||||
ImmutableBlockState blockState = BlockStateUtils.getOptionalCustomBlockState(args[0]).orElse(null);
|
||||
if (blockState == null) return;
|
||||
if (!(boolean) args[4] && blockState.get(this.poweredProperty)) {
|
||||
updateNeighbours(thisBlock, blockState, args[1], args[2]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSignal(Object thisBlock, Object[] args, Callable<Object> superMethod) {
|
||||
ImmutableBlockState blockState = BlockStateUtils.getOptionalCustomBlockState(args[0]).orElse(null);
|
||||
if (blockState == null) return 0;
|
||||
return blockState.get(this.poweredProperty) ? 15 : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDirectSignal(Object thisBlock, Object[] args, Callable<Object> superMethod) {
|
||||
ImmutableBlockState blockState = BlockStateUtils.getOptionalCustomBlockState(args[0]).orElse(null);
|
||||
if (blockState == null) return 0;
|
||||
return blockState.get(this.poweredProperty)
|
||||
&& FaceAttachedHorizontalDirectionalBlockBehavior.getConnectedDirection(blockState)
|
||||
== DirectionUtils.fromNMSDirection(args[3]) ? 15 : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSignalSource(Object thisBlock, Object[] args, Callable<Object> superMethod) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick(Object thisBlock, Object[] args, Callable<Object> superMethod) {
|
||||
Object state = args[0];
|
||||
Object level = args[1];
|
||||
Object pos = args[2];
|
||||
ImmutableBlockState blockState = BlockStateUtils.getOptionalCustomBlockState(state).orElse(null);
|
||||
if (blockState == null) return;
|
||||
if (blockState.get(this.poweredProperty)) {
|
||||
checkPressed(thisBlock, state, level, pos);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void entityInside(Object thisBlock, Object[] args, Callable<Object> superMethod) {
|
||||
Object state = args[0];
|
||||
Object level = args[1];
|
||||
Object pos = args[2];
|
||||
ImmutableBlockState blockState = BlockStateUtils.getOptionalCustomBlockState(state).orElse(null);
|
||||
if (blockState == null) return;
|
||||
if (this.canButtonBeActivatedByArrows && !blockState.get(this.poweredProperty)) {
|
||||
checkPressed(thisBlock, state, level, pos);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkPressed(Object thisBlock, Object state, Object level, Object pos) {
|
||||
Object arrow = this.canButtonBeActivatedByArrows ? FastNMS.INSTANCE.method$EntityGetter$getEntitiesOfClass(
|
||||
level, CoreReflections.clazz$AbstractArrow, FastNMS.INSTANCE.method$AABB$move(
|
||||
FastNMS.INSTANCE.method$VoxelShape$bounds(FastNMS.INSTANCE.method$BlockState$getShape(
|
||||
state, level, pos, CoreReflections.instance$CollisionContext$empty
|
||||
)), pos), MEntitySelectors.NO_SPECTATORS).stream().findFirst().orElse(null) : null;
|
||||
boolean on = arrow != null;
|
||||
ImmutableBlockState blockState = BlockStateUtils.getOptionalCustomBlockState(state).orElse(null);
|
||||
if (blockState == null) return;
|
||||
boolean poweredValue = blockState.get(this.poweredProperty);
|
||||
if (on != poweredValue) {
|
||||
FastNMS.INSTANCE.method$LevelWriter$setBlock(level, pos, blockState.with(this.poweredProperty, on).customBlockState().literalObject(), UpdateOption.UPDATE_ALL.flags());
|
||||
updateNeighbours(thisBlock, blockState, level, pos);
|
||||
playSound(level, pos, on);
|
||||
Object gameEvent = VersionHelper.isOrAbove1_20_5()
|
||||
? FastNMS.INSTANCE.method$Holder$direct(on ? MGameEvents.BLOCK_ACTIVATE : MGameEvents.BLOCK_DEACTIVATE)
|
||||
: on ? MGameEvents.BLOCK_ACTIVATE : MGameEvents.BLOCK_DEACTIVATE;
|
||||
FastNMS.INSTANCE.method$LevelAccessor$gameEvent(level, arrow, gameEvent, pos);
|
||||
}
|
||||
|
||||
if (on) {
|
||||
FastNMS.INSTANCE.method$ScheduledTickAccess$scheduleBlockTick(level, pos, thisBlock, this.ticksToStayPressed);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateNeighbours(Object thisBlock, ImmutableBlockState state, Object level, Object pos) {
|
||||
Direction direction = FaceAttachedHorizontalDirectionalBlockBehavior.getConnectedDirection(state);
|
||||
if (direction == null) return;
|
||||
Direction opposite = direction.opposite();
|
||||
Object nmsDirection = DirectionUtils.toNMSDirection(opposite);
|
||||
Object orientation = null;
|
||||
if (VersionHelper.isOrAbove1_21_2()) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Property<HorizontalDirection> facing = (Property<HorizontalDirection>) state.owner().value().getProperty("facing");
|
||||
if (facing != null) {
|
||||
orientation = FastNMS.INSTANCE.method$ExperimentalRedstoneUtils$initialOrientation(
|
||||
level, nmsDirection, opposite.axis().isHorizontal() ? CoreReflections.instance$Direction$UP : DirectionUtils.toNMSDirection(state.get(facing).toDirection())
|
||||
);
|
||||
}
|
||||
}
|
||||
FastNMS.INSTANCE.method$Level$updateNeighborsAt(level, pos, thisBlock, orientation);
|
||||
FastNMS.INSTANCE.method$Level$updateNeighborsAt(level, FastNMS.INSTANCE.method$BlockPos$relative(pos, nmsDirection), thisBlock, orientation);
|
||||
}
|
||||
|
||||
private void playSound(Object level, Object pos, boolean on) {
|
||||
SoundData soundData = getSound(on);
|
||||
if (soundData == null) return;
|
||||
Object sound = FastNMS.INSTANCE.constructor$SoundEvent(KeyUtils.toResourceLocation(soundData.id()), Optional.empty());
|
||||
FastNMS.INSTANCE.method$LevelAccessor$playSound(level, null, pos, sound, CoreReflections.instance$SoundSource$BLOCKS, soundData.volume().get(), soundData.pitch().get());
|
||||
}
|
||||
|
||||
private SoundData getSound(boolean on) {
|
||||
return on ? this.buttonClickOnSound : this.buttonClickOffSound;
|
||||
}
|
||||
|
||||
private void press(Object thisBlock, ImmutableBlockState state, Object level, Object pos, @Nullable Object player) {
|
||||
FastNMS.INSTANCE.method$LevelWriter$setBlock(level, pos, state.with(this.poweredProperty, true).customBlockState().literalObject(), UpdateOption.UPDATE_ALL.flags());
|
||||
FastNMS.INSTANCE.method$ScheduledTickAccess$scheduleBlockTick(level, pos, thisBlock, this.ticksToStayPressed);
|
||||
playSound(level, pos, true);
|
||||
Object gameEvent = VersionHelper.isOrAbove1_20_5() ? FastNMS.INSTANCE.method$Holder$direct(MGameEvents.BLOCK_ACTIVATE) : MGameEvents.BLOCK_ACTIVATE;
|
||||
FastNMS.INSTANCE.method$LevelAccessor$gameEvent(level, player, gameEvent, pos);
|
||||
}
|
||||
|
||||
public static class Factory implements BlockBehaviorFactory {
|
||||
|
||||
@SuppressWarnings({"unchecked", "DuplicatedCode"})
|
||||
@Override
|
||||
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
|
||||
BooleanProperty powered = (BooleanProperty) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("powered"), "warning.config.block.behavior.button.missing_powered");
|
||||
int ticksToStayPressed = ResourceConfigUtils.getAsInt(arguments.getOrDefault("ticks-to-stay-pressed", 30), "ticks-to-stay-pressed");
|
||||
boolean canButtonBeActivatedByArrows = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-be-activated-by-arrows", true), "can-be-activated-by-arrows");
|
||||
Map<String, Object> sounds = (Map<String, Object>) arguments.get("sounds");
|
||||
SoundData buttonClickOnSound = null;
|
||||
SoundData buttonClickOffSound = null;
|
||||
if (sounds != null) {
|
||||
buttonClickOnSound = Optional.ofNullable(sounds.get("on")).map(obj -> SoundData.create(obj, SoundData.SoundValue.FIXED_1, SoundData.SoundValue.ranged(0.9f, 1f))).orElse(null);
|
||||
buttonClickOffSound = Optional.ofNullable(sounds.get("off")).map(obj -> SoundData.create(obj, SoundData.SoundValue.FIXED_1, SoundData.SoundValue.ranged(0.9f, 1f))).orElse(null);
|
||||
}
|
||||
return new ButtonBlockBehavior(block, powered, ticksToStayPressed, canButtonBeActivatedByArrows, buttonClickOnSound, buttonClickOffSound);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
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.KeyUtils;
|
||||
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.sound.SoundData;
|
||||
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
public class ChimeBlockBehavior extends BukkitBlockBehavior {
|
||||
public static final Factory FACTORY = new Factory();
|
||||
private final SoundData hitSound;
|
||||
|
||||
public ChimeBlockBehavior(CustomBlock customBlock, SoundData hitSound) {
|
||||
super(customBlock);
|
||||
this.hitSound = hitSound;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProjectileHit(Object thisBlock, Object[] args, Callable<Object> superMethod) {
|
||||
Object blockPos = FastNMS.INSTANCE.field$BlockHitResult$blockPos(args[2]);
|
||||
Object sound = FastNMS.INSTANCE.constructor$SoundEvent(KeyUtils.toResourceLocation(hitSound.id()), Optional.empty());
|
||||
FastNMS.INSTANCE.method$LevelAccessor$playSound(args[0], null, blockPos, sound, CoreReflections.instance$SoundSource$BLOCKS, hitSound.volume().get(), hitSound.pitch().get());
|
||||
}
|
||||
|
||||
public static class Factory implements BlockBehaviorFactory {
|
||||
|
||||
@Override
|
||||
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
|
||||
SoundData hitSound = SoundData.create(ResourceConfigUtils.requireNonNullOrThrow(
|
||||
Optional.ofNullable(ResourceConfigUtils.getAsMap(arguments.get("sounds"), "sounds"))
|
||||
.map(sounds -> ResourceConfigUtils.get(sounds, "projectile-hit", "chime"))
|
||||
.orElse(null),
|
||||
"warning.config.block.behavior.chime.missing_sounds_projectile_hit"
|
||||
), SoundData.SoundValue.FIXED_1, SoundData.SoundValue.ranged(0.9f, 1f));
|
||||
return new ChimeBlockBehavior(block, hitSound);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -77,7 +77,7 @@ public class CropBlockBehavior extends BukkitBlockBehavior {
|
||||
return minGrowLight;
|
||||
}
|
||||
|
||||
private static int getRawBrightness(Object level, Object pos) throws InvocationTargetException, IllegalAccessException {
|
||||
public static int getRawBrightness(Object level, Object pos) throws InvocationTargetException, IllegalAccessException {
|
||||
return (int) CoreReflections.method$BlockAndTintGetter$getRawBrightness.invoke(level, pos, 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
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.BlockTags;
|
||||
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.*;
|
||||
import net.momirealms.craftengine.core.world.BlockPos;
|
||||
import net.momirealms.craftengine.core.world.World;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.Registry;
|
||||
|
||||
import java.util.*;
|
||||
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;
|
||||
private final List<Object> tagsCanSurviveOn;
|
||||
private final Set<Object> blockStatesCanSurviveOn;
|
||||
private final Set<String> customBlocksCansSurviveOn;
|
||||
private final boolean blacklistMode;
|
||||
|
||||
public DirectionalAttachedBlockBehavior(CustomBlock customBlock,
|
||||
Property<?> facingProperty,
|
||||
boolean isSixDirection,
|
||||
boolean blacklist,
|
||||
List<Object> tagsCanSurviveOn,
|
||||
Set<Object> blockStatesCanSurviveOn,
|
||||
Set<String> customBlocksCansSurviveOn) {
|
||||
super(customBlock);
|
||||
this.facingProperty = facingProperty;
|
||||
this.isSixDirection = isSixDirection;
|
||||
this.tagsCanSurviveOn = tagsCanSurviveOn;
|
||||
this.blockStatesCanSurviveOn = blockStatesCanSurviveOn;
|
||||
this.customBlocksCansSurviveOn = customBlocksCansSurviveOn;
|
||||
this.blacklistMode = blacklist;
|
||||
}
|
||||
|
||||
@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)
|
||||
&& mayPlaceOn(nmsState);
|
||||
}
|
||||
|
||||
private boolean mayPlaceOn(Object state) {
|
||||
for (Object tag : this.tagsCanSurviveOn) {
|
||||
if (FastNMS.INSTANCE.method$BlockStateBase$is(state, tag)) {
|
||||
return !this.blacklistMode;
|
||||
}
|
||||
}
|
||||
Optional<ImmutableBlockState> optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(state);
|
||||
if (optionalCustomState.isEmpty()) {
|
||||
if (!this.blockStatesCanSurviveOn.isEmpty() && this.blockStatesCanSurviveOn.contains(state)) {
|
||||
return !this.blacklistMode;
|
||||
}
|
||||
} else {
|
||||
ImmutableBlockState belowCustomState = optionalCustomState.get();
|
||||
if (this.customBlocksCansSurviveOn.contains(belowCustomState.owner().value().id().toString())) {
|
||||
return !this.blacklistMode;
|
||||
}
|
||||
if (this.customBlocksCansSurviveOn.contains(belowCustomState.toString())) {
|
||||
return !this.blacklistMode;
|
||||
}
|
||||
}
|
||||
return this.blacklistMode;
|
||||
}
|
||||
|
||||
@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.directional_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.directional_attached.missing_facing");
|
||||
}
|
||||
Tuple<List<Object>, Set<Object>, Set<String>> tuple = readTagsAndState(arguments);
|
||||
boolean blacklistMode = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("blacklist", true), "blacklist");
|
||||
return new DirectionalAttachedBlockBehavior(block, facing, isDirection, blacklistMode, tuple.left(), tuple.mid(), tuple.right());
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("DuplicatedCode")
|
||||
public static Tuple<List<Object>, Set<Object>, Set<String>> readTagsAndState(Map<String, Object> arguments) {
|
||||
List<Object> mcTags = new ArrayList<>();
|
||||
for (String tag : MiscUtils.getAsStringList(arguments.getOrDefault("attached-block-tags", List.of()))) {
|
||||
mcTags.add(BlockTags.getOrCreate(Key.of(tag)));
|
||||
}
|
||||
Set<Object> mcBlocks = new HashSet<>();
|
||||
Set<String> customBlocks = new HashSet<>();
|
||||
for (String blockStateStr : MiscUtils.getAsStringList(arguments.getOrDefault("attached-blocks", List.of()))) {
|
||||
int index = blockStateStr.indexOf('[');
|
||||
Key blockType = index != -1 ? Key.from(blockStateStr.substring(0, index)) : Key.from(blockStateStr);
|
||||
Material material = Registry.MATERIAL.get(new NamespacedKey(blockType.namespace(), blockType.value()));
|
||||
if (material != null) {
|
||||
if (index == -1) {
|
||||
// vanilla
|
||||
mcBlocks.addAll(BlockStateUtils.getPossibleBlockStates(blockType));
|
||||
} else {
|
||||
mcBlocks.add(BlockStateUtils.blockDataToBlockState(Bukkit.createBlockData(blockStateStr)));
|
||||
}
|
||||
} else {
|
||||
// custom maybe
|
||||
customBlocks.add(blockStateStr);
|
||||
}
|
||||
}
|
||||
return new Tuple<>(mcTags, mcBlocks, customBlocks);
|
||||
}
|
||||
}
|
||||
@@ -16,8 +16,8 @@ import net.momirealms.craftengine.core.block.ImmutableBlockState;
|
||||
import net.momirealms.craftengine.core.block.UpdateOption;
|
||||
import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
|
||||
import net.momirealms.craftengine.core.block.properties.Property;
|
||||
import net.momirealms.craftengine.core.block.state.properties.DoorHinge;
|
||||
import net.momirealms.craftengine.core.block.state.properties.DoubleBlockHalf;
|
||||
import net.momirealms.craftengine.core.block.properties.type.DoorHinge;
|
||||
import net.momirealms.craftengine.core.block.properties.type.DoubleBlockHalf;
|
||||
import net.momirealms.craftengine.core.entity.player.InteractionHand;
|
||||
import net.momirealms.craftengine.core.entity.player.InteractionResult;
|
||||
import net.momirealms.craftengine.core.entity.player.Player;
|
||||
|
||||
@@ -6,15 +6,16 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflect
|
||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBlocks;
|
||||
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.LocationUtils;
|
||||
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
|
||||
import net.momirealms.craftengine.core.block.*;
|
||||
import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
|
||||
import net.momirealms.craftengine.core.block.properties.Property;
|
||||
import net.momirealms.craftengine.core.block.state.properties.DoubleBlockHalf;
|
||||
import net.momirealms.craftengine.core.block.properties.type.DoubleBlockHalf;
|
||||
import net.momirealms.craftengine.core.item.context.BlockPlaceContext;
|
||||
import net.momirealms.craftengine.core.util.Direction;
|
||||
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
|
||||
import net.momirealms.craftengine.core.world.*;
|
||||
import net.momirealms.craftengine.core.world.BlockPos;
|
||||
import net.momirealms.craftengine.core.world.World;
|
||||
import net.momirealms.craftengine.core.world.WorldEvents;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
@@ -50,10 +51,6 @@ public class DoubleHighBlockBehavior extends BukkitBlockBehavior {
|
||||
if (anotherHalfCustomState != null && !anotherHalfCustomState.isEmpty()) return blockState;
|
||||
|
||||
// 破坏
|
||||
BlockPos pos = LocationUtils.fromBlockPos(blockPos);
|
||||
net.momirealms.craftengine.core.world.World world = new BukkitWorld(FastNMS.INSTANCE.method$Level$getCraftWorld(level));
|
||||
WorldPosition position = new WorldPosition(world, Vec3d.atCenterOf(pos));
|
||||
world.playBlockSound(position, customState.settings().sounds().breakSound());
|
||||
FastNMS.INSTANCE.method$LevelAccessor$levelEvent(level, WorldEvents.BLOCK_BREAK_EFFECT, blockPos, customState.customBlockState().registryId());
|
||||
return MBlocks.AIR$defaultState;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
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.block.properties.type.AnchorType;
|
||||
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.Tuple;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
public class FaceAttachedHorizontalDirectionalBlockBehavior extends BukkitBlockBehavior {
|
||||
public static final Factory FACTORY = new Factory();
|
||||
private final Property<AnchorType> anchorTypeProperty;
|
||||
private final Property<HorizontalDirection> facingProperty;
|
||||
private final List<Object> tagsCanSurviveOn;
|
||||
private final Set<Object> blockStatesCanSurviveOn;
|
||||
private final Set<String> customBlocksCansSurviveOn;
|
||||
private final boolean blacklistMode;
|
||||
|
||||
public FaceAttachedHorizontalDirectionalBlockBehavior(CustomBlock customBlock,
|
||||
boolean blacklist,
|
||||
List<Object> tagsCanSurviveOn,
|
||||
Set<Object> blockStatesCanSurviveOn,
|
||||
Set<String> customBlocksCansSurviveOn,
|
||||
Property<AnchorType> anchorType,
|
||||
Property<HorizontalDirection> facing) {
|
||||
super(customBlock);
|
||||
this.tagsCanSurviveOn = tagsCanSurviveOn;
|
||||
this.blockStatesCanSurviveOn = blockStatesCanSurviveOn;
|
||||
this.customBlocksCansSurviveOn = customBlocksCansSurviveOn;
|
||||
this.blacklistMode = blacklist;
|
||||
this.anchorTypeProperty = anchorType;
|
||||
this.facingProperty = facing;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canSurvive(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
|
||||
Direction direction = getConnectedDirection(BlockStateUtils.getOptionalCustomBlockState(args[0]).orElse(null));
|
||||
if (direction == null) return false;
|
||||
direction = direction.opposite();
|
||||
Object nmsDirection = DirectionUtils.toNMSDirection(direction);
|
||||
Object targetPos = FastNMS.INSTANCE.method$BlockPos$relative(args[2], nmsDirection);
|
||||
Object targetState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(args[1], targetPos);
|
||||
return canAttach(args[1], targetPos, nmsDirection, targetState) && mayPlaceOn(targetState);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public ImmutableBlockState updateStateForPlacement(BlockPlaceContext context, ImmutableBlockState state) {
|
||||
Property<AnchorType> face = (Property<AnchorType>) state.owner().value().getProperty("face");
|
||||
Property<HorizontalDirection> facing = (Property<HorizontalDirection>) state.owner().value().getProperty("facing");
|
||||
if (face == null || facing == null) return null;
|
||||
for (Direction direction : context.getNearestLookingDirections()) {
|
||||
if (direction.axis() == Direction.Axis.Y) {
|
||||
state = state
|
||||
.with(face, direction == Direction.UP ? AnchorType.CEILING : AnchorType.FLOOR)
|
||||
.with(facing, context.getHorizontalDirection().toHorizontalDirection());
|
||||
} else {
|
||||
state = state.with(face, AnchorType.WALL).with(facing, direction.opposite().toHorizontalDirection());
|
||||
}
|
||||
if (FastNMS.INSTANCE.method$BlockStateBase$canSurvive(state.customBlockState().literalObject(), context.getLevel().serverWorld(), LocationUtils.toBlockPos(context.getClickedPos()))) {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object updateShape(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
|
||||
Direction direction = getConnectedDirection(BlockStateUtils.getOptionalCustomBlockState(args[0]).orElse(null));
|
||||
if (direction == null) return MBlocks.AIR$defaultState;
|
||||
if (DirectionUtils.toNMSDirection(direction.opposite()) == args[updateShape$direction] && !FastNMS.INSTANCE.method$BlockStateBase$canSurvive(args[0], args[updateShape$level], args[updateShape$blockPos])) {
|
||||
return MBlocks.AIR$defaultState;
|
||||
}
|
||||
return superMethod.call();
|
||||
}
|
||||
|
||||
private boolean mayPlaceOn(Object state) {
|
||||
for (Object tag : this.tagsCanSurviveOn) {
|
||||
if (FastNMS.INSTANCE.method$BlockStateBase$is(state, tag)) {
|
||||
return !this.blacklistMode;
|
||||
}
|
||||
}
|
||||
Optional<ImmutableBlockState> optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(state);
|
||||
if (optionalCustomState.isEmpty()) {
|
||||
if (!this.blockStatesCanSurviveOn.isEmpty() && this.blockStatesCanSurviveOn.contains(state)) {
|
||||
return !this.blacklistMode;
|
||||
}
|
||||
} else {
|
||||
ImmutableBlockState belowCustomState = optionalCustomState.get();
|
||||
if (this.customBlocksCansSurviveOn.contains(belowCustomState.owner().value().id().toString())) {
|
||||
return !this.blacklistMode;
|
||||
}
|
||||
if (this.customBlocksCansSurviveOn.contains(belowCustomState.toString())) {
|
||||
return !this.blacklistMode;
|
||||
}
|
||||
}
|
||||
return this.blacklistMode;
|
||||
}
|
||||
|
||||
public static boolean canAttach(Object level, Object targetPos, Object direction, Object targetState) {
|
||||
return FastNMS.INSTANCE.method$BlockStateBase$isFaceSturdy(
|
||||
targetState, level, targetPos,
|
||||
FastNMS.INSTANCE.method$Direction$getOpposite(direction),
|
||||
CoreReflections.instance$SupportType$FULL
|
||||
);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Direction getConnectedDirection(ImmutableBlockState state) {
|
||||
if (state == null) return null;
|
||||
FaceAttachedHorizontalDirectionalBlockBehavior behavior = state.behavior().getAs(FaceAttachedHorizontalDirectionalBlockBehavior.class).orElse(null);
|
||||
if (behavior == null) return null;
|
||||
return switch (state.get(behavior.anchorTypeProperty)) {
|
||||
case CEILING -> Direction.DOWN;
|
||||
case FLOOR -> Direction.UP;
|
||||
default -> state.get(behavior.facingProperty).toDirection();
|
||||
};
|
||||
}
|
||||
|
||||
public static class Factory implements BlockBehaviorFactory {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
|
||||
Property<AnchorType> anchorType = (Property<AnchorType>) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("face"), "warning.config.block.behavior.face_attached_horizontal_directional.missing_face");
|
||||
Property<HorizontalDirection> facing = (Property<HorizontalDirection>) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("facing"), "warning.config.block.behavior.face_attached_horizontal_directional.missing_facing");
|
||||
Tuple<List<Object>, Set<Object>, Set<String>> tuple = DirectionalAttachedBlockBehavior.readTagsAndState(arguments);
|
||||
boolean blacklistMode = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("blacklist", true), "blacklist");
|
||||
return new FaceAttachedHorizontalDirectionalBlockBehavior(block, blacklistMode, tuple.left(), tuple.mid(), tuple.right(), anchorType, facing);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package net.momirealms.craftengine.bukkit.block.behavior;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
|
||||
import net.momirealms.craftengine.bukkit.entity.data.BaseEntityData;
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
|
||||
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
|
||||
@@ -10,11 +11,8 @@ 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.sound.SoundData;
|
||||
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;
|
||||
|
||||
@@ -26,11 +24,15 @@ public class FallingBlockBehavior extends BukkitBlockBehavior {
|
||||
public static final Factory FACTORY = new Factory();
|
||||
private final float hurtAmount;
|
||||
private final int maxHurt;
|
||||
private final SoundData landSound;
|
||||
private final SoundData destroySound;
|
||||
|
||||
public FallingBlockBehavior(CustomBlock block, float hurtAmount, int maxHurt) {
|
||||
public FallingBlockBehavior(CustomBlock block, float hurtAmount, int maxHurt, SoundData landSound, SoundData destroySound) {
|
||||
super(block);
|
||||
this.hurtAmount = hurtAmount;
|
||||
this.maxHurt = maxHurt;
|
||||
this.landSound = landSound;
|
||||
this.destroySound = destroySound;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -67,7 +69,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,28 +78,18 @@ 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) {
|
||||
world.playBlockSound(position, customState.settings().sounds().destroySound());
|
||||
if (!BaseEntityData.Silent.get(entityData)) {
|
||||
Object blockState = CoreReflections.field$FallingBlockEntity$blockState.get(fallingBlockEntity);
|
||||
Optional<ImmutableBlockState> optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(blockState);
|
||||
if (optionalCustomState.isEmpty()) return;
|
||||
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));
|
||||
if (this.destroySound != null) {
|
||||
world.playBlockSound(position, this.destroySound);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,23 +99,33 @@ public class FallingBlockBehavior extends BukkitBlockBehavior {
|
||||
Object level = args[0];
|
||||
Object pos = args[1];
|
||||
Object entityData = CoreReflections.field$Entity$entityData.get(fallingBlock);
|
||||
boolean isSilent = (boolean) CoreReflections.method$SynchedEntityData$get.invoke(entityData, CoreReflections.instance$Entity$DATA_SILENT);
|
||||
Object blockState = args[2];
|
||||
int stateId = BlockStateUtils.blockStateToId(blockState);
|
||||
ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(stateId);
|
||||
if (immutableBlockState == null || immutableBlockState.isEmpty()) return;
|
||||
if (!isSilent) {
|
||||
if (!BaseEntityData.Silent.get(entityData)) {
|
||||
net.momirealms.craftengine.core.world.World world = new BukkitWorld(FastNMS.INSTANCE.method$Level$getCraftWorld(level));
|
||||
world.playBlockSound(Vec3d.atCenterOf(LocationUtils.fromBlockPos(pos)), immutableBlockState.settings().sounds().landSound());
|
||||
if (this.landSound != null) {
|
||||
world.playBlockSound(Vec3d.atCenterOf(LocationUtils.fromBlockPos(pos)), this.landSound);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class Factory implements BlockBehaviorFactory {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
|
||||
float hurtAmount = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("hurt-amount", -1f), "hurt-amount");
|
||||
int hurtMax = ResourceConfigUtils.getAsInt(arguments.getOrDefault("max-hurt", -1), "max-hurt");
|
||||
return new FallingBlockBehavior(block, hurtAmount, hurtMax);
|
||||
Map<String, Object> sounds = (Map<String, Object>) arguments.get("sounds");
|
||||
SoundData fallSound = null;
|
||||
SoundData destroySound = null;
|
||||
if (sounds != null) {
|
||||
fallSound = Optional.ofNullable(sounds.get("fall")).map(obj -> SoundData.create(obj, SoundData.SoundValue.FIXED_1, SoundData.SoundValue.ranged(0.9f, 1f))).orElse(null);
|
||||
destroySound = Optional.ofNullable(sounds.get("destroy")).map(obj -> SoundData.create(obj, SoundData.SoundValue.FIXED_1, SoundData.SoundValue.ranged(0.9f, 1f))).orElse(null);
|
||||
}
|
||||
return new FallingBlockBehavior(block, hurtAmount, hurtMax, fallSound, destroySound);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
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.MFluids;
|
||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistries;
|
||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MTagKeys;
|
||||
import net.momirealms.craftengine.bukkit.util.*;
|
||||
import net.momirealms.craftengine.core.block.*;
|
||||
import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
|
||||
import net.momirealms.craftengine.core.block.properties.BooleanProperty;
|
||||
import net.momirealms.craftengine.core.entity.player.InteractionHand;
|
||||
import net.momirealms.craftengine.core.entity.player.InteractionResult;
|
||||
import net.momirealms.craftengine.core.entity.player.Player;
|
||||
import net.momirealms.craftengine.core.item.context.BlockPlaceContext;
|
||||
import net.momirealms.craftengine.core.item.context.UseOnContext;
|
||||
import net.momirealms.craftengine.core.registry.Holder;
|
||||
import net.momirealms.craftengine.core.util.Direction;
|
||||
import net.momirealms.craftengine.core.util.HorizontalDirection;
|
||||
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 java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
public class FenceBlockBehavior extends BukkitBlockBehavior {
|
||||
public static final Factory FACTORY = new Factory();
|
||||
private final BooleanProperty northProperty;
|
||||
private final BooleanProperty eastProperty;
|
||||
private final BooleanProperty southProperty;
|
||||
private final BooleanProperty westProperty;
|
||||
private final Object connectableBlockTag;
|
||||
private final boolean canLeash;
|
||||
|
||||
public FenceBlockBehavior(CustomBlock customBlock,
|
||||
BooleanProperty northProperty,
|
||||
BooleanProperty eastProperty,
|
||||
BooleanProperty southProperty,
|
||||
BooleanProperty westProperty,
|
||||
Object connectableBlockTag,
|
||||
boolean canLeash) {
|
||||
super(customBlock);
|
||||
this.northProperty = northProperty;
|
||||
this.eastProperty = eastProperty;
|
||||
this.southProperty = southProperty;
|
||||
this.westProperty = westProperty;
|
||||
this.connectableBlockTag = connectableBlockTag;
|
||||
this.canLeash = canLeash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPathFindable(Object thisBlock, Object[] args, Callable<Object> superMethod) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean connectsTo(BlockStateWrapper state, boolean isSideSolid, HorizontalDirection direction) {
|
||||
boolean isSameFence = this.isSameFence(state);
|
||||
boolean flag = CoreReflections.clazz$FenceGateBlock.isInstance(BlockStateUtils.getBlockOwner(state.literalObject()))
|
||||
? FastNMS.INSTANCE.method$FenceGateBlock$connectsToDirection(state.literalObject(), DirectionUtils.toNMSDirection(direction.toDirection()))
|
||||
: FenceGateBlockBehavior.connectsToDirection(state, direction);
|
||||
return !BlockUtils.isExceptionForConnection(state) && isSideSolid || isSameFence || flag;
|
||||
}
|
||||
|
||||
private boolean isSameFence(BlockStateWrapper state) {
|
||||
Object blockState = state.literalObject();
|
||||
return FastNMS.INSTANCE.method$BlockStateBase$is(blockState, MTagKeys.Block$FENCES)
|
||||
&& FastNMS.INSTANCE.method$BlockStateBase$is(blockState, this.connectableBlockTag)
|
||||
== FastNMS.INSTANCE.method$BlockStateBase$is(this.customBlock.defaultState().customBlockState().literalObject(), this.connectableBlockTag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InteractionResult useWithoutItem(UseOnContext context, ImmutableBlockState state) {
|
||||
if (!this.canLeash) return InteractionResult.PASS;
|
||||
Player player = context.getPlayer();
|
||||
if (player == null) return InteractionResult.PASS;
|
||||
if (FastNMS.INSTANCE.method$LeadItem$bindPlayerMobs(player.serverPlayer(), context.getLevel().serverWorld(), LocationUtils.toBlockPos(context.getClickedPos()))) {
|
||||
player.swingHand(InteractionHand.MAIN_HAND);
|
||||
return InteractionResult.SUCCESS;
|
||||
}
|
||||
return InteractionResult.PASS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableBlockState updateStateForPlacement(BlockPlaceContext context, ImmutableBlockState state) {
|
||||
World level = context.getLevel();
|
||||
BlockPos clickedPos = context.getClickedPos();
|
||||
Object fluidState = FastNMS.INSTANCE.method$BlockGetter$getFluidState(level.serverWorld(), LocationUtils.toBlockPos(clickedPos));
|
||||
BlockPos blockPos = clickedPos.north();
|
||||
BlockPos blockPos1 = clickedPos.east();
|
||||
BlockPos blockPos2 = clickedPos.south();
|
||||
BlockPos blockPos3 = clickedPos.west();
|
||||
BlockStateWrapper blockState = level.getBlockAt(blockPos).blockState();
|
||||
BlockStateWrapper blockState1 = level.getBlockAt(blockPos1).blockState();
|
||||
BlockStateWrapper blockState2 = level.getBlockAt(blockPos2).blockState();
|
||||
BlockStateWrapper blockState3 = level.getBlockAt(blockPos3).blockState();
|
||||
BooleanProperty waterlogged = (BooleanProperty) state.owner().value().getProperty("waterlogged");
|
||||
if (waterlogged != null) {
|
||||
state = state.with(waterlogged, FastNMS.INSTANCE.method$FluidState$getType(fluidState) == MFluids.WATER);
|
||||
}
|
||||
return state
|
||||
.with(this.northProperty, this.connectsTo(blockState, FastNMS.INSTANCE.method$BlockStateBase$isFaceSturdy(blockState.literalObject(), level.serverWorld(), LocationUtils.toBlockPos(blockPos), CoreReflections.instance$Direction$SOUTH, CoreReflections.instance$SupportType$FULL), HorizontalDirection.SOUTH))
|
||||
.with(this.eastProperty, this.connectsTo(blockState1, FastNMS.INSTANCE.method$BlockStateBase$isFaceSturdy(blockState1.literalObject(), level.serverWorld(), LocationUtils.toBlockPos(blockPos1), CoreReflections.instance$Direction$WEST, CoreReflections.instance$SupportType$FULL), HorizontalDirection.WEST))
|
||||
.with(this.southProperty, this.connectsTo(blockState2, FastNMS.INSTANCE.method$BlockStateBase$isFaceSturdy(blockState2.literalObject(), level.serverWorld(), LocationUtils.toBlockPos(blockPos2), CoreReflections.instance$Direction$NORTH, CoreReflections.instance$SupportType$FULL), HorizontalDirection.NORTH))
|
||||
.with(this.westProperty, this.connectsTo(blockState3, FastNMS.INSTANCE.method$BlockStateBase$isFaceSturdy(blockState3.literalObject(), level.serverWorld(), LocationUtils.toBlockPos(blockPos3), CoreReflections.instance$Direction$EAST, CoreReflections.instance$SupportType$FULL), HorizontalDirection.EAST));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object updateShape(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
|
||||
Optional<ImmutableBlockState> optionalState = BlockStateUtils.getOptionalCustomBlockState(args[0]);
|
||||
BooleanProperty waterlogged = (BooleanProperty) optionalState
|
||||
.map(BlockStateHolder::owner)
|
||||
.map(Holder::value)
|
||||
.map(block -> block.getProperty("waterlogged"))
|
||||
.orElse(null);
|
||||
if (waterlogged != null) {
|
||||
FastNMS.INSTANCE.method$ScheduledTickAccess$scheduleFluidTick(args[updateShape$level], args[updateShape$blockPos], MFluids.WATER, 5);
|
||||
}
|
||||
if (DirectionUtils.fromNMSDirection(args[updateShape$direction]).axis().isHorizontal() && optionalState.isPresent()) {
|
||||
Direction direction = DirectionUtils.fromNMSDirection(args[updateShape$direction]);
|
||||
ImmutableBlockState state = optionalState.get();
|
||||
if (state.owner() != null) {
|
||||
BooleanProperty booleanProperty = (BooleanProperty) state.owner().value().getProperty(direction.name().toLowerCase(Locale.ROOT));
|
||||
if (booleanProperty != null) {
|
||||
BlockStateWrapper wrapper = BlockStateUtils.toBlockStateWrapper(args[updateShape$neighborState]);
|
||||
return state.with(booleanProperty, this.connectsTo(wrapper, FastNMS.INSTANCE.method$BlockStateBase$isFaceSturdy(wrapper.literalObject(), args[updateShape$level], args[5], DirectionUtils.toNMSDirection(direction.opposite()), CoreReflections.instance$SupportType$FULL), direction.opposite().toHorizontalDirection())).customBlockState().literalObject();
|
||||
}
|
||||
}
|
||||
}
|
||||
return superMethod.call();
|
||||
}
|
||||
|
||||
public static class Factory implements BlockBehaviorFactory {
|
||||
|
||||
@Override
|
||||
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
|
||||
BooleanProperty north = (BooleanProperty) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("north"), "warning.config.block.behavior.fence.missing_north");
|
||||
BooleanProperty east = (BooleanProperty) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("east"), "warning.config.block.behavior.fence.missing_east");
|
||||
BooleanProperty south = (BooleanProperty) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("south"), "warning.config.block.behavior.fence.missing_south");
|
||||
BooleanProperty west = (BooleanProperty) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("west"), "warning.config.block.behavior.fence.missing_west");
|
||||
Object connectableBlockTag = FastNMS.INSTANCE.method$TagKey$create(MRegistries.BLOCK, KeyUtils.toResourceLocation(Key.of(arguments.getOrDefault("connectable-block-tag", "minecraft:wooden_fences").toString())));
|
||||
connectableBlockTag = connectableBlockTag != null ? connectableBlockTag : MTagKeys.Block$WOODEN_FENCES;
|
||||
boolean canLeash = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-leash", false), "can-leash");
|
||||
return new FenceBlockBehavior(block, north, east, south, west, connectableBlockTag, canLeash);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,10 +10,7 @@ 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.BukkitWorld;
|
||||
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.UpdateOption;
|
||||
import net.momirealms.craftengine.core.block.*;
|
||||
import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
|
||||
import net.momirealms.craftengine.core.block.properties.Property;
|
||||
import net.momirealms.craftengine.core.entity.player.InteractionResult;
|
||||
@@ -262,6 +259,22 @@ public class FenceGateBlockBehavior extends BukkitBlockBehavior {
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean connectsToDirection(BlockStateWrapper state, HorizontalDirection direction) {
|
||||
FenceGateBlockBehavior fence = BlockStateUtils.getOptionalCustomBlockState(state.literalObject())
|
||||
.map(ImmutableBlockState::behavior)
|
||||
.flatMap(behavior -> behavior.getAs(FenceGateBlockBehavior.class))
|
||||
.orElse(null);
|
||||
if (fence == null) return false;
|
||||
Direction facing = null;
|
||||
ImmutableBlockState customState = BlockStateUtils.getOptionalCustomBlockState(state.literalObject()).orElse(null);
|
||||
if (customState == null) return false;
|
||||
Property<?> facingProperty = customState.owner().value().getProperty("facing");
|
||||
if (facingProperty != null && facingProperty.valueClass() == HorizontalDirection.class) {
|
||||
facing = ((HorizontalDirection) customState.get(facingProperty)).toDirection();
|
||||
}
|
||||
return facing != null && facing.axis() == direction.toDirection().clockWise().axis();
|
||||
}
|
||||
|
||||
public static class Factory implements BlockBehaviorFactory {
|
||||
|
||||
@Override
|
||||
|
||||
@@ -146,7 +146,7 @@ public class GrassBlockBehavior extends BukkitBlockBehavior {
|
||||
}
|
||||
if (FastNMS.INSTANCE.method$BlockStateBase$isAir(currentState)) {
|
||||
Object chunkGenerator = CoreReflections.method$ServerChunkCache$getGenerator.invoke(FastNMS.INSTANCE.method$ServerLevel$getChunkSource(world));
|
||||
Object placedFeature = CoreReflections.method$Holder$value.invoke(holder.get());
|
||||
Object placedFeature = FastNMS.INSTANCE.method$Holder$value(holder.get());
|
||||
CoreReflections.method$PlacedFeature$place.invoke(placedFeature, world, chunkGenerator, random, nmsCurrentPos);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ public class HangingBlockBehavior extends BushBlockBehavior {
|
||||
public static final Factory FACTORY = new Factory();
|
||||
|
||||
public HangingBlockBehavior(CustomBlock block, int delay, boolean blacklist, boolean stackable, List<Object> tagsCanSurviveOn, Set<Object> blocksCansSurviveOn, Set<String> customBlocksCansSurviveOn) {
|
||||
super(block, delay, blacklist, stackable, tagsCanSurviveOn, blocksCansSurviveOn, customBlocksCansSurviveOn);
|
||||
super(block, delay, blacklist, stackable, -1, tagsCanSurviveOn, blocksCansSurviveOn, customBlocksCansSurviveOn);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,11 +4,11 @@ import io.papermc.paper.event.entity.EntityInsideBlockEvent;
|
||||
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.plugin.reflection.minecraft.MEntitySelectors;
|
||||
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.DirectionUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.EventUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.LocationUtils;
|
||||
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
|
||||
import net.momirealms.craftengine.bukkit.world.BukkitWorldManager;
|
||||
import net.momirealms.craftengine.core.block.BlockBehavior;
|
||||
import net.momirealms.craftengine.core.block.CustomBlock;
|
||||
@@ -20,7 +20,8 @@ import net.momirealms.craftengine.core.util.Direction;
|
||||
import net.momirealms.craftengine.core.util.PressurePlateSensitivity;
|
||||
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
import net.momirealms.craftengine.core.world.*;
|
||||
import net.momirealms.craftengine.core.world.World;
|
||||
import net.momirealms.craftengine.core.world.WorldEvents;
|
||||
import org.bukkit.GameEvent;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
@@ -66,10 +67,6 @@ public class PressurePlateBlockBehavior extends BukkitBlockBehavior {
|
||||
return MBlocks.AIR$defaultState;
|
||||
}
|
||||
ImmutableBlockState customState = optionalCustomState.get();
|
||||
BlockPos pos = LocationUtils.fromBlockPos(blockPos);
|
||||
net.momirealms.craftengine.core.world.World world = new BukkitWorld(FastNMS.INSTANCE.method$Level$getCraftWorld(level));
|
||||
WorldPosition position = new WorldPosition(world, Vec3d.atCenterOf(pos));
|
||||
world.playBlockSound(position, customState.settings().sounds().breakSound());
|
||||
FastNMS.INSTANCE.method$LevelAccessor$levelEvent(level, WorldEvents.BLOCK_BREAK_EFFECT, blockPos, customState.customBlockState().registryId());
|
||||
return MBlocks.AIR$defaultState;
|
||||
}
|
||||
@@ -104,8 +101,6 @@ public class PressurePlateBlockBehavior extends BukkitBlockBehavior {
|
||||
int signalForState = this.getSignalForState(state);
|
||||
if (signalForState == 0) {
|
||||
this.checkPressed(args[3], args[1], args[2], state, signalForState, thisBlock);
|
||||
} else {
|
||||
FastNMS.INSTANCE.method$ScheduledTickAccess$scheduleBlockTick(args[1], args[2], thisBlock, this.pressedTime);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,7 +110,10 @@ public class PressurePlateBlockBehavior extends BukkitBlockBehavior {
|
||||
case MOBS -> CoreReflections.clazz$LivingEntity;
|
||||
};
|
||||
Object box = FastNMS.INSTANCE.method$AABB$move(CoreReflections.instance$BasePressurePlateBlock$TOUCH_AABB, pos);
|
||||
return FastNMS.INSTANCE.method$EntityGetter$getEntitiesOfClass(level, box, clazz) > 0 ? 15 : 0;
|
||||
return !FastNMS.INSTANCE.method$EntityGetter$getEntitiesOfClass(
|
||||
level, clazz, box,
|
||||
MEntitySelectors.NO_SPECTATORS.and(entity -> !FastNMS.INSTANCE.method$Entity$isIgnoringBlockTriggers(entity))
|
||||
).isEmpty() ? 15 : 0;
|
||||
}
|
||||
|
||||
private Object setSignalForState(Object state, int strength) {
|
||||
|
||||
@@ -88,7 +88,7 @@ public class SaplingBlockBehavior extends BukkitBlockBehavior {
|
||||
return;
|
||||
}
|
||||
Object chunkGenerator = CoreReflections.method$ServerChunkCache$getGenerator.invoke(FastNMS.INSTANCE.method$ServerLevel$getChunkSource(world));
|
||||
Object configuredFeature = CoreReflections.method$Holder$value.invoke(holder.get());
|
||||
Object configuredFeature = FastNMS.INSTANCE.method$Holder$value(holder.get());
|
||||
Object fluidState = FastNMS.INSTANCE.method$BlockGetter$getFluidState(world, blockPos);
|
||||
Object legacyState = CoreReflections.method$FluidState$createLegacyBlock.invoke(fluidState);
|
||||
FastNMS.INSTANCE.method$LevelWriter$setBlock(world, blockPos, legacyState, UpdateOption.UPDATE_NONE.flags());
|
||||
|
||||
@@ -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> createAsyncBlockEntityTicker(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.MiscUtils;
|
||||
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 MiscUtils.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 = MiscUtils.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ 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.SlabType;
|
||||
import net.momirealms.craftengine.core.block.properties.type.SlabType;
|
||||
import net.momirealms.craftengine.core.item.CustomItem;
|
||||
import net.momirealms.craftengine.core.item.Item;
|
||||
import net.momirealms.craftengine.core.item.behavior.BlockBoundItemBehavior;
|
||||
|
||||
@@ -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.properties.type.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,8 +10,8 @@ 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.SingleBlockHalf;
|
||||
import net.momirealms.craftengine.core.block.state.properties.StairsShape;
|
||||
import net.momirealms.craftengine.core.block.properties.type.SingleBlockHalf;
|
||||
import net.momirealms.craftengine.core.block.properties.type.StairsShape;
|
||||
import net.momirealms.craftengine.core.item.context.BlockPlaceContext;
|
||||
import net.momirealms.craftengine.core.util.Direction;
|
||||
import net.momirealms.craftengine.core.util.HorizontalDirection;
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
package net.momirealms.craftengine.bukkit.block.behavior;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
|
||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBuiltInRegistries;
|
||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistries;
|
||||
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.DirectionUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.KeyUtils;
|
||||
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.UpdateOption;
|
||||
import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
|
||||
import net.momirealms.craftengine.core.block.properties.IntegerProperty;
|
||||
import net.momirealms.craftengine.core.block.properties.Property;
|
||||
import net.momirealms.craftengine.core.util.*;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
public class StemBlockBehavior extends BukkitBlockBehavior {
|
||||
public static final Factory FACTORY = new Factory();
|
||||
private final IntegerProperty ageProperty;
|
||||
private final Key fruit;
|
||||
private final Key attachedStem;
|
||||
private final int minGrowLight;
|
||||
private final Object tagMayPlaceFruit;
|
||||
private final Object blockMayPlaceFruit;
|
||||
|
||||
public StemBlockBehavior(CustomBlock customBlock,
|
||||
IntegerProperty ageProperty,
|
||||
Key fruit,
|
||||
Key attachedStem,
|
||||
int minGrowLight,
|
||||
Object tagMayPlaceFruit,
|
||||
Object blockMayPlaceFruit) {
|
||||
super(customBlock);
|
||||
this.ageProperty = ageProperty;
|
||||
this.fruit = fruit;
|
||||
this.attachedStem = attachedStem;
|
||||
this.minGrowLight = minGrowLight;
|
||||
this.tagMayPlaceFruit = tagMayPlaceFruit;
|
||||
this.blockMayPlaceFruit = blockMayPlaceFruit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPathFindable(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
|
||||
return (VersionHelper.isOrAbove1_20_5() ? args[1] : args[3]).equals(CoreReflections.instance$PathComputationType$AIR)
|
||||
&& !FastNMS.INSTANCE.field$BlockBehavior$hasCollision(thisBlock) || (boolean) superMethod.call();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void randomTick(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
|
||||
Object state = args[0];
|
||||
Object level = args[1];
|
||||
Object pos = args[2];
|
||||
if (CropBlockBehavior.getRawBrightness(level, pos) < this.minGrowLight) return;
|
||||
ImmutableBlockState customState = BlockStateUtils.getOptionalCustomBlockState(state).orElse(null);
|
||||
if (customState == null || customState.isEmpty()) return;
|
||||
int age = customState.get(ageProperty);
|
||||
if (age < ageProperty.max) {
|
||||
FastNMS.INSTANCE.method$LevelWriter$setBlock(level, pos, customState.with(ageProperty, age + 1).customBlockState().literalObject(), 2);
|
||||
return;
|
||||
}
|
||||
Object randomDirection = CoreReflections.instance$Direction$values[RandomUtils.generateRandomInt(2, 6)];
|
||||
Object blockPos = FastNMS.INSTANCE.method$BlockPos$relative(pos, randomDirection);
|
||||
if (!FastNMS.INSTANCE.method$BlockStateBase$isAir(FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, blockPos)))
|
||||
return;
|
||||
Object blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, FastNMS.INSTANCE.method$BlockPos$relative(blockPos, CoreReflections.instance$Direction$DOWN));
|
||||
if (mayPlaceFruit(blockState)) {
|
||||
Optional<CustomBlock> optionalFruit = BukkitBlockManager.instance().blockById(this.fruit);
|
||||
Object fruitState = null;
|
||||
if (optionalFruit.isPresent()) {
|
||||
fruitState = optionalFruit.get().defaultState().customBlockState().literalObject();
|
||||
} else if (fruit.namespace().equals("minecraft")) {
|
||||
fruitState = FastNMS.INSTANCE.method$Block$defaultState(FastNMS.INSTANCE.method$Registry$getValue(
|
||||
MBuiltInRegistries.BLOCK,
|
||||
FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", fruit.value())
|
||||
));
|
||||
}
|
||||
Optional<CustomBlock> optionalAttachedStem = BukkitBlockManager.instance().blockById(this.attachedStem);
|
||||
if (fruitState == null || optionalAttachedStem.isEmpty()) return;
|
||||
CustomBlock attachedStem = optionalAttachedStem.get();
|
||||
@SuppressWarnings("unchecked")
|
||||
Property<HorizontalDirection> facing = (Property<HorizontalDirection>) attachedStem.getProperty("facing");
|
||||
if (facing == null) return;
|
||||
FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, fruitState, UpdateOption.UPDATE_ALL.flags());
|
||||
FastNMS.INSTANCE.method$LevelWriter$setBlock(level, pos, attachedStem.defaultState().with(facing, DirectionUtils.fromNMSDirection(randomDirection).toHorizontalDirection()).customBlockState().literalObject(), UpdateOption.UPDATE_ALL.flags());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValidBoneMealTarget(Object thisBlock, Object[] args) {
|
||||
ImmutableBlockState state = BlockStateUtils.getOptionalCustomBlockState(args[2]).orElse(null);
|
||||
if (state == null || state.isEmpty()) return false;
|
||||
return state.get(ageProperty) != ageProperty.max;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBoneMealSuccess(Object thisBlock, Object[] args) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performBoneMeal(Object thisBlock, Object[] args) {
|
||||
ImmutableBlockState state = BlockStateUtils.getOptionalCustomBlockState(args[3]).orElse(null);
|
||||
if (state == null || state.isEmpty()) return;
|
||||
int min = Math.min(7, state.get(ageProperty) + RandomUtils.generateRandomInt(Math.min(ageProperty.min + 2, ageProperty.max), Math.min(ageProperty.max - 2, ageProperty.max)));
|
||||
Object blockState = state.with(ageProperty, min).customBlockState().literalObject();
|
||||
FastNMS.INSTANCE.method$LevelWriter$setBlock(args[0], args[2], blockState, 2);
|
||||
if (min >= ageProperty.max) {
|
||||
FastNMS.INSTANCE.method$BlockBehaviour$BlockStateBase$randomTick(blockState, args[0], args[2]);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean mayPlaceFruit(Object blockState) {
|
||||
boolean flag1 = tagMayPlaceFruit != null && FastNMS.INSTANCE.method$BlockStateBase$is(blockState, tagMayPlaceFruit);
|
||||
boolean flag2 = blockMayPlaceFruit != null && FastNMS.INSTANCE.method$BlockStateBase$isBlock(blockState, blockMayPlaceFruit);
|
||||
if (tagMayPlaceFruit == null && blockMayPlaceFruit == null) return true;
|
||||
return flag1 || flag2;
|
||||
}
|
||||
|
||||
public static class Factory implements BlockBehaviorFactory {
|
||||
|
||||
@Override
|
||||
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
|
||||
IntegerProperty ageProperty = (IntegerProperty) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("age"), "warning.config.block.behavior.stem.missing_age");
|
||||
Key fruit = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("fruit"), "warning.config.block.behavior.stem.missing_fruit"));
|
||||
Key attachedStem = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("attached-stem"), "warning.config.block.behavior.stem.missing_attached_stem"));
|
||||
int minGrowLight = ResourceConfigUtils.getAsInt(arguments.getOrDefault("light-requirement", 9), "light-requirement");
|
||||
Object tagMayPlaceFruit = FastNMS.INSTANCE.method$TagKey$create(MRegistries.BLOCK, KeyUtils.toResourceLocation(Key.of(arguments.getOrDefault("may-place-fruit", "minecraft:dirt").toString())));
|
||||
Object blockMayPlaceFruit = FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.BLOCK, KeyUtils.toResourceLocation(Key.of(arguments.getOrDefault("may-place-fruit", "minecraft:farmland").toString())));
|
||||
return new StemBlockBehavior(block, ageProperty, fruit, attachedStem, minGrowLight, tagMayPlaceFruit, blockMayPlaceFruit);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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(ResourceConfigUtils.get(arguments, "can-open-with-hand", "can-toggle-with-hand"), "can-toggle-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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ import net.momirealms.craftengine.core.block.ImmutableBlockState;
|
||||
import net.momirealms.craftengine.core.block.UpdateOption;
|
||||
import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
|
||||
import net.momirealms.craftengine.core.block.properties.Property;
|
||||
import net.momirealms.craftengine.core.block.state.properties.SingleBlockHalf;
|
||||
import net.momirealms.craftengine.core.block.properties.type.SingleBlockHalf;
|
||||
import net.momirealms.craftengine.core.entity.player.InteractionResult;
|
||||
import net.momirealms.craftengine.core.entity.player.Player;
|
||||
import net.momirealms.craftengine.core.item.Item;
|
||||
|
||||
@@ -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,40 @@ 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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stepOn(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
|
||||
for (AbstractBlockBehavior behavior : this.behaviors) {
|
||||
behavior.stepOn(thisBlock, args, superMethod);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProjectileHit(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
|
||||
for (AbstractBlockBehavior behavior : this.behaviors) {
|
||||
behavior.onProjectileHit(thisBlock, args, superMethod);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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> createAsyncBlockEntityTicker(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
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.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);
|
||||
}
|
||||
}
|
||||
@@ -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() {
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package net.momirealms.craftengine.bukkit.entity.data;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface EntityData<T> {
|
||||
@@ -27,6 +29,11 @@ public interface EntityData<T> {
|
||||
list.add(EntityDataValue.create(id(), serializer(), entityDataAccessor(), value));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
default T get(Object entityData) {
|
||||
return (T) FastNMS.INSTANCE.method$SynchedEntityData$get(entityData, entityDataAccessor());
|
||||
}
|
||||
|
||||
static <T> EntityData<T> of(Class<?> clazz, Object serializer, T defaultValue) {
|
||||
return new SimpleEntityData<>(clazz, serializer, defaultValue);
|
||||
}
|
||||
|
||||
@@ -71,7 +71,8 @@ public class EntityDataValue {
|
||||
if (VersionHelper.isOrAbove1_21_5()) Serializers$OPTIONAL_LIVING_ENTITY_REFERENCE = initSerializersByName("OPTIONAL_LIVING_ENTITY_REFERENCE");
|
||||
else Serializers$OPTIONAL_LIVING_ENTITY_REFERENCE = null;
|
||||
Serializers$OPTIONAL_GLOBAL_POS = initSerializersByName("OPTIONAL_GLOBAL_POS");
|
||||
Serializers$COMPOUND_TAG = initSerializersByName("COMPOUND_TAG");
|
||||
if (!VersionHelper.isOrAbove1_21_9()) Serializers$COMPOUND_TAG = initSerializersByName("COMPOUND_TAG");
|
||||
else Serializers$COMPOUND_TAG = null;
|
||||
Serializers$VILLAGER_DATA = initSerializersByName("VILLAGER_DATA");
|
||||
Serializers$OPTIONAL_UNSIGNED_INT = initSerializersByName("OPTIONAL_UNSIGNED_INT");
|
||||
Serializers$POSE = initSerializersByName("POSE");
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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")) {
|
||||
|
||||
@@ -171,7 +171,7 @@ public class ShulkerHitBox extends AbstractHitBox {
|
||||
}
|
||||
|
||||
private static float getPhysicalPeek(float peek) {
|
||||
return 0.5F - MCUtils.sin((0.5F + peek) * 3.1415927F) * 0.5F;
|
||||
return 0.5F - MiscUtils.sin((0.5F + peek) * 3.1415927F) * 0.5F;
|
||||
}
|
||||
|
||||
public boolean interactionEntity() {
|
||||
@@ -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);
|
||||
|
||||
@@ -36,11 +36,17 @@ import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.*;
|
||||
|
||||
public class BukkitFontManager extends AbstractFontManager implements Listener {
|
||||
private static BukkitFontManager instance;
|
||||
private final BukkitCraftEngine plugin;
|
||||
|
||||
public BukkitFontManager(BukkitCraftEngine plugin) {
|
||||
super(plugin);
|
||||
this.plugin = plugin;
|
||||
instance = this;
|
||||
}
|
||||
|
||||
public static BukkitFontManager instance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -20,7 +20,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.*;
|
||||
@@ -337,7 +336,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
|
||||
|
||||
@@ -131,7 +131,7 @@ public class AxeItemBehavior extends ItemBehavior {
|
||||
|
||||
public static class Factory implements ItemBehaviorFactory {
|
||||
@Override
|
||||
public ItemBehavior create(Pack pack, Path path, Key key, Map<String, Object> arguments) {
|
||||
public ItemBehavior create(Pack pack, Path path, String node, Key key, Map<String, Object> arguments) {
|
||||
return INSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ 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.pack.PendingConfigSection;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
import net.momirealms.craftengine.core.plugin.context.ContextHolder;
|
||||
@@ -73,7 +74,7 @@ public class BlockItemBehavior extends BlockBoundItemBehavior {
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
if (!context.canPlace()) {
|
||||
return InteractionResult.FAIL;
|
||||
return InteractionResult.PASS;
|
||||
}
|
||||
|
||||
Player player = context.getPlayer();
|
||||
@@ -89,7 +90,7 @@ public class BlockItemBehavior extends BlockBoundItemBehavior {
|
||||
|
||||
ImmutableBlockState blockStateToPlace = getPlacementState(context, block);
|
||||
if (blockStateToPlace == null) {
|
||||
return InteractionResult.FAIL;
|
||||
return InteractionResult.PASS;
|
||||
}
|
||||
|
||||
BlockPos againstPos = context.getAgainstPos();
|
||||
@@ -232,20 +233,24 @@ public class BlockItemBehavior extends BlockBoundItemBehavior {
|
||||
return this.blockId;
|
||||
}
|
||||
|
||||
static void addPendingSection(Pack pack, Path path, String node, Key key, Map<?, ?> map) {
|
||||
if (map.containsKey(key.toString())) {
|
||||
// 防呆
|
||||
BukkitBlockManager.instance().blockParser().addPendingConfigSection(new PendingConfigSection(pack, path, node, key, MiscUtils.castToMap(map.get(key.toString()), false)));
|
||||
} else {
|
||||
BukkitBlockManager.instance().blockParser().addPendingConfigSection(new PendingConfigSection(pack, path, node, key, MiscUtils.castToMap(map, false)));
|
||||
}
|
||||
}
|
||||
|
||||
public static class Factory implements ItemBehaviorFactory {
|
||||
@Override
|
||||
public ItemBehavior create(Pack pack, Path path, Key key, Map<String, Object> arguments) {
|
||||
public ItemBehavior create(Pack pack, Path path, String node, Key key, Map<String, Object> arguments) {
|
||||
Object id = arguments.get("block");
|
||||
if (id == null) {
|
||||
throw new LocalizedResourceConfigException("warning.config.item.behavior.block.missing_block", new IllegalArgumentException("Missing required parameter 'block' for 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));
|
||||
}
|
||||
addPendingSection(pack, path, node, key, map);
|
||||
return new BlockItemBehavior(key);
|
||||
} else {
|
||||
return new BlockItemBehavior(Key.of(id.toString()));
|
||||
|
||||
@@ -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 final Key COMPASS_ITEM = Key.from("craftengine:compass_item");
|
||||
public static final Key ENDER_EYE_ITEM = Key.from("craftengine:ender_eye_item");
|
||||
public static final Key END_CRYSTAL_ITEM = Key.from("craftengine:end_crystal_item");
|
||||
@@ -25,6 +26,7 @@ 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);
|
||||
register(COMPASS_ITEM, CompassItemBehavior.FACTORY);
|
||||
register(ENDER_EYE_ITEM, EnderEyeItemBehavior.FACTORY);
|
||||
register(END_CRYSTAL_ITEM, EndCrystalItemBehavior.FACTORY);
|
||||
|
||||
@@ -79,7 +79,7 @@ public class CompostableItemBehavior extends ItemBehavior {
|
||||
|
||||
public static class Factory implements ItemBehaviorFactory {
|
||||
@Override
|
||||
public ItemBehavior create(Pack pack, Path path, Key key, Map<String, Object> arguments) {
|
||||
public ItemBehavior create(Pack pack, Path path, String node, Key key, Map<String, Object> arguments) {
|
||||
double chance = ResourceConfigUtils.getAsDouble(arguments.getOrDefault("chance", 0.55), "chance");
|
||||
return new CompostableItemBehavior(chance);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
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.MBlocks;
|
||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MFluids;
|
||||
@@ -11,7 +10,6 @@ import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory;
|
||||
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 org.bukkit.Location;
|
||||
import org.bukkit.block.BlockState;
|
||||
|
||||
@@ -40,18 +38,13 @@ public class DoubleHighBlockItemBehavior extends BlockItemBehavior {
|
||||
|
||||
public static class Factory implements ItemBehaviorFactory {
|
||||
@Override
|
||||
public ItemBehavior create(Pack pack, Path path, Key key, Map<String, Object> arguments) {
|
||||
public ItemBehavior create(Pack pack, Path path, String node, Key key, Map<String, Object> arguments) {
|
||||
Object id = arguments.get("block");
|
||||
if (id == null) {
|
||||
throw new LocalizedResourceConfigException("warning.config.item.behavior.double_high.missing_block", new IllegalArgumentException("Missing required parameter 'block' for double_high_block_item behavior"));
|
||||
throw new LocalizedResourceConfigException("warning.config.item.behavior.double_high.missing_block");
|
||||
}
|
||||
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));
|
||||
}
|
||||
addPendingSection(pack, path, node, key, map);
|
||||
return new DoubleHighBlockItemBehavior(key);
|
||||
} else {
|
||||
return new DoubleHighBlockItemBehavior(Key.of(id.toString()));
|
||||
|
||||
@@ -90,7 +90,7 @@ public class FlintAndSteelItemBehavior extends ItemBehavior {
|
||||
}
|
||||
// 且没有shift或者忽略潜行的可交互方块
|
||||
if (!player.isSecondaryUseActive()) {
|
||||
player.playSound(FLINT_SOUND, firePos, SoundSource.BLOCK, 1f, RandomUtils.generateRandomFloat(0.8f, 1.2f));
|
||||
player.playSound(firePos, FLINT_SOUND, SoundSource.BLOCK, 1f, RandomUtils.generateRandomFloat(0.8f, 1.2f));
|
||||
}
|
||||
} else {
|
||||
// 玩家觉得自定义方块不可燃,且点击了侧面,那么就要判断火源下方的方块是否可燃,如果不可燃,则补发声音
|
||||
@@ -113,16 +113,16 @@ public class FlintAndSteelItemBehavior extends ItemBehavior {
|
||||
if (player.isSecondaryUseActive()) {
|
||||
// 如果底部不能燃烧,则燃烧点位为侧面,需要补发
|
||||
if (!belowCanBurn) {
|
||||
player.playSound(FLINT_SOUND, firePos, SoundSource.BLOCK, 1f, RandomUtils.generateRandomFloat(0.8f, 1.2f));
|
||||
player.playSound(firePos, FLINT_SOUND, SoundSource.BLOCK, 1f, RandomUtils.generateRandomFloat(0.8f, 1.2f));
|
||||
player.swingHand(context.getHand());
|
||||
}
|
||||
} else {
|
||||
player.playSound(FLINT_SOUND, firePos, SoundSource.BLOCK, 1f, RandomUtils.generateRandomFloat(0.8f, 1.2f));
|
||||
player.playSound(firePos, FLINT_SOUND, SoundSource.BLOCK, 1f, RandomUtils.generateRandomFloat(0.8f, 1.2f));
|
||||
}
|
||||
} else {
|
||||
// 如果底部方块不可燃烧才补发
|
||||
if (!belowCanBurn) {
|
||||
player.playSound(FLINT_SOUND, firePos, SoundSource.BLOCK, 1f, RandomUtils.generateRandomFloat(0.8f, 1.2f));
|
||||
player.playSound(firePos, FLINT_SOUND, SoundSource.BLOCK, 1f, RandomUtils.generateRandomFloat(0.8f, 1.2f));
|
||||
player.swingHand(context.getHand());
|
||||
}
|
||||
}
|
||||
@@ -153,7 +153,7 @@ public class FlintAndSteelItemBehavior extends ItemBehavior {
|
||||
}
|
||||
}
|
||||
}
|
||||
player.playSound(FLINT_SOUND, firePos, SoundSource.BLOCK, 1f, RandomUtils.generateRandomFloat(0.8f, 1.2f));
|
||||
player.playSound(firePos, FLINT_SOUND, SoundSource.BLOCK, 1f, RandomUtils.generateRandomFloat(0.8f, 1.2f));
|
||||
player.swingHand(context.getHand());
|
||||
}
|
||||
return InteractionResult.PASS;
|
||||
@@ -161,7 +161,7 @@ public class FlintAndSteelItemBehavior extends ItemBehavior {
|
||||
|
||||
public static class Factory implements ItemBehaviorFactory {
|
||||
@Override
|
||||
public ItemBehavior create(Pack pack, Path path, Key id, Map<String, Object> arguments) {
|
||||
public ItemBehavior create(Pack pack, Path path, String node, Key id, Map<String, Object> arguments) {
|
||||
return INSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
|
||||
import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory;
|
||||
import net.momirealms.craftengine.core.item.context.UseOnContext;
|
||||
import net.momirealms.craftengine.core.pack.Pack;
|
||||
import net.momirealms.craftengine.core.pack.PendingConfigSection;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.context.ContextHolder;
|
||||
import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext;
|
||||
@@ -177,7 +178,7 @@ public class FurnitureItemBehavior extends ItemBehavior {
|
||||
public static class Factory implements ItemBehaviorFactory {
|
||||
|
||||
@Override
|
||||
public ItemBehavior create(Pack pack, Path path, Key key, Map<String, Object> arguments) {
|
||||
public ItemBehavior create(Pack pack, Path path, String node, Key key, Map<String, Object> arguments) {
|
||||
Object id = arguments.get("furniture");
|
||||
if (id == null) {
|
||||
throw new LocalizedResourceConfigException("warning.config.item.behavior.furniture.missing_furniture", new IllegalArgumentException("Missing required parameter 'furniture' for furniture_item behavior"));
|
||||
@@ -185,9 +186,9 @@ public class FurnitureItemBehavior extends ItemBehavior {
|
||||
if (id instanceof Map<?,?> map) {
|
||||
if (map.containsKey(key.toString())) {
|
||||
// 防呆
|
||||
BukkitFurnitureManager.instance().parser().parseSection(pack, path, key, MiscUtils.castToMap(map.get(key.toString()), false));
|
||||
BukkitFurnitureManager.instance().parser().addPendingConfigSection(new PendingConfigSection(pack, path, node, key, MiscUtils.castToMap(map.get(key.toString()), false)));
|
||||
} else {
|
||||
BukkitFurnitureManager.instance().parser().parseSection(pack, path, key, MiscUtils.castToMap(map, false));
|
||||
BukkitFurnitureManager.instance().parser().addPendingConfigSection(new PendingConfigSection(pack, path, node, key, MiscUtils.castToMap(map, false)));
|
||||
}
|
||||
return new FurnitureItemBehavior(key);
|
||||
} else {
|
||||
|
||||
@@ -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,15 +47,19 @@ 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$BlockHitResult$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 {
|
||||
boolean inside = CoreReflections.field$BlockHitResul$inside.getBoolean(blockHitResult);
|
||||
boolean inside = CoreReflections.field$BlockHitResult$inside.getBoolean(blockHitResult);
|
||||
return super.useOnBlock(new UseOnContext(player, hand, new BlockHitResult(hitPos, direction, above, inside)));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
@@ -64,7 +70,7 @@ public class LiquidCollisionBlockItemBehavior extends BlockItemBehavior {
|
||||
|
||||
public static class Factory implements ItemBehaviorFactory {
|
||||
@Override
|
||||
public ItemBehavior create(Pack pack, Path path, Key key, Map<String, Object> arguments) {
|
||||
public ItemBehavior create(Pack pack, Path path, String node, Key key, Map<String, Object> arguments) {
|
||||
Object id = arguments.get("block");
|
||||
if (id == null) {
|
||||
throw new LocalizedResourceConfigException("warning.config.item.behavior.liquid_collision.missing_block", new IllegalArgumentException("Missing required parameter 'block' for liquid_collision_block_item behavior"));
|
||||
@@ -73,9 +79,9 @@ public class LiquidCollisionBlockItemBehavior extends BlockItemBehavior {
|
||||
if (id instanceof Map<?, ?> map) {
|
||||
if (map.containsKey(key.toString())) {
|
||||
// 防呆
|
||||
BukkitBlockManager.instance().parser().parseSection(pack, path, key, MiscUtils.castToMap(map.get(key.toString()), false));
|
||||
BukkitBlockManager.instance().blockParser().parseSection(pack, path, node, key, MiscUtils.castToMap(map.get(key.toString()), false));
|
||||
} else {
|
||||
BukkitBlockManager.instance().parser().parseSection(pack, path, key, MiscUtils.castToMap(map, false));
|
||||
BukkitBlockManager.instance().blockParser().parseSection(pack, path, node, key, MiscUtils.castToMap(map, false));
|
||||
}
|
||||
return new LiquidCollisionBlockItemBehavior(key, offset);
|
||||
} else {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user