mirror of
https://github.com/Xiao-MoMi/craft-engine.git
synced 2025-12-28 19:39:11 +00:00
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")
|
||||
}
|
||||
```
|
||||
@@ -1,5 +1,5 @@
|
||||
plugins {
|
||||
id("com.gradleup.shadow") version "9.0.0-rc2"
|
||||
id("com.gradleup.shadow") version "9.2.2"
|
||||
id("maven-publish")
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ repositories {
|
||||
maven("https://repo.auxilor.io/repository/maven-public/") // eco
|
||||
maven("https://repo.hiusers.com/releases") // zaphkiel
|
||||
maven("https://jitpack.io") // sxitem slimefun
|
||||
maven("https://repo.codemc.io/repository/maven-public/") // quickshop
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@@ -79,6 +80,8 @@ dependencies {
|
||||
compileOnly("com.github.Saukiya:SX-Item:4.4.6")
|
||||
// Slimefun
|
||||
compileOnly("io.github.Slimefun:Slimefun4:RC-32")
|
||||
// QuickShop
|
||||
compileOnly("com.ghostchu:quickshop-api:6.2.0.10")
|
||||
}
|
||||
|
||||
java {
|
||||
|
||||
@@ -12,6 +12,7 @@ import net.momirealms.craftengine.bukkit.compatibility.mythicmobs.MythicItemDrop
|
||||
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.quickshop.QuickShopItemExpressionHandler;
|
||||
import net.momirealms.craftengine.bukkit.compatibility.region.WorldGuardRegionCondition;
|
||||
import net.momirealms.craftengine.bukkit.compatibility.skript.SkriptHook;
|
||||
import net.momirealms.craftengine.bukkit.compatibility.slimeworld.SlimeFormatStorageAdaptor;
|
||||
@@ -20,12 +21,14 @@ 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;
|
||||
@@ -139,6 +142,10 @@ public class BukkitCompatibilityManager implements CompatibilityManager {
|
||||
ModelEngineUtils.registerConstantBlockEntityRender();
|
||||
logHook("ModelEngine");
|
||||
}
|
||||
if (this.isPluginEnabled("QuickShop-Hikari")) {
|
||||
new QuickShopItemExpressionHandler(this.plugin).register();
|
||||
logHook("QuickShop-Hikari");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -246,8 +253,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);
|
||||
|
||||
@@ -16,7 +16,7 @@ public class BetterModelModel extends AbstractExternalModel {
|
||||
|
||||
@Override
|
||||
public void bindModel(AbstractEntity entity) {
|
||||
org.bukkit.entity.Entity bukkitEntity = (org.bukkit.entity.Entity) entity.literalObject();
|
||||
org.bukkit.entity.Entity bukkitEntity = (org.bukkit.entity.Entity) entity.platformEntity();
|
||||
BetterModelUtils.bindModel(bukkitEntity, id());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ public class ModelEngineModel extends AbstractExternalModel {
|
||||
|
||||
@Override
|
||||
public void bindModel(AbstractEntity entity) {
|
||||
org.bukkit.entity.Entity bukkitEntity = (org.bukkit.entity.Entity) entity.literalObject();
|
||||
org.bukkit.entity.Entity bukkitEntity = (org.bukkit.entity.Entity) entity.platformEntity();
|
||||
ModelEngineUtils.bindModel(bukkitEntity, id());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -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,48 @@
|
||||
package net.momirealms.craftengine.bukkit.compatibility.quickshop;
|
||||
|
||||
import com.ghostchu.quickshop.api.QuickShopAPI;
|
||||
import com.ghostchu.quickshop.api.event.QSConfigurationReloadEvent;
|
||||
import com.ghostchu.quickshop.api.registry.BuiltInRegistry;
|
||||
import com.ghostchu.quickshop.api.registry.Registry;
|
||||
import com.ghostchu.quickshop.api.registry.builtin.itemexpression.ItemExpressionHandler;
|
||||
import com.ghostchu.quickshop.api.registry.builtin.itemexpression.ItemExpressionRegistry;
|
||||
import net.momirealms.craftengine.bukkit.api.CraftEngineItems;
|
||||
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class QuickShopItemExpressionHandler implements ItemExpressionHandler, Listener {
|
||||
private final BukkitCraftEngine plugin;
|
||||
|
||||
public QuickShopItemExpressionHandler(BukkitCraftEngine plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public void register() {
|
||||
Registry registry = QuickShopAPI.getInstance().getRegistry().getRegistry(BuiltInRegistry.ITEM_EXPRESSION);
|
||||
if (!(registry instanceof ItemExpressionRegistry itemExpressionRegistry)) return;
|
||||
itemExpressionRegistry.registerHandlerSafely(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Plugin getPlugin() {
|
||||
return this.plugin.javaPlugin();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefix() {
|
||||
return "craftengine";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(ItemStack itemStack, String id) {
|
||||
Key customId = CraftEngineItems.getCustomItemId(itemStack);
|
||||
return customId != null && id.equals(customId.asString());
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
plugins {
|
||||
id("com.gradleup.shadow") version "9.0.0-rc2"
|
||||
id("com.gradleup.shadow") version "9.2.2"
|
||||
}
|
||||
|
||||
repositories {
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
plugins {
|
||||
id("com.gradleup.shadow") version "9.0.0-rc2"
|
||||
id("com.gradleup.shadow") version "9.2.2"
|
||||
id("de.eldoria.plugin-yml.bukkit") version "0.7.1"
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ public class BukkitCraftEnginePlugin extends JavaPlugin {
|
||||
public BukkitCraftEnginePlugin() {
|
||||
this.plugin = new BukkitCraftEngine(this);
|
||||
this.plugin.applyDependencies();
|
||||
this.plugin.setUpConfig();
|
||||
this.plugin.setUpConfigAndLocale();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import net.minecrell.pluginyml.paper.PaperPluginDescription
|
||||
|
||||
plugins {
|
||||
id("com.gradleup.shadow") version "9.0.0-rc2"
|
||||
id("com.gradleup.shadow") version "9.2.2"
|
||||
id("de.eldoria.plugin-yml.paper") version "0.7.1"
|
||||
}
|
||||
|
||||
@@ -75,6 +75,7 @@ paper {
|
||||
}
|
||||
register("LuckPerms") { required = false }
|
||||
register("ViaVersion") { required = false }
|
||||
register("QuickShop-Hikari") { required = false }
|
||||
|
||||
// external models
|
||||
register("ModelEngine") { required = false }
|
||||
|
||||
@@ -55,7 +55,7 @@ public class PaperCraftEngineBootstrap implements PluginBootstrap {
|
||||
);
|
||||
}
|
||||
this.plugin.applyDependencies();
|
||||
this.plugin.setUpConfig();
|
||||
this.plugin.setUpConfigAndLocale();
|
||||
if (isDatapackDiscoveryAvailable()) {
|
||||
new ModernEventHandler(context, this.plugin).register();
|
||||
} else {
|
||||
|
||||
@@ -14,6 +14,7 @@ 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;
|
||||
@@ -105,7 +106,7 @@ public class BukkitAdvancementManager extends AbstractAdvancementManager {
|
||||
}
|
||||
}
|
||||
|
||||
public class AdvancementParser implements ConfigParser {
|
||||
public class AdvancementParser extends IdSectionConfigParser {
|
||||
public static final String[] CONFIG_SECTION_NAME = new String[] {"advancements", "advancement"};
|
||||
|
||||
@Override
|
||||
@@ -119,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,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());
|
||||
@@ -215,16 +213,34 @@ public final class CraftEngineBlocks {
|
||||
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
|
||||
*
|
||||
@@ -270,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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,7 +159,7 @@ 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
|
||||
@@ -193,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -261,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();
|
||||
|
||||
@@ -2,122 +2,89 @@ package net.momirealms.craftengine.bukkit.block;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
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.injector.BlockGenerator;
|
||||
import net.momirealms.craftengine.bukkit.plugin.network.PacketConsumers;
|
||||
import net.momirealms.craftengine.bukkit.plugin.reflection.bukkit.CraftBukkitReflections;
|
||||
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.MBuiltInRegistries;
|
||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistries;
|
||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.*;
|
||||
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
|
||||
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.KeyUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.RegistryUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.TagUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.*;
|
||||
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.parser.BlockStateParser;
|
||||
import net.momirealms.craftengine.core.plugin.config.StringKeyConstructor;
|
||||
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
|
||||
import net.momirealms.craftengine.core.registry.BuiltInRegistries;
|
||||
import net.momirealms.craftengine.core.loot.LootTable;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
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.plugin.logger.Debugger;
|
||||
import net.momirealms.craftengine.core.registry.Holder;
|
||||
import net.momirealms.craftengine.core.registry.WritableRegistry;
|
||||
import net.momirealms.craftengine.core.sound.SoundData;
|
||||
import net.momirealms.craftengine.core.sound.Sounds;
|
||||
import net.momirealms.craftengine.core.sound.SoundSet;
|
||||
import net.momirealms.craftengine.core.util.*;
|
||||
import net.momirealms.craftengine.core.world.chunk.PalettedContainer;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.yaml.snakeyaml.LoaderOptions;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public final class BukkitBlockManager extends AbstractBlockManager {
|
||||
public static final Set<Object> CLIENT_SIDE_NOTE_BLOCKS = new HashSet<>(2048, 0.6f);
|
||||
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 static BukkitBlockManager instance;
|
||||
private final BukkitCraftEngine plugin;
|
||||
|
||||
// The total amount of blocks registered
|
||||
private int customBlockCount;
|
||||
private ImmutableBlockState[] stateId2ImmutableBlockStates;
|
||||
// Minecraft objects
|
||||
// Cached new blocks $ holders
|
||||
private Map<Integer, Object> stateId2BlockHolder;
|
||||
// This map is used to change the block states that are not necessarily needed into a certain block state
|
||||
private Map<Integer, Integer> blockAppearanceMapper;
|
||||
// Record the amount of real blocks by block type
|
||||
private Map<Key, Integer> registeredRealBlockSlots;
|
||||
// A set of blocks that sounds have been removed
|
||||
private Set<Object> affectedSoundBlocks;
|
||||
private Map<Object, Pair<SoundData, SoundData>> affectedOpenableBlockSounds;
|
||||
private Map<Key, Key> soundMapper;
|
||||
// A list to record the order of registration
|
||||
private List<Key> blockRegisterOrder = new ObjectArrayList<>();
|
||||
// Event listeners
|
||||
private BlockEventListener blockEventListener;
|
||||
// cached tag packet
|
||||
// 事件监听器
|
||||
private final BlockEventListener blockEventListener;
|
||||
// 用于缓存string形式的方块状态到原版方块状态
|
||||
private final Map<String, BlockStateWrapper> blockStateCache = new HashMap<>(1024);
|
||||
// 用于临时存储可燃烧自定义方块的列表
|
||||
private final List<DelegatingBlock> burnableBlocks = new ArrayList<>();
|
||||
// 可燃烧的方块
|
||||
private Map<Object, Integer> igniteOdds;
|
||||
private Map<Object, Integer> burnOdds;
|
||||
// 自定义客户端侧原版方块标签
|
||||
private Map<Integer, List<String>> clientBoundTags = Map.of();
|
||||
private Map<Integer, List<String>> previousClientBoundTags = Map.of();
|
||||
// 缓存的原版方块tag包
|
||||
private Object cachedUpdateTagsPacket;
|
||||
|
||||
private final List<Tuple<Object, Key, Boolean>> blocksToDeceive = new ArrayList<>();
|
||||
// 被移除声音的原版方块
|
||||
private Set<Object> missingPlaceSounds = Set.of();
|
||||
private Set<Object> missingBreakSounds = Set.of();
|
||||
private Set<Object> missingHitSounds = Set.of();
|
||||
private Set<Object> missingStepSounds = Set.of();
|
||||
private Set<Key> missingInteractSoundBlocks = Set.of();
|
||||
|
||||
public BukkitBlockManager(BukkitCraftEngine plugin) {
|
||||
super(plugin);
|
||||
instance = this;
|
||||
super(plugin, RegistryUtils.currentBlockRegistrySize(), Config.serverSideBlocks());
|
||||
this.plugin = plugin;
|
||||
this.initVanillaRegistry();
|
||||
this.loadMappingsAndAdditionalBlocks();
|
||||
this.registerBlocks();
|
||||
this.registerEmptyBlock();
|
||||
this.blockEventListener = new BlockEventListener(plugin, this);
|
||||
this.registerServerSideCustomBlocks(Config.serverSideBlocks());
|
||||
EmptyBlock.initialize();
|
||||
instance = this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
this.initMirrorRegistry();
|
||||
this.deceiveBukkit();
|
||||
boolean enableNoteBlocks = this.blockAppearanceArranger.containsKey(BlockKeys.NOTE_BLOCK);
|
||||
this.blockEventListener = new BlockEventListener(plugin, this, enableNoteBlocks);
|
||||
if (enableNoteBlocks) {
|
||||
this.recordVanillaNoteBlocks();
|
||||
}
|
||||
this.stateId2ImmutableBlockStates = new ImmutableBlockState[this.customBlockCount];
|
||||
Arrays.fill(this.stateId2ImmutableBlockStates, EmptyBlock.INSTANCE.defaultState());
|
||||
this.resetPacketConsumers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String stateRegistryIdToStateSNBT(int id) {
|
||||
return BlockStateUtils.idToBlockState(id).toString();
|
||||
this.initFireBlock();
|
||||
this.deceiveBukkitRegistry();
|
||||
this.markVanillaNoteBlocks();
|
||||
Arrays.fill(this.immutableBlockStates, EmptyBlock.INSTANCE.defaultState());
|
||||
this.plugin.networkManager().registerBlockStatePacketListeners(this.blockStateMappings); // 一定要预先初始化一次,预防id超出上限
|
||||
}
|
||||
|
||||
public static BukkitBlockManager instance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public List<Key> blockRegisterOrder() {
|
||||
return Collections.unmodifiableList(this.blockRegisterOrder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delayedInit() {
|
||||
Bukkit.getPluginManager().registerEvents(this.blockEventListener, this.plugin.javaPlugin());
|
||||
@@ -126,9 +93,16 @@ public final class BukkitBlockManager extends AbstractBlockManager {
|
||||
@Override
|
||||
public void unload() {
|
||||
super.unload();
|
||||
this.previousClientBoundTags = this.clientBoundTags;
|
||||
this.clientBoundTags = new HashMap<>();
|
||||
for (DelegatingBlock block : this.burnableBlocks) {
|
||||
this.igniteOdds.remove(block);
|
||||
this.burnOdds.remove(block);
|
||||
}
|
||||
this.burnableBlocks.clear();
|
||||
if (EmptyBlock.STATE != null)
|
||||
Arrays.fill(this.stateId2ImmutableBlockStates, EmptyBlock.STATE);
|
||||
for (DelegatingBlock block : this.registeredBlocks.values()) {
|
||||
Arrays.fill(this.immutableBlockStates, EmptyBlock.STATE);
|
||||
for (DelegatingBlock block : this.customBlocks) {
|
||||
block.behaviorDelegate().bindValue(EmptyBlockBehavior.INSTANCE);
|
||||
block.shapeDelegate().bindValue(BukkitBlockShape.STONE);
|
||||
DelegatingBlockState state = (DelegatingBlockState) FastNMS.INSTANCE.method$Block$defaultState(block);
|
||||
@@ -143,14 +117,24 @@ public final class BukkitBlockManager extends AbstractBlockManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Key, Key> soundMapper() {
|
||||
return this.soundMapper;
|
||||
public void delayedLoad() {
|
||||
this.plugin.networkManager().registerBlockStatePacketListeners(this.blockStateMappings); // 重置方块映射表
|
||||
super.delayedLoad();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delayedLoad() {
|
||||
this.resetPacketConsumers();
|
||||
super.delayedLoad();
|
||||
public BlockBehavior createBlockBehavior(CustomBlock customBlock, List<Map<String, Object>> behaviorConfig) {
|
||||
if (behaviorConfig == null || behaviorConfig.isEmpty()) {
|
||||
return new EmptyBlockBehavior();
|
||||
} else if (behaviorConfig.size() == 1) {
|
||||
return BlockBehaviors.fromMap(customBlock, behaviorConfig.getFirst());
|
||||
} else {
|
||||
List<AbstractBlockBehavior> behaviors = new ArrayList<>();
|
||||
for (Map<String, Object> config : behaviorConfig) {
|
||||
behaviors.add((AbstractBlockBehavior) BlockBehaviors.fromMap(customBlock, config));
|
||||
}
|
||||
return new UnsafeCompositeBlockBehavior(customBlock, behaviors);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -180,47 +164,36 @@ public final class BukkitBlockManager extends AbstractBlockManager {
|
||||
if (state != null) {
|
||||
return state.customBlockState();
|
||||
}
|
||||
return createVanillaBlockState(blockState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockStateWrapper createVanillaBlockState(String blockState) {
|
||||
return this.blockStateCache.computeIfAbsent(blockState, k -> {
|
||||
Object state = parseBlockState(k);
|
||||
if (state == null) return null;
|
||||
return BlockStateUtils.toBlockStateWrapper(state);
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Object parseBlockState(String state) {
|
||||
try {
|
||||
BlockData blockData = Bukkit.createBlockData(blockState);
|
||||
return BlockStateUtils.toBlockStateWrapper(blockData);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Object registryOrLookUp = MBuiltInRegistries.BLOCK;
|
||||
if (CoreReflections.method$Registry$asLookup != null) {
|
||||
registryOrLookUp = CoreReflections.method$Registry$asLookup.invoke(registryOrLookUp);
|
||||
}
|
||||
Object result = CoreReflections.method$BlockStateParser$parseForBlock.invoke(null, registryOrLookUp, state, false);
|
||||
return CoreReflections.method$BlockStateParser$BlockResult$blockState.invoke(result);
|
||||
} catch (Exception e) {
|
||||
Debugger.BLOCK.warn(() -> "Failed to create block state: " + state, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Object getMinecraftBlockHolder(int stateId) {
|
||||
return this.stateId2BlockHolder.get(stateId);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ImmutableBlockState getImmutableBlockStateUnsafe(int stateId) {
|
||||
return this.stateId2ImmutableBlockStates[stateId - BlockStateUtils.vanillaStateSize()];
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public ImmutableBlockState getImmutableBlockState(int stateId) {
|
||||
if (!BlockStateUtils.isVanillaBlock(stateId)) {
|
||||
return this.stateId2ImmutableBlockStates[stateId - BlockStateUtils.vanillaStateSize()];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBlockInternal(Key id, CustomBlock customBlock) {
|
||||
// bind appearance and real state
|
||||
for (ImmutableBlockState state : customBlock.variantProvider().states()) {
|
||||
ImmutableBlockState previous = this.stateId2ImmutableBlockStates[state.customBlockState().registryId() - BlockStateUtils.vanillaStateSize()];
|
||||
if (previous != null && !previous.isEmpty()) {
|
||||
throw new LocalizedResourceConfigException("warning.config.block.state.bind_failed", state.toString(), previous.toString(), BlockStateUtils.getBlockOwnerIdFromState(previous.customBlockState().literalObject()).toString());
|
||||
}
|
||||
this.stateId2ImmutableBlockStates[state.customBlockState().registryId() - BlockStateUtils.vanillaStateSize()] = state;
|
||||
this.tempBlockAppearanceConvertor.put(state.customBlockState().registryId(), state.vanillaBlockState().registryId());
|
||||
this.appearanceToRealState.computeIfAbsent(state.vanillaBlockState().registryId(), k -> new IntArrayList()).add(state.customBlockState().registryId());
|
||||
}
|
||||
super.addBlockInternal(id, customBlock);
|
||||
return this.customBlockHolders[stateId - BlockStateUtils.vanillaBlockStateCount()];
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -233,449 +206,312 @@ public final class BukkitBlockManager extends AbstractBlockManager {
|
||||
return BlockStateUtils.getBlockOwnerIdFromState(BlockStateUtils.idToBlockState(id));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void initFireBlock() {
|
||||
try {
|
||||
this.igniteOdds = (Map<Object, Integer>) CoreReflections.field$FireBlock$igniteOdds.get(MBlocks.FIRE);
|
||||
this.burnOdds = (Map<Object, Integer>) CoreReflections.field$FireBlock$burnOdds.get(MBlocks.FIRE);
|
||||
} catch (IllegalAccessException e) {
|
||||
this.plugin.logger().warn("Failed to get ignite odds", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int availableAppearances(Key blockType) {
|
||||
return Optional.ofNullable(this.registeredRealBlockSlots.get(blockType)).orElse(0);
|
||||
protected void applyPlatformSettings(ImmutableBlockState state) {
|
||||
DelegatingBlockState nmsState = (DelegatingBlockState) state.customBlockState().literalObject();
|
||||
nmsState.setBlockState(state);
|
||||
Object nmsVisualState = state.vanillaBlockState().literalObject();
|
||||
|
||||
BlockSettings settings = state.settings();
|
||||
try {
|
||||
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);
|
||||
CoreReflections.field$BlockStateBase$instrument.set(nmsState, CoreReflections.instance$NoteBlockInstrument$values[settings.instrument().ordinal()]);
|
||||
CoreReflections.field$BlockStateBase$pushReaction.set(nmsState, CoreReflections.instance$PushReaction$values[settings.pushReaction().ordinal()]);
|
||||
boolean canOcclude = settings.canOcclude() == Tristate.UNDEFINED ? BlockStateUtils.isOcclude(nmsVisualState) : settings.canOcclude().asBoolean();
|
||||
CoreReflections.field$BlockStateBase$canOcclude.set(nmsState, canOcclude);
|
||||
boolean useShapeForLightOcclusion = settings.useShapeForLightOcclusion() == Tristate.UNDEFINED ? CoreReflections.field$BlockStateBase$useShapeForLightOcclusion.getBoolean(nmsVisualState) : 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));
|
||||
|
||||
DelegatingBlock nmsBlock = (DelegatingBlock) BlockStateUtils.getBlockOwner(nmsState);
|
||||
ObjectHolder<BlockShape> shapeHolder = nmsBlock.shapeDelegate();
|
||||
shapeHolder.bindValue(new BukkitBlockShape(nmsVisualState, Optional.ofNullable(state.settings().supportShapeBlockState()).map(it -> Objects.requireNonNull(createVanillaBlockState(it), "Illegal block state: " + it).literalObject()).orElse(null)));
|
||||
ObjectHolder<BlockBehavior> behaviorHolder = nmsBlock.behaviorDelegate();
|
||||
behaviorHolder.bindValue(state.behavior());
|
||||
|
||||
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()));
|
||||
|
||||
CoreReflections.method$BlockStateBase$initCache.invoke(nmsState);
|
||||
boolean isConditionallyFullOpaque = canOcclude & useShapeForLightOcclusion;
|
||||
if (!VersionHelper.isOrAbove1_21_2()) {
|
||||
CoreReflections.field$BlockStateBase$isConditionallyFullOpaque.set(nmsState, isConditionallyFullOpaque);
|
||||
}
|
||||
|
||||
if (VersionHelper.isOrAbove1_21_2()) {
|
||||
int blockLight = settings.blockLight() != -1 ? settings.blockLight() : CoreReflections.field$BlockStateBase$lightBlock.getInt(nmsVisualState);
|
||||
CoreReflections.field$BlockStateBase$lightBlock.set(nmsState, blockLight);
|
||||
boolean propagatesSkylightDown = settings.propagatesSkylightDown() == Tristate.UNDEFINED ? CoreReflections.field$BlockStateBase$propagatesSkylightDown.getBoolean(nmsVisualState) : settings.propagatesSkylightDown().asBoolean();
|
||||
CoreReflections.field$BlockStateBase$propagatesSkylightDown.set(nmsState, propagatesSkylightDown);
|
||||
} 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(nmsVisualState));
|
||||
CoreReflections.field$BlockStateBase$Cache$lightBlock.set(cache, blockLight);
|
||||
boolean propagatesSkylightDown = settings.propagatesSkylightDown() == Tristate.UNDEFINED ? CoreReflections.field$BlockStateBase$Cache$propagatesSkylightDown.getBoolean(CoreReflections.field$BlockStateBase$cache.get(nmsVisualState)) : settings.propagatesSkylightDown().asBoolean();
|
||||
CoreReflections.field$BlockStateBase$Cache$propagatesSkylightDown.set(cache, propagatesSkylightDown);
|
||||
if (!isConditionallyFullOpaque) {
|
||||
CoreReflections.field$BlockStateBase$opacityIfCached.set(nmsState, blockLight);
|
||||
}
|
||||
}
|
||||
|
||||
CoreReflections.field$BlockStateBase$fluidState.set(nmsState, settings.fluidState() ? MFluids.WATER$defaultState : MFluids.EMPTY$defaultState);
|
||||
CoreReflections.field$BlockStateBase$isRandomlyTicking.set(nmsState, settings.isRandomlyTicking());
|
||||
Object holder = BukkitCraftEngine.instance().blockManager().getMinecraftBlockHolder(state.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);
|
||||
if (settings.burnable()) {
|
||||
this.igniteOdds.put(nmsBlock, settings.burnChance());
|
||||
this.burnOdds.put(nmsBlock, settings.fireSpreadChance());
|
||||
this.burnableBlocks.add(nmsBlock);
|
||||
}
|
||||
} catch (ReflectiveOperationException e) {
|
||||
this.plugin.logger().warn("Failed to apply platform block settings for block state " + state, e);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Map<Key, List<Integer>> blockAppearanceArranger() {
|
||||
return this.blockAppearanceArranger;
|
||||
private BlockSounds toBlockSounds(Object soundType) throws ReflectiveOperationException {
|
||||
return new BlockSounds(
|
||||
toSoundData(CoreReflections.field$SoundType$breakSound.get(soundType), SoundData.SoundValue.FIXED_1, SoundData.SoundValue.FIXED_0_8),
|
||||
toSoundData(CoreReflections.field$SoundType$stepSound.get(soundType), SoundData.SoundValue.FIXED_0_15, SoundData.SoundValue.FIXED_1),
|
||||
toSoundData(CoreReflections.field$SoundType$placeSound.get(soundType), SoundData.SoundValue.FIXED_1, SoundData.SoundValue.FIXED_0_8),
|
||||
toSoundData(CoreReflections.field$SoundType$hitSound.get(soundType), SoundData.SoundValue.FIXED_0_5, SoundData.SoundValue.FIXED_0_5),
|
||||
toSoundData(CoreReflections.field$SoundType$fallSound.get(soundType), SoundData.SoundValue.FIXED_0_5, SoundData.SoundValue.FIXED_0_75)
|
||||
);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Map<Key, List<Integer>> realBlockArranger() {
|
||||
return this.realBlockArranger;
|
||||
private SoundData toSoundData(Object soundEvent, SoundData.SoundValue volume, SoundData.SoundValue pitch) {
|
||||
Key soundId = KeyUtils.resourceLocationToKey(FastNMS.INSTANCE.field$SoundEvent$location(soundEvent));
|
||||
return new SoundData(soundId, volume, pitch);
|
||||
}
|
||||
|
||||
private void initMirrorRegistry() {
|
||||
int size = RegistryUtils.currentBlockRegistrySize();
|
||||
BlockStateWrapper[] states = new BlockStateWrapper[size];
|
||||
for (int i = 0; i < size; i++) {
|
||||
states[i] = new BukkitBlockStateWrapper(BlockStateUtils.idToBlockState(i), i);
|
||||
for (int i = 0; i < this.vanillaBlockStateCount; i++) {
|
||||
states[i] = new BukkitVanillaBlockStateWrapper(BlockStateUtils.idToBlockState(i), i);
|
||||
}
|
||||
BlockRegistryMirror.init(states, new BukkitBlockStateWrapper(MBlocks.STONE$defaultState, BlockStateUtils.blockStateToId(MBlocks.STONE$defaultState)));
|
||||
}
|
||||
|
||||
private void registerEmptyBlock() {
|
||||
Holder.Reference<CustomBlock> holder = ((WritableRegistry<CustomBlock>) BuiltInRegistries.BLOCK).registerForHolder(ResourceKey.create(BuiltInRegistries.BLOCK.key().location(), Key.withDefaultNamespace("empty")));
|
||||
EmptyBlock emptyBlock = new EmptyBlock(Key.withDefaultNamespace("empty"), holder);
|
||||
holder.bindValue(emptyBlock);
|
||||
}
|
||||
|
||||
private void resetPacketConsumers() {
|
||||
Map<Integer, Integer> finalMapping = new HashMap<>(this.blockAppearanceMapper);
|
||||
int stoneId = BlockStateUtils.blockStateToId(MBlocks.STONE$defaultState);
|
||||
for (int custom : this.internalId2StateId.values()) {
|
||||
finalMapping.put(custom, stoneId);
|
||||
for (int i = this.vanillaBlockStateCount; i < size; i++) {
|
||||
states[i] = new BukkitCustomBlockStateWrapper(BlockStateUtils.idToBlockState(i), i);
|
||||
}
|
||||
finalMapping.putAll(this.tempBlockAppearanceConvertor);
|
||||
PacketConsumers.initBlocks(finalMapping, RegistryUtils.currentBlockRegistrySize());
|
||||
BlockRegistryMirror.init(states, states[BlockStateUtils.blockStateToId(MBlocks.STONE$defaultState)]);
|
||||
}
|
||||
|
||||
private void initVanillaRegistry() {
|
||||
int vanillaStateCount = RegistryUtils.currentBlockRegistrySize();
|
||||
this.plugin.logger().info("Vanilla block count: " + vanillaStateCount);
|
||||
BlockStateUtils.init(vanillaStateCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CustomBlock.Builder platformBuilder(Key id) {
|
||||
return BukkitCustomBlock.builder(id);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void registerBlocks() {
|
||||
this.plugin.logger().info("Registering blocks. Please wait...");
|
||||
// 注册服务端侧的真实方块
|
||||
private void registerServerSideCustomBlocks(int count) {
|
||||
// 这个会影响全局调色盘
|
||||
if (MiscUtils.ceilLog2(this.vanillaBlockStateCount + count) == MiscUtils.ceilLog2(this.vanillaBlockStateCount)) {
|
||||
PalettedContainer.NEED_DOWNGRADE = false;
|
||||
}
|
||||
try {
|
||||
ImmutableMap.Builder<Key, Integer> builder1 = ImmutableMap.builder();
|
||||
ImmutableMap.Builder<Integer, Object> builder2 = ImmutableMap.builder();
|
||||
ImmutableMap.Builder<Key, List<Integer>> builder3 = ImmutableMap.builder();
|
||||
ImmutableMap.Builder<Key, DelegatingBlock> builder4 = ImmutableMap.builder();
|
||||
Set<Object> affectedBlockSounds = new HashSet<>();
|
||||
Map<Object, Pair<SoundData, SoundData>> affectedDoors = new IdentityHashMap<>();
|
||||
Set<Object> affectedBlocks = new HashSet<>();
|
||||
List<Key> order = new ArrayList<>();
|
||||
|
||||
unfreezeRegistry();
|
||||
|
||||
int counter = 0;
|
||||
for (Map.Entry<Key, Integer> baseBlockAndItsCount : this.registeredRealBlockSlots.entrySet()) {
|
||||
counter = registerBlockVariants(baseBlockAndItsCount, counter, builder1, builder2, builder3, builder4, affectedBlockSounds, order);
|
||||
for (int i = 0; i < count; i++) {
|
||||
Key customBlockId = BlockManager.createCustomBlockKey(i);
|
||||
DelegatingBlock customBlock;
|
||||
try {
|
||||
customBlock = BlockGenerator.generateBlock(customBlockId);
|
||||
} catch (Throwable t) {
|
||||
CraftEngine.instance().logger().warn("Failed to generate custom block " + customBlockId, t);
|
||||
break;
|
||||
}
|
||||
this.customBlocks[i] = customBlock;
|
||||
try {
|
||||
Object resourceLocation = KeyUtils.toResourceLocation(customBlockId);
|
||||
Object blockHolder = CoreReflections.method$Registry$registerForHolder.invoke(null, MBuiltInRegistries.BLOCK, resourceLocation, customBlock);
|
||||
this.customBlockHolders[i] = blockHolder;
|
||||
CoreReflections.method$Holder$Reference$bindValue.invoke(blockHolder, customBlock);
|
||||
CoreReflections.field$Holder$Reference$tags.set(blockHolder, Set.of());
|
||||
DelegatingBlockState newBlockState = (DelegatingBlockState) FastNMS.INSTANCE.method$Block$defaultState(customBlock);
|
||||
this.customBlockStates[i] = newBlockState;
|
||||
CoreReflections.method$IdMapper$add.invoke(CoreReflections.instance$Block$BLOCK_STATE_REGISTRY, newBlockState);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
CraftEngine.instance().logger().warn("Failed to register custom block " + customBlockId, e);
|
||||
}
|
||||
}
|
||||
|
||||
} finally {
|
||||
freezeRegistry();
|
||||
this.plugin.logger().info("Registered block count: " + counter);
|
||||
this.customBlockCount = counter;
|
||||
this.internalId2StateId = builder1.build();
|
||||
this.stateId2BlockHolder = builder2.build();
|
||||
this.realBlockArranger = builder3.build();
|
||||
this.registeredBlocks = builder4.build();
|
||||
this.blockRegisterOrder = ImmutableList.copyOf(order);
|
||||
if (MCUtils.ceilLog2(BlockStateUtils.vanillaStateSize() + counter) == MCUtils.ceilLog2(BlockStateUtils.vanillaStateSize())) {
|
||||
PalettedContainer.NEED_DOWNGRADE = false;
|
||||
}
|
||||
for (Object block : (Iterable<Object>) MBuiltInRegistries.BLOCK) {
|
||||
Object soundType = CoreReflections.field$BlockBehaviour$soundType.get(block);
|
||||
if (affectedBlockSounds.contains(soundType)) {
|
||||
Object state = FastNMS.INSTANCE.method$Block$defaultState(block);
|
||||
if (BlockStateUtils.isVanillaBlock(state)) {
|
||||
affectedBlocks.add(block);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
affectedBlocks.remove(MBlocks.FIRE);
|
||||
affectedBlocks.remove(MBlocks.SOUL_FIRE);
|
||||
|
||||
this.affectedSoundBlocks = ImmutableSet.copyOf(affectedBlocks);
|
||||
|
||||
ImmutableMap.Builder<Key, Key> soundMapperBuilder = ImmutableMap.builder();
|
||||
for (Object soundType : affectedBlockSounds) {
|
||||
for (Field field : List.of(CoreReflections.field$SoundType$placeSound, CoreReflections.field$SoundType$fallSound, CoreReflections.field$SoundType$hitSound, CoreReflections.field$SoundType$stepSound, CoreReflections.field$SoundType$breakSound)) {
|
||||
Object soundEvent = field.get(soundType);
|
||||
Key previousId = Key.of(FastNMS.INSTANCE.field$SoundEvent$location(soundEvent).toString());
|
||||
soundMapperBuilder.put(previousId, Key.of(previousId.namespace(), "replaced." + previousId.value()));
|
||||
}
|
||||
}
|
||||
|
||||
Predicate<Key> predicate = it -> this.realBlockArranger.containsKey(it);
|
||||
Consumer<Key> soundCallback = s -> soundMapperBuilder.put(s, Key.of("replaced." + s.value()));
|
||||
BiConsumer<Object, Pair<SoundData, SoundData>> affectedBlockCallback = affectedDoors::put;
|
||||
Function<Key, SoundData> soundMapper = (k) -> SoundData.of(k, SoundData.SoundValue.FIXED_1, SoundData.SoundValue.ranged(0.9f, 1f));
|
||||
collectDoorSounds(predicate, Sounds.WOODEN_TRAPDOOR_OPEN, Sounds.WOODEN_TRAPDOOR_CLOSE, Sounds.WOODEN_TRAPDOORS, soundMapper, soundCallback, affectedBlockCallback);
|
||||
collectDoorSounds(predicate, Sounds.NETHER_WOOD_TRAPDOOR_OPEN, Sounds.NETHER_WOOD_TRAPDOOR_CLOSE, Sounds.NETHER_TRAPDOORS, soundMapper, soundCallback, affectedBlockCallback);
|
||||
collectDoorSounds(predicate, Sounds.BAMBOO_WOOD_TRAPDOOR_OPEN, Sounds.BAMBOO_WOOD_TRAPDOOR_CLOSE, Sounds.BAMBOO_TRAPDOORS, soundMapper, soundCallback, affectedBlockCallback);
|
||||
collectDoorSounds(predicate, Sounds.CHERRY_WOOD_TRAPDOOR_OPEN, Sounds.CHERRY_WOOD_TRAPDOOR_CLOSE, Sounds.CHERRY_TRAPDOORS, soundMapper, soundCallback, affectedBlockCallback);
|
||||
collectDoorSounds(predicate, Sounds.COPPER_TRAPDOOR_OPEN, Sounds.COPPER_TRAPDOOR_CLOSE, Sounds.COPPER_TRAPDOORS, soundMapper, soundCallback, affectedBlockCallback);
|
||||
collectDoorSounds(predicate, Sounds.WOODEN_DOOR_OPEN, Sounds.WOODEN_DOOR_CLOSE, Sounds.WOODEN_DOORS, soundMapper, soundCallback, affectedBlockCallback);
|
||||
collectDoorSounds(predicate, Sounds.NETHER_WOOD_DOOR_OPEN, Sounds.NETHER_WOOD_DOOR_CLOSE, Sounds.NETHER_DOORS, soundMapper, soundCallback, affectedBlockCallback);
|
||||
collectDoorSounds(predicate, Sounds.BAMBOO_WOOD_DOOR_OPEN, Sounds.BAMBOO_WOOD_DOOR_CLOSE, Sounds.BAMBOO_DOORS, soundMapper, soundCallback, affectedBlockCallback);
|
||||
collectDoorSounds(predicate, Sounds.CHERRY_WOOD_DOOR_OPEN, Sounds.CHERRY_WOOD_DOOR_CLOSE, Sounds.CHERRY_DOORS, soundMapper, soundCallback, affectedBlockCallback);
|
||||
collectDoorSounds(predicate, Sounds.COPPER_DOOR_OPEN, Sounds.COPPER_DOOR_CLOSE, Sounds.COPPER_DOORS, soundMapper, soundCallback, affectedBlockCallback);
|
||||
collectDoorSounds(predicate, Sounds.WOODEN_FENCE_GATE_OPEN, Sounds.WOODEN_FENCE_GATE_CLOSE, Sounds.WOODEN_FENCE_GATES, soundMapper, soundCallback, affectedBlockCallback);
|
||||
collectDoorSounds(predicate, Sounds.NETHER_WOOD_FENCE_GATE_OPEN, Sounds.NETHER_WOOD_FENCE_GATE_CLOSE, Sounds.NETHER_FENCE_GATES, soundMapper, soundCallback, affectedBlockCallback);
|
||||
collectDoorSounds(predicate, Sounds.BAMBOO_WOOD_FENCE_GATE_OPEN, Sounds.BAMBOO_WOOD_FENCE_GATE_CLOSE, Sounds.BAMBOO_FENCE_GATES, soundMapper, soundCallback, affectedBlockCallback);
|
||||
collectDoorSounds(predicate, Sounds.CHERRY_WOOD_FENCE_GATE_OPEN, Sounds.CHERRY_WOOD_FENCE_GATE_CLOSE, Sounds.CHERRY_FENCE_GATES, soundMapper, soundCallback, affectedBlockCallback);
|
||||
this.affectedOpenableBlockSounds = ImmutableMap.copyOf(affectedDoors);
|
||||
this.soundMapper = soundMapperBuilder.buildKeepingLast();
|
||||
} catch (Throwable e) {
|
||||
plugin.logger().warn("Failed to inject blocks.", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void collectDoorSounds(Predicate<Key> isUsedForCustomBlock,
|
||||
Key openSound,
|
||||
Key closeSound,
|
||||
List<Key> doors,
|
||||
Function<Key, SoundData> soundMapper,
|
||||
Consumer<Key> soundCallback,
|
||||
BiConsumer<Object, Pair<SoundData, SoundData>> affectedBlockCallback) {
|
||||
for (Key d : doors) {
|
||||
if (isUsedForCustomBlock.test(d)) {
|
||||
soundCallback.accept(openSound);
|
||||
soundCallback.accept(closeSound);
|
||||
for (Key door : doors) {
|
||||
Object block = FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.BLOCK, KeyUtils.toResourceLocation(door));
|
||||
if (block != null) {
|
||||
affectedBlockCallback.accept(block, Pair.of(soundMapper.apply(Key.of("replaced." + openSound.value())), soundMapper.apply(Key.of("replaced." + closeSound.value()))));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Object cachedUpdateTagsPacket() {
|
||||
return cachedUpdateTagsPacket;
|
||||
return this.cachedUpdateTagsPacket;
|
||||
}
|
||||
|
||||
private void loadMappingsAndAdditionalBlocks() {
|
||||
this.plugin.logger().info("Loading mappings.yml.");
|
||||
Path mappingsFile = this.plugin.dataFolderPath().resolve("mappings.yml");
|
||||
if (!Files.exists(mappingsFile)) {
|
||||
this.plugin.saveResource("mappings.yml");
|
||||
}
|
||||
Path additionalFile = this.plugin.dataFolderPath().resolve("additional-real-blocks.yml");
|
||||
if (!Files.exists(additionalFile)) {
|
||||
this.plugin.saveResource("additional-real-blocks.yml");
|
||||
}
|
||||
Yaml yaml = new Yaml(new StringKeyConstructor(mappingsFile, new LoaderOptions()));
|
||||
Map<Key, Integer> blockTypeCounter = new LinkedHashMap<>();
|
||||
try (InputStream is = Files.newInputStream(mappingsFile)) {
|
||||
Map<String, String> blockStateMappings = loadBlockStateMappings(yaml.load(is));
|
||||
this.validateBlockStateMappings(mappingsFile, blockStateMappings);
|
||||
Map<Integer, String> stateMap = new Int2ObjectOpenHashMap<>();
|
||||
Map<Integer, Integer> appearanceMapper = new Int2IntOpenHashMap();
|
||||
Map<Key, List<Integer>> appearanceArranger = new HashMap<>();
|
||||
for (Map.Entry<String, String> entry : blockStateMappings.entrySet()) {
|
||||
this.processBlockStateMapping(mappingsFile, entry, stateMap, blockTypeCounter, appearanceMapper, appearanceArranger);
|
||||
}
|
||||
this.blockAppearanceMapper = ImmutableMap.copyOf(appearanceMapper);
|
||||
this.blockAppearanceArranger = ImmutableMap.copyOf(appearanceArranger);
|
||||
this.plugin.logger().info("Freed " + this.blockAppearanceMapper.size() + " block state appearances.");
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to init mappings.yml", e);
|
||||
}
|
||||
try (InputStream is = Files.newInputStream(additionalFile)) {
|
||||
this.registeredRealBlockSlots = this.buildRegisteredRealBlockSlots(blockTypeCounter, yaml.load(is));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to init additional-real-blocks.yml", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void recordVanillaNoteBlocks() {
|
||||
private void markVanillaNoteBlocks() {
|
||||
try {
|
||||
Object resourceLocation = KeyUtils.toResourceLocation(BlockKeys.NOTE_BLOCK);
|
||||
Object block = FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.BLOCK, resourceLocation);
|
||||
Object block = FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.BLOCK, KeyUtils.toResourceLocation(BlockKeys.NOTE_BLOCK));
|
||||
Object stateDefinition = CoreReflections.field$Block$StateDefinition.get(block);
|
||||
@SuppressWarnings("unchecked")
|
||||
ImmutableList<Object> states = (ImmutableList<Object>) CoreReflections.field$StateDefinition$states.get(stateDefinition);
|
||||
for (Object state : states) {
|
||||
BlockStateUtils.CLIENT_SIDE_NOTE_BLOCKS.put(state, new Object());
|
||||
}
|
||||
CLIENT_SIDE_NOTE_BLOCKS.addAll(states);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
plugin.logger().warn("Failed to init vanilla note block", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Key replaceSoundIfExist(Key id) {
|
||||
return this.soundMapper.get(id);
|
||||
}
|
||||
|
||||
public boolean isBlockSoundRemoved(Object block) {
|
||||
return this.affectedSoundBlocks.contains(block);
|
||||
}
|
||||
|
||||
public boolean isOpenableBlockSoundRemoved(Object block) {
|
||||
return this.affectedOpenableBlockSounds.containsKey(block);
|
||||
}
|
||||
|
||||
public SoundData getRemovedOpenableBlockSound(Object block, boolean open) {
|
||||
return open ? this.affectedOpenableBlockSounds.get(block).left() : this.affectedOpenableBlockSounds.get(block).right();
|
||||
}
|
||||
|
||||
private Map<String, String> loadBlockStateMappings(Map<String, Object> mappings) {
|
||||
Map<String, String> blockStateMappings = new LinkedHashMap<>();
|
||||
for (Map.Entry<String, Object> entry : mappings.entrySet()) {
|
||||
if (entry.getValue() instanceof String afterValue) {
|
||||
blockStateMappings.put(entry.getKey(), afterValue);
|
||||
}
|
||||
}
|
||||
return blockStateMappings;
|
||||
}
|
||||
|
||||
private void validateBlockStateMappings(Path mappingFile, Map<String, String> blockStateMappings) {
|
||||
Map<String, String> temp = new HashMap<>(blockStateMappings);
|
||||
for (Map.Entry<String, String> entry : temp.entrySet()) {
|
||||
String state = entry.getValue();
|
||||
if (blockStateMappings.containsKey(state)) {
|
||||
String after = blockStateMappings.remove(state);
|
||||
plugin.logger().warn(mappingFile, "'" + state + ": " + after + "' is invalid because '" + state + "' has already been used as a base block.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void processBlockStateMapping(Path mappingFile,
|
||||
Map.Entry<String, String> entry,
|
||||
Map<Integer, String> stateMap,
|
||||
Map<Key, Integer> counter,
|
||||
Map<Integer, Integer> mapper,
|
||||
Map<Key, List<Integer>> arranger) {
|
||||
Object before = createBlockState(mappingFile, entry.getKey());
|
||||
Object after = createBlockState(mappingFile, entry.getValue());
|
||||
if (before == null || after == null) return;
|
||||
|
||||
int beforeId = BlockStateUtils.blockStateToId(before);
|
||||
int afterId = BlockStateUtils.blockStateToId(after);
|
||||
|
||||
Integer previous = mapper.put(beforeId, afterId);
|
||||
if (previous == null) {
|
||||
Key key = blockOwnerFromString(entry.getKey());
|
||||
counter.compute(key, (k, count) -> count == null ? 1 : count + 1);
|
||||
stateMap.put(beforeId, entry.getKey());
|
||||
stateMap.put(afterId, entry.getValue());
|
||||
arranger.computeIfAbsent(key, (k) -> new IntArrayList()).add(beforeId);
|
||||
} else {
|
||||
String previousState = stateMap.get(previous);
|
||||
plugin.logger().warn(mappingFile, "Duplicate entry: '" + previousState + "' equals '" + entry.getKey() + "'");
|
||||
}
|
||||
}
|
||||
|
||||
private Key blockOwnerFromString(String stateString) {
|
||||
int index = stateString.indexOf('[');
|
||||
if (index == -1) {
|
||||
return Key.of(stateString);
|
||||
} else {
|
||||
return Key.of(stateString.substring(0, index));
|
||||
}
|
||||
}
|
||||
|
||||
private Object createBlockState(Path mappingFile, String state) {
|
||||
try {
|
||||
Object registryOrLookUp = MBuiltInRegistries.BLOCK;
|
||||
if (CoreReflections.method$Registry$asLookup != null) {
|
||||
registryOrLookUp = CoreReflections.method$Registry$asLookup.invoke(registryOrLookUp);
|
||||
}
|
||||
Object result = CoreReflections.method$BlockStateParser$parseForBlock.invoke(null, registryOrLookUp, state, false);
|
||||
return CoreReflections.method$BlockStateParser$BlockResult$blockState.invoke(result);
|
||||
} catch (Exception e) {
|
||||
this.plugin.logger().warn(mappingFile, "'" + state + "' is not a valid block state.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private LinkedHashMap<Key, Integer> buildRegisteredRealBlockSlots(Map<Key, Integer> counter, Map<String, Object> additionalYaml) {
|
||||
LinkedHashMap<Key, Integer> map = new LinkedHashMap<>(counter);
|
||||
for (Map.Entry<String, Object> entry : additionalYaml.entrySet()) {
|
||||
Key blockType = Key.of(entry.getKey());
|
||||
if (entry.getValue() instanceof Integer i) {
|
||||
int previous = map.getOrDefault(blockType, 0);
|
||||
if (previous == 0) {
|
||||
map.put(blockType, i);
|
||||
this.plugin.logger().info("Loaded " + blockType + " with " + i + " real block states");
|
||||
} else {
|
||||
map.put(blockType, i + previous);
|
||||
this.plugin.logger().info("Loaded " + blockType + " with " + previous + " appearances and " + (i + previous) + " real block states");
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private void unfreezeRegistry() throws IllegalAccessException {
|
||||
CoreReflections.field$MappedRegistry$frozen.set(MBuiltInRegistries.BLOCK, false);
|
||||
CoreReflections.field$MappedRegistry$unregisteredIntrusiveHolders.set(MBuiltInRegistries.BLOCK, new IdentityHashMap<>());
|
||||
}
|
||||
|
||||
private void freezeRegistry() throws IllegalAccessException {
|
||||
CoreReflections.field$MappedRegistry$frozen.set(MBuiltInRegistries.BLOCK, true);
|
||||
}
|
||||
|
||||
private int registerBlockVariants(Map.Entry<Key, Integer> blockWithCount,
|
||||
int counter,
|
||||
ImmutableMap.Builder<Key, Integer> builder1,
|
||||
ImmutableMap.Builder<Integer, Object> builder2,
|
||||
ImmutableMap.Builder<Key, List<Integer>> builder3,
|
||||
ImmutableMap.Builder<Key, DelegatingBlock> builder4,
|
||||
Set<Object> affectSoundTypes,
|
||||
List<Key> order) throws Exception {
|
||||
Key clientSideBlockType = blockWithCount.getKey();
|
||||
boolean isNoteBlock = clientSideBlockType.equals(BlockKeys.NOTE_BLOCK);
|
||||
Object clientSideBlock = getBlockFromRegistry(createResourceLocation(clientSideBlockType));
|
||||
int amount = blockWithCount.getValue();
|
||||
|
||||
List<Integer> stateIds = new IntArrayList();
|
||||
|
||||
for (int i = 0; i < amount; i++) {
|
||||
Key realBlockKey = createRealBlockKey(clientSideBlockType, i);
|
||||
Object blockProperties = createBlockProperties(realBlockKey);
|
||||
|
||||
Object newRealBlock;
|
||||
Object newBlockState;
|
||||
Object blockHolder;
|
||||
Object resourceLocation = createResourceLocation(realBlockKey);
|
||||
|
||||
try {
|
||||
newRealBlock = BlockGenerator.generateBlock(clientSideBlockType, clientSideBlock, blockProperties);
|
||||
} catch (Throwable throwable) {
|
||||
this.plugin.logger().warn("Failed to generate dynamic block class", throwable);
|
||||
continue;
|
||||
}
|
||||
|
||||
blockHolder = CoreReflections.method$Registry$registerForHolder.invoke(null, MBuiltInRegistries.BLOCK, resourceLocation, newRealBlock);
|
||||
CoreReflections.method$Holder$Reference$bindValue.invoke(blockHolder, newRealBlock);
|
||||
CoreReflections.field$Holder$Reference$tags.set(blockHolder, Set.of());
|
||||
|
||||
newBlockState = FastNMS.INSTANCE.method$Block$defaultState(newRealBlock);
|
||||
CoreReflections.method$IdMapper$add.invoke(CoreReflections.instance$Block$BLOCK_STATE_REGISTRY, newBlockState);
|
||||
|
||||
if (isNoteBlock) {
|
||||
BlockStateUtils.CLIENT_SIDE_NOTE_BLOCKS.put(newBlockState, new Object());
|
||||
}
|
||||
|
||||
int stateId = BlockStateUtils.vanillaStateSize() + counter;
|
||||
|
||||
builder1.put(realBlockKey, stateId);
|
||||
builder2.put(stateId, blockHolder);
|
||||
builder4.put(realBlockKey, (DelegatingBlock) newRealBlock);
|
||||
stateIds.add(stateId);
|
||||
|
||||
this.blocksToDeceive.add(Tuple.of(newRealBlock, clientSideBlockType, isNoteBlock));
|
||||
order.add(realBlockKey);
|
||||
counter++;
|
||||
}
|
||||
|
||||
builder3.put(clientSideBlockType, stateIds);
|
||||
Object soundType = CoreReflections.field$BlockBehaviour$soundType.get(clientSideBlock);
|
||||
affectSoundTypes.add(soundType);
|
||||
return counter;
|
||||
}
|
||||
|
||||
private Object createResourceLocation(Key key) {
|
||||
return FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath(key.namespace(), key.value());
|
||||
}
|
||||
|
||||
private Object getBlockFromRegistry(Object resourceLocation) {
|
||||
return FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.BLOCK, resourceLocation);
|
||||
}
|
||||
|
||||
private Key createRealBlockKey(Key replacedBlock, int index) {
|
||||
return Key.of(Key.DEFAULT_NAMESPACE, replacedBlock.value() + "_" + index);
|
||||
}
|
||||
|
||||
private Object createBlockProperties(Key realBlockKey) throws Exception {
|
||||
Object blockProperties = CoreReflections.method$BlockBehaviour$Properties$of.invoke(null);
|
||||
Object realBlockResourceLocation = createResourceLocation(realBlockKey);
|
||||
Object realBlockResourceKey = CoreReflections.method$ResourceKey$create.invoke(null, MRegistries.BLOCK, realBlockResourceLocation);
|
||||
if (CoreReflections.field$BlockBehaviour$Properties$id != null) {
|
||||
CoreReflections.field$BlockBehaviour$Properties$id.set(blockProperties, realBlockResourceKey);
|
||||
}
|
||||
return blockProperties;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void deceiveBukkit() {
|
||||
try {
|
||||
Map<Object, Material> magicMap = (Map<Object, Material>) CraftBukkitReflections.field$CraftMagicNumbers$BLOCK_MATERIAL.get(null);
|
||||
Map<Material, Object> factories = (Map<Material, Object>) CraftBukkitReflections.field$CraftBlockStates$FACTORIES.get(null);
|
||||
for (Tuple<Object, Key, Boolean> tuple : this.blocksToDeceive) {
|
||||
deceiveBukkit(tuple.left(), tuple.mid(), tuple.right(), magicMap, factories);
|
||||
}
|
||||
this.blocksToDeceive.clear();
|
||||
} catch (ReflectiveOperationException e) {
|
||||
this.plugin.logger().warn("Failed to deceive bukkit", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void deceiveBukkit(Object newBlock, Key replacedBlock, boolean isNoteBlock, Map<Object, Material> magicMap, Map<Material, Object> factories) {
|
||||
if (isNoteBlock) {
|
||||
magicMap.put(newBlock, Material.STONE);
|
||||
} else {
|
||||
Material material = org.bukkit.Registry.MATERIAL.get(new NamespacedKey(replacedBlock.namespace(), replacedBlock.value()));
|
||||
if (CraftBukkitReflections.clazz$CraftBlockStates$BlockEntityStateFactory.isInstance(factories.get(material))) {
|
||||
magicMap.put(newBlock, Material.STONE);
|
||||
} else {
|
||||
magicMap.put(newBlock, material);
|
||||
}
|
||||
this.plugin.logger().warn("Failed to init vanilla note block", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getBlockRegistryId(Key id) {
|
||||
protected void setVanillaBlockTags(Key id, List<String> tags) {
|
||||
Object block = FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.BLOCK, KeyUtils.toResourceLocation(id));
|
||||
return FastNMS.INSTANCE.method$IdMap$getId(MBuiltInRegistries.BLOCK, block).orElseThrow(() -> new IllegalStateException("Block " + id + " not found"));
|
||||
this.clientBoundTags.put(FastNMS.INSTANCE.method$IdMap$getId(MBuiltInRegistries.BLOCK, block).orElseThrow(() -> new IllegalStateException("Block " + id + " not found")), tags);
|
||||
}
|
||||
|
||||
public boolean isPlaceSoundMissing(Object sound) {
|
||||
return this.missingPlaceSounds.contains(sound);
|
||||
}
|
||||
|
||||
public boolean isBreakSoundMissing(Object sound) {
|
||||
return this.missingBreakSounds.contains(sound);
|
||||
}
|
||||
|
||||
public boolean isHitSoundMissing(Object sound) {
|
||||
return this.missingHitSounds.contains(sound);
|
||||
}
|
||||
|
||||
public boolean isStepSoundMissing(Object sound) {
|
||||
return this.missingStepSounds.contains(sound);
|
||||
}
|
||||
|
||||
public boolean isInteractSoundMissing(Key blockType) {
|
||||
return this.missingInteractSoundBlocks.contains(blockType);
|
||||
}
|
||||
|
||||
private void unfreezeRegistry() {
|
||||
try {
|
||||
CoreReflections.field$MappedRegistry$frozen.set(MBuiltInRegistries.BLOCK, false);
|
||||
CoreReflections.field$MappedRegistry$unregisteredIntrusiveHolders.set(MBuiltInRegistries.BLOCK, new IdentityHashMap<>());
|
||||
} catch (IllegalAccessException e) {
|
||||
this.plugin.logger().warn("Failed to unfreeze block registry", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void freezeRegistry() {
|
||||
try {
|
||||
CoreReflections.field$MappedRegistry$frozen.set(MBuiltInRegistries.BLOCK, true);
|
||||
} catch (IllegalAccessException e) {
|
||||
this.plugin.logger().warn("Failed to freeze block registry", e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void deceiveBukkitRegistry() {
|
||||
try {
|
||||
Map<Object, Material> magicMap = (Map<Object, Material>) CraftBukkitReflections.field$CraftMagicNumbers$BLOCK_MATERIAL.get(null);
|
||||
for (DelegatingBlock customBlock : this.customBlocks) {
|
||||
magicMap.put(customBlock, Material.STONE);
|
||||
}
|
||||
} catch (ReflectiveOperationException e) {
|
||||
this.plugin.logger().warn("Failed to deceive bukkit magic blocks", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isVanillaBlock(Key id) {
|
||||
if (!id.namespace().equals("minecraft")) {
|
||||
if (!id.namespace().equals("minecraft"))
|
||||
return false;
|
||||
}
|
||||
if (id.value().equals("air")) {
|
||||
if (id.value().equals("air"))
|
||||
return true;
|
||||
}
|
||||
return FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.BLOCK, KeyUtils.toResourceLocation(id)) != MBlocks.AIR;
|
||||
}
|
||||
|
||||
public boolean isBurnable(Object blockState) {
|
||||
Object blockOwner = BlockStateUtils.getBlockOwner(blockState);
|
||||
return this.igniteOdds.getOrDefault(blockOwner, 0) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int vanillaBlockStateCount() {
|
||||
return this.vanillaBlockStateCount;
|
||||
}
|
||||
|
||||
@SuppressWarnings("DuplicatedCode")
|
||||
@Override
|
||||
protected void processSounds() {
|
||||
Set<Object> affectedBlockSoundTypes = new HashSet<>();
|
||||
for (BlockStateWrapper vanillaBlockState : super.tempVisualBlockStatesInUse) {
|
||||
affectedBlockSoundTypes.add(FastNMS.INSTANCE.method$BlockBehaviour$BlockStateBase$getSoundType(vanillaBlockState.literalObject()));
|
||||
}
|
||||
|
||||
Set<Object> placeSounds = new HashSet<>();
|
||||
Set<Object> breakSounds = new HashSet<>();
|
||||
Set<Object> stepSounds = new HashSet<>();
|
||||
Set<Object> hitSounds = new HashSet<>();
|
||||
|
||||
for (Object soundType : affectedBlockSoundTypes) {
|
||||
placeSounds.add(FastNMS.INSTANCE.field$SoundEvent$location(FastNMS.INSTANCE.field$SoundType$placeSound(soundType)));
|
||||
breakSounds.add(FastNMS.INSTANCE.field$SoundEvent$location(FastNMS.INSTANCE.field$SoundType$breakSound(soundType)));
|
||||
stepSounds.add(FastNMS.INSTANCE.field$SoundEvent$location(FastNMS.INSTANCE.field$SoundType$stepSound(soundType)));
|
||||
hitSounds.add(FastNMS.INSTANCE.field$SoundEvent$location(FastNMS.INSTANCE.field$SoundType$hitSound(soundType)));
|
||||
}
|
||||
|
||||
ImmutableMap.Builder<Key, Key> soundReplacementBuilder = ImmutableMap.builder();
|
||||
for (Object soundId : placeSounds) {
|
||||
Key previousId = KeyUtils.resourceLocationToKey(soundId);
|
||||
soundReplacementBuilder.put(previousId, Key.of(previousId.namespace(), "replaced." + previousId.value()));
|
||||
}
|
||||
for (Object soundId : breakSounds) {
|
||||
Key previousId = KeyUtils.resourceLocationToKey(soundId);
|
||||
soundReplacementBuilder.put(previousId, Key.of(previousId.namespace(), "replaced." + previousId.value()));
|
||||
}
|
||||
for (Object soundId : stepSounds) {
|
||||
Key previousId = KeyUtils.resourceLocationToKey(soundId);
|
||||
soundReplacementBuilder.put(previousId, Key.of(previousId.namespace(), "replaced." + previousId.value()));
|
||||
}
|
||||
for (Object soundId : hitSounds) {
|
||||
Key previousId = KeyUtils.resourceLocationToKey(soundId);
|
||||
soundReplacementBuilder.put(previousId, Key.of(previousId.namespace(), "replaced." + previousId.value()));
|
||||
}
|
||||
|
||||
this.missingPlaceSounds = placeSounds;
|
||||
this.missingBreakSounds = breakSounds;
|
||||
this.missingHitSounds = hitSounds;
|
||||
this.missingStepSounds = stepSounds;
|
||||
|
||||
Set<Key> missingInteractSoundBlocks = new HashSet<>();
|
||||
|
||||
for (SoundSet soundSet : SoundSet.getAllSoundSets()) {
|
||||
for (Key block : soundSet.blocks()) {
|
||||
if (super.tempVisualBlocksInUse.contains(block)) {
|
||||
Key openSound = soundSet.openSound();
|
||||
soundReplacementBuilder.put(openSound, Key.of(openSound.namespace(), "replaced." + openSound.value()));
|
||||
Key closeSound = soundSet.closeSound();
|
||||
soundReplacementBuilder.put(closeSound, Key.of(closeSound.namespace(), "replaced." + closeSound.value()));
|
||||
missingInteractSoundBlocks.addAll(soundSet.blocks());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.missingInteractSoundBlocks = missingInteractSoundBlocks;
|
||||
this.soundReplacements = soundReplacementBuilder.buildKeepingLast();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CustomBlock createCustomBlock(@NotNull Holder.Reference<CustomBlock> holder,
|
||||
@NotNull BlockStateVariantProvider variantProvider,
|
||||
@NotNull Map<EventTrigger, List<Function<PlayerOptionalContext>>> events,
|
||||
@Nullable LootTable<?> lootTable) {
|
||||
return new BukkitCustomBlock(holder, variantProvider, events, lootTable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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, BlockStateAppearance> 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 visual block state for " + immutableBlockState + ". This might cause errors!");
|
||||
continue;
|
||||
} else if (immutableBlockState.customBlockState() == null) {
|
||||
CraftEngine.instance().logger().warn("Could not find real block state 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, BlockStateAppearance> 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, BlockStateAppearance> 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,62 @@
|
||||
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.BlockStateWrapper;
|
||||
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 BlockStateWrapper withProperty(String propertyName, String propertyValue) {
|
||||
Optional<ImmutableBlockState> immutableBlockState = getImmutableBlockState();
|
||||
if (immutableBlockState.isPresent()) {
|
||||
Property<?> property = immutableBlockState.get().owner().value().getProperty(propertyName);
|
||||
if (property != null) {
|
||||
Comparable<?> value = property.valueByName(propertyValue);
|
||||
if (value != null) {
|
||||
return ImmutableBlockState.with(immutableBlockState.get(), property, value).customBlockState();
|
||||
}
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@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,45 @@
|
||||
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.BlockRegistryMirror;
|
||||
import net.momirealms.craftengine.core.block.BlockStateWrapper;
|
||||
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();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockStateWrapper withProperty(String propertyName, String propertyValue) {
|
||||
Object newState = this.accessor.withProperty(propertyName, propertyValue);
|
||||
if (newState == super.blockState) return this;
|
||||
return BlockRegistryMirror.byId(BlockStateUtils.blockStateToId(newState));
|
||||
}
|
||||
}
|
||||
@@ -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,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,13 @@ public class BukkitBlockBehaviors extends BlockBehaviors {
|
||||
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);
|
||||
@@ -70,5 +77,12 @@ public class BukkitBlockBehaviors extends BlockBehaviors {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,37 +1,67 @@
|
||||
package net.momirealms.craftengine.bukkit.block.behavior;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
|
||||
import net.momirealms.craftengine.bukkit.plugin.reflection.bukkit.CraftBukkitReflections;
|
||||
import net.momirealms.craftengine.core.block.*;
|
||||
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
|
||||
import net.momirealms.craftengine.core.block.BlockBehavior;
|
||||
import net.momirealms.craftengine.core.block.BlockStateWrapper;
|
||||
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.util.Key;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.util.LazyReference;
|
||||
import net.momirealms.craftengine.core.util.MiscUtils;
|
||||
import net.momirealms.craftengine.core.util.RandomUtils;
|
||||
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
|
||||
import net.momirealms.sparrow.nbt.CompoundTag;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
public class ChangeOverTimeBlockBehavior extends BukkitBlockBehavior {
|
||||
public static final Factory FACTORY = new Factory();
|
||||
private final float changeSpeed;
|
||||
private final Key nextBlock;
|
||||
private final String nextBlock;
|
||||
private final LazyReference<BlockStateWrapper> lazyState;
|
||||
private final List<String> excludedProperties;
|
||||
|
||||
public ChangeOverTimeBlockBehavior(CustomBlock customBlock, float changeSpeed, Key nextBlock) {
|
||||
public ChangeOverTimeBlockBehavior(CustomBlock customBlock, float changeSpeed, String nextBlock, List<String> excludedProperties) {
|
||||
super(customBlock);
|
||||
this.changeSpeed = changeSpeed;
|
||||
this.nextBlock = nextBlock;
|
||||
this.excludedProperties = excludedProperties;
|
||||
this.lazyState = LazyReference.lazyReference(() -> CraftEngine.instance().blockManager().createBlockState(this.nextBlock));
|
||||
}
|
||||
|
||||
public String nextBlock() {
|
||||
return nextBlock;
|
||||
}
|
||||
|
||||
public BlockStateWrapper nextState() {
|
||||
return this.lazyState.get();
|
||||
}
|
||||
|
||||
public CompoundTag filter(CompoundTag properties) {
|
||||
for (String property : this.excludedProperties) {
|
||||
properties.remove(property);
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void randomTick(Object thisBlock, Object[] args, Callable<Object> superMethod) throws ReflectiveOperationException {
|
||||
public void randomTick(Object thisBlock, Object[] args, Callable<Object> superMethod) {
|
||||
if (RandomUtils.generateRandomFloat(0F, 1F) >= this.changeSpeed) return;
|
||||
Optional<Object> nextState = BukkitBlockManager.instance().blockById(this.nextBlock)
|
||||
.map(CustomBlock::defaultState)
|
||||
.map(ImmutableBlockState::customBlockState)
|
||||
.map(BlockStateWrapper::literalObject);
|
||||
if (nextState.isEmpty()) return;
|
||||
CraftBukkitReflections.method$CraftEventFactory$handleBlockFormEvent.invoke(null, args[1], args[2], nextState.get(), UpdateOption.UPDATE_ALL.flags());
|
||||
Object blockState = args[0];
|
||||
BlockStateUtils.getOptionalCustomBlockState(blockState).ifPresent(state -> {
|
||||
BlockStateWrapper nextState = this.nextState();
|
||||
if (nextState == null) return;
|
||||
nextState = nextState.withProperties(filter(state.propertiesNbt()));
|
||||
try {
|
||||
CraftBukkitReflections.method$CraftEventFactory$handleBlockFormEvent.invoke(null, args[1], args[2], nextState.literalObject(), UpdateOption.UPDATE_ALL.flags());
|
||||
} catch (ReflectiveOperationException e) {
|
||||
CraftEngine.instance().logger().warn("Failed to call block form event", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static class Factory implements BlockBehaviorFactory {
|
||||
@@ -39,8 +69,9 @@ public class ChangeOverTimeBlockBehavior extends BukkitBlockBehavior {
|
||||
@Override
|
||||
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
|
||||
float changeSpeed = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("change-speed", 0.05688889F), "change-speed");
|
||||
Key nextBlock = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.getOrDefault("next-block", "minecraft:air"), "warning.config.block.behavior.change_over_time.missing_next_block"));
|
||||
return new ChangeOverTimeBlockBehavior(block, changeSpeed, nextBlock);
|
||||
String nextBlock = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.getOrDefault("next-block", "minecraft:air"), "warning.config.block.behavior.change_over_time.missing_next_block");
|
||||
List<String> excludedProperties = MiscUtils.getAsStringList(arguments.get("excluded-properties"));
|
||||
return new ChangeOverTimeBlockBehavior(block, changeSpeed, nextBlock, excludedProperties);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ 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;
|
||||
@@ -13,24 +14,40 @@ import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
|
||||
import net.momirealms.craftengine.core.block.properties.Property;
|
||||
import net.momirealms.craftengine.core.item.context.BlockPlaceContext;
|
||||
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
|
||||
import net.momirealms.craftengine.core.util.Direction;
|
||||
import net.momirealms.craftengine.core.util.HorizontalDirection;
|
||||
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
|
||||
import net.momirealms.craftengine.core.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.Map;
|
||||
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) {
|
||||
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
|
||||
@@ -65,7 +82,31 @@ public class DirectionalAttachedBlockBehavior extends BukkitBlockBehavior {
|
||||
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);
|
||||
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")
|
||||
@@ -95,13 +136,42 @@ public class DirectionalAttachedBlockBehavior extends BukkitBlockBehavior {
|
||||
|
||||
@Override
|
||||
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
|
||||
Property<?> facing = ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("facing"), "warning.config.block.behavior.surface_attached.missing_facing");
|
||||
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.surface_attached.missing_facing");
|
||||
throw new LocalizedResourceConfigException("warning.config.block.behavior.directional_attached.missing_facing");
|
||||
}
|
||||
return new DirectionalAttachedBlockBehavior(block, facing, isDirection);
|
||||
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,9 @@
|
||||
package net.momirealms.craftengine.bukkit.block.behavior;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.api.BukkitAdaptors;
|
||||
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
|
||||
import net.momirealms.craftengine.bukkit.entity.BukkitEntity;
|
||||
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,6 +13,7 @@ import net.momirealms.craftengine.core.block.BlockBehavior;
|
||||
import net.momirealms.craftengine.core.block.CustomBlock;
|
||||
import net.momirealms.craftengine.core.block.ImmutableBlockState;
|
||||
import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
|
||||
import net.momirealms.craftengine.core.sound.SoundData;
|
||||
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
|
||||
import net.momirealms.craftengine.core.world.Vec3d;
|
||||
import net.momirealms.craftengine.core.world.WorldPosition;
|
||||
@@ -22,11 +26,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
|
||||
@@ -74,42 +82,52 @@ public class FallingBlockBehavior extends BukkitBlockBehavior {
|
||||
public void onBrokenAfterFall(Object thisBlock, Object[] args) throws Exception {
|
||||
Object level = args[0];
|
||||
Object fallingBlockEntity = args[2];
|
||||
Object entityData = CoreReflections.field$Entity$entityData.get(fallingBlockEntity);
|
||||
boolean isSilent = (boolean) CoreReflections.method$SynchedEntityData$get.invoke(entityData, CoreReflections.instance$Entity$DATA_SILENT);
|
||||
if (!isSilent) {
|
||||
BukkitEntity entity = BukkitAdaptors.adapt(FastNMS.INSTANCE.method$Entity$getBukkitEntity(fallingBlockEntity));
|
||||
if (!entity.getEntityData(BaseEntityData.Silent)) {
|
||||
Object blockState = CoreReflections.field$FallingBlockEntity$blockState.get(fallingBlockEntity);
|
||||
Optional<ImmutableBlockState> optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(blockState);
|
||||
if (optionalCustomState.isEmpty()) return;
|
||||
ImmutableBlockState customState = optionalCustomState.get();
|
||||
net.momirealms.craftengine.core.world.World world = new BukkitWorld(FastNMS.INSTANCE.method$Level$getCraftWorld(level));
|
||||
WorldPosition position = new WorldPosition(world, CoreReflections.field$Entity$xo.getDouble(fallingBlockEntity), CoreReflections.field$Entity$yo.getDouble(fallingBlockEntity), CoreReflections.field$Entity$zo.getDouble(fallingBlockEntity));
|
||||
world.playBlockSound(position, customState.settings().sounds().destroySound());
|
||||
if (this.destroySound != null) {
|
||||
world.playBlockSound(position, this.destroySound);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLand(Object thisBlock, Object[] args) throws Exception {
|
||||
public void onLand(Object thisBlock, Object[] args) {
|
||||
Object fallingBlock = args[4];
|
||||
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);
|
||||
BukkitEntity entity = BukkitAdaptors.adapt(FastNMS.INSTANCE.method$Entity$getBukkitEntity(fallingBlock));
|
||||
Object blockState = args[2];
|
||||
int stateId = BlockStateUtils.blockStateToId(blockState);
|
||||
ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(stateId);
|
||||
if (immutableBlockState == null || immutableBlockState.isEmpty()) return;
|
||||
if (!isSilent) {
|
||||
if (!entity.getEntityData(BaseEntityData.Silent)) {
|
||||
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,153 @@
|
||||
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.BlockBehavior;
|
||||
import net.momirealms.craftengine.core.block.BlockStateWrapper;
|
||||
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.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(ImmutableBlockState::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
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -48,7 +48,7 @@ public class SimpleParticleBlockBehavior extends BukkitBlockBehavior implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends BlockEntity> BlockEntityTicker<T> createBlockEntityTicker(CEWorld level, ImmutableBlockState state, BlockEntityType<T> blockEntityType) {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ import net.momirealms.craftengine.core.item.context.UseOnContext;
|
||||
import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext;
|
||||
import net.momirealms.craftengine.core.sound.SoundData;
|
||||
import net.momirealms.craftengine.core.util.AdventureHelper;
|
||||
import net.momirealms.craftengine.core.util.MCUtils;
|
||||
import net.momirealms.craftengine.core.util.MiscUtils;
|
||||
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
|
||||
import net.momirealms.craftengine.core.world.BlockPos;
|
||||
import net.momirealms.craftengine.core.world.CEWorld;
|
||||
@@ -166,7 +166,7 @@ public class SimpleStorageBlockBehavior extends BukkitBlockBehavior implements E
|
||||
}
|
||||
}
|
||||
signal /= (float) inventory.getSize();
|
||||
return MCUtils.lerpDiscrete(signal, 0, 15);
|
||||
return MiscUtils.lerpDiscrete(signal, 0, 15);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
@@ -194,7 +194,7 @@ public class SimpleStorageBlockBehavior extends BukkitBlockBehavior implements E
|
||||
@Override
|
||||
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
|
||||
String title = arguments.getOrDefault("title", "").toString();
|
||||
int rows = MCUtils.clamp(ResourceConfigUtils.getAsInt(arguments.getOrDefault("rows", 1), "rows"), 1, 6);
|
||||
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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.SofaShape;
|
||||
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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +1,53 @@
|
||||
package net.momirealms.craftengine.bukkit.block.behavior;
|
||||
|
||||
import net.momirealms.craftengine.core.block.BlockBehavior;
|
||||
import net.momirealms.craftengine.core.block.BlockStateWrapper;
|
||||
import net.momirealms.craftengine.core.block.CustomBlock;
|
||||
import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.util.LazyReference;
|
||||
import net.momirealms.craftengine.core.util.MiscUtils;
|
||||
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
|
||||
import net.momirealms.sparrow.nbt.CompoundTag;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class StrippableBlockBehavior extends BukkitBlockBehavior {
|
||||
public static final Factory FACTORY = new Factory();
|
||||
private final Key stripped;
|
||||
private final String stripped;
|
||||
private final LazyReference<BlockStateWrapper> lazyState;
|
||||
private final List<String> excludedProperties;
|
||||
|
||||
public StrippableBlockBehavior(CustomBlock block, Key stripped) {
|
||||
public StrippableBlockBehavior(CustomBlock block, String stripped, List<String> excludedProperties) {
|
||||
super(block);
|
||||
this.stripped = stripped;
|
||||
this.lazyState = LazyReference.lazyReference(() -> CraftEngine.instance().blockManager().createBlockState(this.stripped));
|
||||
this.excludedProperties = excludedProperties;
|
||||
}
|
||||
|
||||
public Key stripped() {
|
||||
public String stripped() {
|
||||
return this.stripped;
|
||||
}
|
||||
|
||||
public BlockStateWrapper strippedState() {
|
||||
return this.lazyState.get();
|
||||
}
|
||||
|
||||
public CompoundTag filter(CompoundTag properties) {
|
||||
for (String property : this.excludedProperties) {
|
||||
properties.remove(property);
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
public static class Factory implements BlockBehaviorFactory {
|
||||
|
||||
@Override
|
||||
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
|
||||
String stripped = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("stripped"), "warning.config.block.behavior.strippable.missing_stripped");
|
||||
return new StrippableBlockBehavior(block, Key.of(stripped));
|
||||
List<String> excludedProperties = MiscUtils.getAsStringList(arguments.get("excluded-properties"));
|
||||
return new StrippableBlockBehavior(block, stripped, excludedProperties);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ public class ToggleableLampBlockBehavior extends BukkitBlockBehavior {
|
||||
public static class Factory implements BlockBehaviorFactory {
|
||||
@Override
|
||||
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
|
||||
boolean canOpenWithHand = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-open-with-hand", false), "can-open-with-hand");
|
||||
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;
|
||||
|
||||
@@ -381,4 +381,18 @@ public class UnsafeCompositeBlockBehavior extends BukkitBlockBehavior
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ public class WallTorchParticleBlockBehavior extends BukkitBlockBehavior implemen
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends BlockEntity> BlockEntityTicker<T> createBlockEntityTicker(CEWorld level, ImmutableBlockState state, BlockEntityType<T> blockEntityType) {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import net.momirealms.craftengine.core.block.ImmutableBlockState;
|
||||
import net.momirealms.craftengine.core.plugin.context.Context;
|
||||
import net.momirealms.craftengine.core.plugin.context.ContextHolder;
|
||||
import net.momirealms.craftengine.core.plugin.context.SimpleContext;
|
||||
import net.momirealms.craftengine.core.util.Direction;
|
||||
import net.momirealms.craftengine.core.util.HorizontalDirection;
|
||||
import net.momirealms.craftengine.core.world.BlockPos;
|
||||
import net.momirealms.craftengine.core.world.CEWorld;
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package net.momirealms.craftengine.bukkit.entity;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.util.EntityUtils;
|
||||
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
|
||||
import net.momirealms.craftengine.core.entity.AbstractEntity;
|
||||
import net.momirealms.craftengine.core.entity.data.EntityData;
|
||||
import net.momirealms.craftengine.core.util.Direction;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.world.World;
|
||||
@@ -19,17 +21,17 @@ public class BukkitEntity extends AbstractEntity {
|
||||
|
||||
@Override
|
||||
public double x() {
|
||||
return literalObject().getX();
|
||||
return platformEntity().getX();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double y() {
|
||||
return literalObject().getY();
|
||||
return platformEntity().getY();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double z() {
|
||||
return literalObject().getZ();
|
||||
return platformEntity().getZ();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -38,22 +40,22 @@ public class BukkitEntity extends AbstractEntity {
|
||||
|
||||
@Override
|
||||
public int entityID() {
|
||||
return literalObject().getEntityId();
|
||||
return platformEntity().getEntityId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float xRot() {
|
||||
return literalObject().getYaw();
|
||||
return platformEntity().getYaw();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float yRot() {
|
||||
return literalObject().getPitch();
|
||||
return platformEntity().getPitch();
|
||||
}
|
||||
|
||||
@Override
|
||||
public World world() {
|
||||
return new BukkitWorld(literalObject().getWorld());
|
||||
return new BukkitWorld(platformEntity().getWorld());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -62,22 +64,43 @@ public class BukkitEntity extends AbstractEntity {
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.bukkit.entity.Entity literalObject() {
|
||||
public org.bukkit.entity.Entity platformEntity() {
|
||||
return this.entity.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object serverEntity() {
|
||||
return FastNMS.INSTANCE.method$CraftEntity$getHandle(platformEntity());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key type() {
|
||||
return EntityUtils.getEntityType(literalObject());
|
||||
return EntityUtils.getEntityType(platformEntity());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return literalObject().getName();
|
||||
return platformEntity().getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID uuid() {
|
||||
return literalObject().getUniqueId();
|
||||
return platformEntity().getUniqueId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object entityData() {
|
||||
return FastNMS.INSTANCE.field$Entity$entityData(serverEntity());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> T getEntityData(EntityData<T> data) {
|
||||
return (T) FastNMS.INSTANCE.method$SynchedEntityData$get(entityData(), data.entityDataAccessor());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void setEntityData(EntityData<T> data, T value, boolean force) {
|
||||
FastNMS.INSTANCE.method$SynchedEntityData$set(entityData(), data.entityDataAccessor(), value, force);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflect
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class BaseEntityData<T> extends SimpleEntityData<T> {
|
||||
public class BaseEntityData<T> extends BukkitEntityData<T> {
|
||||
public static final BaseEntityData<Byte> SharedFlags = new BaseEntityData<>(BaseEntityData.class, EntityDataValue.Serializers$BYTE, (byte) 0);
|
||||
public static final BaseEntityData<Integer> AirSupply = new BaseEntityData<>(BaseEntityData.class, EntityDataValue.Serializers$INT, 300);
|
||||
public static final BaseEntityData<Optional<Object>> CustomName = new BaseEntityData<>(BaseEntityData.class, EntityDataValue.Serializers$OPTIONAL_COMPONENT, Optional.empty());
|
||||
|
||||
@@ -2,15 +2,16 @@ package net.momirealms.craftengine.bukkit.entity.data;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.core.entity.data.ClassTreeIdRegistry;
|
||||
import net.momirealms.craftengine.core.entity.data.EntityData;
|
||||
|
||||
public class SimpleEntityData<T> implements EntityData<T> {
|
||||
public class BukkitEntityData<T> implements EntityData<T> {
|
||||
public static final ClassTreeIdRegistry ID_REGISTRY = new ClassTreeIdRegistry();
|
||||
private final int id;
|
||||
private final Object serializer;
|
||||
private final T defaultValue;
|
||||
private final Object entityDataAccessor;
|
||||
|
||||
public SimpleEntityData(Class<?> clazz, Object serializer, T defaultValue) {
|
||||
public BukkitEntityData(Class<?> clazz, Object serializer, T defaultValue) {
|
||||
this.id = ID_REGISTRY.define(clazz);
|
||||
this.serializer = serializer;
|
||||
this.defaultValue = defaultValue;
|
||||
@@ -36,4 +37,9 @@ public class SimpleEntityData<T> implements EntityData<T> {
|
||||
public Object entityDataAccessor() {
|
||||
return entityDataAccessor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object create(Object entityDataAccessor, Object value) {
|
||||
return EntityDataValue.create(entityDataAccessor, value);
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
@@ -98,7 +99,7 @@ public class EntityDataValue {
|
||||
throw new IllegalAccessError("Utility class");
|
||||
}
|
||||
|
||||
public static Object create(int id, Object serializer, Object entityDataAccessor, Object value) {
|
||||
public static Object create(Object entityDataAccessor, Object value) {
|
||||
return FastNMS.INSTANCE.method$SynchedEntityData$DataValue$create(entityDataAccessor, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -27,6 +27,7 @@ import net.momirealms.craftengine.core.util.*;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.incendo.cloud.suggestion.Suggestion;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -402,6 +403,7 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
|
||||
Object resourceLocation = FastNMS.INSTANCE.method$Registry$getKey(MBuiltInRegistries.ITEM, item);
|
||||
Key itemKey = KeyUtils.resourceLocationToKey(resourceLocation);
|
||||
VANILLA_ITEMS.add(itemKey);
|
||||
super.cachedVanillaItemSuggestions.add(Suggestion.suggestion(itemKey.asString()));
|
||||
UniqueKey uniqueKey = UniqueKey.create(itemKey);
|
||||
Object mcHolder = FastNMS.INSTANCE.method$Registry$getHolderByResourceKey(MBuiltInRegistries.ITEM, FastNMS.INSTANCE.method$ResourceKey$create(MRegistries.ITEM, resourceLocation)).get();
|
||||
Set<Object> tags = (Set<Object>) CoreReflections.field$Holder$Reference$tags.get(mcHolder);
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
package net.momirealms.craftengine.bukkit.item.behavior;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
|
||||
import net.momirealms.craftengine.bukkit.block.behavior.StrippableBlockBehavior;
|
||||
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
|
||||
import net.momirealms.craftengine.bukkit.util.*;
|
||||
import net.momirealms.craftengine.bukkit.world.BukkitExistingBlock;
|
||||
import net.momirealms.craftengine.core.block.CustomBlock;
|
||||
import net.momirealms.craftengine.core.block.BlockStateWrapper;
|
||||
import net.momirealms.craftengine.core.block.ImmutableBlockState;
|
||||
import net.momirealms.craftengine.core.block.UpdateOption;
|
||||
import net.momirealms.craftengine.core.entity.player.InteractionHand;
|
||||
@@ -25,7 +24,6 @@ import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
import net.momirealms.craftengine.core.world.BlockPos;
|
||||
import net.momirealms.craftengine.core.world.Vec3d;
|
||||
import net.momirealms.sparrow.nbt.CompoundTag;
|
||||
import org.bukkit.GameEvent;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Statistic;
|
||||
@@ -66,28 +64,25 @@ public class AxeItemBehavior extends ItemBehavior {
|
||||
ImmutableBlockState customState = optionalCustomState.get();
|
||||
Optional<StrippableBlockBehavior> behaviorOptional = customState.behavior().getAs(StrippableBlockBehavior.class);
|
||||
if (behaviorOptional.isEmpty()) return InteractionResult.PASS;
|
||||
Key stripped = behaviorOptional.get().stripped();
|
||||
Item<ItemStack> offHandItem = player != null ? (Item<ItemStack>) player.getItemInHand(InteractionHand.OFF_HAND) : BukkitItemManager.instance().uniqueEmptyItem().item();
|
||||
// is using a shield
|
||||
if (context.getHand() == InteractionHand.MAIN_HAND && !ItemUtils.isEmpty(offHandItem) && canBlockAttack(offHandItem) && player != null && !player.isSecondaryUseActive()) {
|
||||
return InteractionResult.PASS;
|
||||
}
|
||||
|
||||
Optional<CustomBlock> optionalNewCustomBlock = BukkitBlockManager.instance().blockById(stripped);
|
||||
if (optionalNewCustomBlock.isEmpty()) {
|
||||
CraftEngine.instance().logger().warn("stripped block " + stripped + " does not exist");
|
||||
BlockStateWrapper newState = behaviorOptional.get().strippedState();
|
||||
if (newState == null) {
|
||||
CraftEngine.instance().logger().warn("stripped block " + behaviorOptional.get().stripped() + " does not exist");
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
CustomBlock newCustomBlock = optionalNewCustomBlock.get();
|
||||
CompoundTag compoundTag = customState.propertiesNbt();
|
||||
ImmutableBlockState newState = newCustomBlock.getBlockState(compoundTag);
|
||||
|
||||
newState = newState.withProperties(behaviorOptional.get().filter(customState.propertiesNbt()));
|
||||
BukkitExistingBlock clicked = (BukkitExistingBlock) context.getLevel().getBlockAt(context.getClickedPos());
|
||||
org.bukkit.entity.Player bukkitPlayer = null;
|
||||
if (player != null) {
|
||||
bukkitPlayer = ((org.bukkit.entity.Player) player.platformPlayer());
|
||||
// Call bukkit event
|
||||
EntityChangeBlockEvent event = new EntityChangeBlockEvent(bukkitPlayer, clicked.block(), BlockStateUtils.fromBlockData(newState.customBlockState().literalObject()));
|
||||
EntityChangeBlockEvent event = new EntityChangeBlockEvent(bukkitPlayer, clicked.block(), BlockStateUtils.fromBlockData(newState.literalObject()));
|
||||
if (EventUtils.fireAndCheckCancel(event)) {
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
@@ -98,7 +93,7 @@ public class AxeItemBehavior extends ItemBehavior {
|
||||
if (ItemUtils.isEmpty(item)) return InteractionResult.FAIL;
|
||||
BlockPos pos = context.getClickedPos();
|
||||
context.getLevel().playBlockSound(Vec3d.atCenterOf(pos), AXE_STRIP_SOUND, 1, 1);
|
||||
FastNMS.INSTANCE.method$LevelWriter$setBlock(context.getLevel().serverWorld(), LocationUtils.toBlockPos(pos), newState.customBlockState().literalObject(), UpdateOption.UPDATE_ALL_IMMEDIATE.flags());
|
||||
FastNMS.INSTANCE.method$LevelWriter$setBlock(context.getLevel().serverWorld(), LocationUtils.toBlockPos(pos), newState.literalObject(), UpdateOption.UPDATE_ALL_IMMEDIATE.flags());
|
||||
clicked.block().getWorld().sendGameEvent(bukkitPlayer, GameEvent.BLOCK_CHANGE, new Vector(pos.x(), pos.y(), pos.z()));
|
||||
Material material = MaterialUtils.getMaterial(item.vanillaId());
|
||||
if (bukkitPlayer != null) {
|
||||
@@ -126,12 +121,12 @@ public class AxeItemBehavior extends ItemBehavior {
|
||||
itemStack.damage(1, bukkitPlayer);
|
||||
}
|
||||
}
|
||||
return InteractionResult.SUCCESS;
|
||||
return InteractionResult.SUCCESS_AND_CANCEL;
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -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()));
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -47,7 +47,7 @@ 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 = FastNMS.INSTANCE.field$BlockHitResul$blockPos(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 = DirectionUtils.fromNMSDirection(FastNMS.INSTANCE.field$BlockHitResul$direction(blockHitResult));
|
||||
boolean miss = FastNMS.INSTANCE.field$BlockHitResul$miss(blockHitResult);
|
||||
@@ -59,7 +59,7 @@ public class LiquidCollisionBlockItemBehavior extends BlockItemBehavior {
|
||||
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) {
|
||||
@@ -70,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"));
|
||||
@@ -79,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 {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package net.momirealms.craftengine.bukkit.item.behavior;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
|
||||
import net.momirealms.craftengine.core.entity.player.InteractionResult;
|
||||
import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
|
||||
import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory;
|
||||
@@ -9,7 +8,6 @@ import net.momirealms.craftengine.core.item.context.UseOnContext;
|
||||
import net.momirealms.craftengine.core.pack.Pack;
|
||||
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.MiscUtils;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Map;
|
||||
@@ -35,18 +33,13 @@ public class WallBlockItemBehavior 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.wall_block.missing_block", new IllegalArgumentException("Missing required parameter 'block' for wall_block_item behavior"));
|
||||
}
|
||||
if (id instanceof Map<?, ?> map) {
|
||||
if (map.containsKey(key.toString())) {
|
||||
// 防呆
|
||||
BukkitBlockManager.instance().parser().parseSection(pack, path, key, MiscUtils.castToMap(map.get(key.toString()), false));
|
||||
} else {
|
||||
BukkitBlockManager.instance().parser().parseSection(pack, path, key, MiscUtils.castToMap(map, false));
|
||||
}
|
||||
addPendingSection(pack, path, node, key, map);
|
||||
return new WallBlockItemBehavior(key);
|
||||
} else {
|
||||
return new WallBlockItemBehavior(Key.of(id.toString()));
|
||||
|
||||
@@ -49,7 +49,7 @@ public abstract class BukkitItemFactory<W extends ItemWrapper<ItemStack>> extend
|
||||
case "1.21.4" -> {
|
||||
return new ComponentItemFactory1_21_4(plugin);
|
||||
}
|
||||
case "1.21.5", "1.21.6", "1.21.7", "1.21.8" -> {
|
||||
case "1.21.5", "1.21.6", "1.21.7", "1.21.8", "1.21.9", "1.21.10" -> {
|
||||
return new ComponentItemFactory1_21_5(plugin);
|
||||
}
|
||||
default -> throw new IllegalStateException("Unsupported server version: " + plugin.serverVersion());
|
||||
|
||||
@@ -17,7 +17,6 @@ import net.momirealms.craftengine.core.block.ImmutableBlockState;
|
||||
import net.momirealms.craftengine.core.block.UpdateOption;
|
||||
import net.momirealms.craftengine.core.block.properties.Property;
|
||||
import net.momirealms.craftengine.core.item.Item;
|
||||
import net.momirealms.craftengine.core.util.MCUtils;
|
||||
import net.momirealms.craftengine.core.util.MiscUtils;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
@@ -119,7 +118,7 @@ public class DebugStickListener implements Listener {
|
||||
}
|
||||
|
||||
private static <T> T getRelative(Iterable<T> elements, @Nullable T current, boolean inverse) {
|
||||
return inverse ? MCUtils.findPreviousInIterable(elements, current) : MCUtils.findNextInIterable(elements, current);
|
||||
return inverse ? MiscUtils.findPreviousInIterable(elements, current) : MiscUtils.findNextInIterable(elements, current);
|
||||
}
|
||||
|
||||
private static <T extends Comparable<T>> String getNameHelper(ImmutableBlockState state, Property<T> property) {
|
||||
|
||||
@@ -29,7 +29,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.SoundData;
|
||||
import net.momirealms.craftengine.core.sound.SoundSet;
|
||||
import net.momirealms.craftengine.core.sound.SoundSource;
|
||||
import net.momirealms.craftengine.core.util.*;
|
||||
import net.momirealms.craftengine.core.world.BlockHitResult;
|
||||
@@ -41,6 +41,7 @@ import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.block.data.Openable;
|
||||
import org.bukkit.block.data.Powerable;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
@@ -219,14 +220,29 @@ public class ItemEventListener implements Listener {
|
||||
}
|
||||
} else {
|
||||
if (Config.enableSoundSystem() && hitResult != null) {
|
||||
Object blockOwner = FastNMS.INSTANCE.method$BlockState$getBlock(blockState);
|
||||
if (this.plugin.blockManager().isOpenableBlockSoundRemoved(blockOwner)) {
|
||||
Key blockOwner = BlockStateUtils.getBlockOwnerIdFromState(blockState);
|
||||
if (this.plugin.blockManager().isInteractSoundMissing(blockOwner)) {
|
||||
boolean hasItem = player.getInventory().getItemInMainHand().getType() != Material.AIR || player.getInventory().getItemInOffHand().getType() != Material.AIR;
|
||||
boolean flag = player.isSneaking() && hasItem;
|
||||
if (!flag) {
|
||||
if (blockData instanceof Openable openable) {
|
||||
SoundData soundData = this.plugin.blockManager().getRemovedOpenableBlockSound(blockOwner, !openable.isOpen());
|
||||
serverPlayer.playSound(soundData.id(), SoundSource.BLOCK, soundData.volume().get(), soundData.pitch().get());
|
||||
SoundSet soundSet = SoundSet.getByBlock(blockOwner);
|
||||
if (soundSet != null) {
|
||||
serverPlayer.playSound(
|
||||
Vec3d.atCenterOf(hitResult.getBlockPos()),
|
||||
openable.isOpen() ? soundSet.closeSound() : soundSet.openSound(),
|
||||
SoundSource.BLOCK,
|
||||
1, RandomUtils.generateRandomFloat(0.9f, 1));
|
||||
}
|
||||
} else if (blockData instanceof Powerable powerable && !powerable.isPowered()) {
|
||||
SoundSet soundSet = SoundSet.getByBlock(blockOwner);
|
||||
if (soundSet != null) {
|
||||
serverPlayer.playSound(
|
||||
Vec3d.atCenterOf(hitResult.getBlockPos()),
|
||||
soundSet.openSound(),
|
||||
SoundSource.BLOCK,
|
||||
1, RandomUtils.generateRandomFloat(0.9f, 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -458,9 +474,9 @@ public class ItemEventListener implements Listener {
|
||||
if (foodData == null) return;
|
||||
event.setCancelled(true);
|
||||
int oldFoodLevel = player.getFoodLevel();
|
||||
if (foodData.nutrition() != 0) player.setFoodLevel(MCUtils.clamp(oldFoodLevel + foodData.nutrition(), 0, 20));
|
||||
if (foodData.nutrition() != 0) player.setFoodLevel(MiscUtils.clamp(oldFoodLevel + foodData.nutrition(), 0, 20));
|
||||
float oldSaturation = player.getSaturation();
|
||||
if (foodData.saturation() != 0) player.setSaturation(MCUtils.clamp(oldSaturation, 0, 10));
|
||||
if (foodData.saturation() != 0) player.setSaturation(MiscUtils.clamp(oldSaturation, 0, 10));
|
||||
}
|
||||
|
||||
private boolean cancelEventIfHasInteraction(PlayerInteractEvent event, BukkitServerPlayer player, InteractionHand hand) {
|
||||
|
||||
@@ -258,7 +258,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
|
||||
// 已经被替换过的数据包配方
|
||||
private final Set<Key> replacedDatapackRecipes = new HashSet<>();
|
||||
// 换成的数据包配方
|
||||
private Map<Key, Recipe<ItemStack>> lastDatapackRecipes = Map.of();
|
||||
private Map<Key, JsonObject> lastDatapackRecipes = Map.of();
|
||||
private Object lastRecipeManager = null;
|
||||
|
||||
public BukkitRecipeManager(BukkitCraftEngine plugin) {
|
||||
@@ -386,18 +386,33 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
|
||||
}
|
||||
|
||||
boolean hasDisabledAny = !Config.disabledVanillaRecipes().isEmpty();
|
||||
for (Map.Entry<Key, Recipe<ItemStack>> entry : this.lastDatapackRecipes.entrySet()) {
|
||||
for (Map.Entry<Key, JsonObject> entry : this.lastDatapackRecipes.entrySet()) {
|
||||
Key id = entry.getKey();
|
||||
if (hasDisabledAny && Config.disabledVanillaRecipes().contains(entry.getKey())) {
|
||||
this.recipesToUnregister.add(Pair.of(entry.getKey(), false));
|
||||
this.recipesToUnregister.add(Pair.of(id, false));
|
||||
continue;
|
||||
}
|
||||
markAsDataPackRecipe(entry.getKey());
|
||||
registerInternalRecipe(entry.getKey(), entry.getValue());
|
||||
|
||||
JsonObject jsonObject = entry.getValue();
|
||||
Key serializerType = Key.of(jsonObject.get("type").getAsString());
|
||||
@SuppressWarnings("unchecked")
|
||||
RecipeSerializer<ItemStack, ? extends Recipe<ItemStack>> serializer = (RecipeSerializer<ItemStack, ? extends Recipe<ItemStack>>) BuiltInRegistries.RECIPE_SERIALIZER.getValue(serializerType);
|
||||
if (serializer == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
Recipe<ItemStack> recipe = serializer.readJson(id, jsonObject);
|
||||
markAsDataPackRecipe(id);
|
||||
registerInternalRecipe(id, recipe);
|
||||
} catch (Exception e) {
|
||||
this.plugin.logger().warn("Failed to load data pack recipe " + id + ". Json: " + jsonObject, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map<Key, Recipe<ItemStack>> scanResources() throws Throwable {
|
||||
private Map<Key, JsonObject> scanResources() throws Throwable {
|
||||
Object fileToIdConverter = CoreReflections.methodHandle$FileToIdConverter$json.invokeExact((String) (VersionHelper.isOrAbove1_21() ? "recipe" : "recipes"));
|
||||
Object minecraftServer = FastNMS.INSTANCE.method$MinecraftServer$getServer();
|
||||
Object packRepository = CoreReflections.methodHandle$MinecraftServer$getPackRepository.invokeExact(minecraftServer);
|
||||
@@ -406,7 +421,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
|
||||
for (Object pack : selected) {
|
||||
packResources.add(CoreReflections.methodHandle$Pack$open.invokeExact(pack));
|
||||
}
|
||||
Map<Key, Recipe<ItemStack>> recipes = new HashMap<>();
|
||||
Map<Key, JsonObject> recipes = new HashMap<>();
|
||||
|
||||
try (AutoCloseable resourceManager = (AutoCloseable) CoreReflections.methodHandle$MultiPackResourceManagerConstructor.invokeExact(CoreReflections.instance$PackType$SERVER_DATA, packResources)) {
|
||||
Map<Object, Object> scannedResources = (Map<Object, Object>) CoreReflections.methodHandle$FileToIdConverter$listMatchingResources.invokeExact(fileToIdConverter, resourceManager);
|
||||
@@ -414,17 +429,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
|
||||
Key id = extractKeyFromResourceLocation(entry.getKey().toString());
|
||||
Reader reader = (Reader) CoreReflections.methodHandle$Resource$openAsReader.invokeExact(entry.getValue());
|
||||
JsonObject jsonObject = JsonParser.parseReader(reader).getAsJsonObject();
|
||||
Key serializerType = Key.of(jsonObject.get("type").getAsString());
|
||||
RecipeSerializer<ItemStack, ? extends Recipe<ItemStack>> serializer = (RecipeSerializer<ItemStack, ? extends Recipe<ItemStack>>) BuiltInRegistries.RECIPE_SERIALIZER.getValue(serializerType);
|
||||
if (serializer == null) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
Recipe<ItemStack> recipe = serializer.readJson(id, jsonObject);
|
||||
recipes.put(id, recipe);
|
||||
} catch (Exception e) {
|
||||
this.plugin.logger().warn("Failed to load data pack recipe " + id + ". Json: " + jsonObject, e);
|
||||
}
|
||||
recipes.put(id, jsonObject);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
this.plugin.logger().warn("Unknown error occurred when loading data pack recipes", e);
|
||||
|
||||
@@ -23,6 +23,8 @@ import net.momirealms.craftengine.core.item.recipe.input.SmithingInput;
|
||||
import net.momirealms.craftengine.core.item.setting.AnvilRepairItem;
|
||||
import net.momirealms.craftengine.core.item.setting.ItemEquipment;
|
||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext;
|
||||
import net.momirealms.craftengine.core.plugin.context.function.Function;
|
||||
import net.momirealms.craftengine.core.util.*;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
@@ -608,20 +610,25 @@ public class RecipeEventListener implements Listener {
|
||||
inventory.setResult(null);
|
||||
return;
|
||||
}
|
||||
CraftingInput<ItemStack> input = getCraftingInput(inventory);
|
||||
if (input == null) return;
|
||||
Player player = InventoryUtils.getPlayerFromInventoryEvent(event);
|
||||
BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player);
|
||||
if (craftingTableRecipe.hasVisualResult()) {
|
||||
inventory.setResult(craftingTableRecipe.assembleVisual(input, ItemBuildContext.of(serverPlayer)));
|
||||
ItemBuildContext itemBuildContext = ItemBuildContext.of(serverPlayer);
|
||||
if (!craftingTableRecipe.canUse(itemBuildContext)) {
|
||||
inventory.setResult(null);
|
||||
return;
|
||||
}
|
||||
CraftingInput<ItemStack> input = getCraftingInput(inventory);
|
||||
if (input == null) return;
|
||||
if (craftingTableRecipe.hasVisualResult() && VersionHelper.PREMIUM) {
|
||||
inventory.setResult(craftingTableRecipe.assembleVisual(input, itemBuildContext));
|
||||
} else {
|
||||
inventory.setResult(craftingTableRecipe.assemble(input, ItemBuildContext.of(serverPlayer)));
|
||||
inventory.setResult(craftingTableRecipe.assemble(input, itemBuildContext));
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
|
||||
public void onCraftingFinish(CraftItemEvent event) {
|
||||
if (!Config.enableRecipeSystem()) return;
|
||||
if (!Config.enableRecipeSystem() || !VersionHelper.PREMIUM) return;
|
||||
org.bukkit.inventory.Recipe recipe = event.getRecipe();
|
||||
if (!(recipe instanceof CraftingRecipe craftingRecipe)) return;
|
||||
Key recipeId = Key.of(craftingRecipe.getKey().namespace(), craftingRecipe.getKey().value());
|
||||
@@ -634,14 +641,19 @@ public class RecipeEventListener implements Listener {
|
||||
if (!(optionalRecipe.get() instanceof CustomCraftingTableRecipe<ItemStack> craftingTableRecipe)) {
|
||||
return;
|
||||
}
|
||||
if (!craftingTableRecipe.hasVisualResult()) {
|
||||
return;
|
||||
}
|
||||
CraftingInput<ItemStack> input = getCraftingInput(inventory);
|
||||
if (input == null) return;
|
||||
Player player = InventoryUtils.getPlayerFromInventoryEvent(event);
|
||||
BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player);
|
||||
inventory.setResult(craftingTableRecipe.assemble(input, ItemBuildContext.of(serverPlayer)));
|
||||
if (craftingTableRecipe.hasVisualResult()) {
|
||||
CraftingInput<ItemStack> input = getCraftingInput(inventory);
|
||||
inventory.setResult(craftingTableRecipe.assemble(input, ItemBuildContext.of(serverPlayer)));
|
||||
}
|
||||
Function<PlayerOptionalContext>[] functions = craftingTableRecipe.craftingFunctions();
|
||||
if (functions != null) {
|
||||
PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer);
|
||||
for (Function<PlayerOptionalContext> function : functions) {
|
||||
function.run(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private CraftingInput<ItemStack> getCraftingInput(CraftingInventory inventory) {
|
||||
@@ -690,10 +702,16 @@ public class RecipeEventListener implements Listener {
|
||||
event.setResult(null);
|
||||
return;
|
||||
}
|
||||
Player player = InventoryUtils.getPlayerFromInventoryEvent(event);
|
||||
ItemBuildContext itemBuildContext = ItemBuildContext.of(BukkitAdaptors.adapt(player));
|
||||
if (!smithingTrimRecipe.canUse(itemBuildContext)) {
|
||||
event.setResult(null);
|
||||
return;
|
||||
}
|
||||
|
||||
SmithingInput<ItemStack> input = getSmithingInput(inventory);
|
||||
if (smithingTrimRecipe.matches(input)) {
|
||||
Player player = InventoryUtils.getPlayerFromInventoryEvent(event);
|
||||
ItemStack result = smithingTrimRecipe.assemble(getSmithingInput(inventory), ItemBuildContext.of(BukkitAdaptors.adapt(player)));
|
||||
ItemStack result = smithingTrimRecipe.assemble(getSmithingInput(inventory), itemBuildContext);
|
||||
event.setResult(result);
|
||||
} else {
|
||||
event.setResult(null);
|
||||
|
||||
@@ -14,6 +14,7 @@ import net.momirealms.craftengine.core.loot.VanillaLoot;
|
||||
import net.momirealms.craftengine.core.pack.LoadingSequence;
|
||||
import net.momirealms.craftengine.core.pack.Pack;
|
||||
import net.momirealms.craftengine.core.plugin.config.ConfigParser;
|
||||
import net.momirealms.craftengine.core.plugin.config.IdSectionConfigParser;
|
||||
import net.momirealms.craftengine.core.plugin.context.ContextHolder;
|
||||
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
|
||||
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
|
||||
@@ -90,8 +91,8 @@ public class BukkitVanillaLootManager extends AbstractVanillaLootManager impleme
|
||||
return this.vanillaLootParser;
|
||||
}
|
||||
|
||||
public class VanillaLootParser implements ConfigParser {
|
||||
public static final String[] CONFIG_SECTION_NAME = new String[] {"vanilla-loots", "vanilla-loot", "vanilla_loots", "vanilla_loot"};
|
||||
public class VanillaLootParser extends IdSectionConfigParser {
|
||||
public static final String[] CONFIG_SECTION_NAME = new String[] {"vanilla-loots", "vanilla-loot"};
|
||||
|
||||
@Override
|
||||
public int loadingSequence() {
|
||||
@@ -104,7 +105,7 @@ public class BukkitVanillaLootManager extends AbstractVanillaLootManager impleme
|
||||
}
|
||||
|
||||
@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) {
|
||||
String type = ResourceConfigUtils.requireNonEmptyStringOrThrow(section.get("type"), "warning.config.vanilla_loot.missing_type");
|
||||
VanillaLoot.Type typeEnum;
|
||||
try {
|
||||
@@ -126,7 +127,7 @@ public class BukkitVanillaLootManager extends AbstractVanillaLootManager impleme
|
||||
VanillaLoot vanillaLoot = blockLoots.computeIfAbsent(BlockStateUtils.blockStateToId(blockState), k -> new VanillaLoot(VanillaLoot.Type.BLOCK));
|
||||
vanillaLoot.addLootTable(lootTable);
|
||||
} else {
|
||||
for (Object blockState : BlockStateUtils.getAllVanillaBlockStates(Key.of(target))) {
|
||||
for (Object blockState : BlockStateUtils.getPossibleBlockStates(Key.of(target))) {
|
||||
if (blockState == MBlocks.AIR$defaultState) {
|
||||
throw new LocalizedResourceConfigException("warning.config.vanilla_loot.block.invalid_target", target);
|
||||
}
|
||||
|
||||
@@ -20,12 +20,10 @@ import net.momirealms.craftengine.bukkit.plugin.command.BukkitSenderFactory;
|
||||
import net.momirealms.craftengine.bukkit.plugin.gui.BukkitGuiManager;
|
||||
import net.momirealms.craftengine.bukkit.plugin.injector.*;
|
||||
import net.momirealms.craftengine.bukkit.plugin.network.BukkitNetworkManager;
|
||||
import net.momirealms.craftengine.bukkit.plugin.network.PacketConsumers;
|
||||
import net.momirealms.craftengine.bukkit.plugin.scheduler.BukkitSchedulerAdapter;
|
||||
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
|
||||
import net.momirealms.craftengine.bukkit.sound.BukkitSoundManager;
|
||||
import net.momirealms.craftengine.bukkit.util.EventUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.RegistryUtils;
|
||||
import net.momirealms.craftengine.bukkit.world.BukkitWorldManager;
|
||||
import net.momirealms.craftengine.core.item.ItemManager;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
@@ -57,6 +55,7 @@ import org.jspecify.annotations.Nullable;
|
||||
import java.io.*;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
@@ -104,9 +103,15 @@ public class BukkitCraftEngine extends CraftEngine {
|
||||
this.javaPlugin = javaPlugin;
|
||||
}
|
||||
|
||||
protected void setUpConfig() {
|
||||
this.translationManager = new TranslationManagerImpl(this);
|
||||
protected void setUpConfigAndLocale() {
|
||||
this.config = new Config(this);
|
||||
this.config.updateConfigCache();
|
||||
// 先读取语言后,再重载语言文件系统
|
||||
this.config.loadForcedLocale();
|
||||
this.translationManager = new TranslationManagerImpl(this);
|
||||
this.translationManager.reload();
|
||||
// 最后才加载完整的config配置
|
||||
this.config.loadFullSettings();
|
||||
}
|
||||
|
||||
public void injectRegistries() {
|
||||
@@ -146,8 +151,8 @@ public class BukkitCraftEngine extends CraftEngine {
|
||||
throw new InjectionException("Error initializing ProtectedFieldVisitor", e);
|
||||
}
|
||||
super.onPluginLoad();
|
||||
super.blockManager.init();
|
||||
super.networkManager = new BukkitNetworkManager(this);
|
||||
super.blockManager.init();
|
||||
super.itemManager = new BukkitItemManager(this);
|
||||
this.successfullyLoaded = true;
|
||||
super.compatibilityManager().onLoad();
|
||||
@@ -191,7 +196,6 @@ public class BukkitCraftEngine extends CraftEngine {
|
||||
BukkitItemBehaviors.init();
|
||||
BukkitHitBoxTypes.init();
|
||||
BukkitBlockEntityElementConfigs.init();
|
||||
PacketConsumers.initEntities(RegistryUtils.currentEntityTypeRegistrySize());
|
||||
super.packManager = new BukkitPackManager(this);
|
||||
super.senderFactory = new BukkitSenderFactory(this);
|
||||
super.recipeManager = new BukkitRecipeManager(this);
|
||||
@@ -207,6 +211,19 @@ public class BukkitCraftEngine extends CraftEngine {
|
||||
super.furnitureManager = new BukkitFurnitureManager(this);
|
||||
super.onPluginEnable();
|
||||
super.compatibilityManager().onEnable();
|
||||
|
||||
// todo 未来版本移除
|
||||
Path legacyFile1 = this.dataFolderPath().resolve("additional-real-blocks.yml");
|
||||
Path legacyFile2 = this.dataFolderPath().resolve("mappings.yml");
|
||||
if (Files.exists(legacyFile1)) {
|
||||
try {
|
||||
Files.delete(legacyFile1);
|
||||
Files.deleteIfExists(legacyFile2);
|
||||
this.saveResource("resources/internal/configuration/mappings.yml");
|
||||
} catch (IOException e) {
|
||||
this.logger.warn("Failed to delete legacy files", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -331,6 +348,11 @@ public class BukkitCraftEngine extends CraftEngine {
|
||||
return (BukkitPackManager) packManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BukkitFontManager fontManager() {
|
||||
return (BukkitFontManager) fontManager;
|
||||
}
|
||||
|
||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||
@Override
|
||||
public void saveResource(String resourcePath) {
|
||||
@@ -379,6 +401,7 @@ public class BukkitCraftEngine extends CraftEngine {
|
||||
this.antiGrief = AntiGriefLib.builder(this.javaPlugin)
|
||||
.ignoreOP(true)
|
||||
.silentLogs(false)
|
||||
.bypassPermission("craftengine.antigrief.bypass")
|
||||
.build();
|
||||
}
|
||||
return this.antiGrief;
|
||||
|
||||
@@ -6,6 +6,8 @@ import net.momirealms.craftengine.bukkit.plugin.command.feature.*;
|
||||
import net.momirealms.craftengine.core.plugin.command.AbstractCommandManager;
|
||||
import net.momirealms.craftengine.core.plugin.command.CommandFeature;
|
||||
import net.momirealms.craftengine.core.plugin.command.sender.Sender;
|
||||
import net.momirealms.craftengine.core.util.ReflectionUtils;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.incendo.cloud.SenderMapper;
|
||||
import org.incendo.cloud.bukkit.CloudBukkitCapabilities;
|
||||
@@ -24,7 +26,11 @@ public class BukkitCommandManager extends AbstractCommandManager<CommandSender>
|
||||
plugin.javaPlugin(),
|
||||
ExecutionCoordinator.simpleCoordinator(),
|
||||
SenderMapper.identity()
|
||||
));
|
||||
) {{ // TODO:等 cloud 修复后移除,绕过 obc.command.BukkitCommandWrapper 类检查,因为这个类在 1.21.9 版本被移除了,并且项目貌似没用到这个
|
||||
if (VersionHelper.isOrAbove1_21_9() && ReflectionUtils.classExists("com.mojang.brigadier.tree.CommandNode")) {
|
||||
registerCapability(CloudBukkitCapabilities.BRIGADIER);
|
||||
}
|
||||
}});
|
||||
this.plugin = plugin;
|
||||
this.index = Index.create(CommandFeature::getFeatureID, List.of(
|
||||
new ReloadCommand(this, plugin),
|
||||
@@ -56,7 +62,9 @@ public class BukkitCommandManager extends AbstractCommandManager<CommandSender>
|
||||
new ListResourceCommand(this, plugin),
|
||||
new UploadPackCommand(this, plugin),
|
||||
new SendResourcePackCommand(this, plugin),
|
||||
new DebugSaveDefaultResourcesCommand(this, plugin)
|
||||
new DebugSaveDefaultResourcesCommand(this, plugin),
|
||||
new DebugCleanCacheCommand(this, plugin)
|
||||
// new OverrideGiveCommand(this, plugin)
|
||||
));
|
||||
final LegacyPaperCommandManager<CommandSender> manager = (LegacyPaperCommandManager<CommandSender>) getCommandManager();
|
||||
manager.settings().set(ManagerSetting.ALLOW_UNSAFE_REGISTRATION, true);
|
||||
|
||||
@@ -5,7 +5,8 @@ import net.kyori.adventure.text.event.HoverEvent;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
|
||||
import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature;
|
||||
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
|
||||
import net.momirealms.craftengine.core.block.BlockStateWrapper;
|
||||
import net.momirealms.craftengine.core.pack.allocator.VisualBlockStateAllocator;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
@@ -34,37 +35,39 @@ public class DebugAppearanceStateUsageCommand extends BukkitCommandFeature<Comma
|
||||
.required("id", StringParser.stringComponent(StringParser.StringMode.GREEDY_FLAG_YIELDING).suggestionProvider(new SuggestionProvider<>() {
|
||||
@Override
|
||||
public @NonNull CompletableFuture<? extends @NonNull Iterable<? extends @NonNull Suggestion>> suggestionsFuture(@NonNull CommandContext<Object> context, @NonNull CommandInput input) {
|
||||
return CompletableFuture.completedFuture(plugin().blockManager().blockAppearanceArranger().keySet().stream().map(it -> Suggestion.suggestion(it.toString())).toList());
|
||||
return CompletableFuture.completedFuture(plugin().blockManager().blockStateArranger().keySet().stream().map(it -> Suggestion.suggestion(it.toString())).toList());
|
||||
}
|
||||
}))
|
||||
.handler(context -> {
|
||||
String data = context.get("id");
|
||||
BukkitBlockManager blockManager = plugin().blockManager();
|
||||
Key baseBlockId = Key.of(data);
|
||||
List<Integer> appearances = blockManager.blockAppearanceArranger().get(baseBlockId);
|
||||
List<BlockStateWrapper> appearances = blockManager.blockStateArranger().get(baseBlockId);
|
||||
if (appearances == null) return;
|
||||
int i = 0;
|
||||
Component block = Component.text(baseBlockId + ": ");
|
||||
plugin().senderFactory().wrap(context.sender()).sendMessage(block);
|
||||
|
||||
VisualBlockStateAllocator allocator = blockManager.blockParser().visualBlockStateAllocator();
|
||||
List<Component> batch = new ArrayList<>();
|
||||
for (int appearance : appearances) {
|
||||
for (BlockStateWrapper appearance : appearances) {
|
||||
Component text = Component.text("|");
|
||||
List<Integer> reals = blockManager.appearanceToRealStates(appearance);
|
||||
if (reals == null || reals.isEmpty()) {
|
||||
List<Integer> reals = blockManager.appearanceToRealStates(appearance.registryId());
|
||||
if (reals.isEmpty()) {
|
||||
Component hover = Component.text(baseBlockId.value() + ":" + i).color(NamedTextColor.GREEN);
|
||||
hover = hover.append(Component.newline()).append(Component.text(BlockStateUtils.fromBlockData(BlockStateUtils.idToBlockState(appearance)).getAsString()).color(NamedTextColor.GREEN));
|
||||
hover = hover.append(Component.newline()).append(Component.text(appearance.getAsString()).color(NamedTextColor.GREEN));
|
||||
text = text.color(NamedTextColor.GREEN).hoverEvent(HoverEvent.showText(hover));
|
||||
} else {
|
||||
Component hover = Component.text(baseBlockId.value() + ":" + i).color(NamedTextColor.RED);
|
||||
boolean isFixed = allocator.isForcedState(appearance);
|
||||
NamedTextColor namedTextColor = isFixed ? NamedTextColor.RED : NamedTextColor.YELLOW;
|
||||
Component hover = Component.text(baseBlockId.value() + ":" + i).color(namedTextColor);
|
||||
List<Component> hoverChildren = new ArrayList<>();
|
||||
hoverChildren.add(Component.newline());
|
||||
hoverChildren.add(Component.text(BlockStateUtils.fromBlockData(BlockStateUtils.idToBlockState(appearance)).getAsString()).color(NamedTextColor.RED));
|
||||
hoverChildren.add(Component.text(appearance.getAsString()).color(namedTextColor));
|
||||
for (int real : reals) {
|
||||
hoverChildren.add(Component.newline());
|
||||
hoverChildren.add(Component.text(blockManager.getImmutableBlockStateUnsafe(real).toString()).color(NamedTextColor.GRAY));
|
||||
}
|
||||
text = text.color(NamedTextColor.RED).hoverEvent(HoverEvent.showText(hover.children(hoverChildren)));
|
||||
text = text.color(namedTextColor).hoverEvent(HoverEvent.showText(hover.children(hoverChildren)));
|
||||
}
|
||||
batch.add(text);
|
||||
i++;
|
||||
|
||||
@@ -0,0 +1,244 @@
|
||||
package net.momirealms.craftengine.bukkit.plugin.command.feature;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
|
||||
import net.momirealms.craftengine.bukkit.font.BukkitFontManager;
|
||||
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
|
||||
import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature;
|
||||
import net.momirealms.craftengine.core.block.BlockStateWrapper;
|
||||
import net.momirealms.craftengine.core.block.CustomBlock;
|
||||
import net.momirealms.craftengine.core.block.ImmutableBlockState;
|
||||
import net.momirealms.craftengine.core.font.BitmapImage;
|
||||
import net.momirealms.craftengine.core.item.CustomItem;
|
||||
import net.momirealms.craftengine.core.pack.allocator.IdAllocator;
|
||||
import net.momirealms.craftengine.core.pack.allocator.VisualBlockStateAllocator;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager;
|
||||
import net.momirealms.craftengine.core.util.FileUtils;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.incendo.cloud.Command;
|
||||
import org.incendo.cloud.context.CommandContext;
|
||||
import org.incendo.cloud.context.CommandInput;
|
||||
import org.incendo.cloud.parser.standard.StringParser;
|
||||
import org.incendo.cloud.suggestion.Suggestion;
|
||||
import org.incendo.cloud.suggestion.SuggestionProvider;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class DebugCleanCacheCommand extends BukkitCommandFeature<CommandSender> {
|
||||
|
||||
public DebugCleanCacheCommand(CraftEngineCommandManager<CommandSender> commandManager, CraftEngine plugin) {
|
||||
super(commandManager, plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Command.Builder<? extends CommandSender> assembleCommand(org.incendo.cloud.CommandManager<CommandSender> manager, Command.Builder<CommandSender> builder) {
|
||||
return builder
|
||||
.optional("type", StringParser.stringComponent().suggestionProvider(new SuggestionProvider<>() {
|
||||
@Override
|
||||
public @NonNull CompletableFuture<? extends @NonNull Iterable<? extends @NonNull Suggestion>> suggestionsFuture(@NonNull CommandContext<Object> context, @NonNull CommandInput input) {
|
||||
return CompletableFuture.completedFuture(List.of(Suggestion.suggestion("custom-model-data"), Suggestion.suggestion("custom-block-states"), Suggestion.suggestion("visual-block-states"), Suggestion.suggestion("font"), Suggestion.suggestion("all")));
|
||||
}
|
||||
}))
|
||||
.handler(context -> {
|
||||
if (this.plugin().isReloading()) {
|
||||
context.sender().sendMessage("The plugin is reloading. Please wait until the process is complete.");
|
||||
return;
|
||||
}
|
||||
String type = context.getOrDefault("type", "all");
|
||||
switch (type) {
|
||||
case "custom-model-data" -> handleCustomModelData(context);
|
||||
case "font", "images" -> handleFont(context);
|
||||
case "custom-block-states" -> handleCustomBlockState(context);
|
||||
case "visual-block-states" -> handleVisualBlockState(context);
|
||||
case "all" -> {
|
||||
handleCustomModelData(context);
|
||||
handleFont(context);
|
||||
handleCustomBlockState(context);
|
||||
handleVisualBlockState(context);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFeatureID() {
|
||||
return "debug_clean_cache";
|
||||
}
|
||||
|
||||
private void handleVisualBlockState(CommandContext<CommandSender> context) {
|
||||
BukkitBlockManager instance = BukkitBlockManager.instance();
|
||||
Set<BlockStateWrapper> ids = new HashSet<>();
|
||||
for (CustomBlock customBlock : instance.loadedBlocks().values()) {
|
||||
for (ImmutableBlockState state : customBlock.variantProvider().states()) {
|
||||
ids.add(state.vanillaBlockState());
|
||||
}
|
||||
}
|
||||
VisualBlockStateAllocator visualBlockStateAllocator = instance.blockParser().visualBlockStateAllocator();
|
||||
List<String> removed = visualBlockStateAllocator.cleanupUnusedIds(i -> !ids.contains(i));
|
||||
try {
|
||||
visualBlockStateAllocator.saveToCache();
|
||||
} catch (IOException e) {
|
||||
this.plugin().logger().warn("Error while saving visual block states allocation", e);
|
||||
}
|
||||
for (String id : removed) {
|
||||
this.plugin().logger().info("Cleaned unsued block appearance: " + id);
|
||||
}
|
||||
context.sender().sendMessage("Cleaned " + removed.size() + " unused block state appearances");
|
||||
}
|
||||
|
||||
private void handleCustomBlockState(CommandContext<CommandSender> context) {
|
||||
BukkitBlockManager instance = BukkitBlockManager.instance();
|
||||
Set<String> ids = new HashSet<>();
|
||||
for (CustomBlock customBlock : instance.loadedBlocks().values()) {
|
||||
for (ImmutableBlockState state : customBlock.variantProvider().states()) {
|
||||
ids.add(state.toString());
|
||||
}
|
||||
}
|
||||
IdAllocator idAllocator = instance.blockParser().internalIdAllocator();
|
||||
List<String> removed = idAllocator.cleanupUnusedIds(i -> !ids.contains(i));
|
||||
try {
|
||||
idAllocator.saveToCache();
|
||||
} catch (IOException e) {
|
||||
this.plugin().logger().warn("Error while saving custom block states allocation", e);
|
||||
}
|
||||
for (String id : removed) {
|
||||
this.plugin().logger().info("Cleaned unsued block state: " + id);
|
||||
}
|
||||
context.sender().sendMessage("Cleaned " + removed.size() + " unused custom block states");
|
||||
}
|
||||
|
||||
private void handleFont(CommandContext<CommandSender> context) {
|
||||
BukkitFontManager instance = this.plugin().fontManager();
|
||||
Map<Key, Set<String>> idsMap = new HashMap<>();
|
||||
for (BitmapImage image : instance.loadedImages().values()) {
|
||||
Set<String> ids = idsMap.computeIfAbsent(image.font(), k -> new HashSet<>());
|
||||
String id = image.id().toString();
|
||||
ids.add(id);
|
||||
for (int i = 0; i < image.rows(); i++) {
|
||||
for (int j = 0; j < image.columns(); j++) {
|
||||
String imageArgs = id + ":" + i + ":" + j;
|
||||
ids.add(imageArgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
int total = 0;
|
||||
for (Map.Entry<Key, IdAllocator> entry : getAllCachedFont().entrySet()) {
|
||||
Key font = entry.getKey();
|
||||
Set<String> ids = idsMap.getOrDefault(font, Collections.emptySet());
|
||||
List<String> removed = entry.getValue().cleanupUnusedIds(i -> !ids.contains(i));
|
||||
try {
|
||||
entry.getValue().saveToCache();
|
||||
} catch (IOException e) {
|
||||
this.plugin().logger().warn("Error while saving codepoint allocation for font " + font.asString(), e);
|
||||
return;
|
||||
}
|
||||
for (String id : removed) {
|
||||
this.plugin().logger().info("Cleaned unsued image: " + id);
|
||||
}
|
||||
total += removed.size();
|
||||
}
|
||||
context.sender().sendMessage("Cleaned " + total + " unused codepoints");
|
||||
}
|
||||
|
||||
private void handleCustomModelData(CommandContext<CommandSender> context) {
|
||||
BukkitItemManager instance = BukkitItemManager.instance();
|
||||
Map<Key, Set<String>> idsMap = new HashMap<>();
|
||||
for (CustomItem<ItemStack> item : instance.loadedItems().values()) {
|
||||
Set<String> ids = idsMap.computeIfAbsent(item.clientBoundMaterial(), k -> new HashSet<>());
|
||||
ids.add(item.id().asString());
|
||||
}
|
||||
int total = 0;
|
||||
for (Map.Entry<Key, IdAllocator> entry : getAllCachedCustomModelData().entrySet()) {
|
||||
Set<String> ids = idsMap.getOrDefault(entry.getKey(), Collections.emptySet());
|
||||
List<String> removed = entry.getValue().cleanupUnusedIds(i -> !ids.contains(i));
|
||||
total += removed.size();
|
||||
try {
|
||||
entry.getValue().saveToCache();
|
||||
} catch (IOException e) {
|
||||
this.plugin().logger().warn("Error while saving custom model data allocation for material " + entry.getKey().asString(), e);
|
||||
return;
|
||||
}
|
||||
for (String id : removed) {
|
||||
this.plugin().logger().info("Cleaned unsued item: " + id);
|
||||
}
|
||||
}
|
||||
context.sender().sendMessage("Cleaned " + total + " unused custom model data");
|
||||
}
|
||||
|
||||
public Map<Key, IdAllocator> getAllCachedCustomModelData() {
|
||||
Path cacheDir = CraftEngine.instance().dataFolderPath().resolve("cache").resolve("custom-model-data");
|
||||
if (!Files.exists(cacheDir)) {
|
||||
return Map.of();
|
||||
}
|
||||
|
||||
Map<Key, IdAllocator> idAllocators = new HashMap<>();
|
||||
try (Stream<Path> files = Files.list(cacheDir)) {
|
||||
files.filter(this::isJsonFile)
|
||||
.forEach(file -> processIdAllocatorFile("minecraft", file, idAllocators));
|
||||
|
||||
} catch (IOException e) {
|
||||
CraftEngine.instance().logger().warn("Failed to process: " + cacheDir.getFileName(), e);
|
||||
}
|
||||
|
||||
return idAllocators;
|
||||
}
|
||||
|
||||
public Map<Key, IdAllocator> getAllCachedFont() {
|
||||
Path cacheDir = CraftEngine.instance().dataFolderPath().resolve("cache").resolve("font");
|
||||
|
||||
try {
|
||||
List<Path> namespaces = FileUtils.collectNamespaces(cacheDir);
|
||||
Map<Key, IdAllocator> idAllocators = new HashMap<>();
|
||||
|
||||
for (Path namespace : namespaces) {
|
||||
processNamespace(namespace, idAllocators);
|
||||
}
|
||||
return idAllocators;
|
||||
|
||||
} catch (IOException e) {
|
||||
CraftEngine.instance().logger().warn("Failed to load cached id allocators from: " + cacheDir, e);
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
}
|
||||
|
||||
private void processNamespace(Path namespace, Map<Key, IdAllocator> idAllocators) {
|
||||
if (!Files.isDirectory(namespace)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try (Stream<Path> files = Files.list(namespace)) {
|
||||
files.filter(this::isJsonFile)
|
||||
.forEach(file -> processIdAllocatorFile(namespace.getFileName().toString(), file, idAllocators));
|
||||
|
||||
} catch (IOException e) {
|
||||
CraftEngine.instance().logger().warn("Failed to process namespace: " + namespace.getFileName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isJsonFile(Path file) {
|
||||
return Files.isRegularFile(file) && file.getFileName().toString().endsWith(".json");
|
||||
}
|
||||
|
||||
private void processIdAllocatorFile(String namespaceName, Path file, Map<Key, IdAllocator> idAllocators) {
|
||||
try {
|
||||
String fileName = FileUtils.pathWithoutExtension(file.getFileName().toString());
|
||||
|
||||
Key font = Key.of(namespaceName, fileName);
|
||||
IdAllocator allocator = new IdAllocator(file);
|
||||
allocator.loadFromCache();
|
||||
|
||||
idAllocators.put(font, allocator);
|
||||
|
||||
} catch (Exception e) {
|
||||
CraftEngine.instance().logger().warn("Failed to load id allocator from: " + file, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
package net.momirealms.craftengine.bukkit.plugin.command.feature;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature;
|
||||
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
|
||||
import net.momirealms.craftengine.core.block.ImmutableBlockState;
|
||||
@@ -7,6 +10,7 @@ import net.momirealms.craftengine.core.block.parser.BlockStateParser;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager;
|
||||
import net.momirealms.craftengine.core.plugin.command.FlagKeys;
|
||||
import net.momirealms.craftengine.core.plugin.command.sender.Sender;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.incendo.cloud.Command;
|
||||
@@ -38,7 +42,11 @@ public class DebugGetBlockInternalIdCommand extends BukkitCommandFeature<Command
|
||||
String data = context.get("id");
|
||||
ImmutableBlockState state = BlockStateParser.deserialize(data);
|
||||
if (state == null) return;
|
||||
context.sender().sendMessage(BlockStateUtils.getBlockOwnerIdFromState(state.customBlockState().literalObject()).toString());
|
||||
String id = BlockStateUtils.getBlockOwnerIdFromState(state.customBlockState().literalObject()).toString();
|
||||
Sender sender = plugin().senderFactory().wrap(context.sender());
|
||||
sender.sendMessage(Component.text(id)
|
||||
.hoverEvent(Component.text("Copy", NamedTextColor.YELLOW))
|
||||
.clickEvent(ClickEvent.suggestCommand(id)));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
package net.momirealms.craftengine.bukkit.plugin.command.feature;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature;
|
||||
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager;
|
||||
import net.momirealms.craftengine.core.plugin.command.sender.Sender;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.incendo.cloud.Command;
|
||||
import org.incendo.cloud.parser.standard.StringParser;
|
||||
|
||||
@@ -20,13 +23,15 @@ public class DebugGetBlockStateRegistryIdCommand extends BukkitCommandFeature<Co
|
||||
@Override
|
||||
public Command.Builder<? extends CommandSender> assembleCommand(org.incendo.cloud.CommandManager<CommandSender> manager, Command.Builder<CommandSender> builder) {
|
||||
return builder
|
||||
.senderType(Player.class)
|
||||
.required("state", StringParser.greedyStringParser())
|
||||
.handler(context -> {
|
||||
String state = context.get("state");
|
||||
BlockData blockData = Bukkit.createBlockData(state);
|
||||
int id = BlockStateUtils.blockDataToId(blockData);
|
||||
context.sender().sendMessage(String.valueOf(id));
|
||||
Sender sender = plugin().senderFactory().wrap(context.sender());
|
||||
sender.sendMessage(Component.text(id)
|
||||
.hoverEvent(Component.text("Copy", NamedTextColor.YELLOW))
|
||||
.clickEvent(ClickEvent.suggestCommand(String.valueOf(id))));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -5,22 +5,17 @@ import net.kyori.adventure.text.event.HoverEvent;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
|
||||
import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature;
|
||||
import net.momirealms.craftengine.core.block.BlockManager;
|
||||
import net.momirealms.craftengine.core.block.ImmutableBlockState;
|
||||
import net.momirealms.craftengine.core.pack.allocator.IdAllocator;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.incendo.cloud.Command;
|
||||
import org.incendo.cloud.context.CommandContext;
|
||||
import org.incendo.cloud.context.CommandInput;
|
||||
import org.incendo.cloud.parser.standard.StringParser;
|
||||
import org.incendo.cloud.suggestion.Suggestion;
|
||||
import org.incendo.cloud.suggestion.SuggestionProvider;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class DebugRealStateUsageCommand extends BukkitCommandFeature<CommandSender> {
|
||||
|
||||
@@ -31,34 +26,22 @@ public class DebugRealStateUsageCommand extends BukkitCommandFeature<CommandSend
|
||||
@Override
|
||||
public Command.Builder<? extends CommandSender> assembleCommand(org.incendo.cloud.CommandManager<CommandSender> manager, Command.Builder<CommandSender> builder) {
|
||||
return builder
|
||||
.required("id", StringParser.stringComponent(StringParser.StringMode.GREEDY_FLAG_YIELDING).suggestionProvider(new SuggestionProvider<>() {
|
||||
@Override
|
||||
public @NonNull CompletableFuture<? extends @NonNull Iterable<? extends @NonNull Suggestion>> suggestionsFuture(@NonNull CommandContext<Object> context, @NonNull CommandInput input) {
|
||||
return CompletableFuture.completedFuture(plugin().blockManager().blockAppearanceArranger().keySet().stream().map(it -> Suggestion.suggestion(it.toString())).toList());
|
||||
}
|
||||
}))
|
||||
.handler(context -> {
|
||||
String data = context.get("id");
|
||||
BukkitBlockManager blockManager = plugin().blockManager();
|
||||
Key baseBlockId = Key.of(data);
|
||||
List<Integer> reals = blockManager.realBlockArranger().get(baseBlockId);
|
||||
if (reals == null) return;
|
||||
int i = 0;
|
||||
Component block = Component.text(baseBlockId + ": ");
|
||||
plugin().senderFactory().wrap(context.sender()).sendMessage(block);
|
||||
|
||||
plugin().senderFactory().wrap(context.sender()).sendMessage(Component.text("Serverside block state usage:"));
|
||||
List<Component> batch = new ArrayList<>(100);
|
||||
for (int real : reals) {
|
||||
ImmutableBlockState state = blockManager.getImmutableBlockStateUnsafe(real);
|
||||
IdAllocator idAllocator = blockManager.blockParser().internalIdAllocator();
|
||||
for (int i = 0; i < Config.serverSideBlocks(); i++) {
|
||||
ImmutableBlockState state = blockManager.getImmutableBlockStateUnsafe(i + blockManager.vanillaBlockStateCount());
|
||||
if (state.isEmpty()) {
|
||||
Component hover = Component.text("craftengine:" + baseBlockId.value() + "_" + i).color(NamedTextColor.GREEN);
|
||||
Component hover = Component.text(BlockManager.createCustomBlockKey(i).asString()).color(NamedTextColor.GREEN);
|
||||
batch.add(Component.text("|").color(NamedTextColor.GREEN).hoverEvent(HoverEvent.showText(hover)));
|
||||
} else {
|
||||
Component hover = Component.text("craftengine:" + baseBlockId.value() + "_" + i).color(NamedTextColor.RED);
|
||||
NamedTextColor namedTextColor = idAllocator.isForced(state.toString()) ? NamedTextColor.RED : NamedTextColor.YELLOW;
|
||||
Component hover = Component.text(BlockManager.createCustomBlockKey(i).asString()).color(namedTextColor);
|
||||
hover = hover.append(Component.newline()).append(Component.text(state.toString()).color(NamedTextColor.GRAY));
|
||||
batch.add(Component.text("|").color(NamedTextColor.RED).hoverEvent(HoverEvent.showText(hover)));
|
||||
batch.add(Component.text("|").color(namedTextColor).hoverEvent(HoverEvent.showText(hover)));
|
||||
}
|
||||
i++;
|
||||
if (batch.size() == 100) {
|
||||
plugin().senderFactory().wrap(context.sender())
|
||||
.sendMessage(Component.text("").children(batch));
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package net.momirealms.craftengine.bukkit.plugin.command.feature;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
|
||||
import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature;
|
||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
|
||||
@@ -42,17 +44,21 @@ public class DebugTargetBlockCommand extends BukkitCommandFeature<CommandSender>
|
||||
String bData = block.getBlockData().getAsString();
|
||||
Object blockState = BlockStateUtils.blockDataToBlockState(block.getBlockData());
|
||||
Sender sender = plugin().senderFactory().wrap(context.sender());
|
||||
sender.sendMessage(Component.text(bData));
|
||||
sender.sendMessage(Component.text(bData)
|
||||
.hoverEvent(Component.text("Copy", NamedTextColor.YELLOW))
|
||||
.clickEvent(ClickEvent.suggestCommand(bData)));
|
||||
int id = BlockStateUtils.blockStateToId(blockState);
|
||||
|
||||
Object holder = BukkitBlockManager.instance().getMinecraftBlockHolder(id);
|
||||
if (holder != null) {
|
||||
if (!BlockStateUtils.isVanillaBlock(id)) {
|
||||
Object holder = BukkitBlockManager.instance().getMinecraftBlockHolder(id);
|
||||
ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(id);
|
||||
if (immutableBlockState != null) {
|
||||
sender.sendMessage(Component.text(immutableBlockState.toString()));
|
||||
String bState = immutableBlockState.toString();
|
||||
sender.sendMessage(Component.text(bState)
|
||||
.hoverEvent(Component.text("Copy", NamedTextColor.YELLOW))
|
||||
.clickEvent(ClickEvent.suggestCommand(bState)));
|
||||
}
|
||||
ImmutableBlockState dataInCache = plugin().worldManager().getWorld(block.getWorld().getUID()).getBlockStateAtIfLoaded(LocationUtils.toBlockPos(block.getLocation()));
|
||||
sender.sendMessage(Component.text("cache-state: " + !dataInCache.isEmpty()));
|
||||
sender.sendMessage(Component.text("cache-state: " + (dataInCache != null && !dataInCache.isEmpty())));
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
Set<Object> tags = (Set<Object>) CoreReflections.field$Holder$Reference$tags.get(holder);
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
package net.momirealms.craftengine.bukkit.plugin.command.feature;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.momirealms.craftengine.bukkit.api.BukkitAdaptors;
|
||||
import net.momirealms.craftengine.bukkit.api.CraftEngineItems;
|
||||
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
|
||||
import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature;
|
||||
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
|
||||
import net.momirealms.craftengine.bukkit.util.PlayerUtils;
|
||||
import net.momirealms.craftengine.core.item.CustomItem;
|
||||
import net.momirealms.craftengine.core.item.Item;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager;
|
||||
import net.momirealms.craftengine.core.plugin.command.FlagKeys;
|
||||
@@ -37,18 +40,16 @@ public class GetItemCommand extends BukkitCommandFeature<CommandSender> {
|
||||
return builder
|
||||
.senderType(Player.class)
|
||||
.flag(FlagKeys.SILENT_FLAG)
|
||||
.flag(FlagKeys.TO_INVENTORY_FLAG)
|
||||
.required("id", NamespacedKeyParser.namespacedKeyComponent().suggestionProvider(new SuggestionProvider<>() {
|
||||
@Override
|
||||
public @NonNull CompletableFuture<? extends @NonNull Iterable<? extends @NonNull Suggestion>> suggestionsFuture(@NonNull CommandContext<Object> context, @NonNull CommandInput input) {
|
||||
return CompletableFuture.completedFuture(plugin().itemManager().cachedSuggestions());
|
||||
return CompletableFuture.completedFuture(plugin().itemManager().cachedCustomItemSuggestions());
|
||||
}
|
||||
}))
|
||||
.optional("amount", IntegerParser.integerParser(1, 6400))
|
||||
.optional("amount", IntegerParser.integerParser(1, 9999))
|
||||
.handler(context -> {
|
||||
Player player = context.sender();
|
||||
int amount = context.getOrDefault("amount", 1);
|
||||
boolean toInv = context.flags().hasFlag(FlagKeys.TO_INVENTORY);
|
||||
NamespacedKey namespacedKey = context.get("id");
|
||||
Key itemId = Key.of(namespacedKey.namespace(), namespacedKey.value());
|
||||
CustomItem<ItemStack> customItem = CraftEngineItems.byId(itemId);
|
||||
@@ -61,24 +62,17 @@ public class GetItemCommand extends BukkitCommandFeature<CommandSender> {
|
||||
itemId = customItem.id();
|
||||
}
|
||||
}
|
||||
ItemStack builtItem = customItem.buildItemStack(plugin().adapt(context.sender()));
|
||||
int amountToGive = amount;
|
||||
int maxStack = builtItem.getMaxStackSize();
|
||||
while (amountToGive > 0) {
|
||||
int perStackSize = Math.min(maxStack, amountToGive);
|
||||
amountToGive -= perStackSize;
|
||||
ItemStack more = builtItem.clone();
|
||||
more.setAmount(perStackSize);
|
||||
if (toInv) {
|
||||
PlayerUtils.putItemsToInventory(player.getInventory(), more, more.getAmount());
|
||||
} else {
|
||||
PlayerUtils.dropItem(player, more, false, true, false);
|
||||
}
|
||||
BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player);
|
||||
Item<ItemStack> builtItem = customItem.buildItem(serverPlayer);
|
||||
if (builtItem != null) {
|
||||
PlayerUtils.giveItem(serverPlayer, amount, builtItem);
|
||||
}
|
||||
handleFeedback(context, MessageConstants.COMMAND_ITEM_GET_SUCCESS, Component.text(amount), Component.text(itemId.toString()));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String getFeatureID() {
|
||||
return "get_item";
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
package net.momirealms.craftengine.bukkit.plugin.command.feature;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.momirealms.craftengine.bukkit.api.BukkitAdaptors;
|
||||
import net.momirealms.craftengine.bukkit.api.CraftEngineItems;
|
||||
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
|
||||
import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature;
|
||||
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
|
||||
import net.momirealms.craftengine.bukkit.util.PlayerUtils;
|
||||
import net.momirealms.craftengine.core.item.CustomItem;
|
||||
import net.momirealms.craftengine.core.item.Item;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager;
|
||||
import net.momirealms.craftengine.core.plugin.command.FlagKeys;
|
||||
import net.momirealms.craftengine.core.plugin.locale.MessageConstants;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
@@ -40,19 +42,17 @@ public class GiveItemCommand extends BukkitCommandFeature<CommandSender> {
|
||||
public Command.Builder<? extends CommandSender> assembleCommand(org.incendo.cloud.CommandManager<CommandSender> manager, Command.Builder<CommandSender> builder) {
|
||||
return builder
|
||||
.flag(FlagKeys.SILENT_FLAG)
|
||||
.flag(FlagKeys.TO_INVENTORY_FLAG)
|
||||
.required("player", MultiplePlayerSelectorParser.multiplePlayerSelectorParser(true))
|
||||
.required("id", NamespacedKeyParser.namespacedKeyComponent().suggestionProvider(new SuggestionProvider<>() {
|
||||
@Override
|
||||
public @NonNull CompletableFuture<? extends @NonNull Iterable<? extends @NonNull Suggestion>> suggestionsFuture(@NonNull CommandContext<Object> context, @NonNull CommandInput input) {
|
||||
return CompletableFuture.completedFuture(plugin().itemManager().cachedSuggestions());
|
||||
return CompletableFuture.completedFuture(plugin().itemManager().cachedCustomItemSuggestions());
|
||||
}
|
||||
}))
|
||||
.optional("amount", IntegerParser.integerParser(1, 6400))
|
||||
.optional("amount", IntegerParser.integerParser(1, 9999))
|
||||
.handler(context -> {
|
||||
MultiplePlayerSelector selector = context.get("player");
|
||||
int amount = context.getOrDefault("amount", 1);
|
||||
boolean toInv = context.flags().hasFlag(FlagKeys.TO_INVENTORY);
|
||||
NamespacedKey namespacedKey = context.get("id");
|
||||
Key itemId = Key.of(namespacedKey.namespace(), namespacedKey.value());
|
||||
CustomItem<ItemStack> customItem = CraftEngineItems.byId(itemId);
|
||||
@@ -67,30 +67,10 @@ public class GiveItemCommand extends BukkitCommandFeature<CommandSender> {
|
||||
}
|
||||
Collection<Player> players = selector.values();
|
||||
for (Player player : players) {
|
||||
ItemStack builtItem = customItem.buildItemStack(plugin().adapt(player));
|
||||
if (builtItem == null) {
|
||||
return;
|
||||
}
|
||||
int amountToGive = amount;
|
||||
int maxStack = builtItem.getMaxStackSize();
|
||||
while (amountToGive > 0) {
|
||||
int perStackSize = Math.min(maxStack, amountToGive);
|
||||
amountToGive -= perStackSize;
|
||||
ItemStack more = builtItem.clone();
|
||||
more.setAmount(perStackSize);
|
||||
if (toInv) {
|
||||
if (VersionHelper.isFolia()) {
|
||||
player.getScheduler().run(plugin().javaPlugin(), (t) -> PlayerUtils.putItemsToInventory(player.getInventory(), more, more.getAmount()), () -> {});
|
||||
} else {
|
||||
PlayerUtils.putItemsToInventory(player.getInventory(), more, more.getAmount());
|
||||
}
|
||||
} else {
|
||||
if (VersionHelper.isFolia()) {
|
||||
player.getScheduler().run(plugin().javaPlugin(), (t) -> PlayerUtils.dropItem(player, more, false, true, false), () -> {});
|
||||
} else {
|
||||
PlayerUtils.dropItem(player, more, false, true, false);
|
||||
}
|
||||
}
|
||||
BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player);
|
||||
Item<ItemStack> builtItem = customItem.buildItem(serverPlayer);
|
||||
if (builtItem != null) {
|
||||
PlayerUtils.giveItem(serverPlayer, amount, builtItem);
|
||||
}
|
||||
}
|
||||
if (players.size() == 1) {
|
||||
|
||||
@@ -37,7 +37,7 @@ public class SearchRecipeAdminCommand extends BukkitCommandFeature<CommandSender
|
||||
.required("id", NamespacedKeyParser.namespacedKeyComponent().suggestionProvider(new SuggestionProvider<>() {
|
||||
@Override
|
||||
public @NonNull CompletableFuture<? extends @NonNull Iterable<? extends @NonNull Suggestion>> suggestionsFuture(@NonNull CommandContext<Object> context, @NonNull CommandInput input) {
|
||||
return CompletableFuture.completedFuture(plugin().itemManager().cachedSuggestions());
|
||||
return CompletableFuture.completedFuture(plugin().itemManager().cachedCustomItemSuggestions());
|
||||
}
|
||||
}))
|
||||
.handler(context -> {
|
||||
|
||||
@@ -37,7 +37,7 @@ public class SearchUsageAdminCommand extends BukkitCommandFeature<CommandSender>
|
||||
.required("id", NamespacedKeyParser.namespacedKeyComponent().suggestionProvider(new SuggestionProvider<>() {
|
||||
@Override
|
||||
public @NonNull CompletableFuture<? extends @NonNull Iterable<? extends @NonNull Suggestion>> suggestionsFuture(@NonNull CommandContext<Object> context, @NonNull CommandInput input) {
|
||||
return CompletableFuture.completedFuture(plugin().itemManager().cachedSuggestions());
|
||||
return CompletableFuture.completedFuture(plugin().itemManager().cachedCustomItemSuggestions());
|
||||
}
|
||||
}))
|
||||
.handler(context -> {
|
||||
|
||||
@@ -1,17 +1,23 @@
|
||||
package net.momirealms.craftengine.bukkit.plugin.command.feature;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.momirealms.craftengine.bukkit.api.BukkitAdaptors;
|
||||
import net.momirealms.craftengine.bukkit.item.ComponentTypes;
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature;
|
||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
|
||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MSoundEvents;
|
||||
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
|
||||
import net.momirealms.craftengine.bukkit.util.KeyUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.PlayerUtils;
|
||||
import net.momirealms.craftengine.core.item.CustomItem;
|
||||
import net.momirealms.craftengine.core.item.Item;
|
||||
import net.momirealms.craftengine.core.item.ItemBuildContext;
|
||||
import net.momirealms.craftengine.core.item.ItemKeys;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager;
|
||||
import net.momirealms.craftengine.core.plugin.command.FlagKeys;
|
||||
import net.momirealms.craftengine.core.plugin.locale.MessageConstants;
|
||||
import net.momirealms.craftengine.core.sound.SoundData;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
import org.bukkit.NamespacedKey;
|
||||
@@ -27,14 +33,17 @@ import org.incendo.cloud.bukkit.parser.selector.MultiplePlayerSelectorParser;
|
||||
import org.incendo.cloud.context.CommandContext;
|
||||
import org.incendo.cloud.context.CommandInput;
|
||||
import org.incendo.cloud.parser.flag.CommandFlag;
|
||||
import org.incendo.cloud.parser.standard.FloatParser;
|
||||
import org.incendo.cloud.suggestion.Suggestion;
|
||||
import org.incendo.cloud.suggestion.SuggestionProvider;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class TotemAnimationCommand extends BukkitCommandFeature<CommandSender> {
|
||||
public static final Object FIX_TOTEM_SOUND_PACKET = FastNMS.INSTANCE.constructor$ClientboundSoundPacket(FastNMS.INSTANCE.method$Holder$direct(MSoundEvents.TOTEM_USE), CoreReflections.instance$SoundSource$MUSIC, 0, Integer.MIN_VALUE, 0, 0, 0, 0);
|
||||
|
||||
public TotemAnimationCommand(CraftEngineCommandManager<CommandSender> commandManager, CraftEngine plugin) {
|
||||
super(commandManager, plugin);
|
||||
@@ -44,6 +53,7 @@ public class TotemAnimationCommand extends BukkitCommandFeature<CommandSender> {
|
||||
public Command.Builder<? extends CommandSender> assembleCommand(CommandManager<CommandSender> manager, Command.Builder<CommandSender> builder) {
|
||||
return builder
|
||||
.flag(FlagKeys.SILENT_FLAG)
|
||||
.flag(CommandFlag.builder("no-sound"))
|
||||
.required("players", MultiplePlayerSelectorParser.multiplePlayerSelectorParser())
|
||||
.required("id", NamespacedKeyParser.namespacedKeyComponent().suggestionProvider(new SuggestionProvider<>() {
|
||||
@Override
|
||||
@@ -51,8 +61,16 @@ public class TotemAnimationCommand extends BukkitCommandFeature<CommandSender> {
|
||||
return CompletableFuture.completedFuture(plugin().itemManager().cachedTotemSuggestions());
|
||||
}
|
||||
}))
|
||||
.flag(CommandFlag.builder("sound_event").withComponent(NamespacedKeyParser.namespacedKeyParser()).build())
|
||||
.flag(CommandFlag.builder("sound_location").withComponent(NamespacedKeyParser.namespacedKeyParser()).build())
|
||||
.optional("sound", NamespacedKeyParser.namespacedKeyComponent().suggestionProvider(new SuggestionProvider<>() {
|
||||
@Override
|
||||
public @NonNull CompletableFuture<? extends @NonNull Iterable<? extends @NonNull Suggestion>> suggestionsFuture(@NonNull CommandContext<Object> context, @NonNull CommandInput input) {
|
||||
return CompletableFuture.completedFuture(plugin().soundManager().cachedSoundSuggestions());
|
||||
}
|
||||
}))
|
||||
.optional("volume", FloatParser.floatParser(0f))
|
||||
.optional("pitch", FloatParser.floatParser(0f, 2f))
|
||||
.optional("min-volume", FloatParser.floatParser(0f))
|
||||
.optional("min-pitch", FloatParser.floatParser(0f, 2f))
|
||||
.handler(context -> {
|
||||
NamespacedKey namespacedKey = context.get("id");
|
||||
Key key = Key.of(namespacedKey.namespace(), namespacedKey.value());
|
||||
@@ -61,28 +79,31 @@ public class TotemAnimationCommand extends BukkitCommandFeature<CommandSender> {
|
||||
handleFeedback(context, MessageConstants.COMMAND_TOTEM_NOT_TOTEM, Component.text(key.toString()));
|
||||
return;
|
||||
}
|
||||
Item<ItemStack> item = customItem.buildItem(ItemBuildContext.empty());
|
||||
if (VersionHelper.isOrAbove1_21_2()) {
|
||||
if (context.flags().contains("sound_location")) {
|
||||
String soundResourceLocation = context.flags().getValue("sound_location").get().toString();
|
||||
if (soundResourceLocation != null) {
|
||||
item.setComponent(ComponentTypes.DEATH_PROTECTION, Map.of("death_effects", List.of(Map.of("type", "play_sound", "sound", Map.of(
|
||||
"sound_id", soundResourceLocation
|
||||
)))));
|
||||
}
|
||||
} else if (context.flags().contains("sound_event")) {
|
||||
String soundEvent = context.flags().getValue("sound_event").get().toString();
|
||||
if (soundEvent != null) {
|
||||
item.setComponent(ComponentTypes.DEATH_PROTECTION, Map.of("death_effects", List.of(Map.of("type", "play_sound", "sound", soundEvent))));
|
||||
}
|
||||
} else {
|
||||
item.setComponent(ComponentTypes.DEATH_PROTECTION, Map.of());
|
||||
}
|
||||
Optional<NamespacedKey> soundKey = context.optional("sound");
|
||||
SoundData soundData = null;
|
||||
if (soundKey.isPresent()) {
|
||||
float volume = context.getOrDefault("volume", 1.0f);
|
||||
float pitch = context.getOrDefault("pitch", 1.0f);
|
||||
float minVolume = context.getOrDefault("min-volume", 1.0f);
|
||||
float minPitch = context.getOrDefault("min-pitch", 1.0f);
|
||||
soundData = SoundData.of(KeyUtils.namespacedKey2Key(soundKey.get()), SoundData.SoundValue.ranged(minVolume, volume), SoundData.SoundValue.ranged(minPitch, pitch));
|
||||
}
|
||||
ItemStack totemItem = item.getItem();
|
||||
boolean removeSound = context.flags().hasFlag("no-sound");
|
||||
MultiplePlayerSelector selector = context.get("players");
|
||||
for (Player player : selector.values()) {
|
||||
PlayerUtils.sendTotemAnimation(player, totemItem);
|
||||
Collection<Player> players = selector.values();
|
||||
for (Player player : players) {
|
||||
BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player);
|
||||
Item<ItemStack> item = customItem.buildItem(serverPlayer);
|
||||
if (VersionHelper.isOrAbove1_21_2()) {
|
||||
item.setJavaComponent(ComponentTypes.DEATH_PROTECTION, Map.of());
|
||||
}
|
||||
PlayerUtils.sendTotemAnimation(serverPlayer, item, soundData, removeSound);
|
||||
}
|
||||
|
||||
if (players.size() == 1) {
|
||||
handleFeedback(context, MessageConstants.COMMAND_TOTEM_SUCCESS_SINGLE, Component.text(namespacedKey.toString()), Component.text(players.iterator().next().getName()));
|
||||
} else if (players.size() > 1) {
|
||||
handleFeedback(context, MessageConstants.COMMAND_TOTEM_SUCCESS_MULTIPLE, Component.text(namespacedKey.toString()), Component.text(players.size()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ import net.momirealms.craftengine.bukkit.util.InventoryUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.LegacyInventoryUtils;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.gui.*;
|
||||
import net.momirealms.craftengine.core.util.ReflectionUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
@@ -23,11 +22,9 @@ import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.event.inventory.InventoryCloseEvent;
|
||||
import org.bukkit.event.inventory.InventoryDragEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.bukkit.inventory.InventoryView;
|
||||
import org.bukkit.inventory.MenuType;
|
||||
|
||||
public class BukkitGuiManager implements GuiManager, Listener {
|
||||
private static final boolean useNewOpenInventory = ReflectionUtils.getDeclaredMethod(InventoryView.class, void.class, new String[]{"open"}) != null;
|
||||
// private static final boolean useNewOpenInventory = ReflectionUtils.getDeclaredMethod(InventoryView.class, void.class, new String[]{"open"}) != null;
|
||||
private static BukkitGuiManager instance;
|
||||
private final BukkitCraftEngine plugin;
|
||||
|
||||
@@ -46,21 +43,21 @@ public class BukkitGuiManager implements GuiManager, Listener {
|
||||
HandlerList.unregisterAll(this);
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
// @SuppressWarnings("UnstableApiUsage")
|
||||
@Override
|
||||
public void openInventory(net.momirealms.craftengine.core.entity.player.Player player, GuiType guiType) {
|
||||
Player bukkitPlayer = (Player) player.platformPlayer();
|
||||
if (useNewOpenInventory) {
|
||||
switch (guiType) {
|
||||
case ANVIL -> MenuType.ANVIL.create(bukkitPlayer).open();
|
||||
case LOOM -> MenuType.LOOM.create(bukkitPlayer).open();
|
||||
case ENCHANTMENT -> MenuType.ENCHANTMENT.create(bukkitPlayer).open();
|
||||
case CRAFTING -> MenuType.CRAFTER_3X3.create(bukkitPlayer).open();
|
||||
case CARTOGRAPHY -> MenuType.CARTOGRAPHY_TABLE.create(bukkitPlayer).open();
|
||||
case SMITHING -> MenuType.SMITHING.create(bukkitPlayer).open();
|
||||
case GRINDSTONE -> MenuType.GRINDSTONE.create(bukkitPlayer).open();
|
||||
}
|
||||
} else {
|
||||
// if (useNewOpenInventory) {
|
||||
// switch (guiType) {
|
||||
// case ANVIL -> MenuType.ANVIL.create(bukkitPlayer).open();
|
||||
// case LOOM -> MenuType.LOOM.create(bukkitPlayer).open();
|
||||
// case ENCHANTMENT -> MenuType.ENCHANTMENT.create(bukkitPlayer).open();
|
||||
// case CRAFTING -> MenuType.CRAFTING.create(bukkitPlayer).open();
|
||||
// case CARTOGRAPHY -> MenuType.CARTOGRAPHY_TABLE.create(bukkitPlayer).open();
|
||||
// case SMITHING -> MenuType.SMITHING.create(bukkitPlayer).open();
|
||||
// case GRINDSTONE -> MenuType.GRINDSTONE.create(bukkitPlayer).open();
|
||||
// }
|
||||
// } else {
|
||||
switch (guiType) {
|
||||
case ANVIL -> LegacyInventoryUtils.openAnvil(bukkitPlayer);
|
||||
case LOOM -> LegacyInventoryUtils.openLoom(bukkitPlayer);
|
||||
@@ -70,7 +67,7 @@ public class BukkitGuiManager implements GuiManager, Listener {
|
||||
case ENCHANTMENT -> LegacyInventoryUtils.openEnchanting(bukkitPlayer);
|
||||
case CARTOGRAPHY -> LegacyInventoryUtils.openCartographyTable(bukkitPlayer);
|
||||
}
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -17,6 +17,8 @@ import net.momirealms.craftengine.bukkit.block.BukkitBlockShape;
|
||||
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.MRegistries;
|
||||
import net.momirealms.craftengine.bukkit.util.KeyUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.NoteBlockChainUpdateUtils;
|
||||
import net.momirealms.craftengine.core.block.BlockBehavior;
|
||||
import net.momirealms.craftengine.core.block.BlockKeys;
|
||||
@@ -167,6 +169,12 @@ public final class BlockGenerator {
|
||||
// updateEntityMovementAfterFallOn
|
||||
.method(ElementMatchers.is(CoreReflections.method$Block$updateEntityMovementAfterFallOn))
|
||||
.intercept(MethodDelegation.to(UpdateEntityMovementAfterFallOnInterceptor.INSTANCE))
|
||||
// stepOn
|
||||
.method(ElementMatchers.is(CoreReflections.method$Block$stepOn))
|
||||
.intercept(MethodDelegation.to(StepOnInterceptor.INSTANCE))
|
||||
// onProjectileHit
|
||||
.method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$onProjectileHit))
|
||||
.intercept(MethodDelegation.to(OnProjectileHitInterceptor.INSTANCE))
|
||||
;
|
||||
// 1.21.5+
|
||||
if (CoreReflections.method$BlockBehaviour$affectNeighborsAfterRemoval != null) {
|
||||
@@ -193,6 +201,30 @@ public final class BlockGenerator {
|
||||
field$CraftEngineBlock$isTripwire = clazz$CraftEngineBlock.getField("isClientSideTripwire");
|
||||
}
|
||||
|
||||
public static DelegatingBlock generateBlock(Key blockId) throws Throwable {
|
||||
ObjectHolder<BlockBehavior> behaviorHolder = new ObjectHolder<>(EmptyBlockBehavior.INSTANCE);
|
||||
ObjectHolder<BlockShape> shapeHolder = new ObjectHolder<>(STONE_SHAPE);
|
||||
Object newBlockInstance = constructor$CraftEngineBlock.invoke(createEmptyBlockProperties(blockId));
|
||||
field$CraftEngineBlock$behavior.set(newBlockInstance, behaviorHolder);
|
||||
field$CraftEngineBlock$shape.set(newBlockInstance, shapeHolder);
|
||||
Object stateDefinitionBuilder = CoreReflections.constructor$StateDefinition$Builder.newInstance(newBlockInstance);
|
||||
Object stateDefinition = CoreReflections.method$StateDefinition$Builder$create.invoke(stateDefinitionBuilder,
|
||||
(Function<Object, Object>) FastNMS.INSTANCE::method$Block$defaultState, BlockStateGenerator.instance$StateDefinition$Factory);
|
||||
CoreReflections.field$Block$StateDefinition.set(newBlockInstance, stateDefinition);
|
||||
CoreReflections.field$Block$defaultBlockState.set(newBlockInstance, ((ImmutableList<?>) CoreReflections.field$StateDefinition$states.get(stateDefinition)).getFirst());
|
||||
return (DelegatingBlock) newBlockInstance;
|
||||
}
|
||||
|
||||
private static Object createEmptyBlockProperties(Key id) throws ReflectiveOperationException {
|
||||
Object blockProperties = CoreReflections.method$BlockBehaviour$Properties$of.invoke(null);
|
||||
Object resourceLocation = KeyUtils.toResourceLocation(id);
|
||||
Object resourceKey = FastNMS.INSTANCE.method$ResourceKey$create(MRegistries.BLOCK, resourceLocation);
|
||||
if (CoreReflections.field$BlockBehaviour$Properties$id != null) {
|
||||
CoreReflections.field$BlockBehaviour$Properties$id.set(blockProperties, resourceKey);
|
||||
}
|
||||
return blockProperties;
|
||||
}
|
||||
|
||||
public static Object generateBlock(Key replacedBlock, Object ownerBlock, Object properties) throws Throwable {
|
||||
Object ownerProperties = CoreReflections.field$BlockBehaviour$properties.get(ownerBlock);
|
||||
CoreReflections.field$BlockBehaviour$Properties$hasCollision.set(properties, CoreReflections.field$BlockBehaviour$Properties$hasCollision.get(ownerProperties));
|
||||
@@ -708,6 +740,20 @@ public final class BlockGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
public static class StepOnInterceptor {
|
||||
public static final StepOnInterceptor INSTANCE = new StepOnInterceptor();
|
||||
|
||||
@RuntimeType
|
||||
public void intercept(@This Object thisObj, @AllArguments Object[] args, @SuperCall Callable<Object> superMethod) {
|
||||
ObjectHolder<BlockBehavior> holder = ((DelegatingBlock) thisObj).behaviorDelegate();
|
||||
try {
|
||||
holder.value().stepOn(thisObj, args, superMethod);
|
||||
} catch (Exception e) {
|
||||
CraftEngine.instance().logger().severe("Failed to run stepOn", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class FallOnInterceptor {
|
||||
public static final FallOnInterceptor INSTANCE = new FallOnInterceptor();
|
||||
|
||||
@@ -743,4 +789,18 @@ public final class BlockGenerator {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class OnProjectileHitInterceptor {
|
||||
public static final OnProjectileHitInterceptor INSTANCE = new OnProjectileHitInterceptor();
|
||||
|
||||
@RuntimeType
|
||||
public void intercept(@This Object thisObj, @AllArguments Object[] args, @SuperCall Callable<Object> superMethod) {
|
||||
ObjectHolder<BlockBehavior> holder = ((DelegatingBlock) thisObj).behaviorDelegate();
|
||||
try {
|
||||
holder.value().onProjectileHit(thisObj, args, superMethod);
|
||||
} catch (Exception e) {
|
||||
CraftEngine.instance().logger().severe("Failed to run onProjectileHit", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -64,15 +64,17 @@ public final class BlockStateGenerator {
|
||||
.method(ElementMatchers.is(CoreReflections.method$StateHolder$getValue))
|
||||
.intercept(MethodDelegation.to(GetPropertyValueInterceptor.INSTANCE))
|
||||
.method(ElementMatchers.is(CoreReflections.method$StateHolder$setValue))
|
||||
.intercept(MethodDelegation.to(SetPropertyValueInterceptor.INSTANCE));
|
||||
.intercept(MethodDelegation.to(SetPropertyValueInterceptor.INSTANCE))
|
||||
.method(ElementMatchers.is(CoreReflections.method$BlockStateBase$isBlock))
|
||||
.intercept(MethodDelegation.to(IsBlockInterceptor.INSTANCE));
|
||||
Class<?> clazz$CraftEngineBlock = stateBuilder.make().load(BlockStateGenerator.class.getClassLoader()).getLoaded();
|
||||
constructor$CraftEngineBlockState = VersionHelper.isOrAbove1_20_5() ?
|
||||
MethodHandles.publicLookup().in(clazz$CraftEngineBlock)
|
||||
.findConstructor(clazz$CraftEngineBlock, MethodType.methodType(void.class, CoreReflections.clazz$Block, Reference2ObjectArrayMap.class, MapCodec.class))
|
||||
.asType(MethodType.methodType(CoreReflections.clazz$BlockState, CoreReflections.clazz$Block, Reference2ObjectArrayMap.class, MapCodec.class)) :
|
||||
.findConstructor(clazz$CraftEngineBlock, MethodType.methodType(void.class, CoreReflections.clazz$Block, Reference2ObjectArrayMap.class, MapCodec.class))
|
||||
.asType(MethodType.methodType(CoreReflections.clazz$BlockState, CoreReflections.clazz$Block, Reference2ObjectArrayMap.class, MapCodec.class)) :
|
||||
MethodHandles.publicLookup().in(clazz$CraftEngineBlock)
|
||||
.findConstructor(clazz$CraftEngineBlock, MethodType.methodType(void.class, CoreReflections.clazz$Block, ImmutableMap.class, MapCodec.class))
|
||||
.asType(MethodType.methodType(CoreReflections.clazz$BlockState, CoreReflections.clazz$Block, ImmutableMap.class, MapCodec.class));
|
||||
.findConstructor(clazz$CraftEngineBlock, MethodType.methodType(void.class, CoreReflections.clazz$Block, ImmutableMap.class, MapCodec.class))
|
||||
.asType(MethodType.methodType(CoreReflections.clazz$BlockState, CoreReflections.clazz$Block, ImmutableMap.class, MapCodec.class));
|
||||
|
||||
String generatedFactoryClassName = packageWithName.substring(0, packageWithName.lastIndexOf('.')) + ".CraftEngineStateFactory";
|
||||
DynamicType.Builder<?> factoryBuilder = byteBuddy
|
||||
@@ -183,6 +185,23 @@ public final class BlockStateGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
public static class IsBlockInterceptor {
|
||||
public static final IsBlockInterceptor INSTANCE = new IsBlockInterceptor();
|
||||
|
||||
@RuntimeType
|
||||
public boolean intercept(@This Object thisObj, @AllArguments Object[] args) {
|
||||
DelegatingBlockState customState = (DelegatingBlockState) thisObj;
|
||||
ImmutableBlockState thisState = customState.blockState();
|
||||
if (thisState == null) return false;
|
||||
if (FastNMS.INSTANCE.method$Block$defaultState(args[0]) instanceof DelegatingBlockState holder) {
|
||||
ImmutableBlockState holderState = holder.blockState();
|
||||
if (holderState == null) return false;
|
||||
return holderState.owner().equals(thisState.owner());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static class CreateStateInterceptor {
|
||||
public static final CreateStateInterceptor INSTANCE = new CreateStateInterceptor();
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@ import net.bytebuddy.implementation.bytecode.assign.Assigner;
|
||||
import net.bytebuddy.matcher.ElementMatchers;
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
|
||||
import net.momirealms.craftengine.bukkit.util.LocationUtils;
|
||||
import net.momirealms.craftengine.core.block.BlockStateWrapper;
|
||||
import net.momirealms.craftengine.core.block.DelegatingBlockState;
|
||||
import net.momirealms.craftengine.core.block.EmptyBlock;
|
||||
@@ -313,9 +312,7 @@ public final class WorldStorageInjector {
|
||||
@SuppressWarnings("DuplicatedCode")
|
||||
private static void updateLight(@This InjectedHolder thisObj, Object clientState, Object serverState, int x, int y, int z) {
|
||||
CEWorld world = thisObj.ceChunk().world;
|
||||
Object blockPos = LocationUtils.toBlockPos(x, y, z);
|
||||
Object serverWorld = world.world().serverWorld();
|
||||
if (FastNMS.INSTANCE.method$LightEngine$hasDifferentLightProperties(serverState, clientState, serverWorld, blockPos)) {
|
||||
if (FastNMS.INSTANCE.method$LightEngine$hasDifferentLightProperties(serverState, clientState)) {
|
||||
SectionPos sectionPos = thisObj.cePos();
|
||||
List<SectionPos> pos = SectionPosUtils.calculateAffectedRegions((sectionPos.x() << 4) + x, (sectionPos.y() << 4) + y, (sectionPos.z() << 4) + z, 15);
|
||||
world.sectionLightUpdated(pos);
|
||||
@@ -325,16 +322,14 @@ public final class WorldStorageInjector {
|
||||
@SuppressWarnings("DuplicatedCode")
|
||||
private static void updateLight$complex(@This InjectedHolder thisObj, Object newClientState, Object newServerState, Object oldServerState, int x, int y, int z) {
|
||||
CEWorld world = thisObj.ceChunk().world;
|
||||
Object blockPos = LocationUtils.toBlockPos(x, y, z);
|
||||
Object serverWorld = world.world().serverWorld();
|
||||
// 如果客户端新状态和服务端新状态光照属性不同
|
||||
if (FastNMS.INSTANCE.method$LightEngine$hasDifferentLightProperties(newClientState, newServerState, serverWorld, blockPos)) {
|
||||
if (FastNMS.INSTANCE.method$LightEngine$hasDifferentLightProperties(newClientState, newServerState)) {
|
||||
SectionPos sectionPos = thisObj.cePos();
|
||||
List<SectionPos> pos = SectionPosUtils.calculateAffectedRegions((sectionPos.x() << 4) + x, (sectionPos.y() << 4) + y, (sectionPos.z() << 4) + z, 15);
|
||||
world.sectionLightUpdated(pos);
|
||||
return;
|
||||
}
|
||||
if (FastNMS.INSTANCE.method$LightEngine$hasDifferentLightProperties(newServerState, oldServerState, serverWorld, blockPos)) {
|
||||
if (FastNMS.INSTANCE.method$LightEngine$hasDifferentLightProperties(newServerState, oldServerState)) {
|
||||
SectionPos sectionPos = thisObj.cePos();
|
||||
List<SectionPos> pos = SectionPosUtils.calculateAffectedRegions((sectionPos.x() << 4) + x, (sectionPos.y() << 4) + y, (sectionPos.z() << 4) + z, 15);
|
||||
world.sectionLightUpdated(pos);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -60,6 +60,8 @@ public interface PacketIds {
|
||||
|
||||
int clientboundUpdateAdvancementsPacket();
|
||||
|
||||
int clientBoundMerchantOffersPacket();
|
||||
|
||||
int serverboundContainerClickPacket();
|
||||
|
||||
int serverboundSetCreativeModeSlotPacket();
|
||||
|
||||
@@ -4,7 +4,7 @@ import net.kyori.adventure.text.Component;
|
||||
import net.momirealms.craftengine.bukkit.entity.data.BaseEntityData;
|
||||
import net.momirealms.craftengine.bukkit.entity.data.BlockDisplayEntityData;
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.plugin.network.PacketConsumers;
|
||||
import net.momirealms.craftengine.bukkit.plugin.network.BukkitNetworkManager;
|
||||
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.ComponentUtils;
|
||||
import net.momirealms.craftengine.core.entity.player.Player;
|
||||
@@ -36,12 +36,7 @@ public class BlockDisplayPacketHandler implements EntityPacketHandler {
|
||||
if (entityDataId == BlockDisplayEntityData.DisplayedBlock.id()) {
|
||||
Object blockState = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem);
|
||||
int stateId = BlockStateUtils.blockStateToId(blockState);
|
||||
int newStateId;
|
||||
if (!user.clientModEnabled()) {
|
||||
newStateId = PacketConsumers.remap(stateId);
|
||||
} else {
|
||||
newStateId = PacketConsumers.remapMOD(stateId);
|
||||
}
|
||||
int newStateId= BukkitNetworkManager.instance().remapBlockState(stateId, user.clientModEnabled());
|
||||
if (newStateId == stateId) continue;
|
||||
Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem);
|
||||
packedItems.set(i, FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue(
|
||||
|
||||
@@ -41,7 +41,7 @@ public class CommonItemPacketHandler implements EntityPacketHandler {
|
||||
continue;
|
||||
}
|
||||
ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(nmsItemStack);
|
||||
Optional<ItemStack> optional = BukkitItemManager.instance().s2c(itemStack, (BukkitServerPlayer) user);
|
||||
Optional<ItemStack> optional = BukkitItemManager.instance().s2c(itemStack, user);
|
||||
if (optional.isEmpty()) continue;
|
||||
isChanged = true;
|
||||
itemStack = optional.get();
|
||||
|
||||
@@ -4,7 +4,7 @@ import net.kyori.adventure.text.Component;
|
||||
import net.momirealms.craftengine.bukkit.entity.data.BaseEntityData;
|
||||
import net.momirealms.craftengine.bukkit.entity.data.EnderManData;
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.plugin.network.PacketConsumers;
|
||||
import net.momirealms.craftengine.bukkit.plugin.network.BukkitNetworkManager;
|
||||
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.ComponentUtils;
|
||||
import net.momirealms.craftengine.core.entity.player.Player;
|
||||
@@ -38,12 +38,7 @@ public class EndermanPacketHandler implements EntityPacketHandler {
|
||||
Optional<Object> blockState = (Optional<Object>) FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem);
|
||||
if (blockState.isEmpty()) continue;
|
||||
int stateId = BlockStateUtils.blockStateToId(blockState.get());
|
||||
int newStateId;
|
||||
if (!user.clientModEnabled()) {
|
||||
newStateId = PacketConsumers.remap(stateId);
|
||||
} else {
|
||||
newStateId = PacketConsumers.remapMOD(stateId);
|
||||
}
|
||||
int newStateId = BukkitNetworkManager.instance().remapBlockState(stateId, user.clientModEnabled());
|
||||
if (newStateId == stateId) continue;
|
||||
Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem);
|
||||
packedItems.set(i, FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue(
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user